dll插件问题 ( 积分: 100 )

  • 主题发起人 主题发起人 HNXXCXG
  • 开始时间 开始时间
H

HNXXCXG

Unregistered / Unconfirmed
GUEST, unregistred user!
鄙人弄了个插件程序,但有个缺憾:一个DLL里面只能封装一个窗口。想改进成一个DLL可以封装多个窗口的那种。望高人赐教。

附源码。
 
{*******************************************************}
{ }
{ 插件工程文件 }
{ }
{ 版权所有 (C) 2007 咏南工作室(陈新光) }
{ }
{*******************************************************}

library Project1;

{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }

uses
SysUtils,
Classes,
Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

exports
ShowDLLForm,
GetCaption;

begin
end.
 
{*******************************************************}
{ }
{ 插件封装的窗口 }
{ }
{ 版权所有 (C) 2007 咏南工作室(陈新光) }
{ }
{*******************************************************}

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Label1: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

//打开本窗口
function ShowDLLForm(AHandle: THandle; ACaption: string): Boolean; stdcall;
//输出标题
function GetCaption: Pchar; stdcall;

implementation

{$R *.dfm}

function GetCaption: Pchar; stdcall;
begin
Result := '插件演示NO1';
end;

function ShowDLLForm(AHandle: THandle; ACaption: string): boolean;
begin
result := true;
application.Handle := aHandle;
with TForm1.Create(application) do
begin
try
caption := ACaption;
showmodal;
finally
free;
end;
end;
end;

end.
 
{*******************************************************}
{ }
{ 主程序 }
{ }
{ 版权所有 (C) 2007 咏南工作室(陈新光) }
{ }
{*******************************************************}

unit uMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, ComCtrls;

type
//定义接口函数类型
TShowDLLForm = function(aHandle: THandle; ACaption: String): boolean; stdcall; //窗体显示
TGetCaption = function(aHandle: tHandle): pchar; stdcall; //取标题,用于菜单项

//定义TMyPlugin类,存放 Caption、Address,Call信息
TMyPlugin = Class
Caption: string; //存取加载后的DLL中GetCaption返回的标题
Address: THandle; //存取加载后的DLL的句柄
Call: Pointer; //存取ShowDllForm函数的句柄,指针类型
end;

TfrmMain = class(TForm)
MainMenu1: TMainMenu;
plugins1: TMenuItem;
StatusBar1: TStatusBar;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadPlugins; //初始化插件(装入插件,并在菜单提供调用)
procedure PluginsClick(Sender: TObject); //插件菜单点击事件
procedure FreePlugins; //释放插件
public
{ Public declarations }
end;

var
frmMain: TfrmMain;
ShowDllForm: TShowDLLForm; //声明接口函数类型
Plugins: TList; //指针列表,存放每一个DLL加载后的相关信息
StopSearch: boolean;

implementation

{$R *.dfm}

//通用过程,查找指定扩展名的文件,并存于Files中
procedure SearchFileExt(const Dir, Ext: string; Files: TStrings);
var
found: TSearchRec;
i: integer;
Dirs: TStrings;
Finished: integer;
begin
StopSearch := false;
Dirs := tstringlist.create;
finished := findfirst(dir + '*.*', 63, Found);
while (finished = 0) and not (StopSearch) do
begin
if Found.Name[1] <> '.' then
begin
if (found.attr and faDirectory = faDirectory) then
dirs.add(dir + found.name) //加入到目录列表
else
if POS(UpperCase(Ext), UpperCase(found.name)) > 0 then
files.add(dir + found.name);
end;
finished := findnext(found);
end;
findclose(found);
if not StopSearch then
for i := 0 to dirs.count - 1 do
searchfileext(Dirs, Ext, Files);
Dirs.Free;
end;

//装载插件
procedure TFrmMain.loadPlugins;
var
files: tstrings; //存放文件查找结果的文件列表
i: integer;
MyPlugin: TMyPlugin; //存放插件信息的自定义的变量
NewMenu: TMenuItem;
GetCaption: TGetCaption; //获取插件标题的过程引用
begin
//文件列表
files := tstringlist.Create;
//建立指针列表
Plugins := tlist.Create;
//查找当前目录的子目录plugins下扩展名为.dll的文件,并存于 "files 文件列表"中
SearchFileExt(Extractfilepath(application.ExeName) + 'Plugins/', '.dll', files);
//从文件列表中加载找到的DLL
for i := 0 to files.Count - 1 do
begin
myPlugin := TMyPlugin.Create;
myPlugin.Address := loadlibrary(pchar(files)); //装载DLL,返回句柄
if myplugin.Address = 0 then
showmessage('加载' + files + '失败!')
else begin
try
@GetCaption := GetProcAddress(myPlugin.Address, 'GetCaption');
myPlugin.Caption := GetCaption(application.Handle);
myPlugin.Call := GetProcAddress(myPlugin.Address, 'ShowDLLForm');
Plugins.Add(myPlugin);
//创建菜单,并将菜单标题OnClick事件赋值
NewMenu := TMenuItem.Create(self);
NewMenu.Caption := myplugin.Caption;
newmenu.OnClick := PluginsClick;
NewMenu.Tag := i;
plugins1.Add(newMenu);
except
showmessage('初始化失败!');
raise;
end;
end;
end;
files.Free;
end;

//插件菜单项点击事件
procedure TFrmMain.PluginsClick(Sender: TObject);
begin
//根据菜单项的TAG属性对应函数调用的地址
@ShowDLLForm := TMyPlugin(plugins[tmenuitem(sender).Tag]).Call;
//执行ShowDllForm函数
try
ShowDLLForm(application.handle,TMyPlugin(plugins[TMenuItem(sender).Tag]).Caption);
except
showmessage('打开窗体错误!');
end;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
self.LoadPlugins;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
self.FreePlugins;
end;

procedure TFrmMain.FreePlugins;
var
i: integer;
begin
for i := 0 to plugins.Count - 1 do
begin
//按DLL的句柄释放内存
freelibrary(tmyplugin(plugins).Address);
end;
plugins.free;
end;

end.
 
function(aHandle: THandle; ACaption: String): boolean; stdcall;函数可以加一个指出窗体类型的参数
 
窗体类型是指模式和非模式窗体吗?
 
想到了一个解决方法:每个子模块都弄个主菜单窗口包括所有功能,那么一个插件就只要导出那个主窗口就可以了。
 
你的方法和系统架构思路我都明白。
对于一个安整的项目全部采用DLL方法设计,在各模块及单元以及分工或升级这些都是好处,但对于各DLL模块数据共享就要花更的时间处理,虽然内存映射可以解决变量共享,但各DLL很可能会引发API或系统的地址错误,要解决这些问题,那我们原来独立的EXE程序要花的时间就好得好了。对此是否值就看你是怎么采用了。
总结:对于DLL以插件的优点特性,我还是喜欢的。
 
接受答案了.
 
后退
顶部