Изменить стиль страницы

Хотя OXID представляют интерес как логические идентификаторы, сами по себе они бесполезны, так как заместителям нужен какой-нибудь механизм IРС или сетевой протокол для связи с апартаментом объекта. Для перевода OXID в полностью квалифицированные адреса сети или IPC в каждой хост-машине, поддерживающей СОМ, существует специальная служба распознавателя OXID (OXID Resolver – OR). Под Windows NT 4.0 OR является частью службы RPCSS. Когда апартамент инициализируется впервые, СОМ назначает OXID и регистрирует его с помощью локального OR. Это означает, что каждый OR знает обо всех работающих апартаментах локальной системы. Кроме того, OR следит за каналом локального IPC-порта для каждого апартамента. Когда CoUnmarshalInterface требуется соединить новый заместитель с апартаментом объекта, то для преобразования OXID в действительный адрес сети или IРС используется локальный OR. Если демаршалированный указатель встречается на той же машине, где находится апартамент объекта, то OR просто ищет OXID в своей локальной OXID -таблице и возвращает локальный IPC-адрес. Если же демаршалированный указатель встречается не на той машине, на которой находится объект, то локальный OR сначала проверяет, встречался ли данный OXID ранее, для чего смотрит в специальном кэше, где хранятся недавно распознанные OXID удаленного доступа. Если OXID ранее не встречался, то он передает запрос OR на хост-машину объекта, используя RPC. Отметим, что маршалированная объектная ссылка содержит адрес хост-машины объекта в формате, подходящем для целого ряда сетевых протоколов, так что OR с демаршалированной стороны знает, куда направлять запрос.

Чтобы распознавать распределенные OXID, служба OR на каждой хост-машине ждет удаленные запросы на распознавание OXID на известном адресе (порт 135 для TCP и UDP) для каждого поддерживаемого сетевого протокола. Когда запрос на распознавание OXID получен из сети, опрашивается локальная таблица OXID. Запрос на распознавание покажет, какие сетевые протоколы поддерживает машина клиента. Если запрошенный процесс из апартамента еще не начал использовать один из запрошенных протоколов, то OR свяжется с СОМ-библиотекой в апартаменте объекта, используя локальный IРС, и инициирует использование запрошенного протокола в процессе объекта. Как только это произойдет, OR занесет новый сетевой адрес апартамента в локальную таблицу OXID. После записи новый сетевой адрес возвращается к OR демаршалирующей стороны, где он кэшируется, чтобы предотвратить дополнительные сетевые запросы на часто используемые апартаменты. Может показаться странным, что контрольное считывание полностью квалифицированных сетевых адресов в ссылке маршалировапного объекта не выполняется сразу. Но уровень косвенности, который допускают не зависящие от протокола идентификаторы апартаментов (OXID), разрешает процессу на основе СОМ откладывать использование сетевых протоколов до тех пор, пока они не потребуются. Это особенно важно, поскольку СОМ может иметь дело с множеством различных протоколов (не только TCP), и требовать от каждого процесса слежения за запросами с использованием всех поддерживаемых протоколов было бы чрезвычайно неэффективно. Фактически, если СОМ-процесс никогда не экспортирует указатели внехостовым клиентам, он никогда не потратит вообще никаких сетевых ресурсов.

Вспомогательные средства для внутрипроцессного маршалинга

Хотя фрагменты кода для WritePtr и ReadPtr из предыдущего раздела достаточно просто реализовать, большинство явных вызовов CoMarshalInterface будут использоваться для передачи интерфейсного указателя от одного потока к другому в том же самом процессе. Для упрощения этой задачи в СОМ предусмотрены две оберточные функции (wrapper functions), которые реализуют нужный стандартный код вокруг CoMarshalInterface и CoUnmarshalInterface. API-функция СОМ CoMarshalInterThreadInterfaceInStream

HRESULT CoMarshalInterThreadInterfaceInStream( [in] REFIID riid, [in, iid_is(riid)] IUnknown *pItf, [out] IStream **ppStm );

обеспечивает простую обертку вокруг CreateStreamOnHGlobal и CoMarshalInterface, как показано ниже:

// from OLE32.DLL (approx.)

// из OLE32.DLL (приблизительно)

HRESULT CoMarshalInterThreadInterfaceInStream( REFIID riid, IUnknown *pItf, IStream **ppStm) {

HRESULT hr = CreateStreamOnHGlobal(0, TRUE, ppStm);

if (SUCCEEDED(hr))

hr = CoMarshalInterface(*ppStm, riid, pItf, MSHCTX_INPROC, 0, MSHLFLAGS_NORMAL);

return hr;

}

В СОМ предусмотрена также обертка вокруг CoUnmarshalInterface:

HRESULT CoGetInterfaceAndReleaseStream( [in] IStream *pStm, [in] REFIID riid, [out, iid_is(riid)] void **ppv );

которая является очень тонкой оберткой вокруг CoUnmarshalInterface:

// from OLE32.DLL (approx.)

// из OLE32.DLL (приблизительно)

HRESULT CoGetInterfaceAndReleaseStream( IStream *pStm, REFIID riid, void **ppv)

{

HRESULT hr = CoUnmarshalInterface(pStm, riid, ppv);

pStm->Release();

return hr;

}

Ни одна из этих двух функций не обеспечивает каких-либо особых возможностей, но в некоторых случаях удобнее использовать их, а не низкоуровневые аналоги.

Следующий фрагмент кода мог бы быть применен для передачи интерфейсного указателя в другой апартамент того же процесса с использованием глобальной переменной для хранения промежуточной маршалированной объектной ссылки:

HRESULT WritePtrToGlobalVarable(IRacer *pRacer)

{

// where to write the marshaled ptr

// куда записывать маршалированный указатель

extern IStream *g_pStmPtr;

// thread synchronization for read/write

// синхронизация потока для чтения/записи

extern HANDLE g_heventWritten;

// write marshaled object reference to global variable

// записываем маршалированную объектную ссыпку

// в глобальную переменную

HRESULT hr = CoMarshalInterThreadInterfaceInStream( IID_IRacer, pRacer, &g_pStmPtr);

// signal other thread that ptr is now available

// подаем сигнал другому процессу о том, что указатель

// теперь доступен

SetEvent (g_heventWritten); return hr; }

Соответствующий код будет корректно демаршалировать объектную ссылку в апартамент вызывающего объекта при условии, что он находится и том же самом процессе:

HRESULT ReadPtrFromGlobalVariable(IRacer * &rpRacer) {

// where to write the marshaled ptr

// куда записывать маршалированный указатель

extern IStream *g_pStmPtr;

// thread synchronization for read/write

// синхронизация потока для чтения/записи extern

HANDLE g_heventWritten;

// wait for other thread to signal that ptr is available

// ожидаем другой поток, чтобы дать сигнал о том. что

// указатель доступен

WaitForSingleObject(g_heventWritten, INFINITE);

// read marshaled object reference from global variable

// читаем маршалированную объектную ссылку из глобальной переменной

HRESULT hr = CoGetInterfaceAndReleaseStream( g_pStmPtr, IID_IRacer. (void**) &rpRacer);

// MSHLFLAGS_NORMAL means no more unmarshals are legal

// MSHLFLAGS_NORMAL означает, что больше не может быть

// извлечено никаких демаршалированных указателей

g_pStmPtr = 0;

return hr;