Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.
Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...
Иногда нужные мысли приходят после того, как программа сдана заказчику. Для этого придумали плугины. Плугины - это простая dll библиотека, в которой обязательно присутствует ряд процедур и функций, которые выполняют определенные разработчиком действия, например (из моей практики):
function PluginType : PChar;
function PluginName : PChar;
function PluginExec(AObject: ТТип): boolean;
и ещё, я делал res файл с небольшим битмапом и компилировал его вместе с плугином, который отображался в меню соответствующего плугина. Откомпилировать res фaйл можно так:
Загрузка плагина
Перейдём к теоретической части.
Раз плугин это 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.
Пару советов: