半路出家的delphier一如我,对于win32api式的调用有着近乎厌恶的抵触情绪,我很早以前看c++的教程时看到lptrXXXXX的变量就头大,以至于到现在都不会用c++,都不曾写成功过那怕一个hello world程序,所以对于dll式的导出函数后面加stdcall使用右到左的参数调用方式,总觉得没有delphi的美感.再加上看到了许多bpl可以导出类,而dll很麻烦的文章,更是坚定了我的方向.
开始吧.....
一个插件系统需要什么?
一个最小的插件系统当然需要插件本身,调用插件的容器,最后需要契约.
契约是什么呢?契约就是两个对象相互沟通的一个标准,这个标准应该统一,这样容器才能和不同的插件通讯.我们可以使用接口来表述这个契约.例如
type
IPlugin = interface
['{48BF4000-B028-4B57-9955-B1A8305DA394}']
procedure Execute;
end;
容器,它可以配置加载哪些插件,并能调用插件的功能,并和插件交互数据,这种数据应该有统一性,因此我们的目标当然是需要和插件能够交互TObject,因为我们可以封装任何的数据在TObject中去,至于这个TObject中是些什么什么数据,只需要插件和容器知道就可以了.那么我们修改契约如下:
type
IPlugin = interface
['{48BF4000-B028-4B57-9955-B1A8305DA394}']
function GetObject: TObject;
procedure SetObject( value: TObject );
procedure Execute;
end;
插件,我们使用实现了接口的一个bpl来构建插件,让容器动态载入一个bpl,然后访问其中的IPlugin来调用插件
coding吧
我们构造一个容器,它动态的载入一个bpl,并且通过预定义的名称来访问其中的 IPlugin,并调用IPlugin.Execute,这个预定义的名称其实是在bpl中实现了IPlugin的类的名称,这个类的名称我们可以通过修改bpl的名称或者同时发布一个配置文件来让容器获得.现在我们先暂时写死在程序里,毕竟这个问题是个小问题
构建插件
new->package生成一个package,就用'package1'的缺省名称,new->unit
unit TPluginImpl1;
interface
uses uIPlugin, dialogs, Classes;
type
{$M+}
TPlugin = class( TInterfacedPersistent, IPlugin )
function GetObject: TObject;
procedure SetObject( value: TObject );
procedure Execute;
private
FMsg: string;
public
procedure AfterConstruction; override;
end;
{$M-}
implementation
{ TPlugin }
procedure TPlugin.AfterConstruction;
begin
inherited;
FMsg := 'init String';
end;
procedure TPlugin.Execute;
begin
showmessage( FMsg );
end;
function TPlugin.GetObject: TObject;
begin
result := TObject( FMsg );
end;
procedure TPlugin.SetObject( value: TObject );
begin
FMsg := string( Value );
end;
initialization
RegisterClass( TPlugin );
finalization
UnRegisterClass( TPlugin );
end.
TPlugin实现了IPlugin接口,并且注册了该组件,使它能够被容器访问到.
compile,之后,会在%delphi%/bpl目录生成package1.bpl.
构建容器
procedure TForm3.Button1Click( Sender: TObject );
var
theClass : TPersistentClass;
thePlugin : TPersistent;
IPlug : IPlugin;
FPackege : Cardinal;
begin
FPackege := LoadPackage( 'package1.bpl' ); //加载包
theClass := GetClass( 'TPlugin' ); //通过字符串获得类定义
if theClass = nil then
begin
ShowMessage( 'TPlugin not load' );
exit;
end;
thePlugin := theClass.Create; //创建实例
Supports( thePlugin, StringToGUID( '{48BF4000-B028-4B57-9955-B1A8305DA394}'
), IPlug ); //转换成IPlugin接口
try
IPlug.Execute; //执行插件的
finally
IPlug := nil;
end;
UnloadPackage( FPackege ); //卸载包
end;
project->options->package
点选build with runtime package
修改成vcl;rtl,确定
可以发布测试了
拷贝你的project1.exe,package1,windowSystem32目录下的vcl70.bpl,rtl70.bpl到一个目录,把他们拷贝到一个目录下,发布到一个没有delphi的机器上试试吧.