Дональд Бокс - Сущность технологии СОМ. Библиотека программиста Страница 20
- Категория: Компьютеры и Интернет / Программирование
- Автор: Дональд Бокс
- Год выпуска: -
- ISBN: -
- Издательство: -
- Страниц: 95
- Добавлено: 2019-05-29 11:55:05
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста краткое содержание
Прочтите описание перед тем, как прочитать онлайн книгу «Дональд Бокс - Сущность технологии СОМ. Библиотека программиста» бесплатно полную версию:В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста читать онлайн бесплатно
// Inttable.h (заголовочный файл, специфический для данной книги)
#define BASEOFFSET(ClassName, BaseName) \ (DWORD(staticcast<BaseName*>(reinterpretcast\ <ClassName*>(0x10000000))) – 0х10000000)
#define BEGININTERFACETABLE(ClassName) \ typedef ClassName ITCls;\ const INTERFACEENTRY *GetInterfaceTable(void) {\ static const INTERFACEENTRY table [] = {\
#define IMPLEMENTSINTERFACE(Itf) \ {&IID##Itf,ENTRYISOFFSET,BASEOFFSET(ITCls,Itf)},
#define IMPLEMENTSINTERFACEAS(req, Itf) \ {&IID##req,ENTRYISOFFSET, BASEOFFSET(ITCls, Itf)},
#define ENDINTERFACETABLE() \ { 0, 0, 0 } }; return table; }
Все, что требуется, – это стандартная функция, которая может анализировать интерфейсную таблицу в ответ на запрос QueryInterface. Такая функция содержится в файле Inttable.h:
// inttable.cpp (book-specific source file)
// inttable.h (заголовочный файл, специфический для данной книги)
HRESULT InterfaceTableQueryInterface(void *pThis, const INTERFACEENTRY *pTable, REFIID riid, void **ppv)
{
if (InlineIsEqualGUID(riid, IIDIUnknown))
{
// first entry must be an offset
// первый элемент должен быть смещением
*ppv = (char*)pThis + pTable->dwData;
((Unknown*) (*ppv))->AddRef () ;
// A2
return SOK;
} else
{
HRESULT hr = ENOINTERFACE;
while (pTable->pfnFinder)
{
// null fn ptr == EOT
if (!pTable->pIID || InlineIsEqualGUID(riid,*pTable->pIID))
{
if (pTable->pfnFinder == ENTRYISOFFSET)
{
*ppv = (char*)pThis + pTable->dwData;
((IUnknown*)(*ppv))->AddRef();
// A2
hr = SOK;
break;
} else
{
hr = pTable->pfnFinder(pThis, pTable->dwData, riid, ppv);
if (hr == SOK) break;
}
}
pTable++;
}
if (hr!= SOK)
*ppv = 0;
return hr;
}
}
Получив указатель на запрошенный объект, InterfaceTableQueryInterface сканирует таблицу в поисках элемента, соответствующего запрошенному IID, и либо добавляет соответствующее смещение, либо вызывает соответствующую функцию. Приведенный выше код использует усовершенствованную версию IsEqualGUID, которая генерирует несколько больший код, но результаты по скорости примерно на 20-30 процентов превышают данные по существующей реализации, которая не управляется таблицей. Поскольку код для InterfaceTableQueryInterface появится в исполняемой программе только один раз, это весьма неплохой компромисс.
Очень легко автоматизировать поддержку СОМ для любого класса C++, основанную на таком табличном управлении, простым использованием С-препроцессора. Следующий фрагмент из заголовочного файла impunk.h определяет QueryInterface, AddRef и Release для объекта, использующего интерфейсные таблицы и расположенного в динамически распределяемой области памяти:
// impunk.h (book-specific header file)
// impunk.h (заголовочный файл, специфический для данной книги)
// AUTOLONG is just a long that constructs to zero
// AUTOLONG – это просто long, с конструктором,
// устанавливающим значение в О
struct AUTOLONG
{
LONG value;
AUTOLONG (void) : value (0) {}
};
#define IMPLEMENTUNKNOWN(ClassName)
\ AUTOLONG mcRef;
\ STDMETHODIMP QueryInterface(REFIID riid, void **ppv){
\ return InterfaceTableQueryInterface(this,
\ GetInterfaceTable(), riid, ppv);
\ }
\ STDMETHODIMP(ULONG) AddRef(void) {
\ return InterlockedIncrement(&mcRef.value);
\ }
\ STDMETHODIMP(ULONG) Release(void) {
\ ULONG res = InterlockedDecrement(&mcRef.value) ;
\ if (res == 0)
\ delete this;
\ return res;
\ }
Настоящий заголовочный файл содержит дополнительные макросы для поддержки объектов, не находящихся в динамически распределяемой области памяти.
Для реализации примера PugCat, уже встречавшегося в этой главе, необходимо всего лишь удалить текущие реализации QueryInterface, AddRef и Release и добавить соответствующие макросы:
class PugCat : public IPug, public ICat
{
protected:
virtual ~PugCat(void);
public: PugCat(void);
// IUnknown methods
// методы IUnknown
IMPLEMENTUNKNOWN (PugCat)
BEGININTERFACETABLE(PugCat)
IMPLEMENTSINTERFACE(IPug)
IMPLEMENTSINTERFACE(IDog)
IMPLEMENTSINTERFACEAS(IAnimal,IDog)
IMPLEMENTSINTERFACE(ICat)
ENDINTERFACETABLE()
// IAnimal methods
// методы IAnimal
STDMETHODIMP Eat(void);
// IDog methods
// методы IDog
STDMETHODIMP Bark(void);
// IPug methods
// методы IPug
STDMETHODIMP Snore(void);
// ICat methods
// методы ICat
STDMETHODIMP IgnoreMaster(void);
};
Когда используются эти макросы препроцессора, для поддержки IUnknown не требуется никакого дополнительного кода. Все, что осталось сделать, это реализовать текущие методы интерфейса, которые делают этот класс уникальным.
Типы данных
Все интерфейсы СОМ должны быть определены в IDL. IDL позволяет описывать довольно сложные типы данных в стиле, не зависящем от языка и платформы. Рисунок 2.6 показывает базовые типы, которые поддерживаются IDL, и их отображения в языки С, Java и Visual Basic. Целые и вещественные типы не требуют объяснений. Первые «интересные» типы данных, встречающиеся при программировании в СОМ, – это символы и строки.
Все символы в СОМ представлены с использованием типа данных OLECHAR. Для Windows NT, Windows 95, Win32s и Solaris OLECHAR – это просто typedef для типа данных С wchar_t. Специфика других платформ описана в соответствующих документациях. Платформы Win32 используют тип данных wchar_t для представления 16-битных символов Unicode[1]. Поскольку типы указателей в IDL созданы так, что указывают на одиночные переменные, а не на массивы, то IDL вводит атрибут [string], чтобы подчеркнуть, что указатель указывает на массив-строку с завершающим нулем:
HRESULT Method([in, string] const OLECHAR *pwsz);
Для определения строк и символов, совместимых с OLECHAR, в СОМ введен макрос OLESTR, который приписывает букву L перед строковой или символьной константой, информируя таким образом компилятор о том, что эта константа имеет тип wchar_t. Например, правильным будет такой способ инициализировать указатель OLECHAR с помощью строкового литерала:
const OLECHAR *pwsz = OLESTR(«Hello»);
Под Win32 или Solaris это эквивалентно
const wchar_t *pwsz = L"Hello";
Первый вариант предпочтительней, так как он будет надежно компилироваться на всех платформах.
Поскольку часто возникает необходимость копировать строки на основе типа wchar_t в обычные буфера на основе char, то динамическая библиотека С предлагает две процедуры для преобразования типов:
size_t mbstowcs(wchar_t *pwsz, const char *psz, int cch);
size_t wcstombs(char *psz, const wchar_t *pwsz, int cch);
Эти две процедуры работают аналогично динамической С-процедуре strncpy, за исключением того, что в эти процедуры как часть операции копирования включено расширение или сужение строки. Следующий код показывает, как параметр метода, размещенный в OLECHAR, можно скопировать в элемент данных, размещенный в char:
class BigDog : public ILabrador
{
char m_szName[1024] ;
public:
STDMETHODIMP SetName(/* [in,string]*/ const OLECHAR *pwsz)
{
HRESULT hr = S_OK;
size_t cb = wcstombs(m_szName, pwsz, 1024);
// check for buffer overflow or bad conversion
// проверяем переполнение буфера или неверное преобразование
if (cb == sizeof(m_szName) || cb == (size_t)-1)
{
m_szName[0] = 0; hr = E_INVALIDARG;
}
return hr;
}
};
Этот код является довольно простым, хотя программист должен осознавать, что используются два различных типа символов. Несколько более сложный (и чаще встречающийся) случай – преобразование между типами данных OLECHAR и TCHAR из Win32. Так как OLECHAR условно компилируется как char или wchar_t, то при реализации метода необходимо должным образом рассмотреть оба сценария:
class BigDog : public ILabrador
{
TCHAR m_szName[1024];
// note TCHAR-based string
// отметим строку типа TCHAR
public:
STDMETHODIMP SetName( /*[in,string]*/ const OLECHAR *pwsz)
{
HRESULT hr = S_OK;
#ifdef UNICODE
// Unicode build (TCHAR == wchar_t)
// конструкция Unicode (TCHAR == wchar_t)
wcsncpy(m_szName, pwsz, 1024);
// check for buffer overflow
// проверка на переполнение буфера
if (m_szName[1023] != 0)
{
m_szName[0] = 0;
hr = E_INVALIDARG;
}
#else
// Non-Unicode build (TCHAR == char)
// не является конструкцией Unicode (TCHAR == char)
size_t cb = wcstombs(m_szName, pwsz, 1024);
// check for buffer overflow or bad conversion
// проверка переполнения буфера или ошибки преобразования
if (cb == sizeof(m_szName) || cb == (size_t)-1)
{
m_szName[0] =0;
hr = E_INVALIDARG;
}
#endif return hr;
}
};
Очевидно, операции с преобразованиями OLECHAR в TCHAR значительно сложнее. Но, к сожалению, это самый распространенный сценарий при программировании в СОМ на базе Win32.
Жалоба
Напишите нам, и мы в срочном порядке примем меры.