Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
Шрифт:
Интервал:
Закладка:
Обычно указатели интерфейса маршалируются неявно как часть стандартной операции СОМ. Когда запрос на внутрипроцессную активацию сделан для класса с несовместимой моделью поточной обработки, СОМ неявно маршалирует интерфейс из апартамента объекта и демаршалирует заместитель в апартаменте клиента. Если сделан запрос на внепроцессную или внехостовую активацию, то СОМ также маршалирует результирующий указатель из апартамента объекта и демаршалирует заместитель для клиента. Если вызовы метода выполняются на заместители, то любые интерфейсные указатели, проходящие в качестве параметров метода, будут маршалированы с целью сделать объектные ссылки доступными в апартаментах и клиента, и объекта. Иногда необходимо маршалировать интерфейсы явным образом из одного апартамента в другой вне контекста запроса на активацию или вызова метода. Для поддержки этого режима в СОМ предусмотрена API-функция низкого уровня CoMarshalInterface , предназначенная для явного маршалинга интерфейсных указателей.
CoMarshalInterface принимает на входе интерфейсный указатель и записывает преобразованное в последовательную форму представление указателя в предоставленный вызывающим объектом байтовый поток. Этот байтовый поток может затем быть передан в другой апартамент, где API-функция CoUnmarshalInterface использует байтовый поток для возвращения интерфейсного указателя, который семантически эквивалентен исходному объекту, и к которому можно легально обращаться в апартаменте, выполняющем вызов функции CoUnmarshalInterface. При вызове CoMarshalInterface вызывающий объект должен указать, насколько далеко может располагаться импортирующий апартамент. В СОМ определен список рекомендуемых расстояний:
typedef enum tagMSHCTX
{
MSHCTX_INPROC = 4,
// in-process/same host
// внутрипроцессный/тот же хост
MSHCTX_LOCAL = 0,
// out-of-process/same host
// внепроцессный/тот же хост
MSHCTX_NOSHAREDMEM = 1,
// 16/32 bit/same host
// 16/32-битный/тот же хост
MSHCTX_DIFFERENTMACHINE = 2
// off-host
// внехостовый
} MSHCTX;
Допускается указывать большую дистанцию, чем необходимо, но для большей эффективности следует использовать по мере возможности корректное значение MSHCTX. CoMarshalInterface также позволяет вызывающему объекту специфицировать семантику маршалинга с помощью следующих специфических флагов:
typedef enum tagMSHLFLAGS
{
MSHLFLAGS_NORMAL,
// marshal once, unmarshal once
// маршалируем один раз, демаршалируем один раз
MSHLFLAGS_TABLESTRONG,
// marshal опсе, unmarshal many
// маршалируем один раз. демаршалируем много раз
MSHLFLAGS_TABLEWEAK,
// marshal once, unmarshal many
// маршалируем один раз, демаршалируем много раз
MSHLFLAGS_NOPING = 4,
// suppress dist. garbage collection
// подавляем ненужный набор дистанций
} MSHLFLAGS;
Нормальный (normal) маршалинг, иногда его называют еще маршалингом вызовов (call marshaling), означает, что маршалированная объектная ссылка должна быть демаршалирована только один раз, а если нужны дополнительные заместители, то требуются дополнительные вызовы CoMarshalInterface. Табличный (table) маршалинг означает, что маршалированная объектная ссылка может быть демаршалирована нуль и более раз без требования дополнительных вызовов CoMarshalInterface. Подробности табличного маршалинга будут описаны далее в этой главе.
Чтобы разрешить маршалинг интерфейсных указателей на различные носители, функция CoMarshalInterface преобразует интерфейсный указатель в последовательную форму через интерфейс типа IStream , предоставляемый вызывающим объектом. Интерфейс IStream моделирует произвольное устройство ввода-вывода и выставляет методы Read и Write . Функция CoMarshalInterface просто вызывает метод Write на предоставленный вызывающим объектом интерфейс типа IStream , не интересуясь тем, куда эти фактические байты будут записаны. Вызывающие объекты могут получить обертку IStream на необработанную (raw ) память, вызвав API-функцию CreateStreamOnHGlobal :
HRESULT CreateStreamOnHGlobal(
[in] HGLOBAL hglobal,
// pass null to autoalloc
// передаем нуль для автовыдепения памяти
[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
// куда поместить демаршалированный указатель
);
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;
}
Результирующий заместитель будет реализовывать каждый из экспортируемых объектом интерфейсов путем переадресации запросов методов в апартамент объекта.