lUnknown
{
public: virtual HRESULT _stdcall Querylnterface(REFIID riid,
void **ppvObject) = 0;
virtual ULONG _stdcall AddRef(void) = 0;
virtual ULONG _stdcall Release(void) = 0;
};
Как видите, «неизвестный» содержит три чисто виртуальные функции и ни одного элемента данных. Каждый новый интерфейс, который создает разработчик, должен иметь среди своих предков I Unknown, а следовательно, он наследует все три указанных метода. Первый метод Querylnterface представляет собой фундаментальный механизм, используемый для получения доступа к желаемой функциональности СОМ-объекта. Он позволяет получить указатель на существующий интерфейс или получить отказ, если интерфейс отсутствует. Первый — входной параметр riid — содержит уникальную ссылку на зарегистрированный идентификатор желаемого интерфейса. Это та уникальная, вечная бирка (клеймо), которую конкретный интерфейс должен носить вечно. Второй — выходной параметр — используется для записи по адресу ppvOb j ect адреса запрошенного интерфейса или нуля в случае отказа. Дважды использованное слово адрес оправдывает количество звездочек в типе void**. Тип возвращаемого значения HRESULT, обманчиво относимый к семейству handle (дескриптор), представляет собой 32-битное иоле данных, в котором кодируются признаки, рассмотренные нами в четвергом уроке.
Предположим, вы хотите получить указатель на какой-либо произвольный интерфейс 1Му, уже зарегистрированный системой и получивший уникальный идентификатор IID_IMY, с тем чтобы пользоваться предоставляемыми им методами. Тогда следует действовать по одной из общепринятых схем1:
//====== Указатель на незнакомый объект
lUnknown *pUnk;
// Иногда приходит как параметр IМу *рМу;
// Указатель на желаемый интерфейс
//====== Запрашиваем его у объекта
HRESULT hr=pUnk->Query!nterfасе(IID_IMY,(void **)&pMy);
if (FAILED(hr)) // Макрос, расшифровывающий HRESULT
{
//В случае неудачи
delete pMy; // Освобождаем память
//====== Возвращаем результат с причиной отказа