
- •7.1. Основные определения
- •7.4. Обработка ошибок
- •7.4.1. Возвращаемое значение типа hresult
- •7.4.2. Возврат информации об ошибках
- •8. Стандарт бинарного интерфейса com
- •8.1. Основные определения
- •8.2. Доступ к com-объектам, работа с памятью
- •8.3. Интерфейс iUnknown и наследование интерфейсов
- •8.3.1. Интерфейс iUnknown
- •8.3.2. Получение указателя на интерфейс – QueryInterface
- •8.3.3. Управление временем жизни объекта – подсчет ссылок
- •8.4. Реализация com
- •9. Управление потоками com
- •9.1. Основные определения
- •9.2. Однопоточные и многопоточные апартаменты (sta и mta)
- •9.2.1. Однопоточные апартаменты (sta)
- •9.2.2. Многопоточный апартамент (mta)
- •9.2.3. Взаимодействие апартаментов
- •9.3. Межпроцессорное взаимодействие
- •9.4. Маршалинг
- •9.4.1. Стандартный маршалинг
- •9.4.2. TypeLib-маршалинг
- •9.4.3. Ручной маршалинг
- •9.5. Заключение
9.4.2. TypeLib-маршалинг
Интерфейсы, помеченные атрибутами [oleautomation] или [dual], получают особую обработку. При регистрации библиотеки типов, в которой они описаны, COM добавляет ключ ProxyStubClsid32 и заносит {00020424-0000-0000-C0000-000000000046} в (Default)-значение этого ключа. Это CLSID маршалера с именем PSOAInterface. Этот компонент проживает в OLEAUT32.DLL (OLE automation DLL). Из-за места проживания этот маршалер иногда называют [oleautomation]-маршалером, typelibrary-маршалером или универсальным маршалером. Я буду называть его typelib-маршалером.
Typelib-маршалер не может загрузить proxy/stub-DLL, ведь ее попросту не существует. Вместо этого он в своих процедурах CreateProxy и CreateStub, черпая описание интерфейса из библиотеки типов, на лету создает /Oicf-proxy/stub. Поскольку эффективного пути отыскать ITypeInfo для произвольного интерфейса не существует, то, чтобы узнать описание интерфейса, ему надо знать три составляющие: LIBID библиотеки типов, версию библиотеки типов и IID-интерфейса. IID typelib-маршалер получает в качестве параметров своих методов CreateProxy и CreateStub, а LIBID и версию библиотеки типов должны храниться в ключе реестра HKCR\Interface\{XXX}\TypeLib.
После загрузки ITypeInfo, описывающего интерфейс, typelib-маршалер вызывает одну из двух недокументированных функций: CreateProxyFromTypeInfo для создания proxy или CreateStubFromTypeInfo для создания Stub'а.
Создаваемые typelib-маршалером proxy и stub ничем не отличаются от созданных компилятором MIDL с /Oicf-опцией. Главное отличие в том, что основанные на /Oicf proxy/stub-DLL теоретически должны быстрей создавать первые proxy и/или stub, так как строки формата /Oicf перекомпилированы и не требуют дополнительной обработки. Однако разница может оказаться совсем несущественной, так как библиотеки типов, по сути, хранят прекомпилированную информацию о маршалинге (ITypeInfo::GetMops).
9.4.3. Ручной маршалинг
Когда указатель на интерфейс передается как параметр некоторого метода удаленного объекта, всегда вызывается функция CoMarshalInterface. Она спрашивает объект, которому принадлежит интерфейс, хочет ли он использовать стандартный маршалинг, или желает сам заняться маршалингом. Этот вопрос ставится в форме запроса (через QueryInterface) интерфейса IMarshal. Если объект не поддерживает этого интерфейса (по умолчанию объекты его не поддерживают), то COM предполагает, что связи через стандартные proxy и stub будет более чем достаточно. Однако если вы сказали "да" при вызове QueryInterface, ваш объект получит возможность собственноручно заняться установкой связи с получателем.
Итак, объекты, реализующие интерфейс IMarshal, считаются поддерживающими ручной маршалинг. Для такого объекта stub создаваться не будет. Вместо этого вам дается возможность указать объект, который будет создан в удаленном апартаменте, и послать ему блок байтов произвольной длины. Этот блок передается в одном пакете с сообщением об установке соединения. При получении этого сообщения апартаментом получателя COM создаст inprocess-объект указанного класса. Этот объект носит гордое имя unmarshaler. Он должен реализовать интерфейс IMarshal. Задача этого объекта – прочитать блок предназначенных для него данных и, основываясь на запакованной информации, создать и проинициализировать inprocess-объект, который будет возвращен получателю. Этот объект называется proxy, но это не стандартный proxy, а созданный вручную. Вместо создания нового объекта unmarshaler может просто возвратить получателю указатель на один из своих интерфейсов. При этом функции unmarshaler'а и proxy объединяются «в одном флаконе». В любом случае, получателю должен возвращаться указатель на интерфейс, семантически эквивалентный интерфейсу исходного объекта. Proxy, созданный таким образом, волен делать, что угодно, например, он может использовать очередь MSMQ для организации асинхронного взаимодействия, или создать proxy (по данным, запакованным в присланном блоке) для другого интерфейса того же удаленного объекта с целью организации кэширования данных с блочной подчиткой, или для создания локальной копии объекта (в этом случае в блоке данных должно быть запаковано состояние удаленного объекта).