源码 http://www.aidelphi.com/6to23/docu/plugin.zip
相信各位应该都用过WinAmp,也相信它对插件的支持是它最终流行起来的主要原因。能不能让我们自已的程序也支持插件呢,以面我们就用Delphi来为我们编第一个支持插件的程序。
对于一般用户来说,插件就是一个DLL文件,但与一般DLL不同的是,插件支持对主程序功能的扩展,主程序没有插件也一样能运行,但一般的DLL大多数是主程序不可缺少的部份。当需要经常为客户更新应用程序版本时,插件也许是你不错有选择。
首先,在编写应用程序之前需要清楚应用程序本身可以完成什么主要功能,需要扩展什么样的功能,因为插件本身是通过主程序提供的特定接口来与主程序进行行交互的,在编写主程序时你需要确定这些接口。在本例中,定义了两个接口,一个完成插件的初始化,主要是指选择并载入相应的图片,另一个是在主Form重绘时绘制出相应的图片,其中第二个接口需要传递画布句柄和绘制区域。
以下是主程序主Form interface段的定义:
TPluginDescribe = Function:Boolean; stdcall;
TPluginDrawDescribe = procedure(DC:HDC;Rect:TRect); stdcall;
TForm1 = class(TForm)
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
procedure ListBox1DblClick(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
LibHandle: HMODULE;
DwarProc: TPluginDrawDescribe;
procedure LoadPlugins;
procedure LoadPlugin(sr: TSearchRec);
{ Private declarations }
public
{ Public declarations }
end;
其中定义了一个过程类型和一个函数类型,其中过程类型有两个变量,一个HDC类型的画布句柄,另一个是TRECT类型的绘制区域。TForm1有两个私有字段LibHandle和DwarProc用于保存装入的插件DLL的句柄和绘图过程句柄。
在主Form创建时检查是否有插件程序,就列出可用插件,TForm1的OnCreate事件句柄FormCreate实现代码如下:
procedure TForm1.FormCreate(Sender: TObject);
begin
LibHandle:=0;//初始化LibHandle的值。
LoadPlugins;//调入LoadPlugins过程。
end;
以下是LoadPlugins的实现代码:
procedure TForm1.LoadPlugins;
var
sr: TSearchRec;
path: string;
Found: Integer;
begin
path := ExtractFilePath(Application.Exename);
try
Found := FindFirst(path + '*.DLL', 0, sr);
while Found = 0 do//查找文件。
begin
LoadPlugin(sr);
Found := FindNext(sr);
end;
finally
FindClose(sr);
end;
end;
查找当前路径下所有扩展名为DLL的文件,如果找到则检查是否为本例插件。
检查插件文件的过程LoadPlugin实现代码如下:
procedure TForm1.LoadPlugin(sr: TSearchRec);
var
iLibHandle: HMODULE;
pDescribeProc: TPluginDescribe;
pDwarProc: TPluginDrawDescribe;
begin
iLibHandle := LoadLibrary(Pchar(sr.Name));
if iLibHandle <> 0 then
begin
try
pDescribeProc := GetProcAddress(iLibHandle, 'loadpicture');
pDwarProc := GetProcAddress(iLibHandle, 'drawpicture');
if Assigned(pDescribeProc) and Assigned(pDwarProc) then
ListBox1.Items.Add(sr.Name);
finally
FreeLibrary(iLibHandle);
end;
end else
ShowMessage('loading Dll file error!');
end;
首先动态装入插件库使用LoadLibrary API函数,如果其中有接口过程,则将文件名加入到ListBox1中。其中GetProcAddress是获得DLL中某个例程的地址。不使用DLL时用FreeLibrary卸载。
在ListBox1的双击事件中才真正启动插件:
procedure TForm1.ListBox1DblClick(Sender: TObject);
var pDescribeProc: TPluginDescribe;
begin
if ListBox1.ItemIndex<>-1 then
begin
if LibHandle<>0 then
FreeLibrary(LibHandle);
DwarProc:=nil;
LibHandle := LoadLibrary(PChar(ListBox1.Items[ListBox1.ItemIndex]));
if LibHandle <> 0 then
begin
try
pDescribeProc := GetProcAddress(LibHandle, 'loadpicture');
if pDescribeProc then
begin
DwarProc := GetProcAddress(LibHandle, 'drawpicture');
Invalidate;
end;
Except
FreeLibrary(LibHandle);
LibHandle:=0;
end;
end
else
ShowMessage('loading Dll file error!');
end;
end;
//这段代码主要是先调用装入图片的插件过程,如果成功,则取绘图片的过程地址。只有LibHandle<>0和给DwarProc赋值之后才算真正启动了插件。
在TForm1的OnPaint事件中调用DwarProc过程,启动插件中的绘图过程:
procedure TForm1.FormPaint(Sender: TObject);
begin
try
if (LibHandle<>0) and Assigned(DwarProc) then
DwarProc(Self.Canvas.Handle,Self.ClientRect);
Except
if LibHandle<>0 then
begin
FreeLibrary(LibHandle);
LibHandle:=0;
end;
end;
end;
最后别忘了在TForm1的OnClose事件中卸载DLL。
完成后运行,发现ListBox1中什么也没有,因为插件程序还没有建立。
本例共有两个插件,一个完成图片在主Form上的平铺绘制,另一个完成缩放绘制。
插件程序主单元如下:
library PlugIn1;
uses
SysUtils,
Classes,
PlugInUnit1 in 'PlugInUnit1.pas';
exports loadpicture,drawpicture;
//导出两个过程。
begin
end.
两个过程的定义和实现代码在单元PlugInUnit1中:
unit PlugInUnit1;
interface
Uses Graphics,extdlgs,Windows;
Function loadpicture:Boolean;
procedure drawpicture(DC:HDC;Rect:TRect);export; stdcall;
Var Picture:TPicture;
implementation
procedure drawpicture(DC:HDC;Rect:TRect);
Var cCanvas:TCanvas;
begin
if (Picture<>nil) and not Picture.Graphic.Empty then
begin
cCanvas:=TCanvas.Create;
try
cCanvas.Handle:=DC;
cCanvas.StretchDraw(Rect,Picture.Graphic);
finally
cCanvas.Free;
end;
end;
end;
Function loadpicture:Boolean;
begin
Result:=False;
Picture:=TPicture.Create;
with TopenpictureDialog.Create(nil) do
begin
try
if Execute then
begin
Picture.LoadFromFile(FileName);
Result:=True;
end;
finally
Free;
end;
end;
end;
end.
上面的插件完成图片的缩放绘制,另一个插件的代码除单元名和drawpicture的实现不同之外,其余均是相同的。在此不重复。相信读者也能自行完成。笔者的主页http://www.aidelphi.com上有该例程最完整的代码。