Выбрать главу

if (SUCCEEDED(hr))

{

// cache the bound object in binding context

// кэшируем связанный объект в контексте связывания

pbc->RegisterObjectBound(poic);

// get bind speed from Bind Context

// получаем быстроту связывания из контекста связывания

DWORD dwBindSpeed = this->MyGetSpeedFromCtx(pbc);

// ask object for named sub-object

// запрашиваем объект об именованном подобъекте

hr = poic->GetObject(m_pszItem, dwBindSpeed, pbc, riid, ppv);

poic->Release();

}

}

Эта реализация означает, что такой код:

HRESULT GetUrsus(IApe *&rpApe)

{

const OLECHAR pwsz[] = OLESTR(«clsid:571F1680-CC83-11d0-8C48-0080C73925BA:!Ursus»);

return CoGetObject(pwsz, 0, IID_IApe, (void**)&rpApe);

}

эквивалентен следующему:

HRESULT GetUrsus(IApe *&rpApe) {

IOleItemContainer *poic = 0;

HRESULT hr = CoGetClassObject(CLSID_Gorilla, CLSCTX_ALL,

0, IID_IOleItemContainer, (void**)&poic);

if (SUCCEEDED(hr)) {

hr = poic->GetObject(OLESTR(«Ursus»), BINDSPEED_INFINITE,

0, IID_IApe, (void**)&rpApe);

poic->Release();

}

return hr; }

Отметим, что уровень изоляции (indirection), обеспеченный использованием CoGetObject, позволяет клиенту менять стратегию связывания просто путем чтения различных отображаемых имен из файла конфигурации или из ключа реестра.

Моникеры и сохраняемость

Обсуждение моникеров не может быть полным без обсуждения файлового моникера (File Moniker). Напомним, что СОМ предусматривает три примитива активации: привязывание к объектам класса, привязывание к новым экземплярам класса и привязывание к постоянным объектам, хранящимся в файлах. В данной главе детально анализировались первые два из этих примитивов. Третий примитив основан на API-функции СОМ CoGetInstanceFromFile:

HRESULT CoGetInstanceFromFile( [in, unique] COSERVERINFO *pcsi,

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

[in, unique] CLSID *pClsid,

// explicit CLSID (opt) – явный CLSID (opt)

[in, unique] IUnknown *punk0uter,

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

[in] DWORD dwClsCtx,

// locality? – локализация?

[in] DWORD grfMode,

// file open mode – режим открытия файла

[in] OLECHAR *pwszName,

// file name of object – файловое имя объекта

[in] DWORD cmqi,

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

[out, size_is(cmqi)] MULTI_QI *prgmq

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

);

Эта функция принимает на вход имя файла, которое относится к постоянному состоянию (persistent state) объекта[1]. CoGetInstanceFromFile удостоверяется в том, что объект исполняется, после чего возвращает один или несколько интерфейсных указателей на (ре)активированный объект. Чтобы выполнить эту работу, CoGetInstanceFromFile в первую очередь требуется определить CLSID данного объекта. CLSID требуется по двум причинам. Если объект не исполняется, то CLSID необходим СОМ для создания нового экземпляра, который будет инициализирован от постоянного (находящегося в файле) образа. Во-вторых, если вызывающий объект не указывает определенное хост-имя до запроса на активацию, то СОМ будет использовать CLSID для выяснения того, на какой машине следует активировать объект[2].

Если CLSID не передается явно вызывающим объектом, то CoGetInstanceFromFile извлекает CLSID из самого файла с помощью вызова API-функции СОМ GetClassFile:

HRESULT GetClassFile([in, string) OLECHAR *pwszFileName, [out] CLSID *pclsid);

Для определения типа объекта, содержащегося в файле, GetClassFile использует информацию из заголовка файла, а также информацию из реестра.

После того как определены класс и хост-машина, СОМ исследует ROT (Running Object Table – таблица исполняющихся объектов) на целевой хост-машине для того, чтобы выяснить, является ли объект уже активированным. Таблица ROT является инструментом SCM, который преобразует произвольные моникеры в экземпляры объектов, исполняющихся на локальной хост-машине. Ожидается, что постоянные объекты будут регистрировать себя в локальной ROT во время загрузки. Чтобы представить файловое имя постоянного объекта в качестве моникера, СОМ предусматривает стандартный тип моникера – файловый моникер, который оборачивает имя файла в интерфейс IMoniker. Файловые моникеры могут создаваться либо путем передачи файлового имени в МkParseDisplayName , либо вызовом явной API-функции CreateFileMoniker:

HRESULT CreateFileMoniker( [in, string] const OLECHAR *pszFileName, [out] IMoniker **ppmk);

Если постоянный объект уже зарегистрировал в ROT свой файловый моникер, то CoGetInstanceFromFile просто возвращает указатель на уже работающий объект. Если же объект в ROT не найден, то СОМ создает новый экземпляр файлового класса и инициализирует его из постоянного объекта с помощью метода IPersistFile::Load этого экземпляра:

[object, uuid(0000010b-0000-0000-C000-000000000046)]

interface IPersistFile : IPersist

{

// called by CoGetInstanceFromFile to initialize object

// вызывается функцией CoGetInstanceFromFile для

// инициализации объекта

HRESULT Load(

[in, string] const OLECHAR * pszFileName, [in] DWORD grfMode

);

// remaining methods deleted for clarity // остальные методы удалены для ясности

}

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

STDMETHODIMP::Load(const OLECHAR *pszFileName, DWORD grfMode)

{

// read in persisted object state

// считываем сохраненное состояние объекта

HRESULT hr = this->MyReadStateFromFile(pszFile, grfMode);

if (FAILED(hr)) return hr;

// get pointer to ROT from SCM

// берем указатель на ROT от SCM

IRunningObjectTable *prot = 0;

hr = GetRunningObjectTable(0, &prot);

if (SUCCEEDED(hr))

{

// create a file moniker to register in ROT

// создаем файловый моникер для регистрации в ROT

IMoniker *pmk = 0;

hr = CreateFileMoniker(pszFileName, &pmk);

if (SUCCEEDED(hr))

{

// register self in ROT

// саморегистрация в ROT

hr = prot->Register(0, this, pmk, &m_dwReg);

pmk->Release();

}

prot->Release();

}

return hr;

}

Метод IPersistFile::Load нового созданного экземпляра будет вызван диспетчером SCM во время выполнения CoGetInstanceFromFile . В приведенном выше примере для получения указателя на интерфейс IRunningObjectTable в SCM используется API-функция СОМ GetRunningObjectTable. Этот интерфейс затем используется для регистрации своего моникера в ROT, так что последующие вызовы CoGetInstanceFromFile , использующие то же файловое имя, не будут создавать новые объекты, а вместо этого возвратят ссылки на этот объект[3].

Существование File Moniker обусловлено двумя причинами. Во-первых, он нужен, чтобы позволить объектам самим регистрироваться в ROT, чтобы их мог найти CoGetInstanceFromFile. Во-вторых, чтобы скрыть от клиента использование CoGetInstanceFromFile за интерфейсом IMoniker. Реализация File Moniker из BindToObject просто вызывает CoGetInstanceFromFile:

// pseudo-code from OLE32.DLL // псевдокод из OLE32.DLL

STDMETHODIMP FileMoniker::BindToObject(IBindCtx *pbc,

IMoniker *pmkToLeft,

REFIID riid, void **ppv) {

// assume failure – на случай сбоя

*ppv = О;

HRESULT hr = E_FAIL;

if (pmkToLeft == 0) {

вернуться

1 Альтернативная версия этой API-функции. CoGetInstanceFromIStorage , вместо имени файла принимает указатель на иерархическое хранилище (storage medium).

вернуться

2 В дополнение к обычной переадресации CLSID на хост-машины, которое используется функциями CoGetClassObject/CoCreateInstanceEx, CoGetInstanceFromFile может использовать в качестве имени файла UNC-имя хоста (universal naming convention – общее соглашение по именам), чтобы переадресовать запрос на активацию на ту хост-машину, где расположен данный файл. Этот режим активации упоминается в Спецификации СОМ как побитовая активация («AtВits» activation) и описывается с использованием установок реестра «ActivateAtStorage», как описано в главе 6.

вернуться

3 На практике областью действия ROT является не вся машина, а только Winstation. Это означает, что по умолчанию не все зарегистрированные сессии (logon sessions) получат доступ к объекту. Чтобы убедиться, что объект является видимым для всех возможных клиентов, при вызове IRunningObjectTable::Register объект должен выставить флаг ROTFLAGS_ALLOWANYCLIENT.