Напомним, что SCM поддерживает три примитива активации (связывание с объектами класса, связывание с экземплярами класса, связывание с постоянными экземплярами из файлов). Как показано на рис. 3.2, эти примитивы логически разделены на уровни [1] . Примитивом нижнего уровня является связывание с объектом класса. Этот примитив также наиболее прост для понимания.
Вместо того чтобы вручную загружать код класса, клиенты пользуются услугами SCM посредством низкоуровневой API-функции СОМ CoGetClassObject . Эта функция запрашивает SCM присвоить значение указателю на требуемый объект класса:
HRESULT CoGetClassObject(
[in] REFCLSID rclsid,
// which class object?
// Какой объект класса?
[in] DWORD dwClsCtx,
// locality?
//местонахождение?
[in] COSERVERINFO *pcsi,
// host/security info
//сведения о сервере и обеспечении безопасности
[in] REFIID riid,
// which interface?
// какой интерфейс?
[out, ii dis(riid)] void **ppv);
// put it here !
// поместим его здесь!
Первый параметр в CoGetClassObject показывает, какой класс реализации запрашивается. Последний параметр – это ссылка на указатель интерфейса, с которым нужно связаться, а четвертый параметр – это просто IID , описывающий тип указателя интерфейса, на который ссылается последний параметр. Более интересные параметры – второй и третий, которые определяют, когда объект класса должен быть активирован.
В качестве второго параметра CoGetClassObject принимает битовую маску (bitmask), которая позволяет клиенту указать характеристики скрытого и живучего состояний объекта (например, будет ли объект запущен в процессе, вне процесса или вообще на другом сервере). Допустимые значения для этой битовой маски определены в стандартном перечислении CLSCTX :
enum tagCLSCTX { CLSCT XINPROC SERVER = 0х1,
// run -inprocess
// запуск в процесс
CLSCT XINPROC HANDLER = 0х2,
// see note [2]
// смотрите сноску [2]
CLSCT XLOCAL SERVER = 0х4,
// run out-of-process
// запуск вне процесса
CLSCT XREMOTE SERVER = 0х10
// run off-host
// запуск вне хост-машины
} CLSCTX;
Эти флаги могут быть подвергнуты побитному логическому сложению (bit-wise-ORed together), и в случае, когда доступен более чем один запрошенный CLSCTX , СОМ выберет наиболее эффективный тип сервера (это означает, что СОМ будет, когда это возможно, использовать наименее значимый бит битовой маски). Заголовочные файлы SDK также включают в себя несколько сокращенных макросов, которые сочетают несколько флагов CLSCTX , используемых во многих обычных сценариях:
#define CLSCT XINPROC (CLSCTX INPRO CSERVER |
\ CLSCTX INPRO CHANDLER)
#define CLSCTX SERVER (CLSCT XINPROC SERVER |
\ CLSCT XLOCAL SERVER |
\ CLSCT XREMOTE SERVER)
#define CLSCT XALL (CLSCTX INPRO CSERVER |
\ CLSCTX INPRO CHANDLER |
\ CLSCTX LOCA LSERVER |
\ CLSCTX REMOT ESERVER)
Заметим, что такие среды, как Visual Basic и Java, всегда используют CLSCT XALL , показывая тем самым, что подойдет любая доступная реализация.
Третий параметр CoGetClassObject – это указатель на структуру, содержащую информацию об удаленном доступе и безопасности. Эта структура имеет тип COSERVERINFO и позволяет клиентам явно указывать, какой машине следует активировать объект, а также как конфигурировать установки обеспечения безопасности, используемые при создании запроса на активацию объекта:
typedef struct COSERVERINFO
{
DWORD dwReserved1;
// reserved , must be zero
// зарезервировано, должен быть нуль
LPWSTR pwszName;
// desired host name, or null
// желаемое имя хост-машины или нуль
COAUTHINFO *pAuthInfo;
// desired security settings
// желаемые установки безопасности DWORD dwReserved2;
// reserved, must be zero
// зарезервировано, должен быть нуль
} COSERVERINFO;
Если клиент не указывает имя хоста (host name), а использует только флаг CLSCTXREMOTESERVER, то для определения того, какая машина будет активировать объект, СОМ использует информацию о конфигурации каждого CLSID. Если клиент передает явное имя хоста, то оно получит приоритет перед любыми ранее сконфигурированными именами хостов, о которых может знать СОМ. Если клиент не желает передавать явную информацию о безопасности или имя хоста в CoGetClassObject, можно применить нулевой указатель COSERVERINFO.
Имея в наличии CoGetClassObject , клиент может дать запрос SCM на связывание указателя интерфейса с объектом класса:
HRESULT GetGorillaClass(IApeClass * &rpgc)
{
// declare the CLSID for Gorilla as a GUID
// определяем CLSID для Gorilla как GUID
const CLSID CLSI DGorilla =
{ 0x571F1680 , 0xCC83, 0x11d0,
{ 0x8C, 0х48, 0х00, 0х80, 0xС7, 0х39, 0x25, 0xBA
} };
// call CoGetClassObject directly
// вызываем прямо CoGetClassObject
return CoGetClassObject(CLSI DGorilla , CLSCT XALL , 0, II DIApeClass , (void**)&rpgc);
}
Отметим, что если запрошенный класс доступен как внутрипроцессный сервер, то СОМ автоматически загрузит соответствующую DLL и вызовет известную экспортируемую функцию, которая возвращает указатель на требуемый объект класса [3] . Когда вызов CoGetClassObject завершен, библиотека СОМ и SCM полностью выходят из игры. Если бы класс был доступен только с внепроцессного или удаленного сервера, СОМ вместо этого возвратила бы заместитель, который позволил бы клиенту получить удаленный доступ к объекту класса.
Напомним, что интерфейс IApeClass придуман для того, чтобы позволить клиентам находить или создавать экземпляры заданного класса. Рассмотрим следующий пример:
HRESULT FindAGorillaAndEatBanana(long nGorillaID)
{
IApeClass *pgc = 0;
// find the class object via CoGetClassObject
// находим объект класса с помощью CoGetClassObject
HRESULT hr = CoGetClassObject(CLSI DGorilla , CLSCT XALL , 0, II DIApeClass , (void**)&pgc);
if (SUCCEEDED(hr))
{
IApe *pApe = 0;
// use the class object to find an existing gorilla
// используем объект класса для нахождения существующей гориллы
hr = pgc->GetApe(nGorillaID, &pApe);
if (SUCCEEDED(hr))
{
// tell the designated gorilla to eat a banana
// прикажем указанной горилле есть бананы
hr = pApe->EatBanana();
pApe->Release();
}
pgc->Release();
}
return hr;
}
Данный пример использует объект класса для того, чтобы Gorilla нашла именованный объект и проинструктировала его есть бананы. Чтобы этот пример работал, нужно, чтобы какой-нибудь внешний посредник дал вызывающему объекту имя какой-нибудь известной гориллы. Можно построить пример и таким образом, чтобы любая неизвестная горилла могла быть использована для удовлетворения запроса:
HRESULT CreateAGorillaAndEatBanana(void)
{
IApeClass *pgc = 0;
// find the class object via CoGetClassObject
// находим объект класса с помощью CoGetClassObject
HRESULT hr = CoGetClassObject(CLSI DGorilla , CLSCT XALL , 0, II DIApeClass , (void**)&pgc);
if (SUCCEEDED(hr))
{
IApe *pApe = 0;
// use the class object to create a new gorilla
// используем объект класса для создания новой гориллы
hr = pgc->CreateApe(&pApe);
if (SUCCEEDED(hr))
{
// tell the new gorilla to eat a banana
// прикажем новой горилле есть бананы
hr = pApe->EatBanana();
pApe->Release();
}
pgc->Release();
}
return hr;
}
Отметим, что за исключением специфического использования метода IApeClass , эти примеры идентичны. Поскольку объекты класса могут экспортировать сколь угодно сложные интерфейсы, то их можно использовать для моделирования довольно изощренной стратегии активации, инициализации и размещения объектов. |