有一个对象了,我们可以拿它的方法(holder.clickhandler)当作newitem函数的参数。
搞了这一通,clickhandler除了显示一个"clicked!"消息对话框以外什么以没干。
也许这不怎么有趣,不过它仍然证明了一点:插件dll成功的修改了父应用的主菜单,
表现了它的新用途。并且如同第一个例子一样,不管这个插件在不在应用程序都能执行。
由于我们创建了一个对象来处理菜单点击,那么在不再需要这个插件时,就要释放这
个对象。修改后的单元中会在finalization段中处理这件事情。finalization端时与
initialization段相对应的,如果前面有一个initialization段,那么在应用程序终
止时finalization段一定会得到执行。把下面的语句
holder.free
加到finalization段中,以确保holder对象会被正确的释放。
显而易见,虽然这个插件只是修改了外壳应用的主菜单,但是它可以轻易地操纵传
递到initplugin过程中的任何其他对象。如果有必要,插件也可以打开自己的对话框,
向列表框(list boxes)和树状视图(tree views)中添加项目,或者在画布(canvas)
中绘画。
事件驱动的插件
到现在为止我们所描述的技术可以产生一种通用的扩展应用程序的方法。通过增加
新菜单、窗体和对话框,就可以实现全新的功能而不必对父应用做任何修改。不过仍
然有一个限制:这只是一种单侧(one-sided)机制。正如所看到的,系统依赖用户的
某些操作才能启动插件代码,比如点击菜单或者类似的动作。代码运行起来以后,又要
依靠另外一个用户动作来停止它,例如,关闭插件可能已经打开的窗体。克服这种缺
陷的一种可行的方法就是使插件可以响应父应用中的动作--模仿在delphi中工作地
很好的事件驱动编程模型的确有效。
在最后一个例子插件中,我们将创建一种机制,插件可以藉此响应父应用中产生的事
件。通常情况下,可以通过判定需要触发哪些事件、在父应用中为每个事件创建一个
tlist对象来实现。然后每个tlist对象都被传递到插件的初始化过程中,如果插件想
在某个事件中执行动作,它就把负责执行的函数地址加入到对应的tlist中。父应用在
适当的时刻循环这些函数指针的列表,按次序调用每个函数。通过这种方法,就为多
个插件在同一事件中执行动作提供了可能。
应用程序产生的事件完全依赖于程序已确定的功能。例如,一个tcp/ip网络应用程序
可能希望通过tclientsocket的onread事件通知插件数据抵达,而一个图形应用程序可
能对调色板的变化更感兴趣。
为了说明事件驱动的插件应答的概念,我们将创建一个用于限制主窗口最小尺寸
的插件。这个例子有点儿造作,因为把这个功能做到应用程序里边会比这简单的多。
不过这个例子的优点在语容易编码而且易于理解,而这正是本文想要做到的。
很明显,我们要做的第一件事情就是决定到底要产生哪些事件。在本例中,答案
很简单:要限制一个应用程序窗口的尺寸,有必要捕获并且修改windows消息
wm_getminmaxsinfo。因此,要创建一个完成这项功能的插件,我们必须捕获这个消
息并且在这个消息处理器中调用插件例程。这就是我们要创建的事件。
接下来我们要创建一个tlist来处理这个事件。在主窗体的initialization段中将
会创建lstminmax对象,然后,创建一个消息处理器来捕获windows消息
wm_getminmaxinfo。图9中的代码显示了这个消息处理器。
{ 捕获 wm_getminmaxinfo. 为每个消息调用插件例程. }
procedure tfrmmain.minmaxinfo(var msg: tmessage);
var
m: pminmaxinfo; file://在 windows.pas 中定义.
i: integer;
begin
m := pointer(msg.lparam);
for i := 0 to lstminmax.count -1 do begin
tresizeproc(lstminmax[i])(m.ptmintracksize.x,
m.ptmintracksize.y);
end;
end;
图 9: wm_getminmaxinfo 的消息处理器
外壳应用的loadplugin过程必须再次修改以便调用初始化例程。这个新初始化
函数把我们的tlist当作参数接受,在其中加入修改消息参数的函数地址。图10
显示了loadplugin过程的最终版本,它可以执行到目前为止所讨论的全部几个插件
的初始化工作。
{ 加载指定的插件dll. }
procedure tfrmmain.loadplugin(sr: tsearchrec);
var
description: string;
libhandle: integer;
describeproc: tplugindescribe;
initproc: tplugininit;
initevents: tinitpluginevents;
begin
libhandle := loadlibrary(pchar(sr.name));
if libhandle <> 0 then
begin
// 查找 describeplugin.
describeproc := getprocaddress(libhandle,
cplugin_describe);
if assigned(describeproc) then
begin
// 调用 describeplugin.
describeproc(description);
memplugins.lines.add(description);
file://查找initplugin.
initproc := getprocaddress(libhandle, cplugin_init);
if assigned(initproc) then
begin
file://调用initplugin.
initproc(mnumain);
end;
// 为第三方插件查找 initpluginevents
initevents := getprocaddress(libhandle,
cplugin_initevents);
if assigned(initevents) then
begin
// 调用 initplugin.
initevents(lstminmax);
end;
end
else
begin