Сущность технологии СОМ. Библиотека программиста
Шрифт:
Чтобы реализовать управляемый таблицей QueryInterface, необходимо сначала определить, что эта таблица будет содержать. Как минимум, каждый элемент таблицы должен содержать указатель на IID и некое дополнительное состояние, которое позволит реализации найти указатель vptr объекта для запрошенного интерфейса. Хранение указателя функции в каждом элементе таблицы придаст этому способу максимальную гибкость, так как это даст возможность добавлять новые методики поиска интерфейсов к обычному вычислению смещения, которое используется для приведения к
// inttable.h (book-specific header file)
// inttable.h (заголовочный файл, специфический для этой книги)
// typedef for extensibility function
// typedef для функции расширяемости
typedef HRESULT (*INTERFACEFINDER) (void *pThis, DWORD dwData, REFIID riid, void **ppv);
// pseudo-function to indicate entry is just offset
// псевдофункция для индикации того, что запись просто
// является смещением
#define ENTRYISOFFSET INTERFACEFINDER(-1)
// basic table layout // представление базовой таблицы
typedef struct INTERFACEENTRY
{
const IID * pIID;
// the IID to match
// соответствующий IID
INTERFACEFINDER pfnFinder;
// функция finder DWORD dwData;
// offset/aux data
// данные по offset/aux
} INTERFACEENTRY;
Заголовочный файл также содержит следующие макросы для создания интерфейсных таблиц внутри определения класса:
// Inttable.h (book-specific header file)
// 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.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 появится в исполняемой программе только один раз, это весьма неплохой компромисс.