ThreadingModel='Free', поскольку требования для работы в МТА выглядят как расширенный набор требований соответствия STA. Если разработчик объекта планирует рабочие потоки, которым необходим доступ к объекту, то очень полезно предотвратить создание объекта в каком-либо STA. Дело в том, что рабочие потоки не могут входить в STA, где живет объект, и поэтому вынуждены работать в другом апартаменте. Если класс обозначен ThreadingModel='Both' и запрос на активацию исходит от STA -потока, то объект будет существовать в STA. Это означает, что рабочие потоки (которые будут работать в МТА) должны обращаться к объекту через межапартаментные вызовы методов, значительно менее эффективные, нежели внутриапартаментные вызовы. Тем не менее, если класс помечен как ThreadingModel='Free', то любые запросы на активацию со стороны STA вызовут создание нового экземпляра в МТА, где любые рабочие потоки смогут иметь прямой доступ к объекту. Это означает, что при вызове клиентом, размещенным в STA, методов такого объекта, эффективность будет сниженной, в то время как рабочие потоки будут обрабатываться с большей эффективностью. Это является приемлемым компромиссом, если рабочие потоки будут обращаться к объекту чаще, чем действующий клиент из STA. Было бы весьма соблазнительно смягчить правила СОМ и записать, что не будет ошибкой прямо обращаться к некоторым объектам из более чем одного апартамента. Однако в общем случае это неверно, особенно для объектов, которые используют другие объекты для своей работы.
Для того чтобы объекты могли находиться в апартаментах, отличных от апартаментов клиента, в СОМ предусмотрена возможность экспорта интерфейсов из одного апартамента и импорта их в другой. Чтобы сделать интерфейс объекта видимым вне апартамента этого объекта, нужно экспортировать этот интерфейс. Чтобы сделать внешний интерфейс видимым внутри апартамента, нужно импортировать этот интерфейс. Когда интерфейс импортирован, то результирующий интерфейсный указатель ссылается на заместитель, доступ к которому разрешен для любого потока в импортирующем апартаменте[1]. Обязанностью заместителя является передача управления обратно в апартамент объекта для того, чтобы удостовериться, что все вызовы метода выполняются в нужном апартаменте. Эта передача управления от одного апартамента к другому называется удaленным вызовом метода (method remoting ) и является механизмом действия всех межпотоковых, межпроцессных и межмашинных связей в СОМ.
По умолчанию удаленный вызов метода использует имеющийся в СОМ протокол передачи 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-битный/тот же хост