скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Принцип создания плагинов в Delphi



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


function PluginType : PChar;

функция, определяющая назначение плугина.


function PluginName : PChar;

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


function PluginExec(AObject: ТТип): boolean;

главный обработчик, выполняет определённые действия и возвращает TRUE;

и ещё, я делал res файл с небольшим битмапом и компилировал его вместе с плугином, который отображался в меню соответствующего плугина. Откомпилировать res фaйл можно так:

  1. создайте файл с расширением *.rc
  2. напишите в нём : bitmap RCDATA LOADONCALL 1.bmp где bitmap - это идентификатор ресурса RCDATA LOADONCALL - тип и параметр 1.bmp - имя локального файла для кампиляций
  3. откомпилируйте этот файл программой brcc32.exe, лежащей в папке ...\Delphi5\BIN\ .

Загрузка плагина

Перейдём к теоретической части.

Раз плугин это dll значит её можно подгрузить следующими способами:

  • Прищипыванием её к программе!

function PluginType : PChar; external 'myplg.dll';
// в таком случае dll должна обязательно лежать возле exe и мы не можем передать
// туда конкретное имя! не делать же все плугины одного имени! это нам не подходит.
// Программа просто не загрузится без этого файла! Выдаст сообщение об ошибке.
// Этот способ может подойти для поддержки обновления вашей программы!

  • Динамический

это означает, что мы грузим её так, как нам надо! Вот пример:


var
  // объявляем процедурный тип функции из плугина
  PluginType: function: PChar;
  //объявляем переменную типа хендл в которую мы занесём хендл плугина
  PlugHandle: THandle;

procedure Button1Click(Sender: TObject);
begin
  //грузим плугин
  PlugHandle := LoadLibrary('MYplg.DLL');
  //Получилось или нет?
  if PlugHandle <> 0 then
  begin
    // ищем функцию в dll
    @PluginType := GetProcAddress(plugHandle,'Plugintype');
    if @PluginType <> nil then
      //вызываем функцию
      ShowMessage(PluginType);
  end;
  //освобождаем библиотеку
  FreeLibrary(LibHandle);
end;

Вот этот способ больше подходит для построения плугинов!

Функции:


//как вы поняли загружает dll и возвращает её хендл
function LoadLibrary(lpLibFileName : Pchar):THandle;
// пытается найти обработчик в переданной ей хендле dll,
// при успешном выполнении возвращает указатель обработчика.
function GetProcAddress(Module: THandle; ProcName: PChar): TFarProc 
//освобождает память, занитую dll
function FreeLibrary(LibModule: THandle);

Самое сложное в построений плугинов, это не реализация всего кода, а придусмотрение всего, для чего в программе могут они понадобиться! То есть придусмотреть все возможные типы плугинов! А это не так просто.

Вот полноценный пример реализации простой программы для поддержки плугинов...

Исходный текст модуля программы:


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, Menus, Grids, DBGrids;

type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    //меню, которое будет содержать ссылки на плугины
    N1231: TMenuItem;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    //лист, в котором мы будем держать имена файлов плугинов
    PlugList : TStringList;
    //Процедура загрузки плугина
    procedure LoadPlug(fileName : string);
    //Процедура инициализации и выполнения плугина
    procedure PlugClick(sender : TObject);
  public
    { Public declarations }
end;

var
Form1: TForm1;

implementation
{$R *.DFM}

Процедура загрузки плугина. Здесь мы загружаем, вносим имя dll в список и создаём для него пункт меню; загружаем из dll картинку для пункта меню


procedure TForm1.LoadPlug(fileName: string);
var
  //Объявление функции, которая будет возвращать имя плугина
  PlugName : function : PChar;
  //Новый пункт меню
  item : TMenuItem;
  //Хендл dll
  handle : THandle;
  //Объект, с помощью которого мы загрузим картинку из dll
  res :TResourceStream;
begin
  item := TMenuItem.create(mainMenu1); //Создаём новый пункт меню
  handle := LoadLibrary(Pchar(FileName)); //загружаем dll
  if handle <> 0 then //Если удачно, то идём дальше...
  begin
    @PlugName := GetProcAddress(handle,'PluginName'); //грузим процедуру
    if @PlugName <> nil then
      item.caption := PlugName
      //Если всё прошло, идём дальше...
    else
    begin
      ShowMessage('dll not identifi '); //Иначе, выдаём сообщение об ошибке
      Exit; //Обрываем процедуру
    end;
    PlugList.Add(FileName); //Добавляем название dll
    res:= TResourceStream.Create(handle,'bitmap',rt_rcdata); //Загружаем ресурс из dll
    res.saveToFile('temp.bmp'); res.free; //Сохраняем в файл
    item.Bitmap.LoadFromFile('Temp.bmp'); //Загружаем в пункт меню
    FreeLibrary(handle); //Уничтожаем dll
    item.onClick:=PlugClick; //Даём ссылку на обработчик
    Mainmenu1.items[0].add(item); //Добавляем пункт меню
  end;
end;

Процедура выполнения плугина. Здесь мы загружаем, узнаём тип и выполняем


procedure TForm1.PlugClick(sender: TObject);
var
  //Объявление функции, которая будет выполнять плугин
  PlugExec : function(AObject : TObject): boolean;
  //Объявление функции, которая будет возвращать тип плугина
  PlugType : function: PChar;
  //Имя dll
  FileName : string;
  //Хендл dll
  handle : Thandle;
begin
  with (sender as TmenuItem) do
    filename:= plugList.Strings[MenuIndex];
  //Получаем имя dll
  handle := LoadLibrary(Pchar(FileName)); //Загружаем dll
  //Если всё в порядке, то идём дальше
  if handle <> 0 then
  begin
    //Загружаем функции
    @plugExec := GetProcAddress(handle,'PluginExec');
    @plugType := GetProcAddress(handle,'PluginType');
    //А теперь, в зависимости от типа, передаём нужный ей параметр...
    if PlugType = 'FORM' then
      PlugExec(Form1)
    else
    //Если плугин для формы, то передаём форму
    if PlugType = 'CANVAS' then
      PlugExec(Canvas)
    else
    //Если плугин для канвы, то передаём канву
    if PlugType = 'MENU' then
      PlugExec(MainMenu1)
    else
    //Если плугин для меню, то передаём меню
    if PlugType = 'BRUSH' then
      PlugExec(Canvas.brush)
    else
    //Если плугин для заливки, то передаём заливку
    if PlugType = 'NIL' then
      PlugExec(nil);
    //Если плугину ни чего не нужно, то ни чего не передаём
  end;
  FreeLibrary(handle); //Уничтожаем dll
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  SearchRec : TSearchRec; //Запись для поиска
begin
  plugList:=TStringList.create; //Создаём запись для имён dll'ок
  //ищем первый файл
  if FindFirst('*.dll',faAnyFile, SearchRec) = 0 then
  begin
    LoadPlug(SearchRec.name); //Загружаем первый найденный файл
    while FindNext(SearchRec) = 0 do
      LoadPlug(SearchRec.name);
    //Загружаем последующий
    FindClose(SearchRec); //Закрываем поиск
  end;
  //Левые параметры
  canvas.Font.pitch := fpFixed;
  canvas.Font.Size := 20;
  canvas.Font.Style:= [fsBold];
end;

end.

Здесь написан простой исходный текст dll, то есть нашего плугина. Он обязательно возвращает название, тип и выполняет свои задачи


library plug;

uses
  SysUtils, graphics, Classes, windows;

{$R bmp.RES}

function PluginType : Pchar;
begin
  //Мы указали реакцию на этот тип
  Plugintype := 'CANVAS';
end;

function PluginName:Pchar;
begin
  //Вот оно, название плугина. Эта строчка будет в менюшке
  PluginName := 'Canvas painter';
end;

Функция выполнения плугина! Здесь мы рисуем на переданной канве анимационную строку.


function PluginExec(Canvas:TCanvas):Boolean;
var
  X : integer;
  I : integer;
  Z : byte;
  S : string;
  color : integer;
  proz : integer;
begin
  color := 10;
  proz :=0;
  S:= 'hello всем это из плугина ля -- ля';
  for Z:=0 to 200 do
  begin
    proz:=proz+2;
    X:= 0;
    for I:=1 to length(S) do
    begin
      X:=X + 20;
      Canvas.TextOut(X,50,S[i]);
      color := color+X*2+Random(Color);
      canvas.Font.Color := color+X*2;
      canvas.font.color := 10;
      canvas.TextOut(10,100,'execute of '+inttostr(proz div 4) + '%');
      canvas.Font.Color := color+X*2;
      sleep(2);
    end;
  end;
  PluginExec:=True;
end;

exports
  PluginType, PluginName, PluginExec;

end.

Пару советов:

  • Не оставляйте у своих плугинов расширение *.dll, это не катит. А вот сделайте, например *.plu . Просто в исходном тексте плугина напишите {$E plu} Ну и в исходном тексте программы ищите не Dll, а уже plu.
  • Когда вы сдаёте программу, напишите к ней уже готовых несколько плугинов, что бы юзеру было интересно искать новые.
  • Сделайте поддержку обновления через интернет. То есть программа заходит на ваш сервер, узнаёт, есть ли новые плугины или нет, если есть - то она их загружает. Этим вы увеличите спрос своей программы и конечно трафик своего сайта!





Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook