}
}
// call function to get pointer to class object
// вызываем функцию для получения указателя на объект класса
hr = (*pfnGCO)(rclsid, riid, ppv);
}
if ((dwClsCtx & (CLSCTXLOCALSERVER | CLSCTXREMOTESERVER)) && hr == REGDBECLASSNOTREG)
{
// handle out-of-proc/remote request
// обрабатываем внепроцессный/удаленный запрос
}
return hr;
}
Отметим, что реализация CoGetClassObject является единственным местом, откуда вызывается DllGetClassObject . Чтобы укрепить это обстоятельство, компоновщики в модели СОМ выдадут предупреждение в случае, если входная точка DllGetClassObject экспортируется без использования ключевого слова private в соответствующем файле определения модулей:
// from APELIB.DEF
// из APELIB.DEF LIBRARY
APELIB EXPORTS DllGetClassObject private
Фактически в модели СОМ компоновщики предпочитают, чтобы во всех связанных с СОМ точках входа использовалось это ключевое слово.
Обобщения
В предыдущем примере интерфейс IApeClass рассматривался как интерфейс уровня класса, специфический для классов, которые объявляют интерфейс IАре из своих экземпляров. Этот интерфейс позволяет клиентам создавать новые объекты или находить существующие, но в любом случае результирующие объекты должны реализовывать интерфейс IАре . Если бы новый класс хотел разрешить клиентам создавать или находить объекты, несовместимые с IApe , то объект этого класса должен был бы реализовывать другой интерфейс. Поскольку создание и поиск объектов являются общими требованиями, которые большинство классов хотели бы поддерживать, СОМ определяет стандартные интерфейсы для моделирования поиска и создания объектов унифицированным образом (generically). Один стандартный интерфейс для поиска объектов назван IOleItemContainer:
// from oleidl.idl из oleidl.idl
[ object, uuid(0000011c-0000-0000-C000-000000000046) ]
interface IOleItemContainer : IOleContainer {
// ask for object named by pszItem
// запрашиваем объект, именованный
pszItem HRESULT Get0bject(
[in] LPOLESTR pszItem,
// which object? какой объект?
[in] DWORD dwSpeedNeeded,
// deadline
[in, unique] IBindCtx *pbc,
// binding info информация о связывании
[in] REFIID riid,
// which interface? какой интерфейс?
[out, iidis(riid)] void **ppv);
// put it here! разместим его здесь!
// remaining methods deleted for clarity
// остальные методы удалены для ясности
}
Отметим, что метод GetObject позволяет клиенту задавать тип результирующего интерфейсного указателя. Действительный класс результирующего объекта зависит от контекста и конкретной реализации IOleItemContainer . Следующий пример запрашивает объект класса Gorilla найти объект под именем «Ursus»:
HRESULT FindUrsus(IApe * &rpApe)
{
// bind a reference to the class object
// связываем ссылку с объектом класса
rpApe = 0;
IOleItemContainer *poic = 0;
HRESULT hr = CoGetClassObject(CLSIDGorilla, CLSCTXALL, 0, IIDIOleItemContainer, (void**)&poic);
if (SUCCEEDED(hr))
{
// ask Gorilla class object for Ursus
// запрашиваем объект класса Gorilla на поиск
Ursus hr = poic->GetObject(OLESTR(«Ursus»), BINDSPEEDINDEFINITE, 0, IIDIApe, (void**)&rpApe);
poic->Release();
}
return hr;
}
Хотя такое использование вполне допустимо, интерфейс IOleItemContainer был предназначен для работы в тандеме с моникером элемента (Item Moniker), который будет рассматриваться позже в данной главе.
В СОМ определен еще один стандартный интерфейс для создания объектов. Он называется IClassFactory:
// from unknwn.idl из unknwn.idl
[ object, uuid(00000001-0000-0000-C000-000000000046) ]
interface IClassFactory : IUnknown
{
HRESULT CreateInstance( [in] IUnknown *pUnkOuter, [in] REFIID riid, [out, iidis(riid)] void **ppv) ;
HRESULT LockServer([in] BOOL bLock);
}
Хотя экземпляры класса могли бы экспортировать интерфейс IClassFactory, данный интерфейс обычно экспортируется только объектами класса. Объекты класса не обязаны реализовывать IClassFactory, но, для единообразия, они часто делают это. В момент написания этой книги классы, которые будут встраиваться в среду Microsoft Transaction Server (MTS), должны реализовывать IClassFactory (фактически никакие другие интерфейсы объектов класса не будут распознаваться в MTS).
Интерфейс IClassFactory имеет два метода: LockServer и CreateInstance. Метод LockServer вызывается внутри СОМ во время запроса на внепроцессную активацию и подробно обсуждается в главе 6. Метод CreateInstance используется для запроса на создание объектом класса нового экземпляра класса. Как было в случае IApeClass::CreateApe, тип объекта, который будет подвергаться обработке, определяется объектом класса, которому клиент посылает запрос CreateInstance. Первый параметр CreateInstance используется в агрегировании СОМ и обсуждается в главе 4. Пока же, в рамках третьей главы, для простоты изложения положим этот параметр равным нулю. Второй и третий параметры CreateInstance позволяют методу возвращать клиенту динамически типизируемый указатель интерфейса.
Предполагая, что объект класса экспортирует интерфейс IClassFactory вместо IApeClass, клиенты должны использовать метод IClassFactory::CreateInstance для создания новых экземпляров:
HRESULT CreateAGorillaAndEatBanana()
{
IClassFactory *pcf = 0;
// find the class object находим объект класса
HRESULT hr = CoGetClassObject(CLSIDGorilla, CLSCTXALL, 0, IIDIClassFactory, (void **)&pcf);
if (SUCCEEDED(hr))
{
IApe *pApe = 0;
// use the class object to create a gorilla
// используем объект класса для создания gorilla
hr = pcf->CreateInstance(0, IIDIApe, (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:
IMPLEMENTUNKNOWNNODELETE(GorillaClass)
BEGININTERFACETABLE(GorillaClass)
IMPLEMENTSINTERFACE(IClassFactory)
ENDINTERFACETABLE()
STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
*ppv = 0;
if (pUnkOuter != 0)
// we don't support aggregation yet
// мы еще не поддерживаем агрегирование
return CLASSENOAGGREGATION;
// 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