Взаимодействие динамических типов с COM

93

Теперь давайте рассмотрим другое полезное применение ключевого слова dynamic — в контексте проекта взаимодействия с COM. Если нет опыта разработки для COM, то имейте в виду, что следующий пример, в котором компилируется библиотека COM, содержит метаданные, подобно библиотеке .NET. Тем не менее, ее формат совершенно отличается. По этой причине если программа .NET нуждается в использовании объекта COM, то первое, что нужно сделать — это сгенерировать то, что называется "сборкой взаимодействия" (interop assembly), используя Visual Studio 2010. Делается это довольно легко. Просто откройте диалоговое окно Add Reference (Добавить ссылку), перейдите на вкладку COM и найдите библиотеку COM, которую необходимо использовать:

Библиотеки COM

В случае выбора библиотеки COM среда Visual Studio 2010 отреагирует генерацией совершенно нового описания .NET для метаданных COM. Формально они называются "сборками взаимодействия" и не содержат никакого кода реализации, помимо самого минимума, который помогает транслировать события COM в события .NET. Однако эти сборки взаимодействия очень полезны в том, что защищают кодовую базу .NET от сложностей внутреннего механизма COM.

В коде C# можно напрямую работать со сборкой взаимодействия, позволяя CLR (и DLR, если используется ключевое слово dynamic) автоматически отображать типы данных .NET на типы COM и наоборот. "За кулисами" данные маршализуются между приложениями .NET и COM с использованием оболочки исполняющей среды (Runtime Callable Wrapper — RCW), которая на самом деле является динамически сгенерированным прокси. RCW маршализует и трансформирует типы данных .NET в типы COM, изменяя счетчик ссылок COM-объекта и отображая все возвращаемые COM значения на их эквиваленты в .NET.

На рисунке показана общая картина взаимодействия .NET с COM:

Взаимодействие с объектами COM

Роль первичных сборок взаимодействия

Многие поставщики библиотек COM (таких как библиотеки Microsoft COM, обеспечивающие доступ к объектной модели продуктов Microsoft Office), предоставляют "официальную" сборку взаимодействия, которая называется первичной сборкой взаимодействия (primary interop assembly — PIA). Сборки PIA — это оптимизированные сборки взаимодействия, которые делают яснее (и возможно, расширяют) код, обычно генерируемый при ссылке на библиотеку COM через диалоговое окно Add Reference.

Сборки PIA обычно перечисляются на вкладке .NET диалогового окна Add Reference, подобно базовым библиотекам .NET. Фактически, если вы ссылаетесь на библиотеку COM из вкладки COM диалогового окна Add Reference, то Visual Studio не генерирует новой библиотеки взаимодействия, как делает это обычно, а вместо этого использует предоставленную сборку PIA.

Встраивание метаданных взаимодействия

До появления .NET 4.0, когда приложение C# использовало библиотеку COM (в виде сборки PIA или нет), нужно было обеспечить наличие на клиентской машине копии сборки взаимодействия. Это увеличивало размер установочного пакета приложения, к тому же в сценарии установки должно было проверяться существование сборки PIA и в случае ее отсутствия — установка копии в GAC.

Однако в .NET 4.0 теперь можно встраивать данные взаимодействия непосредственно в скомпилированное приложение .NET. В этом случае поставлять копию сборки взаимодействия вместе с приложением .NET необязательно, поскольку все необходимые метаданные взаимодействия жестко встраиваются в приложение .NET.

По умолчанию, после выбора библиотеки COM (PIA или нет) в диалоговом окне Add Reference интегрированная среда разработки автоматически устанавливает свойство Embed Interop Types (Внедрить типы взаимодействия) библиотеки в True. Чтобы увидеть эту установку, необходимо выбрать ссылаемую сборку взаимодействия в папке References (Ссылки) окна Solution Explorer и открыть ее окно свойств:

Встраивание сборок взаимодействия

Компилятор C# включит только те части библиотеки взаимодействия, которые действительно используются. Таким образом, даже если реальная библиотека взаимодействия содержит .NET-описания сотен COM-объектов, будут получены определения только подмножества, которое действительно используется в написанном коде C#. Помимо сокращения размеров приложения, поставляемого клиенту, также упрощается процесс установки, поскольку не понадобится копировать лишние сборки PIA на целевую машину.

Общие сложности взаимодействия с COM

До выхода версии .NET 4.0 при написании кода C#, имеющего дело с библиотекой COM (через сборку взаимодействия), неизбежно возникало множество сложностей. Например, многие COM-библиотеки определяют методы, принимающие необязательные аргументы, что вплоть до нынешнего выпуска в C# не поддерживалось. Это требовало указания значения Type.Missing для каждого появления необязательного аргумента.

Например, если метод COM принимал пять аргументов, и все они были необязательны, приходилось писать следующий код C#, чтобы принять значения по умолчанию:

myComObj.SomeMethod(
      Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

К счастью, в .NET 4.0 теперь можно писать упрощенный код, учитывая, что значения Type.Missing будут вставлены во время компиляции, если не указано реальное значение:

myComObj.SomeMethod();

В связи с этим стоит отметить, что многие методы COM предлагают поддержку именованных аргументов, которые позволяют передавать значения членам в любом порядке. Поскольку в .NET 4.0 язык C# поддерживает и это средство, можно очень просто "пропускать" множество необязательных аргументов и устанавливать только те, которые важны в данном случае.

Другая сложность взаимодействия с COM была связана с тем фактом, что многие методы COM спроектированы так, чтобы принимать и возвращать очень специфический тип данных по имени Variant. Во многом подобный ключевому слову C# dynamic, типу данных Variant может быть присвоен любой тип данных COM на лету (строка, интерфейсная ссылка, числовое значение и т.п.). До появления ключевого слова dynamic передача или прием элементов данных типа Variant требовал значительных ухищрений, обычно связанных с многочисленными операциями приведения.

С появлением .NET 4.0 и Visual Studio 2010, кода свойство Embed Interop Types устанавливается в True, все типы Variant из COM автоматически отображаются на динамические данные. Это не только сокращает потребность в излишних операциях приведения при работе с типом данных Variant, но также еще более скрывает некоторые сложности, присущие COM, вроде работы с индексаторами COM.

Пройди тесты
Лучший чат для C# программистов