CoCreateInstanceEx дает клиенту возможность задать CLSID для определения, какой объект следует реализовать. В случае успешного выполнения СоСгеаteInstanceEx возвращает один или более указателей интерфейса, которые ссылаются на новый экземпляр заданного класса. При использовании CoCreateInstanceEx клиент никогда не видит промежуточный объект класса, который используется для создания экземпляра объекта. Чтобы клиенты могли вызывать CoCreateInstanceEx , никаких дополнительных функций серверу реализовывать не нужно. С точки зрения сервера все, что необходимо, – это объявить свои объекты классов, как требуется для CoGetClassObject . Реализация CoCreateInstanceEx для нахождения соответствующего объекта класса будет использовать ту же технологию, что и CoGetClassObject . Основное различие заключается в том, что после нахождения объекта CoCreateInstanceEx выполняет дополнительный вызов IClassFactory::CreateInstance , за которым могут последовать один или более вызовов QueryInterface , и все это во время выполнения процесса объекта класса . Можно получить значительную экономию на этапе выполнения, если запрос на активацию закрепить за определенным процессом.
Подобно CoGetClassObject , CoCreateInstanceEx позволяет клиенту задавать желаемые параметры CLSCTX и COSERVERINFO . Кроме того, CoCreateInstanceEx дает клиенту возможность запрашивать более одного указателя интерфейса на вновь создаваемый объект. Это делается путем предоставления клиенту возможности передавать массив структур MULTI_QI , который будет использован для вызова QueryInterface , новому экземпляру во время выполнения серверного процесса:
typedef struct tagMULTI_QI {
// which interface is desired?
// какой интерфейс требуется?
const IID *piid;
// null on input, will contain the pointer on output
// на входе нуль, на выходе будет содержать указатель
[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);
Читать дальше