Попробуем теперь реализовать 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 GetNextLine(var S: String): Boolean; end; implementation end.Обратите внимание, что метод ILoadFilter.Init больше не получает ссылки на внутренний API программы — он будет реализован в виде COM-объекта.
Создадим DLL c COM-сервером, реализующим ILoadFilter. Для этого создадим новую ActiveX-библиотекуи добавим в нее COM-объект TLoadFilter. Установим ThreadingModel в Single, поскольку использование сервера в потоках не предусмотрено. После этого реализуем методы интерфейса ILoadFilter:
unit Unit3; interfaceДеструктор и метод GetNextLine аналогичны предыдущему примеру:
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. Это освобождает нас от необходимости передавать ссылку в модуль расширения.
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.GetNextLine(S) do Memo1.Lines.Add(S); finally Memo1.Lines.EndUpdate; end; end;Очевидно, что код метода стал гораздо более коротким и читабельным. COM взял на себя всю черновую работу по поиску, загрузке и и выгрузке DLL, поиску и созданию объектов.
Внимание! Поскольку в EXE и DLL используются длинные строки, не забудьте включить в список uses обоих проектов модуль ShareMem. |
![]() |
![]() |