Создание COM-сервера
Для создания COM-сервера Delphi предоставляет широкий набор мастеров, автоматизирующих выполнение рутинных задач и позволяющих программисту сконцентрироваться на реализации функциональности. Мастера доступны при помощи команды меню File->New, на закладке ActiveX
Чтобы сделать COM-сервером EXE-файл, Вы должны просто добавить в него модуль с COM-объектом. Для создания COM-сервера в виде DLL Вы должны сначала создать библиотеку, оформленную с учетом требований COM. Это делается при помощи мастера «ActiveX Library». При его выборе будет создан новый проект, реализующий DLL и сгенерирован следующий код:
library Project1; uses ComServ; exports DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer; {$R *.RES} begin end.
Созданная DLL экспортирует функции, необходимые для работы COM и Вы можете не заботиться о рутинной работе и сразу приступить к реализации COM-сервера.Для этого выберите мастер «COM-объект».
От заполнения полей этой формы зависит реализация создаваемого COM-объекта
ClassName Имя класса Delphi, реализующего COM-сервер. Мастер создаст заготовку класса с этим именем. Под этим же именем COM-сервер будет зарегистрирован в реестре.Instansing Определяет режим создания COM-объектов. Параметр может принимать следующие значения: Internal Объект может использоваться только внутри этого приложения. Single Instance Создание каждого экземпляра объекта приводит к запуску нового экземпляра приложения-сервера. После создания объекта «фабрика объектов» приложения удаляет информацию о себе из системного списка зарегистрированных фабрик, что заставляет COM при создании нового объекта запустить приложение сервер в новом процессе Multiple Instance После создания экземпляра объекта «фабрика» не удаляет себя из списка зарегистрированных. При создании запросе на создание нового объекта COM обнаружит её в этом списке и запросит создание у той же фабрики и новый экземпляр объекта будет создан в том же приложении. Другими словами для создания всех объектов данного типа будт запущено не более одного экземпляра сервера. Этот параметр имеет смысл только для EXE-серверов, для DLL он игнорируется.
Threading Model Потоковая модель сервера. Действие этого параметра зависит от типа сервера (EXE или DLL) Single Нет никакой поддержки потоков. Для DLL-сервера при регистрации не будет создан параметр ThreadingModel Для EXE-сервера указание этого параметра (в отличие от любого другого) не приведет к установке флага IsMultiThread, будет создана STA. Обычно эта модель используется для Internal серверов Apartment Для DLL сервера в реестре будет создан параметр ThreadingModel= Apartment, для EXE – создана STA Free Для DLL сервера в реестре будет создан параметр ThreadingModel= Free, для EXE – создана MTA Both Для DLL сервера в реестре будет создан параметр ThreadingModel= Both, для EXE – создана MTA
Include Type Library Установка этого флажка приводит к включению в сервер библиотеки типов – специального двоичного ресурса, описывающего реализуемые сервером интерфейсы, их методы и параметры вызова. COM предоставляет стандартные средства работы с библиотеками типов. В частности, Delphi может импортировать имеющуюся в сервере библиотеку типов и автоматически построить по ней интерфейсный модуль для работы с ним. При использовании библиотеки типов интерфейсы описываются при помощи Type Library Editor. Объект наследуется от TTypedComObject Если этот флажок выключен, то объект наследуется от TComObject. Это более легковесная реализация сервера.Implemented Interfaces Это поле разрешено, только если объект не использует библиотеку типов. В этом случае Вы должны сами описать интерфейсы в коде своей программы и перечислить их в этом поле, например «ITest, IAnotherTest»
Mark interface Oleautomation Установка этого флага делает COM-сервер совместимым с OLE Automation. Вы должны использовать в методах интерфейса только совместимые с OLE Automation типы данных. Это необходимо, если Вы хотите передавать ссылку на интерфейс между разными «комнатами». Такая операция, называемая маршаллингом интерфейсов требует написания специальной proxy/stub DLL. Однако, если интерфейс помечен как Oleautomation эту работу выполняет маршаллер OLE и Вы будете избавлены от лишней работы.! Для поддержки Oleautomation маршаллинга необходимо, чтобы:
• | сервер был унаследован от TTypedComObject (реализация IDispatch не обязательна) |
• | все методы интерфейса должны быть объявлены как safecall. Если Вы создаете интерфейс унаследованный от IUnknown, то по умолчанию все его методы объявляются как stdcall. Чтобы создать safecall методы необходимо в диалоге Tools->Environment Options на закладке Type Library установить переключатель Safecall function mapping в значение All v-table interfaces |
Сервер без библиотеки типов
Такой сервер, если он не реализует интерфейс IMarshall, может работать только в одной «комнате» с клиентом, поэтому его следует использовать только для In-Process серверов с потоковой моделью идентичной клиенту.
При создании сервера, не включающего библиотеку типов Вы должны указать мастеру реализуемые им интерфейсы. Укажем имя интерфейса ITest. По завершении работы мастера будет создан следующий модуль:
unit Unit1; interface uses Windows, ActiveX, Classes, ComObj; type TTest = class(TComObject, ITest) protected end; const Class_Test: TGUID = '{1302FB00-703F-11D4-84DD-825B45DBA617}'; implementation uses ComServ; initialization TComObjectFactory.Create(ComServer, TTest, Class_Test, 'Test', '', ciMultiInstance, tmApartment); end.
! Если Вы создаете COM-сервер, который может использоваться различными клиентами (а не только в рамках конкретного проекта, в котором спецификации клиентов жестко заданы) не рекомендуется делать серверы без поддержки маршаллинга данных, поскольку в этом случае невозможно обеспечить гарантированное нахождение его в одной «комнате» с клиентом. Если вы все же делаете такой сервер – необходимо отразить требуемые спецификации клиента в документации на него.
Посмотрим на сгенерированный код подробнее. Особый интерес вызывает секция Initialization. В ней создается экземпляр «фабрики объектов» - COM-сервера, реализующего интерфейс IClassFactory2. К нему COM будет обращаться для создания экземпляра объекта Test. VCL автоматически реализует всю рутинную работу по взаимодействию с COM.
Для реализации сервера требуется написать интерфейсный модуль с описанием реализуемого интерфейса. Кроме этого, вынесем в него описание константы Class_Test и добавим его в строку uses модуля Unit1
unit TestInterface; interface const Class_Test: TGUID = '{1302FB00-703F-11D4-84DD-825B45DBA617}'; type ITest = interface ['{1C986802-6D6D-11D4-84DD-996A491CE716}'] procedure ShowIt(S: String); end; implementation end.
Этот модуль содержит всю необходимую информацию для использования сервера и должен использоваться при компиляции клиента.
Дополним код COM-объекта реализацией методов реализуемого интерфейса:
unit Unit1; interface uses Windows, ActiveX, Classes, ComObj, TestInterface; type TTest = class(TComObject, ITest) protected procedure ShowIt(S: String); end; implementation uses ComServ; { TTest } procedure TTest.ShowIt(S: String); begin MessageBox(0, PChar(S), NIL, 0); end; initialization TComObjectFactory.Create(ComServer, TTest, Class_Test, 'Test', '', ciMultiInstance, tmApartment); end.
Откомпилировав проект, мы получим файл Project1.dll
Последним шагом является регистрация COM-сервера.
Введем в командной строке: «regsvr32 project1.dll»
Если все было проделано правильно, на экране должно появиться сообщение об успешной регистрации: «DllRegisterServer in Project1.dll succeeded».
Можно приступать к написанию клиента. Для этого создадим новый проект, добавим в модуль с его главной формой строку uses TestInterface и напишем следующий код:
uses TestInterface, ComObj; procedure TForm1.Button1Click(Sender: TObject); var Test: ITest; begin Test := CreateComObject(Class_Test) as ITest; Test.ShowIt('Hi'); end;
Как видно из этого примера создание и использование COM-сервера не сложнее, чем работа с обычными классами Delphi. Сервер без библиотеки типов является хорошим выбором для реализации COM-серверов, используемых внутри проекта, поскольку для его использования нужен интерфейсный модуль. При передаче сервера другим разработчикам Вам придется передать им этот модуль и, при необходимости, перевести его на другой язык (например, С).
Сервер с библиотекой типов
Библиотека типов – это специальный двоичный ресурс, описывающий интерфейсы и методы, реализуемые COM-сервером. Кроме наличия библиотеки типов сервер должен поддерживать интерфейс IProvideClassInfo. В Delphi такой сервер реализуется путем наследования его от TTypedComObject. Для этого оставьте флажок Include Type Library в мастере создания COM-объекта включенным.
Создадим COM-сервер в виде EXE (разумеется, он может быть также создан и виде DLL).
Сначала создадим новый проект (File-New Application), а затем добавим в него COM-объект.
Если не отключать флажок Include Type Library, то мастер создаст уже не один, а два модуля. Первый из них напоминает созданный ранее.
unit Unit1; interface uses Windows, ActiveX, Classes, ComObj, Project1_TLB, StdVcl; type TTest1 = class(TTypedComObject, ITest1) protected {Declare ITest1 methods here} end; implementation uses ComServ; initialization TTypedComObjectFactory.Create(ComServer, TTest1, Class_Test1, ciMultiInstance, tmApartment); end.
Наиболее интересна строка: uses … Project1_TLB. Это автоматически сгенерированный интерфейсный модуль к нашему COM-объекту (аналогично TestInterface.pas в предыдущем примере). Он содержит описание всех необходимых для работы с сервером интерфейсов. В отличие от пердыдущего примера Вы не должны редактировать его вручную. Для этого Delphi откроет редактор библиотеки типов
Это специализированный редактор для описания интерфейсов COM-объектов. Вы должны описать все требуемые интерфейсы, методы и т.п. в этом редакторе, после чего можно нажать кнопку «обновить» и изменения будут автоматически внесены во все требуемые модули. Вам останется лишь дописать реализацию методов.
Добавим описание нового метода. Для этого щелкнем правой кнопкой на интерфейсе ITest и выберем в контекстном меню New->Method. Введем имя метода «ShowIt».
На закладке Parameters зададим параметр S и тип BSTR. После этого нажмем кнопку обновить и посмотрим, что случилось с исходными текстами нашей программы. В модуле Project1_TLB в описании интерфейса ITest1 появился метод ShowIt
ITest1 = interface(IUnknown) ['{1302FB06-703F-11D4-84DD-825B45DBA617}'] function ShowIt(const S: WideString): HResult; stdcall; end;
А в модуле Unit1:
type TTest1 = class(TTypedComObject, ITest1) protected function ShowIt(const S: WideString): HResult; stdcall; end; implementation uses ComServ; function TTest1.ShowIt(const S: WideString): HResult; begin end;
Нам остается лишь написать реализацию метода:
function TTest1.ShowIt(const S: WideString): HResult; begin MessageBoxW(0, PWideChar(S), NIL, 0) Result := S_OK; // Стандартный код успешного завершения end;
Для регистрации сервера достаточно один раз запустить его на компьютере клиента.