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

HRESULT CoMarshalInterface(

[in] IStream *pStm,

// where to write marshaled state

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

[in] REFIID riid, // type of ptr being marshaled

// тип маршалируемого указателя

[in, iid_is(riid)] IUnknown *pItf,

// pointer being marshaled

// маршалируемый указатепь

[in] DWORD dwDestCtx,

// MSHCTX for destination apt.

// MSHCTX для апартамента адресата

[in] void *pvDestCtx,

// reserved, must be zero

// зарезервирован, должен равняться нулю

[in] DWORD dwMshlFlags

// normal, vs. table marshal

// нормальный маршалинг против табличного

);

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

HRESULT WritePtr(IRacer *pRacer, HGLOBAL& rhglobal)

{ IStream *pStm = 0; гhglobal = 0;

// alloc and wrap block of memory

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

HRESULT hr = CreateStreamOnHGlobal(0, FALSE, &pStm);

if (SUCCEEDED(hr)) {

// write marshaled object reference to memory

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

hr = CoMarshalInterface(pStm, IID_Iracer, pRacer, MSHCTX_DIFFERENTMACHINE, 0, MSHLFLAGS_NORMAL);

// extract handle to underlying memory

// извлекаем дескриптор памяти

if (SUCCEEDED(hr)) hr = GetHGlobalFromStream(pStm, &rhglobal);

pStm->Release();

}

return hr;

}

Рисунок 5.1 иллюстрирует взаимоотношения между интерфейсным указателем и памятью, содержащей маршалированную объектную ссылку. После вызова CoMarshalInterface апартамент объекта готов получить от другого апартамента запрос на соединение. Поскольку был использован флаг MSHCTX_DIFFERENTMACHINE, то импортирующий апартамент может находиться на другой хост-машине.

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

HRESULT CoUnmarshalInterface(

[in] IStream *pStm,

// where to read marshaled state

// откуда читать маршалированное состояние

[in] REFIID riid, // type of ptr being unmaгshaled

// тип демаршалируемого указателя

[out, iid_is(riid)] void **ppv

// where to put unmarshaled ptr

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

);

CoUnmarshalInterface просто читает преобразованную в последовательную форму объектную ссылку и возвращает указатель на исходный объект, к которому есть легальный доступ в апартаменте вызывающего потока. Если импортирующий апартамент отличается от того апартамента, который изначально экспортировал интерфейс, то результирующий указатель будет указателем на заместитель. Если по какой-то причине вызов CoUnmarshalInterface осуществлен из исходного апартамента, где располагается объект, то в этом случае будет возвращен указатель на сам объект и не будет создано никакого заместителя. Следующий код переводит маршалированную объектную ссылку в нормальный указатель интерфейса:

HRESULT ReadPtr(HGLOBAL hglobal, IRacer * &rpRacer) {

IStream *pStm = 0; rpRacer = 0;

// wrap block of existing memory passed on input

// заключаем в оболочку блок существующей памяти,

// переданный на вход

HRESULT hr = CreateStreamOnHGlobal(hglobal, FALSE, &pStm);

if (SUCCEEDED(hr)) {

// get a pointer to the object that is legal in this apt.

// получаем указатель на объект, легальный в этом апартаменте

hr = CoUnmarshalInterface(pStm, IID_Iracer, (void**)&rpRacer);

pStm->Release();

}

return hr;

}

Результирующий заместитель будет реализовывать каждый из экспортируемых объектом интерфейсов путем переадресации запросов методов в апартамент объекта.

До появления выпуска СОМ под Windows NT 4.0 формат маршалированной объектной ссылки не был документирован. Для того чтобы позволить сторонним участникам создавать сетевые продукты, совместимые с СОМ, этот формат был документирован для открытого использования в 1996 году и представлен на рассмотрение как проект стандарта для Интернета. На рис. 5.2 показан формат маршалированной объектной ссылки. Заголовок маршалированной объектной ссылки начинается с изысканной сигнатуры «MEOW» (мяу)[3], а поле флагов указывает на выбранную технологию маршалинга (например, стандартный (standard), специальный (custom)), а также IID интерфейса, содержащегося в ссылке. В случае стандартного маршалинга подзаголовок объектной ссылки показывает, сколько внешних ссылок представляет данная маршалированная ссылка.

Этот счетчик внешних ссылок является частью распределенного протокола «сборки мусора» (garbage collection) СОМ и не полностью совпадает со счетчиком ссылок AddRef/Release, который может быть реализован объектом. Интересным элементом объектной ссылки является кортеж (tuple) OXID/OID/IPID, который единственным образом идентифицируют интерфейсный указатель. Каждому апартаменту в сети во время его создания присваивается уникальный Идентификатор Экспортера Объектов (Object Exporter Identifier – OXID). Этот OXID используется для нахождения сетевой или IPC -адресной информации при первом соединении заместителя с объектом. Идентификатор Объекта (Object Identifier – OID) единственным образом определяет идентификационную единицу СОМ в сети и использует CoUnmarshalInterface для поддержания правил идентификации СОМ для заместителей. Идентификатор Интерфейсного Указателя (Interface Pointer Identifier – IPID) единственным образом определяет интерфейсный указатель в апартаменте и помещается в заголовок каждого последующего запроса метода. IPID используется для эффективной диспетчеризации запросов ORPC на нужный интерфейсный указатель в апартаменте объекта.

Хотя 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 с демаршалированной стороны знает, куда направлять запрос.

вернуться

3 Один из администраторов программ фирмы Microsoft (Microsoft Program Manager), пожелавший остаться неизвестным, утверждает, что MEOW означает Microsoft Extended Object Wire (расширенная передачи объектов Microsoft). Но автор, несмотря на свою доверчивость, относится к этому скептически и желал бы выразить в адрес вышеупомянутого источника свои сомнения.