讨论下插件设计

sanguomi 2009-09-22 09:07:51
加精
今天早上看大富翁资料,看到以前小雨哥发的一个,觉得很不错

WinAMP的插件结构至今都还是经典的插件框架模型,它的模块框架设计通常在外部
看来只输出一个以winampXXXGetHeader命名的函数(XXX是插件模块的种类名称)。

首先设计插件函数。插件应用的特点是主程序可以控制插件程序的行为,但因为真
正的插件往往在主程序完成后才被开发出来,所以主程序要事先约定调用插件时的
调用函数,这种函数主要:

a) 初始化函数
b) 退出函数
c) 配置函数

初始化函数用于插件初始化,退出函数用于插件在退出前的清理。配置函数用于主
程序通知插件本次加载只做配置操作的目的。

这些函数如果一个一个定义,像上面描述的,就需要定义分别定义三个唯一的名字,
在WinAMP中是使用结构体的形式来输出的,而不是对以上三个函数做一一定义,这样
真正的插件只要输出一个函数就可以了,这个函数就是我上面提到的:

winampXXXGetHeader()

通过对这个函数的调用,函数返回一个数据结构的指针,这个数据结构中则包含了上
述三个函数的真实地址,整个插件DLL就类似下面的样子:

==========================================================================

library Project1;

uses
Windows,
SysUtils,
Classes;

{$R *.res}

type
PwinampXXXModule = ^TwinampXXXModule;
TwinampXXXModule = record
Desc:PChar;
Parenthwnd:HWND;
hDllInst:THandle; //HInstance;
ConfigFunc:Pointer;
InitFunc:Pointer;
QuitFunc:Pointer;
userData:Pointer;
end;

上面结构中:
Desc:这个插件模块的描述,由插件填充
Parenthwnd:父窗口句柄,用于消息通讯。由主程序填充
hDllInst:模块实例。由主程序填充
ConfigFunc:配置函数。由插件填充
InitFunc:初始化函数。由插件填充
QuitFunc:退出函数。由插件填充
userData:用户数据。如果有的话,由主程序填充

var
mod1:TwinampXXXModule;

function Config(module:PwinampXXXModule):integer;stdcall;
begin
Result := 0;
end;

function Init(module:PwinampXXXModule):integer;stdcall;
begin
Result := 0;
end;

function Quit(module:PwinampXXXModule):integer;stdcall;
begin
Result := 0;
end;

function winampXXXGetHeader():PwinampXXXModule;stdcall;
begin
FillChar(mod1,sizeof(mod1),0);
with mod1 do
begin
Desc:='插件描述';
ConfigFunc:= @Config;
InitFunc := @init;
QuitFunc:= @Quit;
end;
end;

exports
winampXXXGetHeader;

begin
end.

==========================================================================

主程序调用winampXXXGetHeader获得TwinampXXXModule记录的指针,当主程序开始
调用这个记录中记录的init或者Config等函数时,首先对这个记录的Parenthwnd成员
进行填充,把自己的窗口句柄填到里面,当调用进入插件函数时,插件函数首先检查
传回来的参数是否是自己:

主程序调用 winampXXXGetHeader:

type
PXXXModule = ^TXXXModule;
TXXXModule = record
Desc, Parenthwnd,
hDllInst,ConfigFunc,
InitFunc,QuitFunc,
userData:Integer;
end;

type
TBackCallFunc = function(module:PXXXModule):integer;stdcall;

var
p:PXXXModule;
Func:TBackCallFunc;
begin
p := winampXXXGetHeader;
p^.Parenthwnd := Self.Handle;

// 如果调用配置:
Func := TBackCallFunc(Pointer(P^.ConfigFunc)^);
if @Func <> nil then
Func(p);

// 如果调用初始化:
Func := TBackCallFunc(Pointer(P^.InitFunc)^);
if @Func <> nil then
Func(p);

// 如果调用退出:
Func := TBackCallFunc(Pointer(P^.QuitFunc)^);
if @Func <> nil then
Func(p);

end;

==========================================================================

插件收到调用后(例如调用了 Config 函数):

function Config(module:PwinampXXXModule):integer;stdcall;
begin
if module = mod1 then
begin
// 这里开始初始化并运行配置部分的代码
// 最后返回运行的状态结果
Result := 0;
end
esle Result := 1;
end;

==========================================================================

上面举例中重复写了 Module 的记录结构,但这个记录结构与真正的插件记录的指定类型
并不完全相同,这是没有关系的,只要保证记录结构的数据块大小一致就可以了,调用的
时候可以根据需要来强制转换类型。

真实的插件设计实际上与上面举的样例非常相似,因为不知道记录结构最终按照什么样子
定义最好,通常是做一个基本抽象后,利用字节占位来保证记录块大小即可。

按照上面的方法,就可以先设计主程序,后实现功能模块了,而实现的功能模块几乎不受
主程序的限制。

需要说明的是 WinAMP 中还大量使用了消息机制,用于同步和通讯。在这种框架设计中,
使用消息来连接各个模块的运行还是非常有效的,比如初始化时,init 函数通常会发出一
个初始化开始的消息,来通知插件的主体程序代码进行初始化等等。有效归有效,是否是
最好的方法就不好说了,因为这个不涉及框架,仅仅是运行的触发条件和数据交互的方法。
所以不在这里讨论了(值得注意的是如果使用消息通讯,如果主程序与插件模块在同一个
线程中跑的话,对于 SendMessage 这类的消息发送,就需要开线程来发送,否则,呵呵,
等着死锁吧)。

...全文
899 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
panzhaojl 2009-09-24
  • 打赏
  • 举报
回复
还得回家严究一会
aiq 2009-09-23
  • 打赏
  • 举报
回复
学习了
zhengbo2006 2009-09-23
  • 打赏
  • 举报
回复
学习了~~
phoeni_xin 2009-09-23
  • 打赏
  • 举报
回复
学习了。
GinaKing 2009-09-23
  • 打赏
  • 举报
回复
学习了
grzx2210 2009-09-23
  • 打赏
  • 举报
回复
ding
godly 2009-09-23
  • 打赏
  • 举报
回复
很好。。不错。。。
Harryfin 2009-09-22
  • 打赏
  • 举报
回复
学习
jin20000 2009-09-22
  • 打赏
  • 举报
回复
mark
starluck 2009-09-22
  • 打赏
  • 举报
回复
讨论哪里呢? PostMessage 代替 SendMessage
fenshm 2009-09-22
  • 打赏
  • 举报
回复
学习``期待大牛们讨论.!
wzzwwz 2009-09-22
  • 打赏
  • 举报
回复
学习
7年 2009-09-22
  • 打赏
  • 举报
回复
先MARK。
qinw2002 2009-09-22
  • 打赏
  • 举报
回复
学习!!!
阿三 2009-09-22
  • 打赏
  • 举报
回复
不错,欢迎讨论
JPEXE 2009-09-22
  • 打赏
  • 举报
回复
学习了......
liu_54743234 2009-09-22
  • 打赏
  • 举报
回复
mark...
ilovedrv 2009-09-22
  • 打赏
  • 举报
回复
比较成熟的插件体系有很多,比如 Eclipse
heikeyanxi 2009-09-22
  • 打赏
  • 举报
回复
学习
mike3467 2009-09-22
  • 打赏
  • 举报
回复
sofa
加载更多回复(4)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧