По умолчанию удаленный вызов метода использует имеющийся в СОМ протокол передачи ORPC (Object Remote Procedure Call – вызов объектом удаленной процедуры). СОМ ORPC является упрощенным протоколом MS-RPC (протокол вызова удаленной процедуры Microsoft), производным от DCE (Distributed Computing Environment – распределенная вычислительная среда). MS-RPC является независимым от протокола механизмом связи, который можно расширять с целью поддержки новых транспортных протоколов (посредством динамически загружаемых транспортных DLL) и новых пакетов аутентификации (посредством динамически загружаемых библиотек поставщика поддержки безопасности Security Support Provider DLL ). СОМ использует наиболее эффективный на доступных транспортных протоколов в зависимости от подобия и типов импортирующего и экспортирующего апартаментов. При связи вне хост-машины СОМ предпочитает UDP (User Datagram Protocol – протокол передачи дейтаграмм пользователя), хотя и поддерживает большинство общеупотребительных сетевых протоколов[2]. При локальной связи СОМ использует один из нескольких транспортных протоколов, каждый из которых оптимален для определенного типа апартаментов.
СОМ осуществляет передачу интерфейсных указателей через границы апартаментов с помощью особой технологии, именуемой маршалингом (marshaling), тo есть расположением в определенном порядке, выстраиванием. Маршалинг интерфейсного указателя – это преобразование его в передающийся байтовый поток, содержимое которого единственным образом идентифицирует объект и его собственный апартамент. Этот байтовый поток является маршалированным состоянием (marshaled state) интерфейсного указателя и дает возможность любому апартаменту импортировать интерфейсный указатель и осуществлять вызовы метода на объект. Отметим, что поскольку СОМ имеет дело исключительно с интерфейсными указателями, а не с самими объектами, это состояние маршалинга не представляет собой состояние объекта, а скорее преобразованное в последовательную форму (serialized) состояние не зависящей от апартаментов ссылки на объект. Такие маршалированные объектные ссылки просто содержат информацию об установлении связи, которая совершенно не зависит от состояния объекта.
Обычно указатели интерфейса маршалируются неявно как часть стандартной операции СОМ. Когда запрос на внутрипроцессную активацию сделан для класса с несовместимой моделью поточной обработки, СОМ неявно маршалирует интерфейс из апартамента объекта и демаршалирует заместитель в апартаменте клиента. Если сделан запрос на внепроцессную или внехостовую активацию, то СОМ также маршалирует результирующий указатель из апартамента объекта и демаршалирует заместитель для клиента. Если вызовы метода выполняются на заместители, то любые интерфейсные указатели, проходящие в качестве параметров метода, будут маршалированы с целью сделать объектные ссылки доступными в апартаментах и клиента, и объекта. Иногда необходимо маршалировать интерфейсы явным образом из одного апартамента в другой вне контекста запроса на активацию или вызова метода. Для поддержки этого режима в СОМ предусмотрена 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)) {