Two Pilots™    Главная  |  Навигация  |  Программы  |  Скачать  |  Купить  |  Поддержка  |  Издателям  |  OEM

Блог компании Два Пилота

обсуждение разных тем

COM в быту: первые шаги.

Я буду описывать процесс создания COM компонента на примере Visual Studio 2008 с использованием ATL, язык С++.

Для начала создаем проект Visual C++/ATL как показано на картинке:


Жмем «ОК», и выбираем опцию «Allow merging of proxy/stub code»:

Жмем «Finish». В итоге студия создаст нам проект и в Solution Explorer мы увидим такие файлы:

Это, так сказать, «оболочка». Т.е. если сейчас скомпилировать и собрать проект, то на выходе получим DLL файл, в котором пока нет никаких компонентов.

Для того, чтобы в нашем проекте появился некий компонент, изучим файл «FirstCOM.idl» – файл, в котором хранятся описания интерфейсов.

Пока в нем есть только это (за вычетом коментариев):

import "oaidl.idl";
import "ocidl.idl";
[
  uuid(0652E0B7-4360-4542-8D22-8F52940AEFCE),
  version(1.0),
  helpstring("FirstCOM 1.0 Type Library")
]
library FirstCOMLib
{
  importlib("stdole2.tlb");
};

Т.е. пока у нас описана только библиотека типов, которая называется FirstCOM, но нет самих типов. Причем, аттрибутом «version» задана версия нашей библиотеки типов, а «helpstring» – это описание библиотеки.

Добавляем наш компонент. Из контекстного меня нашего проекта выбираем «Add -> Class»:

Выбираем «ATL Simple Object»:

Далее вводим информацию о классе как показано на картинке:

И жмем «Finish». Студия создаст необходимые файлы и добавит в ресурсы константы. Помимо этого, файл «FirstCOM.idl» станет выглядеть так:

import "oaidl.idl";
import "ocidl.idl";
[
  object,
  uuid(5F9CB782-BDE1-48E3-91CC-E8EC0280EFA0),
  dual,
  nonextensible,
  helpstring("IFirstCOMClass Interface"),
  pointer_default(unique)
]
interface IFirstCOMClass : IDispatch
{
};
[
  uuid(FD0B5C1A-6E02-4EFA-958B-046A0AD17381),
  version(1.0),
  helpstring("FirstCOM 1.0 Type Library")
]
library FirstCOMLib
{
  importlib("stdole2.tlb");
  [
      uuid(61A52F01-320D-41E4-B5F2-A59C6E220292),
      helpstring("FirstCOMClass Class")
  ]
  coclass FirstCOMClass
  {
      [default] interface IFirstCOMClass;
  };
};

Т.е. в секцию описания библиотеки (library FirstCOMLib{…}) добавилось описание класса (компонента) с неким интерфейсом IFirstCOMClass. А так же в начало файла добавилось само описание этого интерфейса.

Итого, мы добавили компонент (класс) FirstCOMClass, и указали его интерфейс – IFirstCOMClass. Интерфейс нужен для того, чтобы можно было получить доступ к нашему компоненту.

Теперь необходимо описать те методы и функции, которые будут в нашем компоненте. Пусть, например, мы хотим добавить 2 метода: метод, складывающий 2 числа и возвращающий результат; и метод, вычисляющий длину строки. И еще добавим 1 свойство «Author», с доступом на чтение и запись. Для этого в секцию

interface IFirstCOMClass : IDispatch
{
};

добавляем описания наших методов и свойств. Получим:

interface IFirstCOMClass : IDispatch
{
  [id(1)] HRESULT CalcSumm([in] LONG lParam1, [in] LONG lParam2,
      [out, retval] LONG* retVal);
  [id(2)] HRESULT GetStringLen([in] BSTR strParam,
      [out, retval] LONG* retVal);
 
 
[propget, id(3)]
  HRESULT Author([out, retval] BSTR* retVal);
  [propput, id(3)]
  HRESULT Author(BSTR strValue);
};

Здесь:
[id(N)] – обозначает номер метода/свойства, должен быть уникальным;
[in] – обозначает входной параметр;
[out, retval] – выходной параметр.

Важно. Свойства на чтение и запись имеют как одно имя, так и номер (id).

Теперь нужно описать эти же методы в классе CFirstCOMClass, который будет реализовывать заявленный интерфейс. Ищем файл «CFirstCOMClass.h», в нем видим такое объявление класса:


class ATL_NO_VTABLE CFirstCOMClass :
  public CComObjectRootEx<CComSingleThreadModel>,
  public CComCoClass<CFirstCOMClass, &CLSID_FirstCOMClass>,
  public IDispatchImpl<IFirstCOMClass, &IID_IFirstCOMClass,
      &LIBID_FirstCOMLib, /*wMajor =*/ 1, /*wMinor =*/ 0>

Это описание и позволяет компилятору связать наш COM класс и его интерфейс с реальным С++ классом.

В секцию public, добавляем такой код:

public:
  STDMETHOD(CalcSumm)(LONG lParam1, LONG lParam2, VARIANT_BOOL* retVal);
  STDMETHOD(GetStringLen)(BSTR strParam, VARIANT_BOOL* retVal);
  STDMETHOD(get_Author)(BSTR* sVal);
  STDMETHOD(put_Author)(BSTR sVal);

Кстати, рекомендую так же объявить деструктор и реализацию конструктора и деструктора перенести в файл «CFirstCOMClass.cpp».

Важно. Обратите внимание, что к методам чтения и записи свойства «Author» нужно добавить префиксы «get_» и «put_».

Далее создаем секцию «private» и добавим строку, для хранения значения нашего свойства «Author»:

private:
   CComBSTR m_author;

А в файле «CFirstCOMClass.cpp» пишем:

// FirstCOMClass.cpp : Implementation of CFirstCOMClass#include "stdafx.h"
#include "FirstCOMClass.h" // CFirstCOMClass

CFirstCOMClass::CFirstCOMClass()
{
   // initial value of the property Author
   m_author = OLESTR("Vasya");
}

CFirstCOMClass::~CFirstCOMClass()
{
}

STDMETHODIMP CFirstCOMClass::CalcSumm(LONG lParam1, LONG lParam2, LONG* retVal)
{
   // if pointer is invalid - return error
   if (!retVal)
      return S_FALSE;

   *retVal = lParam1 + lParam2;

   // no errors - return OK
   return S_OK;
}

STDMETHODIMP CFirstCOMClass::GetStringLen(BSTR strParam, LONG* retVal)
{
   // if pointer is invalid - return error
   if (!retVal)
      return S_FALSE;

   CComBSTR str(strParam);
   *retVal = (LONG)str.Length();
   // no errors - return OK
   return S_OK;
}

STDMETHODIMP CFirstCOMClass::get_Author(BSTR* retVal)
{
   // if pointer is invalid - return error
   if (!retVal)
      return S_FALSE;
   
   
   // don't forget to allocate memory for the result string
   *retVal = SysAllocString((BSTR)m_author);
   // no errors - return OK
   return S_OK;
}

STDMETHODIMP CFirstCOMClass::put_Author(BSTR newVal)
{
   CComBSTR newAuthor(newVal);
   m_author = newAuthor;

   // no errors - return OK
   return S_OK;
}

В принципе, наша COM библиотека готова. Мы описали саму библиотеку, класс и интерфейс. А так же добавили реализацию интерфейса. Осталось только узнать как называется наша COM библиотека и класс. Их имена можно найти в файле «FirstCOMClass.rgs». Открывем его и ищем «VersionIndependentProgID»:

VersionIndependentProgID = s 'FirstCOM.FirstCOMClass'

Т.е. библиотека называется «FirstCOM», а класс – «FirstCOM.FirstCOMClass».
Если мы добавим еще несколько классов, то для каждого из них будет создан «.rgs» файл, и там так же будет написано :

VersionIndependentProgID = s 'FirstCOM._имя_класса_'

Собираем проект и в папке «Debug» или «Release» найдем файл «FirstCOM.dll» – это и есть наша COM библиотека. Для того, чтобы пользоваться ей на другом компьютере нужно зарегистрировать ее в системе. Это делается либо через инсталлятор, либо через команду:

regsvr32 FirstCOM.dll

Осталось проверить, работает ли все, что мы написали. Для этого напишем тестовый скрипт на VBS (Visual Basic Script). Создаем текстовый файл «test.vbs» и в нем пишем:

' create COM object, use library name and class name
Set comObj = CreateObject("FirstCOM.FirstCOMClass")

' test CalcSumm method
res = comObj.CalcSumm(1, 2)
WScript.Echo "1 + 2 = " & res

' test GetStringLen method
res = comObj.GetStringLen("QwertY123")
WScript.Echo "Len = " & res

' read property Author
auth = comObj.Author
WScript.Echo "Author = " & auth

' read property Author
comObj.Author = "Petya"
auth = comObj.Author
WScript.Echo "Author = " & auth

' cleanup
Set comObj = Nothing

Сохраняем файл и запускаем его так:

wscript.exe test.vbs

или

cscript.exe test.vbs

Если все сделано правильно, то мы увидим несколько текстовых сообщений, сообщающих нам результаты наших проверок.

Или вот пример использования на PHP:

<?php
// create COM object, use library name and class name
$comObj = new COM("FirstCOM.FirstCOMClass") or die("Can't create COM.");

// test CalcSumm method
$res = $comObj.CalcSumm(1, 2);
echo("1 + 2 = " . $res);

// test GetStringLen method
$res = $comObj.GetStringLen("QwertY123");
echo("Len = " . $res);

// read property Author
$auth = $comObj.Author;
$echo("Author = " . $auth);

// read property Author
$comObj.Author = "Petya";
$auth = $comObj.Author;
echo("Author = " . $auth);

// cleanup
$comObj = null;
?>

Как использовать COM из С/С++ и .NET приложений, вы можете посмотреть, например, здесь.
Скачать исходный код тестового COM проекта можно здесь.

Максим Филимонов.

Оставить комментарий