Создание Plug-In в виде COM-Сервера

Попробуем теперь реализовать Plug-In к своей программе, в виде COM-сервера и сравним код, полученный в этом случае с кодом, полученным при «ручном» программировании. В начале создадим модуль с описанием интерфейсов:

unit PluginInterface;

interface

const

 Class_TAPI: TGUID = '{A132D1A1-721C-11D4-84DD-E2DEF6359A17}';

type

 IAPI = interface

 ['{64CFF1E0-61A3-11D4-84DD-B18D6F94141F}']

  procedure ShowMessage(const S: String);

 end;



 ILoadFilter = interface

 ['{64CFF1E1-61A3-11D4-84DD-B18D6F94141F}']

  procedure Init(const FileName: String);

  function GetLine(var S: String): Boolean;

 end;

implementation

end.

Обратите внимание, что метод ILoadFilter.Init больше не получает ссылки на внутренний API программы – он будет реализован в виде COM-объекта.
Создадим DLL c COM-сервером, реализующим ILoadFilter. Для этого создадим новую ActiveX library и добавим в неё COM-объект TLoadFilter. Установим ThreadingModel в Single, поскольку использования сервера в потоках не предполагается. После этого реализуем методы интерфейса ILoadFilter.

unit Unit3;

interface

uses

 Windows, ActiveX, Classes, ComObj, PluginInterface;

type

 TLoadFilter = class(TComObject, ILoadFilter)

 private

  FAPI: IAPI;

  F: TextFile;

  Lines: Integer;

  InitSuccess: Boolean;

  protected

  procedure Init(const FileName: String);

  function GetLine(var S: String): Boolean;

 public

  destructor Destroy; override;

 end;

const

 Class_LoadFilter: TGUID = '{A132D1A2-721C-11D4-84DD-E2DEF6359A17}';



implementation

uses ComServ, SysUtils;

Деструктор и метод GetLine аналогичны предыдущему примеру:

destructor TLoadFilter.Destroy;

begin

 if InitSuccess then

  CloseFile(F);

 inherited;

end;

function TLoadFilter.GetLine(var S: String): Boolean;

begin

 if InitSuccess then begin

  Inc(Lines);

  Result := not Eof(F);

  if Result then begin

  Readln(F, S);

  FAPI.ShowMessage('Загружено ' + IntToStr(Lines) + ' строк.');

  end;

 end else

  Result := FALSE;

end;

В методе Init имеется существенное отличие – теперь ссылку на внутреннее API программы мы получаем при помощи COM. Это освобождает нас от необходимости передавать ссылку в модуль расширения.

procedure TLoadFilter.Init(const FileName: String);

begin

 FAPI := CreateComObject(Class_TAPI) as IAPI;

 {$I-}

 AssignFile(F, FileName);

 Reset(F);

 {$I+}

 InitSuccess := IOResult = 0;

 if not InitSuccess then

  FAPI.ShowMessage('Ошибка инициализации загрузки');

end;

В конце модуля код, автоматически сгенерированный Delphi для создания фабрики объектов

initialization

 TComObjectFactory.Create(ComServer, TLoadFilter, Class_LoadFilter,

  'LoadFilter', '', ciMultiInstance, tmSingle);

end.

Компилируем DLL и регистрируем её при помощи regsvr32.
Поскольку программа может поддерживать множество различных фильтров, организуем их подключение через INI файл следующего вида:
[Filters]
TXT={A132D1A2-721C-11D4-84DD-E2DEF6359A17}
Параметром строки служит CLSID сервера, реализующего фильтр. В нашем случае это будет содержание константы Class_LoadFilter. Для подключения дополнительных фильтров необходимо создать DLL с сервером, реализующим ILoadFilter, зарегистрировать её в системе и добавить CLSID сервера в INI-файл.
Теперь можно приступать к написанию программы-клиента. Она аналогична используемой в предыдущем примере. Добавим в неё COM-сервер, реализующий внутреннее API.
За исключением кода, сгенерированного COM объект полностью аналогичен объекту, приведенному ранее. Константу Class_TAPI вынесем в модуль PluginInterface, чтобы сделать её доступной для модулей расширения.

unit Unit2;

interface

uses

 Windows, ActiveX, Classes, ComObj, PluginInterface;

type

 TTAPI = class(TComObject, IAPI)

 protected

  procedure ShowMessage(const S: String);

 end;

implementation

uses Forms, ComServ, Unit1;

{ TTAPI }

procedure TTAPI.ShowMessage(const S: String);

begin

 (Application.MainForm as TForm1).StatusBar1.SimpleText := S;

end;

initialization

 TComObjectFactory.Create(ComServer, TTAPI, Class_TAPI,

  'TAPI', '', ciMultiInstance, tmSingle);

end.

Теперь все готово к реализации функциональности клиента. Для экономии места приведем лишь метод LoadData
procedure TForm1.LoadData(FileName: String);

var

 PlugInName: String;

 Filter: ILoadFilter;

 S, Ext: String;

begin

 Memo1.Lines.Clear;

 Memo1.Lines.BeginUpdate;

 try

  Ext := ExtractFileExt(FileName);

  Delete(Ext, 1, 1);

  with TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'plugins.ini') do

  try

  PlugInName := ReadString('Filters', Ext, '');

  finally

  Free;

  end;

  Filter := CreateComObject(StringToGUID(PlugInName)) as ILoadFilter;

  Filter.Init(FileName);

  while Filter.GetLine(S) do

  Memo1.Lines.Add(S);

 finally

  Memo1.Lines.EndUpdate;

 end;

end;

Очевидно, что код метода стал гораздо короче и читабельнее. COM взял на себя всю черновую работу по поиску загрузке и выгрузке DLL, поиску и созданию объектов.
! Поскольку в EXE и DLL используются длинные строки, не забудьте включить в список uses обоих проектов модуль ShareMem

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

Проверка
Антиспам проверка
Image CAPTCHA
...