Предполагая, что объект класса экспортирует интерфейс IClassFactory вместо IApeClass , клиенты должны использовать метод IClassFactory::CreateInstance для создания новых экземпляров:
HRESULT CreateAGorillaAndEatBanana()
{
IClassFactory *pcf = 0;
// find the class object находим объект класса
HRESULT hr = CoGetClassObject(CLSI DGorilla , CLSCT XALL , 0, II DIClassFactory , (void **)&pcf);
if (SUCCEEDED(hr))
{
IApe *pApe = 0;
// use the class object to create a gorilla
// используем объект класса для создания gorilla
hr = pcf->CreateInstance(0, II DIApe , (void**)&pApe);
// we're done with the class object, so release it
// мы закончили с объектом класса, поэтому освобождаем его
pcf->Release();
if (SUCCEEDED(hr))
{
// tell the new gorilla to eat a banana
// приказываем новой горилле есть банан
hr = pApe->EatBanana();
pApe->Release();
}
}
return hr;
}
Этот код является семантически идентичным варианту с функцией, которая использовала интерфейс IApeClass вместо интерфейса IClassFactory .
Для того чтобы предыдущий пример работал корректно, объекту класса Gorilla следует реализовать
IClassFactory : class GorillaClass : public IClassFactory
{
public:
IMPLEMEN TUNKNOWN N ODELETE(GorillaClass)
BEGIN INTERFAC ETABLE(GorillaClass)
IMPLEMENTS INTERFACE(IClassFactory)
EN DINTERFACE TABLE()
STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
*ppv = 0;
if (pUnkOuter != 0)
// we don't support aggregation yet
// мы еще не поддерживаем агрегирование
return CLAS SE NOAGGREGATION;
// create a new instance of our C++ class Gorilla
// создаем новый экземпляр нашего С++-класса Gorilla
Gorilla *p = new Gorilla;
if (p == 0) return EOUTOFMEMORY :
// increment reference count by one
// увеличиваем счетчик ссылок на единицу
p->AddRef();
// store the resultant interface pointer into *ppv
// записываем результирующий указатель интерфейса в *ppv
HRESULT hr = p->QueryInterface(riid, ppv);
// decrement reference count by one, which will delete the
// object if QI fails
// уменьшаем на единицу счетчик ссылок,
// что уничтожит объект при отказе QI
p->Release();
// return result of Gorilla::QueryInterface
// возвращаем результат работы Gorilla::QueryInterface
return hr;
}
STDMETHODIMP LockServer(BOOL bLock);
};
Реализация LockServer будет обсуждаться в этой главе позже. Отметим, что реализация CreateInstance , в первую очередь, создает новый объект C++ на базе класса Gorilla и запрашивает объект, поддерживает ли он нужный интерфейс. Если объект поддерживает требуемый интерфейс, то вызов QueryInterface инициирует вызов AddRef , и клиент в конечном счете выполнит соответствующий вызов Release . Если же произойдет отказ QueryInterface , то потребуется некоторый механизм для уничтожения созданного нового объекта. Предыдущий пример использует стандартную технологию «заключения в скобки» (bracketing) вызова QueryInterface между парой AddRef/Release . Если произошел сбой вызова QueryInterface , то вызов Release сбросит счетчик ссылок на нуль, запуская тем самым удаление объекта. Если же вызов QueryInterface прошел успешно, то вызов Release установит счетчик ссылок на единицу. Остающаяся ссылка принадлежит клиенту, который и выполнит последний вызов Release , когда объект более не нужен.
Одно из преимуществ использования стандартного интерфейса для создания экземпляров ( instantiation ) состоит в том, что СОМ может обеспечить более эффективную технологию реализации. Рассмотрим следующий код, который создает новый экземпляр класса Chimp :
HRESULT CreateChimp(/* [out] */ IApe * &rpApe)
{
extern const CLSID CLSID_Chimp;
rpApe = 0;
IClassFactory *pcf = 0;
HRESULT hr = CoGetClassObject(CLSID_Chimp, CLSCTX_ALL, 0, IID_IClassFactory, (void**)&pcf);
if (SUCCEEDED(hr))
{
hr = pcf->CreateInstance(0, IID_IApe, (void**)&rpApe);
pcf->Release();
}
return hr;
}
Этот код выполняет одну операцию: создание объекта Chimp . Заметим, что для выполнения одной операции понадобилось три вызова подопераций (suboperations) – CoGetClassObject , CreateInstance , Release . Если сервер загружен как внутрипроцессный, то эти три подоперации не обойдутся слишком дорого. Если, однако, сервер является внепроцессным или внехостовым, то каждая из этих подопераций потребует вызова между процессами клиента и сервера. Хотя СОМ использует очень эффективную передачу IPC/RPC (Interprocess Communication/Remote Procedure Call), каждая из этих подопераций потребует немалых исполнительных затрат. В идеале было бы лучше потребовать, чтобы СОМ перешел на серверный процесс один раз и, находясь там, использовал объект класса для вызова CreateInstance от имени клиента. Если объект класса используется только для реализации IClassFactory , то этот способ будет более эффективным, чем трехшаговый способ, показанный ранее. В СОМ имеется API-функция: CoCreateInstanceEx , относящаяся по своему назначению к той же категории, что CoGetClassObject и IClassFactory::CreateInstance – обеспечивающая создание новых объектов за одно обращение между процессами.
Читать дальше