COM- технология COM- сервер :48:021
Классификация COM- серверов Сервер СОМ представляет собой исполняемый файл : приложение или динамическую библиотеку, который может содержать один или несколько объектов одного или разных классов. Различают три типа серверов. 1) Внутренний сервер (in-process server) реализуется динамическими библиотеками, которые подключаются к приложению - клиенту и работают в одном с ним адресном пространстве. 2) Локальный сервер (local server) создается отдельным процессом, который работает на одном компьютере с клиентом. 3) Удаленный сервер (remote server) создается процессом, который работает на другом компьютере по отношению к клиенту. Канонически известно два типа COM- серверов - загружаемые в адресное пространство клиентского процесса, и работающие в адресном пространстве другого процесса. Последний тип сервера также рассматривается в двух разных качествах - работающий в параллельном процессе на той же самой машине ( в одном и том же экземпляре операционной системы ) и работающий на другой машине (DCOM - Distributed COM) :48:022
Минимальный функциональный набор inproc- сервера DllGetClassObject – выдача ссылки на объект ; DllRegisterServer – регистрация COM- сервера в системном реестре ; DllUnregisterServer – удаление информации о COM- сервере из системного реестра ; DllCanUnloadNow – ни один сервер из состава DLL не используется ни одним клиентом. Сервер может быть выгружен из памяти :48:023
Регистрация сервера в системном реестре Минимальная информация, заносимая функцией DllRegisterServer в реестр: HKCR\CLSID\{CLSID нашего статического типа} = HKCR\CLSID\{CLSID нашего статического типа}\InprocServer32 = C объектом обычно (обязательно этого не требуется, просто часть сопутствующих сервисов будет неработоспособна) связывается еще и "человеческое имя" - обозначение из нескольких символьных идентификаторов, разделённых точками, структура которого устанавливается следующей:... … например: Word.Document.6 Структурированность имени позволяет выяснить отношения преемственности между серверами, компонентами и версиями. Параметр реестра VersionIndependentProgID имеет своим значением только., что позволяет в случае необходимости разыскивать либо " общеизвестную " версию компонента, либо - точно заданную :48:024
Регистрация сервера в системном реестре Более полная информация о статическом типе будет содержать параметр ProgID: HKCR\CLSID\{CLSID нашего статического типа}\ProgID = HKCR\CLSID\{CLSID нашего статического типа}\VersionIndependentProgID = К последовательности идентификаторов, являющихся значением ProgID и VersionIndependentProgID применимы следующие правила: 1) Они в совокупности не должны быть длиннее GUID, т.е. 39 символов; 2) Они не могут содержать знаков пунктуации, в том числе - знаков подчёркивания, исключение - только разделяющие точки; 3) Последовательность не должна начинаться с цифры :48:025
Существует " зеркальный набор параметров реестра ", который задаёт связь "ProgID - CLSID". Его можно отыскать прямо в разделе реестра HKEY_CLASSES_ROOT: HKCR\. = HKCR\.. = Внутри этих параметров определен вложенный параметр CLSID, значением которого является GUID из раздела HKEY_CLASSES_ROOT\CLSID: HKCR\. = HKCR\. \{CLSID нашего статического типа } = HKCR\.. = HKCR\.. \{CLSID нашего статического типа } = :48:026 Регистрация сервера в системном реестре
Существует практическое правило, по которому инсталлятор самой первой версии продукта записывает данные параметры в реестр следующим образом : HKCR\..1 = "первая версия продукта« HKCR\..1\{CLSID данного типа в версии 1} = HKCR\. = "последняя известная системе версия продукта« HKCR\. \{CLSID данного типа в версии 1} = Такое правило позволяет хранить в системе одновременно все версии компонентов данного статического типа и реализующих их серверов без взаимной интерференции, если, конечно, инсталлятор последующих версий намеренно не удаляет все предыдущие :48:027 Регистрация сервера в системном реестре
Средства регистрации 1) Для регистрации объектов COM в системном реестре можно использовать.reg- файл Windows Registry Editor Version 5.00 Office Spreadsheet 9.0" [HKEY_CLASSES_ROOT\CLSID\{0002E C000- [HKEY_CLASSES_ROOT\CLSID\{0002E C000- [HKEY_CLASSES_ROOT\CLSID\{0002E C000- regedit.exe.reg :48:028
Средства регистрации 2) Использование "профессионального инсталлятора". Например, NSIS, Install Shield или MS Installer. Эта категория программных средств позволяет составить скрипт дистрибутива, потом его как-то преобразовать в "человеконеисправляемую форму" и предложить специальной программе setup.exe которую сам же "профессиональный инсталлятор" и составляет - именно для того, чтобы исполнить этот самый скрипт на пользовательской машине. Section " Регистрация серверов" RegServer ; Set output path to the installation directory. SetOutPath $INSTDIR ; Put file there FILE /r D:\!Version\Moreprom\2.0\Install\RemoteClient\servers.dll ;Server Registartion WriteRegStr HKCR CLSID\ \{0004EF FE00-C }\ InprocServer32 "$INSTDIR\servers.dll WriteRegStr HKCR CLSID\ \{0004EF FE00-C }\ PROGID RemoteClient SectionEnd ; end the section :48:029
3) Использовать функцию DllRegisterServer Function DllRegisterServer: HResult; stdcall; var r:Tregistry; begin try r:=TRegistry.Create; r.RootKey:=HKEY_CLASSES_ROOT; r.OpenKey(CLSID\ {0004EF FE00-C } ',true); r.WriteString(InprocServer32, ExtractFilePath(paramstr(0))+servers.dll); r.WriteString(ProgID, remoteclient); finally r.free; end; :48:0210 Средства регистрации
Фабрика класса Для запуска экземпляра класса используется специальный объект фабрика класса. С его помощью можно создать как один объект, так и несколько его экземпляров. Для каждого класса должна существовать собственная фабрика класса. Объект СОМ имеет право называться фабрикой класса, если он поддерживает интерфейс iClassFactory. В нем реализованы всего два метода: CoCreateinstance создает новый экземпляр класса. Все необходимые параметры, кроме iid, метод получает от фабрики класса. В этом его отличие от одноименной общей функции библиотеки; LockServer оставляет сервер функционировать после создания объекта. На самом деле общий метод coCreateinstance при помощи переданного ему clsid осуществляет вызов соответствующей фабрики класса и метода CoCreateinstance интерфейса IClassFactory. Для вызова фабрики класса существует специальная функция CoGetCiassObject. В качестве параметра ей передается clsid нужного класса и iid интерфейса (iClassFactory). Функция ищет требуемую фабрику и возвращает указатель на интерфейс. С его помощью, используя метод CoCreateinstance, клиент заставляет фабрику класса создать объект :48:0211
Библиотека типов Чтобы документировать интерфейсы объекта для пользователей, разработчик создает информацию о типах объекта. Для этого используется язык IDL. Вся информация объединяется в специальной библиотеке типов. Она может описывать свойства и методы ( а также их параметры ) интерфейсов и содержать сведения о необходимых заглушках и заместителях. Информация об отдельном интерфейсе оформляется в виде отдельного объекта внутри библиотеки. // После обработки файла компилятором MIDL [ object, uuid(7D785DE3-07С0-11D0-896С ), dual, helpstring("Интерфейс IAtltest1"), pointer_default(unique) ] interface IAtltest1 : IDispatch { import "oaidl.idl"; HRESULT Bar( [in] BSTR s); // Пример рабочей функции }; :48:0212
Библиотека типов Для создания библиотеки типов, описанной при помощи операторов IDL, используются специальные компиляторы. Доступ к библиотеке осуществляется по clsid класса объекта. Кроме того, библиотека имеет собственный GUID, который сохраняется в системном реестре при регистрации объекта. Каждая библиотека типов имеет интерфейс iTypeLib, который дает возможность работать с ней, как с единым объектом. Для доступа к информации об отдельном интерфейсе используется интерфейс ITypeinfo. Для доступа к библиотеке по GUID используется функция LoadRegTypeLib. Если клиенту известно имя файла библиотеки, то можно воспользоваться функцией LoadTypeLib :48:0213
РАЗРАБОТКА COM- СЕРВЕРА СРЕДСТВАМИ DELPHI :48:0214
Создание Inproc- Сервера :48:0215 library First; uses ComServ; exports DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer; {$R *.RES} begin end.
Создание Inproc- Сервера При создании объекта СОМ в модуль с его описанием автоматически добавляется МОДУЛЬ ComServ. Этот модуль описывает класс TComServer, который инкапсулирует свойства сервера СОМ, в котором работает соответствующий объект. Обращение к свойствам и методам этого класса позволяет получить информацию о работающих в сервере объектах и их состоянии, а также о самом сервере. При включении модуля ComServ в модуль объекта автоматически создается экземпляр класса TComServer, указатель на который присваивается переменной ComServ. Используя эту переменную, можно получать информацию от сервера. Класс сервера используется для создания экземпляров фабрик классов, то есть непосредственно участвует в работе механизма взаимодействия объектов СОМ и клиентов. В модуле ComServ объявлены и описаны глобальные переменные, которые автоматически экспортируются в каждый внутренний сервер и выполняют базовые операции регистрации, перерегистрации и выгрузки сервера :48:0216
Класс TComServer :48:0217
:48:0218 Создание объекта COM
Мастер ComObject Wizard Однострочный редактор Class Name должен содержать имя нового класса. Комбинированный список Instancing определяет способ создания объекта : internal объект используется в процессе ; single instance при обращении к объекту нескольких клиентов в единственном экземпляре сервера создается необходимое число объектов ; Multiple instance если к объекту обращается несколько клиентов, то для каждого создается собственный экземпляр сервера объекта. Комбинированный список Threading Model определяет способ взаимодействия объекта и клиентов : single сервер может обслуживать вызовы только последовательно, один за другим ; Apartment вызов объекта осуществляется только в одном потоке, который создается самим объектом, в сервере в таком режиме одновременно могут работать несколько объектов ; Free вызов объекта может осуществляться через любой созданный им поток ; Both объект может работать с клиентами в модели Apartment и Free. Однострочный редактор Description содержит описание объекта. Флажок Include Type Library управляет созданием библиотеки типов для объекта :48:0219
Объект сервера TTFirstCom :48:0220 unit unFirst; Interface uses Windows, ActiveX, Classes, ComObj, First_TLB, StdVcl; type TTFirstCom = class(TTypedComObject, ITFirstCom) protected {Declare ITFirstCom methods here} end; Implementation uses ComServ; initialization TTypedComObjectFactory.Create(ComServer, TTFirstCom, Class_TFirstCom, ciMultiInstance, tmApartment); end.
Библиотека типов объекта TTFirstCom :48:0221 unit First_TLB; Interface uses Windows, ActiveX, Classes, Graphics, StdVCL, Variants; const // TypeLibrary Major and minor versions FirstMajorVersion = 1; FirstMinorVersion = 0; LIBID_First: TGUID = '{B4624B AA1D9871A31A}'; IID_ITFirstCom: TGUID = '{476136E1-2CBB CF- 922BE820513F}'; CLASS_TFirstCom: TGUID = '{4BB56F09-ADC1-4C BC26328DE6}';
:48:0222 type ITFirstCom = interface; TFirstCom = ITFirstCom; ITFirstCom = interface(IUnknown) ['{476136E1-2CBB CF-922BE820513F}'] end; CoTFirstCom = class class function Create: ITFirstCom; class function CreateRemote(const MachineName: string): ITFirstCom; end; Библиотека типов объекта TTFirstCom
:48:0223 implementation uses ComObj; class function CoTFirstCom.Create: ITFirstCom; begin Result := CreateComObject(CLASS_TFirstCom) as ITFirstCom; end; class function CoTFirstCom.CreateRemote(const MachineName: string): ITFirstCom; begin Result := CreateRemoteComObject(MachineName, CLASS_TFirstCom) as ITFirstCom; end; end. Библиотека типов объекта TTFirstCom
Редактор библиотеки типов :48:0224
Разработка интерфейсов Нам необходимо показать использование нескольких интерфейсов одного объекта, иначе объект СОМ принципиально не будет отличаться от обычного объекта. Пусть первый и второй интерфейсы реализуют различные простейшие линейные и степенные функции. Для создания второго интерфейса и всех необходимых методов воспользуемся Редактором библиотеки типов. Для этого требуется выполнить следующие действия. 1) В иерархическом списке необходимо выбрать интерфейс ISimpleCOM и щелкнуть на кнопке Method в панели инструментов. 2) Появившийся в результате в иерархическом списке метод Method1 переименуем в LinearX. При необходимости на странице Attributes в правой части можно задать тип возвращаемого методом результата. Для этого используется комбинированный список Invoke Kind. 3) Затем необходимо перейти на страницу Parameters в правой части редактора и в списке Return Type задать integer тип возвращаемого методом результата. 4) Теперь щелкните на кнопке Add в нижней части страницы, чтобы добавить методу первый параметр. В появившейся строке в ячейке Name измените имя параметра на AValue :48:0225
Создание метода интерфейса :48:0226
Добавление метода в интерфейс :48:0227
Создание нового интерфейса :48:0228
Добавление интерфейса в coClass :48:0229
Библиотека типов на IDL :48:0230 [ uuid(B4624B AA1D9871A31A), version(1.0), helpstring("First Library") ] library First { importlib("stdole2.tlb"); [ uuid(476136E1-2CBB CF-922BE820513F), version(1.0), helpstring("Interface for TFirstCom Object"), oleautomation ]
:48:0231 interface ITFirstCom: IUnknown { [ id(0x ) ] int _stdcall LinearX([in] long X ); [ id(0x ) ] HRESULT _stdcall SquareX([in] long x ); }; [ uuid(4BB56F09-ADC1-4C BC26328DE6), version(1.0), helpstring("TFirstCom") ] Библиотека типов на IDL
:48:0232 coclass TFirstCom { [default] interface ITFirstCom; interface ITFirstCom2; }; [ uuid(82C4D8A3-9FC5-4A07-BA08-95BD1ED37CB9), version(1.0), dual, oleautomation ] interface ITFirstCom2: IDispatch { [ id(0x000000C9) ] int _stdcall QubeX([in] long X ); [ id(0x000000CA) ] int _stdcall Lienear2X([in] long X ); }; Библиотека типов на IDL
Изменения в библиотеке типов :48:0233 ITFirstCom = interface(IUnknown) ['{476136E1-2CBB CF-922BE820513F}'] function LinearX(X: Integer): SYSINT; stdcall; function SquareX(x: Integer): HResult; stdcall; end; ITFirstCom2 = interface(IDispatch) ['{82C4D8A3-9FC5-4A07-BA08-95BD1ED37CB9}'] function QubeX(X: Integer): SYSINT; safecall; function Lienear2X(X: Integer): SYSINT; safecall; end; ITFirstCom2Disp = dispinterface ['{82C4D8A3-9FC5-4A07-BA08-95BD1ED37CB9}'] function QubeX(X: Integer): SYSINT; dispid 201; function Lienear2X(X: Integer): SYSINT; dispid 202; end;
Изменения в объекте сервера :48:0234 TTFirstCom = class(TTypedComObject, ITFirstCom, ITFirstCom2) protected function Lienear2X(X: Integer): SYSINT; safecall; function LinearX(X: Integer): HResult; stdcall; procedure QubeX(X: Integer); safecall; function SquareX(X: Integer): HResult; stdcall; procedure Linear2X(X: Integer); safecall; {Declare ITFirstCom methods here} end; function TTFirstCom.LinearX(X: Integer): HResult; begin result:=X; end; …