Сообщений 32    Оценка 160        Оценить  
Система Orphus

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

Автор: Алексей Дубовцев
Опубликовано: 19.11.2001
Исправлено: 15.04.2009
Версия текста: 1.0

Введение
Пишем компонент
Основная задача
Что же мы написали?
Регистрация сборки для COM
Процесс использования .NET компонента в COM среде
Дополнительные способы взаимодействия с компонентом

Исходники к статье - Source.zip (12.7K)

Введение

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

Пишем компонент

Основная задача

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

Что же мы написали?

Взглянув на код, вы можете увидеть, что мы используем файл Orbit.snk. (Для IL этот файл задаётся параметром командной строки /KEY). Это пара криптографических ключей, которые будут идентифицировать нашу сборку в GAC (Global Assebly Cache - Глобальный кэш сборок). В него нам впоследствии придется поместить нашу сборку, чтобы ее увидел COM. А все сборки, помещаемые в GAC, должны, как известно, идентифицироваться открытым криптографическим ключом. Эту пару ключей вы можете создать для себя, используя утилиту sn следующим образом:

sn.exe -k Имя_вашего_файла_с_ключом.sn

Теперь сделайте саму сборку. У вас должна получиться DLL, в которой будет находиться наша с вами сборка. Теперь, когда у нас с вами есть эта сборка, давайте заглянем ей "под капот". Смело запускайте утилиту ildasm:

ildasm имя_вашей_сборки.dll /adv

Вот что получилось у меня:


ildasm

Я предлагаю вам немного поизучать эту сборку при помощи ildasm, которою я описывал в статье "Немного о сборках".

Регистрация сборки для COM

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

regasm /tlb Имя_вашей_борки.dll

После выполнения данной команды утилита regasm создаст библиотеку типов COM из вашей сборки и автоматически зарегистрирует её в системном реестре. Если у вас нет необходимости регистрировать библиотеку типов в реестре, вы можете воспользоваться для её создания утилитой tlbexp.exe. Данная утилита предназначена для создания библиотек типов COM из сборок. Если вам нужно создать библиотеку типов программным путём, для этого вам следует использовать класс TypeLibConverter, расположенный в пространстве имен System.Runtime.Interop. Для регистрации же сборок предназначена утилита regasm, которую я упоминал немного выше.

После регистрации в реестре вырисовывается следующая картина:


Реестр

Желтым цветом здесь обозначены ключи, а темно-желтым значения.

Для начала регистрируется ProgID нашего компонента, в данном случае это TestComponentLib.TestComponent, который находится в подключе HKCR\CLSID. По этому ключу COM будет искать CLSID, если его запросят создать компонент при помощи ProgID. К примеру, из какого-нибудь скриптового языка, который не поддерживает CLSID непосредственно. Далее регистрируется CLSID приложения в подключе HKCR\CLSID\{XXX...XXX}. Кстати, это значение всегда будет постоянно и не будет меняться от билда к билду. Далее нас может смутить значение праметра (Default) в ключе ...InprocServer32, оно явно не похоже на нашу библиотеку. И правильно, это среда исполнения .NET. Как COM с ней взаимодействует, я расскажу немного позднее, а сейчас давайте вернемся к реестру. Далее мы с вами видим параметр Assembly, задающий нашу сборку. Вы можете удивиться почему к ней не указано имя: оно в данном случае не нужно, так как сборка храниться в GAC, в котором она будет найдена при помощи параметров Some, Version=1.0.0.0, Culture=neutral, PublicKeyToken=023eff9be46a57df. Здесь будет уместно раскрыть смысл параметров данной строки:

Далее идет название класса и версия среды исполнения, в которой была создана сборка. Затем указана потоковая модель, которую поддерживает компонент. По умолчанию компонент поддерживает обе потоковые модели (STA и MTA).

Процесс использования .NET компонента в COM среде

Для начала взгляните, как использовать созданный нами .NET-компонент при помощи COM. Я умышленно привожу пример только на языке VC++, так как языки более высокого уровня, такие как Visual Basic, практически полностью скрывают от нас процесс создания COM-компонента. Они весьма "любезно" исполняют за нас всю черную работу, на которую я хочу обратить ваше внимание.

#include <windows.h>
#include <stdio.h>
#include <comdef.h>

#import "../Some.tlb" raw_interfaces_only

//Название данного пространства имен должно быть
//эквивалентно имени сборки компонента.
using namespace Some;

void main()
{
  //Инициализируем СOM как STA, хотя значения это, в общем-то, не имеет,
  //так как наш компонент автоматически поддерживает обе потоковые модели.

  if (FAILED(CoInitialize(NULL)))
    return;

   _bstr_t bstrCLSID;
  CLSID   clsidOrbit;

  ITestComponent* pIOrbit;

  HRESULT hr;

  //Это ProgID нашего компонента.
  //Как видите, он складывается из области видимости и имени
  //самого компонента, записанных через точку.
  bstrCLSID = "TestComponentLib.TestComponent";

  //Получаем CLSID (Class ID) через ProgID
  if (FAILED(CLSIDFromProgID (bstrCLSID,&clsidOrbit)))
    return;

  //Создаём объект
  //и сразу же получаем интересующий нас интерфейс ITestComponent
  if (FAILED(CoCreateInstance(clsidOrbit,0,CLSCTX_ALL,__uuidof(ITestComponent),(void**)&pIOrbit)))
      return;


  BSTR  strAbout;

  //Запрашиваем информацию о компоненте
  pIOrbit->get_About(&strAbout);

  //Для вывода используем UNICODE-версию MessageBox
  MessageBoxW(0,strAbout,0,0);


  //Ну а теперь демонстрируем возможности нашего компонента

  long lResult;
  pIOrbit->Mul(5,4,&lResult);

  printf("Multiply 5 * 4 = %d\n",lResult);

  pIOrbit->put_Square(5);
  pIOrbit->get_Square(&lResult);

  printf("Printf square of 5 = %d\n",lResult);

  //Освобождаем интерфейс
  pIOrbit->Release();

  CoUninitialize();
}

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


Таким образом, мы видим, что CoCreateInstance создает в памяти не сам компонент, а библиотеку mscoree.dll, которая является средой исполнения .NET. Эта библиотека предоставляет интерфейс фабрики классов IClassFactory для создания компонентов. При попытке создания компонента через этот интерфейс среда исполнения автоматически создаёт нужный компонент .NET и предоставляет его COM-совместимый интерфейс. При обращениях из среды COM, .NET берет на себя заботу о маршалинге данных из среды в среду.

Дополнительные способы взаимодействия с компонентом

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

#include <windows.h>
#include <stdio.h>

#import "../Some.tlb" raw_interfaces_only


using namespace Some;

void main()
{
  if (FAILED(CoInitialize(NULL)))
    return;

  //Это интеллектуальный указатель, который объявляется в файле Some.tlh
  //при помощи макроса _COM_SMARTPTR_TYPEDEF.
  //Данный указатель базируется на классе _com_ptr,
  //который поставляется вместе с компилятором VC.

  //Мы получаем CLSDID при помощи оператора __uuidof,
  //который извлекает CLSDID, определённый в файле Some.tlh.
  
  //Вся идея в том, что мы не создаем компонент самостоятельно.
  //За нас это делает интеллектуальный указатель, он-то и вызывает
  //CoCreateInstance.
  ITestComponentPtr pIOrbit(__uuidof(TestComponent));

  BSTR  strAbout;

  pIOrbit->get_About(&strAbout);

  MessageBoxW(0,strAbout,0,0);


  long lResult;
  pIOrbit->Mul(5,4,&lResult);

  printf("Multiply 5 * 4 = %d\n",lResult);

  pIOrbit->put_Square(5);
  pIOrbit->get_Square(&lResult);

  printf("Printf square of 5 = %d\n",lResult);


  //Внимание: освобождать интерфейс путём вызова Release()
  //нет необходимости, так как за нас это сделает
  //интеллектуальный указатель.

  CoUninitialize();
}

Далее я приведу примеры использования нашего компонента на разных языках. Сразу извиняюсь за отсутствие примера на Visual Basic. Его не будет, поскольку у меня на компьютере он попросту не установлен. (Прошу не путать Visual Basic и VBScript).

Ну как, впечатляет? Главное, посмотрите, сколько вы затратили усилий на создание компонента. Разве много? Вот и я о том же. Теперь вы можете создавать ваши собственные .NET компоненты и с легкостью, присущей матёрому профессионалу, использовать их как COM-компоненты.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 32    Оценка 160        Оценить