• Сравнение COM и .NET
  • Принципы работы COM
  • Недостатки COM
  • Как работают компоненты .NET
  • COM или .NET?
  • Использование компонентов COM в .NET
  • Диалоговое окно ссылок
  • Оболочки времени выполнения
  • TlbImp.exe
  • Позднее связывание с компонентами COM
  • Использование элементов управления ActiveX в .NET
  • AxImp.exe
  • Ссылка на сборку прокси ActiveX
  • Размещение элемента управления ActiveX в WinForm
  • Использование компонентов .NET в COM
  • RegAsm.exe
  • TlbExp.exe
  • Службы вызова платформы
  • Неуправляемый код и ненадежный код
  • Доступ к неуправляемому коду
  • Недостатки PInvoke
  • Заключение
  • Глава 19

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

    Компоненты COM и компоненты .NET не являются по своей природе совместимыми, так как они опираются на различные внутренние архитектуры. К счастью, однако, компания Microsoft предоставляет средства в SDK .NET для создания прокси COM для компонентов .NET и прокси .NET для компонентов COM. Используя эти прокси вместе с парой других технологий, организации могут использовать унаследованные компоненты COM в своих проектах .NET и могут также использовать компоненты .NET в своих приложениях, не являющихся приложениями .NET.

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

    Сравнение COM и .NET

    COM обозначает "Компонентная объектная модель" (Component Object Model). Для новичков платформы Windows понимание того, что делает COM, может быть трудным, а понимание того, как она действует, может показаться почти невозможным. Для новичков, не интересующихся созданием драйверов устройств или работой на компанию Microsoft, появление .NET и компонентов .NET позволит избежать сложностей программирования COM. Однако, так как COM была до сегодняшнего дня все же существенной частью программирования Windows, желательно быть знакомым с тем, как она действует и какие преимущества она предоставляет.

    Повторное использование кода является одной из священных идей разработки программного обеспечения, и ранее разработчики добивались этого, поддерживая файлы стандартных функций. Когда разработчик хотел использовать стандартную функцию в одной из своих программ, он использовал инструкцию #include для соединения библиотечного файла и файла основной программы. После компиляции стандартные функции и программа, которая их вызывала, статически компоновались в один исполнимый файл.

    Существует, по крайней мере, два недостатка у статической компоновки: она требует излишнего пространства памяти, размещая избыточные копии идентичных функций в нескольких исполнимых файлах, и, если обнаруживается ошибка в одной из стандартных функций, требуется перекомпиляция и повторное распространение всех исполнимых файлов, использующих эту функцию. Чтобы избежать таких проблем, разработчики нашли способ компилировать библиотеки функций в автономные двоичные файлы, которые могут динамически компоноваться различными исполнимыми файлами. С помощью этой схемы несколько исполнимых программ могут совместно использовать один и тот же двоичный файл динамически компонуемой библиотеки (Dynamic Link Library, DLL). Если требуется исправить ошибку в служебной функции, можно просто распространить заново DLL, в которой находится эта функция, без перекомпиляции или повторного распространения всех исполнимых файлов, которые ее используют.

    Несмотря на свое удобство и эффективность, DLL имеют один серьезный недостаток: они зависят от конкретного языка программирования. Другими словами, DLL, компилированная из исходного кода С, может использоваться только из клиентской программы, которая также была написана на С. (Существуют исключения из этого правила, но такие исключения требуют от программиста специальных усилий).

    COM была следующим шагом в эволюции повторного использования кода. С ее помощью программисты могли написать библиотеку классов на таком языке, как C++, откомпилировать библиотеку и использовать классы этой библиотеки из другого совместимого с COM языка программирования, такого как Delphi или Visual Basic. COM был технологией, лежащей в основе других технологий, таких как OLE (Связывание и встраивание объектов) и элементов управления ActiveX.

    Службы COM+ — последняя версия технологии COM, первоначально известная как Сервер транзакций Microsoft, является частью операционной системы Windows 2000, которую компоненты COM могут использовать для общей, необходимой компонентам, функциональности: поддержки транзакций, обеспечения безопасности и события, что позволит сохранить программистам COM ценное время разработки. Более подробно об этом рассказывается в следующей главе.

    Принципы работы COM

    Чтобы понять, почему компоненты COM и компоненты .NET внутренне несовместимы, необходимо иметь общее понимание того, как работает COM. Далее следует существенно упрощенное объяснение. Более детальную информацию можно найти в книге "Professional COM Applications with ATL" издательства Wrox Press (ISBN 1861001703).

    COM устанавливает стандарты для интерфейсов, с помощью которых клиентский код общается с массами компонентов. Так как клиентский код общается с классами компонентов только через эти стандартизованные интерфейсы, он может оставаться полностью несведущим о деталях, специфических для языка программирования, то есть о том, как реализованы эти классы компонентов. Например, используя интерфейсы COM в качестве промежуточных, клиент VB может ссылаться на компоненты COM, первоначально закодированные на C++.

    Интерфейсы COM предоставляют другие возможности в дополнение к межъязыковой коммуникации. Интерфейс COM IUnknown, например, позволяет объекту COM подсчитать число клиентов, которые на него ссылаются, и автоматически выгрузить себя из памяти, когда этот счетчик уменьшится до нуля. Более того, реализуя интерфейсы, распознаваемые службами COM+, класс COM может воспользоваться написанной ранее функциональностью для безопасности, организации пула объектов, и сохранения объектов.

    Каждый класс COM и каждый интерфейс, который он поддерживает, имеет 128-битный идентификатор, который гарантированно является уникальным во все времена и во всех местах. Эти глобально уникальные идентификаторы, или GUID, централизованно хранятся в реестре машины, и поддерживающие COM языки имеют средства для получения значения GUID компонента COM и использования его для вызова функциональности компонента.

    Недостатки COM

    Хотя COM предоставляет значительные преимущества, она имеет также недостатки. Первое: компоненты COM могут быть трудными для кодирования. В C++ разработка компонента COM включает реализацию стандартных интерфейсов COM и использование GUIDGEN.EXE для того, чтобы генерировать GUID для каждого класса и каждого интерфейса. (Хотя технологии, подобные VB и ATL Object Wizard упрощают процесс создания COM, они обеспечивают только подмножество свойств COM).

    Второе: компоненты COM могут оказаться трудными для развертывания. Разработчики COM серверных компонентов предполагали обеспечить совместимость новых версий компонентов с более старыми, но не всегда это удавалось, поэтому установка нового приложения, которое ссылается на новую версию компонента COM, может внезапно дать отказ существующих приложений. Проблемы такого рода называются "адом DLL" и являются причиной большой головной боли и потерь времени.

    Дополнительная информация о том, как .NET обращается с адом DLL, находится по адресу msdn.microsoft.com/library/techart/dplywithnet.htm.

    Как работают компоненты .NET

    Подход .NET к созданию компонентов использует многие возможности COM, игнорируя при этом ее недостатки. Компоненты должны иметь некоторый способ описания клиентам классов, которые они поддерживают. Вместо использования для этого GUID и реестра каждый файл компонента .NET инкапсулирует свое собственное описание во внутреннем сегменте, называемом манифестом (manifest).

    Это означает, что размещение компонента выполняется просто: надо скопировать компонент .NET в папку исполнимого файла, который на него ссылается. Когда исполнимому файлу необходимо создать компонент, он просматривает в файле компонента в манифесте информацию, которая ему нужна. Различные версии одного и того же компонента могут существовать бок о бок на одной машине до тех пор, пока они хранятся в различных папках. (Существует центральная папка для хранения компонентов, которую можно сделать доступной для множества компонентов, она называется иногда "Global Assembly Cache" — Глобальный кэш сборок (см. главу 8).

    Компоненты .NET также легко создавать в C# и VB.NET разработчик защищен от процесса создания манифеста. Разработчик создает просто проект библиотеки классов, заполняет его классами и позволяет компилятору выполнить грязную работу предоставления этих классов клиентам.

    COM или .NET?

    .NET не исключает COM. Многие организации, включая Microsoft, вложили значительные ресурсы в разработку COM. К тому же, поскольку компоненты .NET интерпретируются и используют среду времени выполнения .NET, они не так хорошо подходят, как компоненты COM, для сред выполнения, где скорость и эффективность являются основными требованиями.

    Однако для разработки на уровне предприятия может оказаться, что компоненты .NET окажутся более практичными, чем компоненты COM. В организациях, которые действуют со скоростью Интернет, достаточно хорошее программное обеспечение, сделанное вовремя, предпочтительнее оптимизированного программного обеспечения, которое опоздало на шесть месяцев. Если программное обеспечение запускается с сервера Web, то можно гарантировать, что среда выполнения .NET правильно сконфигурирована, и таким образом получается выигрыш от сохраненного времени разработки и развертывания, что обеспечивает платформа .NET.

    Речь не идет о том, что компоненты COM лучше, чем сборки .NET, или что сборки .NET лучше, чем компоненты COM. Сборки .NET, хотя и требуют больше поддержки во время выполнения, делают компонентную архитектуру более доступной для программистов.

    Использование компонентов COM в .NET

    В типичной организации, вероятно, невозможно (или, по крайней мере, нежелательно) выбросить компоненты COM, которые были в ней разработаны, так как компоненты .NET сделаны хорошо. Следовательно, можно ожидать ссылок на унаследованные компоненты COM из нового кода .NET, по крайней мере, в первых проектах .NET масштаба предприятия. В частности, если создается новое приложение .NET поверх существующей базы данных, вероятно, захочется использовать существующие объекты COM доступа к данным в слое доступа к данным создаваемого проекта. Если доступ к данным происходит через унаследованные компоненты, то .NET реализует бизнес-правила и доставит данные в формы ASP.NET или Windows интерфейса пользователя.

    Взаимодействие COM использует классы "оболочки" и компоненты "прокси" — обычные соглашения в мире программирования в целом. Класс оболочки окружает класс, соответствующий другой архитектуре, предоставляя к нему знакомый интерфейс клиентам, которые не будут распознавать собственный интерфейс завернутого класса. Аналогично, клиент может использовать компонент прокси для доступа к компоненту, который соответствует другой архитектуре или является географически удаленным.

    Диалоговое окно ссылок

    Прежде чем погружаться в тонкости вопросов взаимодействия с унаследованными объектами COM, давайте кратко обсудим, как достичь такого взаимодействия. Наше объяснение строится с точки зрения IDE VS.NET, так как большинство читателей будут использовать его для написания своих программ C#, но альтернативные редакторы могут предоставить свои собственные эквивалентные методы.

    Для этого понадобиться использовать диалоговое окно ссылок (References Dialog), которое доступно из пункта меню Add Reference… в меню Project IDE Visual Studio.NET. Это диалоговое окно имеет три вкладки, первая из которых просто перечисляет DLL, которые являются вспомогательными, но при этом очень важными для среды времени выполнения платформы .NET.

    Самая правая вкладка, Projects, перечисляет все проекты .NET, на которые ссылаются в данном решении. На следующем экране, например, клиентское приложение C# для онлайновых заказов опирается на несколько проектов библиотек классов C#, которые реализуют бизнес-правила. (Конечно, они не обязаны быть проектами C#, чтобы на них ссылался клиент C#, они могут также легко быть проектами VB.NET. Фактически, один клиент .NET может одновременно ссылаться на проекты компонентов на нескольких различных языках платформы .NET).

    Средняя вкладка, COM, является вкладкой для импортирования компонентов COM в проекте .NET.

    В верхнем правом углу диалогового окна имеется кнопка Browse. При нажатии на эту кнопку появится другое диалоговое окно, которое позволит найти в файловой системе DLL COM, требующиеся проекту .NET:

    Когда файл будет найден, добавляем его в список компонентов в панели формы COM:

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

    Посмотрим на следующий пример кода. В нем определен метод для добавления нового заказчика в базу данных. В качестве аргумента ввода этот метод получает ссылку на объект

    CustomerInfo
    , поля которого содержат имя определенного заказчика, код социального обеспечения, и т.д. Экземпляр класса доступа к данным
    CustomerTable
    создается из компонента COM и используется для вставки информации о заказчике в базу данных. В этом примере важно то, что код, связанный с объектом COM, является кодом обыкновенного объекта C#. Мы создаем экземпляр оболочки .NET и позволяем ему делегировать свою работу реальному объекту COM за сценой:

    /// <summary>

    /// Этот код добавляет нового заказчика в базу данных,

    /// обеспечивая при этом выполнение бизнес-правил.

    /// </summary>

    public long AddNewCustomer(CustomerInfo objCustomerInfo) {

     long lngNewCustomerID;

     DataAccess.CustomerTable objCustomerTable;

     // Добавить запись в таблицу заказчиков.

     objCustomerTable = new DataAccess.CustomerTable();

     lngNewCustomerID =

      objCustomerTable.InsertRecord(

      objCustomerInfo.LastName, objCustomerInfо.FirstName,

      objCustomerInfo.MiddleName, objCustomerInfo.SocialSecurityNumber);

    Конечно, если надоест вводить пространство имен компонента COM, то инструкция

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

    // Размещение следующей инструкции в начале файла ...

    using DataAccess;


    // позволяет ссылаться на классы DataAccess по их относительным именам

    CustomerTable objCustomerTable;

    objCustomerTable = new CustomerTable();

    IDE VS.NET будет даже использовать Intellisense, чтобы помочь запомнить членов класса компонента:

    Intellisense поможет также с членами данных и списком аргументов.

    Оболочки времени выполнения

    Чтобы добавить ссылку на DLL COM при использовании диалогового окна References, IDE VS.NET делает некоторую работу за сценой. В частности, создается компонент прокси .NET для DLL COM и копия DLL COM помещается в каталог проекта .NET.

    Вспомните, что компоненты .NET описывают сами себя, в то время как компоненты COM хранят свои описания в реестре. Прокси, который генерирует IDE VS.NET, описывает DLL COM и служит для нее в качестве делегата, пересылая вызовы от клиента .NET через службы COM в DLL COM, которую он обертывает. Клиент .NET не знает, что он вызывает компонент COM; он общается только с прокси и получает данные, которые прокси пересылает обратно из DLL COM.

    В терминологии .NET такой прокси называется оболочкой времени выполнения, или RCW. IDE создает DLL, которая имеет такое же имя, как и исходный компонент COM, но является в действительности .NET RCW, которая просто обертывает первоначальный компонент, предоставляя его клиентам .NET через интерфейс .NET, который они могут понять. Интересное дополнительное замечание: IDE VS.NET создает оболочку не только для каждой импортируемой DLL COM, но для каждой DLL COM, на которую ссылается импортированная DLL COM в своем открытом интерфейсе, поэтому в этой папке будет также находиться файл

    ADODB.dll
    , на который в
    DataAccess.dll
    существует ссылка.

    В рассматриваемом примере DLL COM,

    DataAccess.dll
    предоставляет методы для вставки, извлечения, обновления и удаления записей в нескольких таблицах базы данных. Так как методы вставки возвращают множества записей ADO, клиентам компонента COM требуется ссылка на библиотеку типов ADO. При импорте
    DataAccess.dll
    среда разработки распознает эту необходимость и автоматически создает также RCW для компонента COM ADO.

    TlbImp.exe

    Опасно ли для RCW, созданной IDE VS.NET, иметь такое же имя, как и исходная DLL COM? В конце концов, если приложение переносится с одной машины на другую, будет очень легко спутать RCW с DLL COM, которую он заворачивает, и случайно перезаписать файл DLL COM. Или можно по ошибке попытаться зарегистрировать RCW в службах COM, удивляясь, почему программа регистрации (

    regsvr32.exe
    ) не работает.

    Чтобы избежать таких проблем, попробуйте воспользоваться напрямую утилитой

    TlbImp.exe
    . Поставляемая вместе с SDK .NET эта исполняемая программа специализируется на создании прокси .NET для DLL COM. Так как она вызывается из командной строки, можно задать
    TlbImp.exe
    с аргументом командной строки
    out
    , чтобы получающийся RCW имел имя, отличное от имени DLL COM.

    TlbImp
    является сокращением от Type Library Importer (Импортер библиотеки типов). При выполнении этой программы с DLL COM она запрашивает библиотеку типов DLL COM и транслирует информацию оттуда в формат .NET, преобразуя стандартные типы данных COM в типы, распознаваемые .NET. После выполнения
    TlbImp.exe
    для DLL COM, файл вывода (RCW) просто помещается в папку исполняемого файла клиента, который будет его использовать. (Это дополнительный шаг, который необходим при явном использовании
    TlbImp.exe
    вместо диалогового окна References в IDE.) В приведенном выше примере TlbImp.exe выполняется с DLL COM, которая находится в той же папке, что и
    TlbImp
    , но
    TlbImp.exe
    работает, как любая другая утилита командной строки, т.е. можно определить файл в другой папке с помощью абсолютного или относительного пути доступа.

    В заключение пара предупреждении о

    TlbImp.exe
    и RCW. Первое: не забывайте задавать аргумент
    out
    при использовании
    TlbImp.exe
    . Если не сделать этого, то программа
    TlbImp.exe
    будет заявлять, что она не может перезаписать исходный файл:

    Второе: помните, что хотя RCW служит в качестве посредника между компонентом COM и клиентом .NET, который его вызывает, по-прежнему всю реальную работу делает компонент COM. То есть необходимо выполнить те же требования к развертыванию компонента COM, которые пришлось бы сделать, если бы он использовался напрямую. Это означает, что завернутый компонент COM тем не менее надо регистрировать в службах COM. Если попробовать сослаться на незарегистрированный компонент COM, то IDE VS.NET будет порождать ошибку.

    Чтобы справиться с этой проблемой, понадобится, конечно, программа регистрации COM —

    regsvr32.exe
    . Ее можно вызвать из диалогового окна Windows Run, которое доступно из меню Start рабочего стола.

    Поэтому не забывайте регистрировать компоненты COM.

    Позднее связывание с компонентами COM

    Прежде чем исполнимый файл клиента сможет вызвать методы и свойства объекта компонента, ему необходимо узнать адреса памяти этих методов и свойств. Существуют две различные технологии, которые клиентские программы могут использовать для определения этих адресов.

    Программы с ранним связыванием узнают адреса на ранней стадии процесса компиляции/выполнения — во время компиляции. Когда программа (ранним связыванием компилируется, компилятор использует библиотеку типов компонента для включения адресов методов и свойств компонента в клиентскую исполнимую программу, чтобы к адресам можно было очень быстро и безошибочно обращаться. Технологии взаимодействия COM которые были рассмотрены до сих пор, используют раннее связывание.

    Со своей стороны, программы с поздним связыванием узнают адреса свойств и методов на поздней стадии процесса компиляции/выполнения, в тот самый момент, когда эти свойства и методы вызываются. Код с поздним связыванием обычно обращается к клиентским объектам через базовые типы данных, такие как

    object
    , и использует среду времени выполнения для динамического определения адресов методов. Хотя код с поздним связыванием позволяет использовать некоторые сложные технологии программирования, такие как полиморфизм, он требует некоторых связанных расходов, которые мы вскоре увидим. 

    Но сначала проверим, как позднее связывание выполняется с помощью отражения в C# (Отражение, является способом, который используется кодом во время выполнения для определения информации об интерфейсах серверных классов; см. главу 5.) 

    При позднем связывании с объектом COM в программе C# не нужно создавать RCW для компонента COM. Вместо этого вызывается метод класса

    GetTypeFromProgID
    класса Type для создания экземпляра объекта, представляющего тип объекта COM. Класс
    Type
    является членом пространства имен
    System.Runtime.InteropServices
    и в коде ниже мы конфигурируем объект
    Type
    для того же компонента COM доступа к данным, который использовался в предыдущих примерах:

    using System.Runtime.InteropServices;

    Type objCustomerTableType;

    objCustomerTableType = Type.GetTypeFromProgID("DataAccess.CustomerTable");

    Когда имеется объект

    Type
    , инкапсулирующий информацию о типе объекта COM, он используется для создания экземпляра самого объекта COM. Это реализуется передачей объекта
    Type
    в метод класса
    CreateInstance
    класса
    Activator.CreateInstance
    создает экземпляр объекта COM и возвращает на него ссылку позднего связывания, которую можно сохранить в ссылке типа object.

    object objCustomerTable;

    objCustomerTable = Activator.CreateInstance(objCustomerTableType);

    В этом месте код C# имеет ссылку позднего связывания на готовый экземпляр класса COM.

    К сожалению, невозможно вызывать методы непосредственно на ссылке типа

    object
    . Чтобы можно было обратиться к объекту COM, необходимо использовать метод
    InvokeMember
    объекта
    Type
    , который был создан вначале. При вызове метода
    InvokeMember
    ему передается ссылка на объект COM вместе с именем вызываемого метода COM, а также массив типа
    object
    всех входящих аргументов метода.

    ObjCustomerTableType.InvokeMember("Delete", BindingFlags.InvokeMethod, null, objCustomerTable, aryInputArgs);

    Напомним еще раз последовательность действий:

    1. Создать объект

    Type
    для типа объекта COM с помощью метода класса
    Type.GetTypeFromProgID()
    .

    2. Использовать этот объект

    Type
    для создания объекта COM с помощью
    Activator.CreateInstance()
    .

    3. Методы вызываются на объекте COM, вызывая метод

    InvokeMember
    на объекте
    Type
    и передавая в него ссылку
    object
    в качестве входящего аргумента. Ниже приведен пример кода, объединяющий все это в один блок:

    using System.Runtime.InteropServices;

    Type objCustomerTableType;

    object objCustomerTable;

    objCustomerTableType=Type.GetTypeFromProgID("DataAccess.CustomerTable");

    objCustomerTable=Activator.CreateInstance(ObjCustomerTableType);

    objCustomerTableType.InvokeMember("Delete", BindingFlags, InvokeMethod, null, objCustomerTable, aryInputArgs);

    objCustomerTableType = Type.GetTypeFromProgID("DataAccess.CustomerTable");

    Хотя средства позднего связывания C# позволяют избежать трудностей RCW, необходимо знать о некоторых, связанных с этим, недостатках.

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

    InvokeMember()
    , что может породить ошибку во время выполнения.

    Второй: позднее связывание может быть медленным. Каждый раз при использовании

    InvokeMember()
    на объектной ссылке среда времени выполнения должна найти требуемый член в библиотеке функций класса COM. Это приводит к снижению производительности программы.

    Третий: написание кода с поздним связыванием может оказаться трудоемким. Так как не требуется ссылаться на библиотеку типов компонента COM, IDE VS.NET не может использовать Intellisense, чтобы помочь с именами членов и списками аргументов, поэтому в коде могут появиться ошибки, которые будет трудно найти до времени выполнения.

    Использование элементов управления ActiveX в .NET

    Элемент управления ActiveX является частным типом компонента COM, который поддерживает специальное множество интерфейсов для обеспечения графического представления. Также, как можно импортировать стандартные компоненты COM для использования в проектах .NET, можно также импортировать элементы управления ActiveX. Это позволяет сделать утилита

    AxImp.exe
    .

    AxImp.exe

    Чтобы импортировать компонент ActiveX в среду .NET, утилита

    AxImp.exe
    вызывается из командной строки. Команда состоит из двух частей:

    1. Имени

    AxImp
    .

    2. Абсолютного или относительного пути доступа к файлу ActiveX (

    *.осх
    ), который должен быть импортирован.

    Для примера рассмотрим снимок экрана, приведенный ниже. Здесь импортируется элемент управления ActiveX Win32 MAPI и определяется расположение файла

    .осх
    (
    C:\windows\system\msmapi32.осх
    ):

    Как можно видеть, программа

    AxImp.exe
    выводит два файла.

    Первый выходной файл

    MSMAPI.dll
    является прокси для сборки. Он позволяет ссылаться на компонент ActiveX, как если бы это был неграфический объект, с помощью его методов и свойств таким же образом, каким используются методы и свойства неграфического класса.

    Второй файл

    AxMSMAPI.dll
    является элементом управления Windows. Он позволяет использовать графический аспект импортированного элемента управления ActiveX как элемент управления Windows в проектах Forms Windows .NET.

    При использовании

    AxImp
    желательно убедиться, что для этих двух выходных файлов предоставляются явные и уникальные имена, так как, по крайней мере, в бета-версии SDK .NET
    AxImp.exe
    имеет потенциальную возможность перезаписывать входные файлы без предупреждения.

    Ссылка на сборку прокси ActiveX

    Как сказано выше, вывод сборки прокси утилитой AxImp.exe позволяет ссылаться на компонент ActiveX программным путем без использования графических аспектов. Для этого необходимо просто добавить ссылку на прокси сборки с помощью вкладки Reference в IDE VS.NET после того, как AxImp.exe сделает свою работу:

    После создания ссылки на сборку прокси можно использовать в коде программы компонент ActiveX. В примере ниже используется прокси

    MSMAPI32.осх
    для отправки почтового сообщения:

    public static int Main() {

     MAPISession objSession=new MAPISession();

     MAPIMessages objMessage=new MAPIMessages();

     objSession.Password="password";

     objSession.UserName="me@mydomain.com";

     objSession.SignOn();

     objMessage.SessionID=objSession.SessionID;

     objMessage.Compose();

     objMessage.MsgSubject="MAPI Contol Test";

     objMessage.MsgNoteText="The message body.";

     objMessage.RecipAddress="you@yourdomain.com";

     objMessage.ResolveName();

     objMessage.Send(null);

     objSession.SignOff();

     return 0;

    }

    Размещение элемента управления ActiveX в WinForm

    Также достаточно просто разместить элемент управления ActiveX в форме Windows. Чтобы сделать это, сначала запустим диалоговое окно Customize Toolbox из IDE VS.NET, щелкнув правой кнопкой мыши в панели инструментов IDE и выбрав соответствующий пункт в контекстном меню. Когда появится диалоговое окно, надо перейти на вкладку .NET Framework Components и найти созданный утилитой

    AxImp.exe
    файл элемента управления Windows. После закрытия диалогового окна Customize Toolbox импортированный элемент управления появится в панели управления IDE и можно будет добавить его в Windows Form таким же образом, каким добавляются любые другие элементы управления Windows.

    Использование компонентов .NET в COM

    Также, как используются компоненты COM и элементы управления ActiveX в коде .NET, можно применять компоненты .NET в стандартном коде Windows. Только несколько свойств сборок .NET недоступны через COM, включая параметризованные конструкторы, статические методы и константные поля. Кроме того, доступ к перегруженным методам .NET из COM требует дополнительной работы.

    RegAsm.exe

    Применение компонентов COM в коде .NET требует другой утилиты, которая существует в SDK .NET, являющейся аналогом программы импорта библиотеки типов, которую мы видели ранее. Название этой утилиты —

    RegAsm.exe
    .

    Название утилиты

    RegAsm
    (Register Assembly) обозначает ее функцию, она отвечает за ввод информации о типе компонента .NET в системный реестр, чтобы службы COM могли к нему обратиться. После регистрации компонента .NET с помощью RegAsm, стандартные клиенты Windows могут сделать позднее соединение с классами компонента. Процесс регистрации компонента должен быть сделан только один раз. После регистрации все клиенты COM могут к нему обращаться.

    В качестве примера рассмотрим следующий код. Он принадлежит классу в библиотеке классов .NET. Функция получает просто число в качестве аргумента ввода и возвращает факториал этого числа:

    namespace Factorial {

     using System;


     public class Factorial {

      // Этот метод вычисляет факториал числа

      public int ComputeFactorial(int n) {

       int intFactorial=n;

       for (int i = 1; i<n; i++) {

       intFactorial*=i;

      }

      return intFactorial;

     }

     }

    }

    После компиляции класса примера в сборку .NET можно зарегистрировать эту сборку в службах COM с помощью

    RegAsm.exe
    :

    Теперь, когда сборка зарегистрирована в службах COM с помощью

    RegAsm
    , можно выполнить позднее связывание со сборкой .NET через службы COM. С целью демонстрации создадим простой сценарий VB, который это делает. (Сценарий VB можно создать с помощью текстового редактора, такого как Notepad; введите просто следующий код и сохраните файл с расширением
    .vbs
    . Предполагая, что Windows Script Host установлен, файл при вызове будет выполняться как сценарий. Помните, что сценарий VB выполняет позднее связывание для компонентов COM.)

    Option Explicit

    Dim objFactorial

    Dim lngResult

    Dim lngInputValue

    Set objFactorial=CreateObject("Factorial.Factorial")

    lngInputValue=InputBox("Numbers?")

    IngResult=objFactorial.ComputeFactorial(CLng(lngInputValue))

    Call MsgBox(lngResult)

    Однако, прежде чем это можно будет использовать, необходимо установить сборку в глобальный кэш. Для этого сначала создадим устойчивое имя сборки с помощью следующей команды:

    sn -k Factorial.dll

    Далее необходимо создать файл

    AssemblyInfo.cs
    со следующим содержимым:

    using System.Reflection;

    [assembly: AssemblyKeyFile("factorial.snk")]

    Затем это надо откомпилировать с помощью следующей команды, чтобы превратить в модуль:

    CSC /t:module /out: AssemblyInfo.dll AssemblyInfo.cs

    После этого компилируется файл

    Factorial.cs
    , и полученная DLL устанавливается в глобальный кэш с помощью
    gacutil
    следующим образом:

    csc /t:library /addmodule:assemblyinfо.dll Factorial.cs gacutil /i Factorial.dll

    При выполнении сценарий VB воспользуется службами COM для создания объекта .NET, вызовет метод на этом объекте и выведет возвращаемое из объекта .NET значение в окне сообщения:


    Интересная техника, не правда ли? Но она не решает ни одной из упомянутых выше проблем, связанных с поздним связыванием. К счастью, другой член набора инструментов SDK .NET может в этом помочь. Это утилита

    TlbExp.exe
    .

    Однако, прежде чем попрощаться с

    TlbImp.exe
    , необходимо запомнить одну вещь: службы COM должны иметь возможность найти компонент сборки .NET, когда он вызывается. Это означает, что сборка должна располагаться в рабочей папке клиента или в глобальном коде сборки системы.

    TlbExp.exe

    TlbExp
    обозначает Type Library Exporter (экспортер библиотеки типов). При выполнении на файле сборки .NET
    TlbExp
    может запросить внутренний манифест этой сборки и вывести соответствующий файл библиотеки типов COM (
    *.tlb
    ). Когда TlbExp создаст файл библиотеки типов для компонента .NET, языки разработки не принадлежащие .NET, такие как VB6, смогут ссылаться на него, используя для того, чтобы получить эффективное раннее связывание с компонентами .NET:

    Как можно было уже догадаться, утилиты

    TlbExp
    и
    RegAsm
    были созданы для работы в тесном взаимодействии.
    RegAsm
    используется для регистрации компонента .NET в службах COM, а затем
    TlbExp
    — для экспорта библиотеки типов COM клиенту и ссылки на нее из языков не принадлежащих .NET.

    Службы вызова платформы

    Мы рассказали о взаимодействии между компонентами COM и .NET. Теперь поговорим о другом виде — о взаимодействии между кодом .NET и так называемом неуправляемом коде. Его обеспечивает технология, называемая службами вызова платформы, или, кратко,

    PInvoke
    .

    Неуправляемый код и ненадежный код

    Первое, о чем необходимо знать, — термины неуправляемый и ненадежный не являются синонимами.

    Ненадежный код C# является кодом, который встроен в блок с префиксом в виде ключевого слова

    unsafe
    . Код в таком блоке может использовать весь диапазон идиом C++, таких как указатели и массивы на основе стека. Он считается ненадежным, так как такие идиомы часто ассоциируются с ошибками, но такой код по-прежнему управляется средой времени выполнения .NET.

    Со своей стороны, неуправляемый код не управляется средой времени выполнения .NET. Когда поток выполнения приложения .NET входит в сегмент неуправляемого кода, среда времени выполнения .NET больше не имеет контроля над действиями этого кода, и не может обеспечить для него сборку мусора или правила безопасности (по этой причине приложения, которые используют неуправляемый код, должны наделяться доверием со стороны системного администратора).

    Службы вызова платформы позволяют коду .NET взаимодействовать не только с ненадежным кодом, но и с достоверно неуправляемым.

    Доступ к неуправляемому коду

    Хотя .NET может взаимодействовать с неуправляемым кодом в любой DLL, чаще всего он взаимодействует с кодом в DLL, составляющим базовую функциональность Windows API. Это включает

    user32.dll
    ,
    gdi32.dll
    и
    kernel32.dll
    . Процесс представления функций из этих DLL для кода .NET должен быть знаком любому, кто использовал ключевое слово
    Declare
    для предоставления вызовов Win32 API для кода VB6:

    [sysimport(dll="user32.dll")]

    public static extern int MessageBoxA(int Modal, string Message, string Caption, int Options);

    В приведенном примере с помощью оболочки .NET вызова осуществляется вызов Windows API, который выводит окно с сообщением. В атрибуте выше функции оболочки определяется DLL, которой функция в оболочке должна делегировать свою работу. Теперь клиент кода .NET может вызывать функцию оболочки для вызова функций API:

    MessageBoxA(0, "PInvoke worked!", "PInvoke Example", 0);

    Хотя здесь для функции оболочки задано такое же имя, как и для вызова Windows API, в который она отображается, можно задать для нее и другое имя, как делалось в примере выше. Изменим теперь имя "

    MessageBox
    " на имя, которое более точно определяет, как будет использоваться вызов API. Сделаем это, определяя дополнительное значение в атрибуте
    sysimport
    :

    [sysimport (dll="ustr32.dll", name="MessageBoxA") ]

    public static extern int ErrorMessage(int Modal, string Message, string Caption, int Options);

    При переименовании вызова Windows API таким образом клиенты могут вызывать функцию с новым именем:

    ErrorMessage(0, "PInvoke worked!", "PInvoke.Example", 0);

    Недостатки PInvoke

    Мы видели, что достаточно просто ссылаться и вызывать неуправляемую функцию из кода .NET. К сожалению, существуют потенциальные недостатки использования неуправляемого кода.

    Хотя Microsoft сознательно откладывает вопрос взаимодействия платформ, многие люди подозревают, что оно уже на горизонте для платформы .NET. При взаимодействии платформ можно выполнить программу .NET на любой платформе — от Macintosh до Unix при условии, что платформ? обеспечена средой времени выполнения .NET. Однако при использовании PInvoke, код .NET соединяется с операционной системой Windows.

    Рассматривая использование PInvoke, прежде всего необходимо проверить, что требуемая функциональность не представлена базовым классом .NET. Если среда времени выполнения .NET будет когда-либо перенесена на другую платформу, базовые классы .NET также будут перенесены, и код получит шанс правильно выполниться на новой платформе, возможно, с небольшими изменениями.

    Заключение

    Как показывает эта глава, COM и .NET являются различными технологиями, которые способны работать совместно, если применяются соответствующие технологии. Используя утилиты взаимодействия, такие как

    TlbImp.exe
    ,
    RegAsm.exe
    и
    TlbExp.exe
    , разработчики могут применять унаследованные компоненты COM в качестве строительных блоков для новых приложений .NET.

    По сравнению с компонентами COM сборки легче создавать, развертывать и поддерживать. Маловероятно, что .NET когда-либо полностью заменит разработчикам COM, для которых скорость выполнения является основной задачей. Однако, скорее всего, разработчики приложений Web и программ, которые внутренне используются организациями, сочтут сборки .NET избавлением от ада DLL.







     

    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх