Шрифт:
Интервал:
Закладка:
• Данный поток не инициировал порождение данного объекта
В этом случае автоматический маршалинг указателя на интерфейс в апартамент потока не проводится и нужно явным образом запросить систему выполнить такой маршалинг.
Процедура состоит из двух шагов
♦ Маршализация указателя на интерфейс
Поток из апартамента, в котором живет интересующий нас объект, формирует нейтральное относительно апартамента представление указателя на интерфейс.
Для этого вызывается функция библиотеки СОМ
♦ WINOLEAPI CoMarshallnterThreadlnterfacelnStream(
♦ [in] REFIID riid,
♦ [in] LPUNKNOWN pUnk,
♦ [out] LPSTREAM *ppStm);
В данную функцию передаются сылка на GUID интерфейса и указатель на этот интерфейс. В качестве результата получаем указатель на доступный для обоих потоков объект-поток (stream object), который поддерживает стандартный интерфейс istream и используется для временного хранения нейтрального представления указателя на запрошенный интерфейс.
♦ Демаршализация указателя на интерфейс
Теперь поток, которому нужен указатель на наш объект, должен выполнить обратную операцию — преобразовать указатель, хранимый в объекте-потоке из нейтральной формы в форму, которую он сможет использовать для получения доступа к объекту через границу между апартаментами. Для этого используется следующая функция
♦ WINOLEAPI CoGetlnterfaceAndReleaseStream(
♦ [in] LPSTREAM pStm,
♦ [in] REFIID riid,
♦ [out] LPVOID FAR* ppv);
Выходной параметр дает указатель на желаемый интерфейс, который на самом деле является указателем на прокси, через который поток может делать вызов через границу между апартаментами. Как и при автоматическом маршалинге интерфейсов здесь используется proxy/stab DLL, содержащая код маршалинга для интересующего нас интерфейса.
Заметим, что описанный прием можно применить и в том случае, когда вызывающий поток и вызываемый объект находятся в одном апартаменте, но никто в данном апартаменте не имеет прямого указателя на этот объект. Такая ситуация может возникнуть, например, когда поток из STA создает объект с потоковой моделью Free, а потом некоторый поток из МТА желает вызвать метод этого объекта. В этом случае никто в МТА не имеет прямого указателя на построенный объект. Только поток в STA имеет указатель на прокси для этого объекта. Необходимо выполнить маршализацию указателя на интерфейс в STA и его демаршализацию в МТА. Система сама обнаружит, что вызывающий поток и вызываемый объект находятся в одном апартаменте и вместо прокси предоставит прямой указатель на объект.
Описанный способ маршалинга указателя на интерфейс имеет определенные недостатки:
• Неоптимальность вызова объекта с потоковой моделью Both
Объект с потоковой моделью Both столь безопасен, что может жить в апартаменте любого типа совместно с объектами любого типа. Если, например, такой объект был создан потоком из некоторого STA апартамента, то этот объект будет создан в этом же апартаменте. И согласно правилам СОМ потоки из всех других апартаментов должны обращаться к данному объекту только через прокси. На самом деле, они могли бы обращаться к нему, используя прямой указатель, т. к. этот объект сможет правильно обработать все вызовы, не зависимо от апартамента, из которого они были сделаны.
Дня использования прямого указателя при вызове из другого апартамента необходимо обмануть СОМ. Этот прием называется Free-Threaded Marshaler (FTM). Вот его суть.
При проектировании объекта с потоковой моделью Both нужно предусмотреть агрегацию этим объектом одного специального уже реализованного в СОМ объекта, поддерживающего специальным образом реализованный стандартный интерфейс IMarshaler.
При использовании пары функций
CoMarshaIinterThreadInterfaceInStream
и
CoGetInterfaceAndReieaseStream
система вначале проверяет — не поддерживает ли объект, указатель на который маршалируется, интерфейс IMarshal. Если такой интерфейс не поддерживается, то используется стандартный маршалинг и мы получаем прокси для доступа к объекту, так как было описано ранее. Если же интерфейс IMarshal реализован в данном объекте, то именно он определяет способ маршалинга.
Упомянутый выше агрегируемый объект реализует интерфейс IMarshal следующим образом. Прежде всего проверяется — выполняется ли маршалинг в рамках одного процесса, или между различными процессами или машинами. В первом случае в объект-поток записывается прямой указатель на запрашиваемый интерфейс, который и получает вызывающий поток при демаршализации указателя на интерфейс. Во втором случае запускается стандартный процесс маршалинга указателя на интерфейс с формированием прокси.
Создать агрегируемый объект, реализующий marshal, можно с помощью функции
CoCreateFreeThreadedMarshaleг.
При использовании описанного приема нужно соблюдать определенные ограничения. Объект с FTM не должен использовать прямые указатели на другие объекты, которые не используют FTM. Причина понятна. При нарушении указанного ограничения некоторый поток может получить косвенным образом прямой указатель на объект из другого апартамента, чья потоковая модель может отличаться от Both. Использование этого указателя может привести к краху всего приложения.
• При демаршализации указателя на интерфейс уничтожается объект-поток
Это означает, что если указатель на некоторый объект нужен потокам в нескольких различных апартаментах, то мы должны несколько раз вызывать пару функций
CoMarshaInterThreadInterfaceInStream
и
CoGetInterfaceAndReleaseStream.
Было бы удобно иметь одно общее дл всего процесса хранилище представленных в нейтральной относительно апартамента форме маршалированных указателей на пользующиеся успехом объекты. Тогда любой поток, которому нужен указатель на объект из другого апартамента, мог бы получить копию маршалированного указателя и демаршалировать его в своем апартаменте, получив соответствующий прокси.
Такая технология реализована в СОМ и называется Global Interface Table (GIT). В классе с GUID CLSID_StdGiobalInterfасеTаЫе реализован интерфейс IGlobalInterfасеаЫе, методы которого позволяют зарегистрировать новый указатель на некоторый интерфейс в этой таблице, получить его из таблицы, удалить указатель из таблицы. При этом автоматически происходит маршализация указателя на интерфейс при его регистрации в таблице и демаршализация при получении из таблицы.
А теперь в заключении рассмотрим механизм вызова метода через границу апартамента.
Начнем с механизма вызова объекта в STA.
При вызове объекта из STA вызов, пришедший из канала, преобразуется в сообщение, которое становится в очередь сообщений, связанную с данным STA. Поток, живущий в STA, выбирает из очереди очередное сообщение, которое через оконную процедуру попадает в стаб нужного интерфейса, где вновь преобразуется в вызов, который и выполняется объектом. Следующее сообщение из очереди выбирается после обработки предыдущего. Тем самым обеспечивается синхронизация вызовов объектов из STA.
Тут, однако, имеется одна тонкость. При выполнении вызова объект из этого STA может вызвать