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

Previous  Top  Next

    
 

 

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

Code:

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 GetNextLine(var S: String): Boolean;

end;

 

implementation

 

end.

 

 

 

Обратите внимание, что метод ILoadFilter.Init больше не получает ссылки на внутренний API программы он будет реализован в виде COM-объекта.

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

Code:

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 GetNextLine(var S: String): Boolean;

public

   destructor Destroy; override;

end;

 

const

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

 

 

implementation

 

uses ComServ, SysUtils;

 

 

 

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

Code:

 

destructor TLoadFilter.Destroy;

begin

if InitSuccess then

   CloseFile(F);

inherited;

end;

 

function TLoadFilter.GetNextLine(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. Это освобождает нас от необходимости передавать ссылку в модуль расширения.

Code:

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 для создания фабрики объектов

Code:

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, чтобы сделать её доступной для модулей расширения.

Code:

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

Code:

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.GetNextLine(S) do

     Memo1.Lines.Add(S);

finally

   Memo1.Lines.EndUpdate;

end;

end;

 

 

 

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

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