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

[in] BOOL bFreeMemoryOnRelease,

[out] IStream **ppStm);

С использованием семантики IStream следующий фрагмент кода:

void UseRawMemoryToPrintString(void)

{

void *pv = 0;

// alloc memory

// выделяем память

pv = malloc(13);

if (pv != 0) {

// write a string to the underlying memory

// пишем строку в основную память

memcpy(pv, «Hello, World», 13);

printf((const char*)pv);

// free all resources

// освобождаем все ресурсы free (pv);

}

}

эквивалентен такому фрагменту кода, использующему интерфейс IStream вместо memcpy:

void UseStreamToPrintString(void)

{

IStream *pStm = 0;

// alloc memory and wrap behind an IStream interface

// выделяем память и затем заворачиваем ее в интерфейс

IStream HRESULT hr = CreateStreamOnHGlobal(0, TRUE, &pStm);

if (SUCCEEDED(hr)) {

// write a string to the underlying memory

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

hr = pStm->Write(«Hello. World», 13, 0);

assert (SUCCEEDED (hr));

// suck out the memory

// извлекаем память

HGLOBAL hglobal = 0;

hr == GetHglobalFromStream(pStm, &hglobal);

assert(SUCCEEDED(hr));

printf((const char*)GlobalLock(hglobal));

// free all resources

// освобождаем все ресурсы

GlobalUnlock(hglobal); pStm->Release();

}

}

API-функция GetHGlobalFromStream позволяет вызывающему объекту получать дескриптор (handle ) памяти, выделенной функцией CreateStreamOnHGlobal. Использование HGLOBAL сложилось исторически и никоим образом не означает использование разделяемой памяти.

После осмысления всех типов параметров API-функции CoMarshalInterface она выглядит достаточно просто:

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

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

);

Сущность технологии СОМ. Библиотека программиста fig5_1.jpg

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 интерфейса, содержащегося в ссылке. В случае стандартного маршалинга подзаголовок объектной ссылки показывает, сколько внешних ссылок представляет данная маршалированная ссылка.

Сущность технологии СОМ. Библиотека программиста fig5_2.jpg

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

вернуться

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