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 :
Читать дальше