Изменить стиль страницы

[iid_is(piid)] IUnknown *pItf;

// will contain the HRESULT from QueryInterface on output

// на выходе будет содержать HRESULT от QueryInterface

HRESULT hr;

}

MULTI_QI;

Так как клиент может запрашивать более чем один указатель интерфейса на новый объект, он более не нуждается в явном вызове QueryInterface, если ему требуется более чем один тип указателей интерфейса. Поскольку эти вызовы QueryInterface будут сделаны СОМ от имени клиента по время выполнения внутри процесса объекта класса, более не потребуется никаких обменов данными между клиентом и объектом. Отметим, что каждый из указателей интерфейса, возвращенных CoCreateInstanceEx , будет указывать на один и тот же объект. СОМ не поддерживает внутреннего режима создания нескольких экземпляров за одно обращение.

После постижения структуры MULTI_QI понять определение CoCreateInstanceEx уже нетрудно:

HRESULT CoCreateInstanceEx( [in] REFCLSID rclsid,

// what kind of object? – какого сорта объект?

[in] IUnknown *pUnkOuter,

// for aggregation – для агрегирования

[in] DWORD dwClsCtx,

// locality? – размещение?

[in] COSERVERINFO *pcsi,

// host/security info – информация о хосте/безопасности

[in] ULONG cmqi,

// how many interfaces? – сколько интерфейсов?

[out, size_is (cmqi)] MULTI_QI *prgmq

// where to put itfs – куда разместить интерфейсы );

Если все запрошенные интерфейсы доступны в новом объекте, то CoCreateInstanceEx возвращает S_OK. Если хотя бы один (но не все) из запрошенных интерфейсов недоступен, то CoCreateInstanceEx возвратит CO_S_NOTALLINTERFACES, индицируя частичный успех. Затем вызывающий объект должен исследовать индивидуальные HRESULT в каждой структуре MULTI_QI, чтобы определить, какие интерфейсы были доступны, а какие – нет. Если CoCreateInstanceEx не может создать объект или ни один из запрошенных интерфейсов не доступен, то CoCreateInstanceEx возвращает HRESULT с кодом серьезности ошибки SEVERITY_ERROR, который сообщит, почему произошел отказ в операции.

CoCreateInstanceEx чрезвычайно эффективен в случае, когда требуются интерфейсы нескольких типов. Рассмотрим дополнительное определение интерфейса:

[object,uuid(753A8F7C-A7FF-11d0-8C30-0080C73925BA)]

interface IEgghead : IUnknown

{

import «unknwn.idl»;

HRESULT ContemplateNavel(void);

}

Имея такое определение интерфейса, клиент может привязать оба типа указателей к новому шимпанзе за одно обращение:

void CreateChimpEatBananaAndThinkAboutIt(void)

{

// declare and initialize an array of MULTI_QI's

// объявляем и инициализируем массив MULTI_QI

MULTI_QI rgmqi[2] = { { &IID_IApe, 0, 0 }, { &IID_IEgghead, 0, 0 } };

HRESULT hr = CoCreateInstanceEx( CLSID_Chimp,

// make a new chimp – создаем нового шимпанзе

0,

// no aggregation – без агрегирования

CLSCTX_ALL,

// any locality – размещение любое

0,

// no explicit host/security info

// нет явной информации о хосте и безопасности

2,

// asking for two interfaces – запрашиваем 2 интерфейса

rgmqi);

// array of MULTI_QI structs – массив структур

MULTI_QI

if (SUCCEEDED(hr)) {

// hr may be CO_S_NOTALLINTERFACES, so check each result

// hresult может быть равен CO_S_NOTALLINTERFACES,

// поэтому проверяем каждый результат

if (hr == S_OK || SUCCEEDED(rgmqi[0].hr)) {

// it is safe to blindly cast the resultant ptr to the type

// that corresponds to the IID used to request the interface

// безопасно вслепую преобразовать результирующий

// указатель к типу, соответствующему тому IID,

// который использовался при запросе интерфейса

IАре *рАре = reinterpret_cast(rgmqi[0].pItf);

assert(pApe);

HRESULT hr2 = pApe->EatBanana();

assert(SUCCEEDED(hr2));

pApe->Release();

}

if(hr == S_OK || SUCCEEDED(rgmqi[1].hr)) {

IEgghead *peh = reinterpret_cast(rgmqi[1].pItf);

assert(peh);

HRESULT hr2 = peh->ContemplateNavel();

assert(SUCCEEDED(hr2));

peh->Release();

}

} }

Сущность технологии СОМ. Библиотека программиста fig3_3.jpg

Рисунок 3.3 показывает шаги, которые предпринимаются CoCreateInstanceEx для создания нового объекта. Важно отметить, что оба результирующих указателя будут указывать на один и тот же объект. Если нужны два различных объекта, то требуются и два отдельных вызова CoCreateInstanceEx.

Использование СоСrеateInstanceЕх достаточно просто, если нужен только один интерфейс:

HRESULT CreateChimpAndEatBanana(void)

{

// declare and Initialize a MULTI_QI

// определяем и инициализируем MULTI_QI

MULTI_QI mqi = { &IID_IApe, 0, 0 };

HRESULT hr = CoCreateInstanceEx( CLSID_Chimp,

// make a new chimp – создаем нового шимпанзе

0,

// по aggregation – без агрегирования CLSCTX_ALL,

// any locality – любое расположение

0,

// no explicit host/security Info

// нет явной информации о хосте/безопасности

1,

// asking for one interface – запрашиваем один интерфейс

&mqi);

// array of MULTI_QI structs – массив структур

MULTI_QI

if (SUCCEEDED(hr))

{

IApe *pApe = reinterpret_cast(mqi.pItf);

assert(pApe);

// use the new object – используем новый объект

hr = pApe->EatBanana();

// release the Interface pointer

// освобождаем указатель интерфейса

pApe->Release();

}

return hr;

}

Если нужен только один интерфейс и не передается COSERVERINFO, то СОМ предусматривает несколько более удобную версию CoCreateInstanceEx, именуемую CoCreateInstance[1]:

HRESULT CoCreateInstance( [in] REFCLSID rclsid,

// what kind of object? – какой тип объекта?

[in] IUnknown *pUnkOuter,

// for aggregation – для агрегирования

[in] DWORD dwClsCtx,

// locality? – размещение?

[in] REFIID riid,

// what kind of interface – какой вид интерфейса

[out, iid_is(riid)] void **ppv);

// where to put itf – куда разместить интерфейс

Предыдущий пример становится намного проще, если применить CoCreateInstance

HRESULT CreateChimpAndEatBanana(void)

{

IАре *рАре = 0;

HRESULT hr = CoCreateInstance( CLSID_Chimp,

// make a new chimp – создаем нового шимпанзе

0,

// по aggregation – без агрегирования

CLSCTX_ALL,

// any locality – любое расположение

IID_IApe,

// what kind of itf – какой вид интерфейса

(void**)&pApe);

// where to put iff – куда поместить интерфейс

if (SUCCEEDED(hr)) {

assert(pApe);

// use the new object используем новый объект

hr = pApe->EatBanana();

// release the interface pointer

// освобождаем указатель интерфейса

pApe->Release();

}

return hr;

}

Оба предыдущих примера функционально эквивалентны. В сущности, реализация CoCreateInstance просто осуществляет внутренний вызов CoCreateInstanceEx:

// pseudo-code for implementation of CoCreateInstance API

// псевдокод для реализации API-функции

вернуться

1 Формально CoCreateInstance возникла первой. CoCreateInstanceEx была добавлена в Windows NT 4.0, когда стало ясно, что некоторые разработчики хотели бы передавать информацию о безопасности и хосте API-функциям активации модели СОМ. В исходном прототипе для CoGetClassObject третий параметр был резервным, и NT 4.0 смог заимствовать этот резервный параметр для COSERVERINFO. К сожалению, в CoCreateInstance не было неиспользуемых параметров, поэтому была создана CoCreateInstanceEx . Можно поспорить, была бы ли также полезной версия CoGetClassObject, использующая MULTI_QI для связывания с более чем одним интерфейсом, но увы – на момент написания книги никакой CoGetClassObjectEx не существует. Тот же аргумент мог бы быть применен и по отношению к IMoniker::BindToObject и MULTI_QI.