Поддержка плагинов в своем приложении

Введение

В этой статье будет рассмотрен способ реализации поддержки плагинов в своем приложении при помощи интерфейсов в среде разработки Delphi.

Данная реализация системы плагинов имеет ряд преимуществ по сравнению с другими системами (dll, packages, classes):

Если вы незнакомы с интерфейсами, то почитать о них вы можете в справочной системе, Object Pascal Language Guide. Или в книге Тейкстры и Пачеко "Delphi 5"

Эта статья содержит уже готовую к работе систему, а также примеры пары плагинов.

Описание системы

Итак, наша система основана на интерфейсах. Для начала, нам нужно определить какие типы плагинов мы хотим поддерживать и описать для каждого типа плагина свой интерфейс. По сути интерфейс - это описание функциональности и свойств соответствующего типа плагина. Следует очень внимательно подойти к процессу написания наборов интерфейсов, т.к. неудачно составленный набор значительно осложнит жизнь в будущем как вам, так и разработчикам плагинов. Нужно заранее предусматривать возможность расширения функциональности и добавления новых типов плагинов.

Очевидно, что у всех плагинов, какого бы типа они не были, существуют свойства и функции общие для всех них. Поэтому для таких свойств и функций можно объявить интерфейс, который мы назовем базовым. Каждый плагин обязан реализовать этот интерфейс. Реализовывать или нет другие интерфейсы это дело автора плагина.

Какие же свойства и функции общие для всех плагинов?

Во-первых, это уникальный идентификатор плагина, в качестве которого можно использовать GUID - это будет гарантировать уникальность идентификатора (для его генерации в среде Delphi нужно нажать Ctrl + Shift + G). Этот идентификатор можно будет использовать, например, для того, чтобы позволить пользователю отключить загрузку указанного им плагина, или для того чтобы хранить настройки плагина.

Во-вторых, это данные об авторе плагина, например Имя и E-mail.

Название плагина, также будет не лишним.

Рано или поздно любой плагин будет выгружен, поэтому мы должны иметь возможность предупредить его об этом.

Таким образом, базовый интерфейс может иметь такой вид:

IPlugin = interface(IUnknown)
  ['{970F72FC-9D35-449C-B1EC-F25B2908AD59}']
    function getName: WideString; stdcall;
    function getVendorName: WideString; stdcall;
    function getVendorEMail: WideString; stdcall;
    function getID: WideString; stdcall;
    procedure BeforeUnload; stdcall;
    property Name: WideString read getName;
    property VendorName: WideString read getVendorName;
    property VendorEMail: WideString read getVendorEMail;
    property ID: WideString read getID;
end;

Из названий ясно назначение того или иного метода/свойства.

Так как мы описали базовый интерфейс для всех плагинов, то теперь можно более подробно рассмотреть, что же такое плагин в нашей системе. Плагин - это просто DLL, которая содержит экспортируемую функцию LoadPlugin, результат которой - указатель на класс реализующий, по крайней мере, базовый интерфейс.

Т.е. исходный текст простейшего плагина выглядит следующим образом:

library simpleplugin;
  uses
    PluginSDK,
    SimplePluginClass in 'SimplePluginClass.pas';

{$R *.res}      

function LoadPlugin: IPlugin; stdcall;
begin
  try
    Result:= TSimplePlugin.Create;
  except
    Result:= nil;       
  end;
end;

exports
  LoadPlugin;

begin
end.

Полные исходные коды плагина вы найдете в разделе "Ссылки". Там есть пример плагина IInfoPlugin возвращающего имя пользователя.

Менеджер плагинов

Для работы с плагинами нам понадобится менеджер, который будет загружать и выгружать их. В "Ссылках" вы можете взять уже готовый менеджер плагинов. Он умеет загружать их из указанной папки, выгружать, и предупреждать их об выгрузке. Рассмотрим его устройство. Это класс с несколькими методами и полями.

TPluginManager = class(TObject)
  private
    FHandles: TDLLsHandles;
    FPlugins: TPluginList;
    FInfoPlugins: TInfoPluginList;
    function AddPlugin(plug: IPlugin): Boolean;
    procedure CallBeforeUnload;
  public
    constructor Create;
    destructor Destroy; override;

    procedure LoadPlugins(const Folder: String);
    procedure UnloadPlugins;

    property Plugins: TPluginList read FPlugins;
    property InfoPlugins: TInfoPluginList read FInfoPlugins;
end;

Когда вы вызовете метод LoadPlugins, то менеджер плагинов будет перебирать все файлы *.dll и загружать их. Затем он пытается получить адрес функции LoadPlugin и вызывает ее. Если ее результат не nil, то он добавляет полученный указатель в список всех плагинов. А затем он начинает перебирать интерфейсы (при добавлении новых интерфейсов, их обработку вам нужно добавить самим, по аналогии) для того, чтобы понять какого типа является данный плагин и добавляет его в список соответствующей категории.

Ну вот собственно и все, посмотреть примеры, как уже говорилось ранее вы можете в разделе "Ссылки".

Ссылки

Примеры, менеджер плагинов - здесь.

Книги по Delphi - здесь. Особенно советую почитать Пачеко. У него очень хорошо написано про интерфейсы. Также полезно почитать "Inside COM / Основы COM".

Слава Антонов © 2002 — August 13, 2008
Индекс цитирования
Hosted by uCoz