说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

Delphi 插件创建、调试与使用应用程序扩展

HTML文档下载 WORD文档下载 PDF文档下载
Delphi 插件创建、调试与使用应用程序扩展

有没有使用过Adobe Photoshop如果用过,你就会对插件的概念比较熟悉。对外行人来说,插件仅仅是从外部提供给应用程序的代码块而已(举个例子来说,在一个DLL中)。一个插件和一个普通DLL之间的差异在于插件具有扩展父应用程序功能的能力。例如,Photoshop本身并不具备进行大量的图像处理功能。插件的加入使其获得了产生诸如模糊、斑点,以及其他所有风格的奇怪效果,而其中任何一项功能都不是父应用程序自身所具有的。

对于图像处理程序来说这很不错,可是为什么要花偌大的力气去完成支持插件的商业应用程序呢?假设,我们举个例子,你的应用程序要产生一些报表。你的客户肯定会一直要求更新或者增加新的报表。你可以使用一个诸如Report Smith的外部报表生成器,这是个不怎么样的解决方案,需要发布附加的文件,要对用户进行额外的培训,等等。你也可以使用QuickReport,不过这会使你身处版本控制的噩梦之中——如果每改变一次字体你就要Rebuild你的应用程序的话。

然而,只要你把报表做到插件中,你就可以使用它。需要一个新的报表吗?没问题,只要安装一个DLL,下次应用程序启动时就会看见它了。另外一个例子是处理来自外部设备(比如条形码扫描器)的数据的应用程序,为了给用户更多的选择,你不得不支持半打的各种设备。通过将每种设备接口处理例程写成插件,不用对父应用程序作任何变动就可以获得最大程度的可伸缩性。

入门

在开始写代码之前最重要的事情就是搞清楚你的应用程序到底需要扩展哪些功能。这是因为插件是通过一个特定的接口与父应用程序交互的,而这个接口将根据你的需要来定义。在本文中,我们将建立3个插件,以便展示插件与父应用程序相交互的几种方式。

我们将把插件制作成DLL。不过,在做这项工作之前,我们得先制作一个外壳程序来载入和测试它们。图1显示的是加载了第一个插件以后的测试程序。第一个插件没有完成什么大不了的功能,实际上,它所做的只是返回一个描述自己的字符串。不过,它证明了很重要的一点——不管有没有插件应用程序都可以正常运行。如果没有插件,它就不会出现在已安装的插件列表中,但是应用程序仍然可以正常的行使功能。

我们的插件外壳程序与普通应用程序之间的唯一不同就在于工程源文件中出现在uses子句中的Sharemem单元和加载插件文件的代码。任何在自身与子DLL之间传递字符串参数的应用程序都需要Sharemem单元,它是DelphiMM.dll(Delphi提供该文件)的接口。要测试这个外壳,需要将DelphiMM.dll文件从Delphi\Bin目录复制到path环境变量所包含的路径或者应用程序所在目录中。发布最终版本时也需要同时分发该文件。

插件通过LoadPlugins过程载入到这个测试外壳中,这个过程在主窗口的FormCreate事件中调用,见图2。该过程使用FindFirst和FindNext函数在应用程序所在目录中查找插件文件。找到一个文件以后,就使用图3所示的LoadPlugins过程将其载入。

{ 在应用程序目录下查找插件文件 }

procedure TfrmMain.LoadPlugins;

var

sr: TSearchRec;

path: string;

Found: Integer;

begin

path := ExtractFilePath(Application.Exename);

try

Found := FindFirst(path + cPLUGIN_MASK

0

sr);

while Found = 0 do begin

LoadPlugin(sr);

Found := FindNext(sr);

end;

finally

FindClose(sr);

end;

end;

{ 加载指定的插件 DLL. }

procedure TfrmMain.LoadPlugin(sr: TSearchRec);

var

Description: string;

LibHandle: Integer;

DescribeProc: TPluginDescribe;

begin

LibHandle := LoadLibrary(Pchar(sr.Name));

if LibHandle <> 0 then

begin

DescribeProc := GetProcAddress(LibHandle

cPLUGIN_DESCRIBE);

if Assigned(DescribeProc) then

begin

DescribeProc(Description);

memPlugins.Lines.Add(Description);

end

else

begin

MessageDlg(’File "’ + sr.Name + ’" is not a valid plug-in.’

mtInformation

[mbOK]

0);

end;

end

else

MessageDlg(’An error occurred loading the plug-in "’ +

sr.Name + ’".’

mtError

[mbOK]

0);

end;

LoadPlugin方法展示了插件机制的核心。首先,插件被写成DLL。其次,通过LoadLibrary API它被动态的加载。一旦DLL被加载,我们就需要一个访问它所包含的过程和函数的途径。API调用GetProcAddress提供这种机制,它返回一个指向所需例程的指针。在我们这个简单的演示中,插件仅仅包含一个名为DescribePlugin的过程,由常数cPLUGIN_DESCRIBE指定(过程名的大小写非常重要,传递到GetProcAddress的名称必须与包含在DLL中的例程名称完全一致)。如果在DLL中没有找到请求的例程,GetProcAddree将返回nil,这样就允许使用Assigned函数测定返回值。

为了以一种易用的方式存储指向一个函数的指针,有必要为用到的变量创建一个特定的类型。注意,GetProcAddress的返回值被存储在一个变量中,DescribeProc,属于TpluginDescribe类型。下面是它的声明:

type

TPluginDescribe = procedure(var Desc: string); stdcall;

由于过程存在于DLL内部,它通过标准调用转换编译所有导出例程,因此需要使用stdcall指示字。这个过程使用一个var参数,当过程返回的时候它包含插件的描述。

要调用刚刚获得的过程,只需要使用保存地址的变量作为过程名,后面跟上任何参数。就我们的例子而言,声明:

DescribeProc(Description)

将会调用在插件中获得的描述过程,并且用描述插件功能的字符串填充Description变量。

构造插件

我们已经创建好了父应用程序,现在该轮到创建我们希望加载的插件了。插件文件是一个标准的Delphi DLL,所以我们从Delphi IDE中创建一个新DLL工程,保存它。由于导出的插件函数将用到字符串参数,所以要在工程的uses子句中把Sharemen单元放在最前面。图4列出的就是我们这个简单插件的工程源文件。

uses

Sharemem

SysUtils

Classes

main in ’main.pas’;

{$E plg.}

exports

DescribePlugin;

begin

end.

虽然插件是一个DLL文件,但是没有必要一定要给它一个.DLL的扩展名。实际上,一个原因就足以让我们有理由改变扩展名:当父应用程序寻找要加载的文件时,新的扩展名可以作为特定的文件掩模。通过使用别的扩展名(我们的例子使用了*.plg),你可以在一定程度上确信应用程序只会载入相应的文件。编译指示字$X可以实现这个改变,也可以通过Project Options对话框的Application页来设置扩展名。

第一个例子插件的代码是很简单的。图5显示了包含在一个新单元中的代码。注意,DescribePlugin原型与外壳应用程序中的TpluginDescribe类型相一致,使用附加的export保留字指定该过程将被导出。被导出的过程名称也将会出现在主工程源代码的exports段中(在图4中列出)。

unit main;

interface

procedure DescribePlugin(var Desc: string);

export; stdcall;

implementation

procedure DescribePlugin(var Desc: string);

begin

Desc := ’Test plugin v1.00’;

end;

end.

在测试这个插件之前,要先把它复制到主应用程序的路径下。最简单的办法就是在主目录的子目录下创建插件,然后把输出路径设置为主路径(Project Options对话框的Directories/Conditionals也可以作这个设置)。

调试

现在介绍一下Delphi 3中一个较好的功能:从IDE中调试DLL的能力。在DLL工程中可以通过Run paramaters对话框指定某程序为宿主应用程序,这就是指向将调用DLL的应用程序的路径(在我们这个例子中,就是刚刚创建的测试外壳程序)。然后你就可以在DLL代码中设置断点并且按F9运行它——就像在一个普通应用程序中做的那样。Delphi会运行指定的宿主程序,并且,通过编译带有调试信息的DLL,把你指引到DLL代码内的断点处。

程序员性格怪癖是才华横溢的表现,还是危险分子的征兆? JavaScript Source Map详解 8条学习编程的可靠建议 一周消息树:亲爱的GitHub,我们怎么访问不了你? [回顾] 2012年几大编程语言就业趋势分析 研发周报:微软无视W3C 拒绝遵循WebRTC标准 移动周报:夫妻档闯江湖 事业爱情双丰收 当服务器遇见锤子:第六届AWS全球创业大赛落幕 C# DataTable 和List之间相互转换的方法 iPad如何在三年内改变世界 划时代的成果:新DNA存储技术 轻而易举装下世界 谷歌产品设计:Larry Page的魅力革命 开源移动统计工具Countly更新 支持Unity 3D DailyCost传奇设计师访谈,彰显HTML5移动应用优势 如何成为一名架构师? Apache Hadoop集群的安全性架构 Wood Camera开发者访谈:我们如何冲到榜首的 代码审查:程序员内炼之道 Twitter Q1推广告API 收入剑指10亿 Dell:在OpenStack成熟后才会推出服务 一个开源工作者对开源与赚钱的一些想法 宏碁:Windows8仍不成功 Chrome表现令人意外 雅虎发布Q4财报 净利润同比下滑8% 游戏机禁售令起风波,索尼任天堂股价大涨 国外开发者吐槽Facebook Android设计一例 Rovio转向发行业务 将发布新作Tiny Thief 上山下海 “硬汉”服务器无所不能 访金山云CTO杨刚:开源改进PK自主研发,技术驱动前行 苹果涉及色情问题,且进一步恶化 开源产品系列:面向组件的Web框架tapestry 360指责百度窃取用户信息 公布美杜莎插件技术分析 有北京的狼吗? Access + ADO 的程序发布,需要那些文件?(就这么点分了) 一个小问题,有100分可以相送,送完为止!! Update 时出现 ODBC错误文件被另一线程占用,问题出在那里?? 怎样判断一个控件是否获得焦点. 通过row和col不行,我以前也用过, 用SetSQLSelect更改dw的sql语句后,如果dw没有检索参数时能行,如果dw有检索参数则返回-1,请问有办法吗解决吗? SQL Server中的datetime类型数据是否能比较大小? Update 时出现 ODBC错误文件被另一线程占用,问题出在那里??? 请问U-C矩阵是什么? 庆祝一下 请教程序组加入程序的问题 老是想来灌水,把这点可用分送光得了,省得挂念 IT老虎,与天为敌快进来,谢谢我只有20多分了,你们一人十分吧,我留点好吧? 如何实现打印,尤其是分页打印? paw_001,paw_001 在马? NetGuardian??哪为兄弟有啊,给我一份copy~~~~~~~~~~~~ 关于自增长字段使用的问题,高手请进 这一贴,分就少点了,就这么多啦 有人能告诉我广东省C程序员与JAVA程序员的薪水比较吗?为什么会有这种差距呢 请问用javaApplet如何控制IE界面? 点对点的讨论 水平考试与资格考试及格分别是多少分? 如何再网页中实现打印功能,份也打印。 我发现了一个网站的BUG,我该怎样进去? 到处都是泡泡,俺看的想吐,晚上再来算了。 怪事, 屏幕变黑白 我的问题_6 MYSQL中like的问题 请教打印预览问题 请问,如果我想把,file edit tools window这个主菜单栏动态去掉,怎么做? 如何用程序代码实现在Memo文本编辑框中被选中的文字加下划线。 怎样分析tcp报文(我很急呀) *.idl文件如何使用? 各位大侠,怎么写一个php在线打分的程序阿? 我测试一下 感兴趣的去试试! IE外壳扩展的程序 Delphi 6.0的一个Bug??? 我怎么把字符串放进缓冲里?急急急!!! 恭喜 zhipop 喜得漂亮的可爱的千金 几个小问题想请教大家 如何将浏览器的地址栏里的IE图标换成自己的图标? r俺明天想去给我的宝马生生级,不算显示器,3000元左右,请各位大侠们赐教该什么配置 2000下写的程序,NT 4.0workstation上为什么不能跑? 启用新主页下载数据窗口编辑器 请教Qbasic中的几个问题..... 用BCB如何向Oracle8.0.5中更新BLOB 用ODBC编程时发现错误:nvarchar 类型无数据类型同其绑定,用CString 出错!如何解决? 救命,救命!!!!!!!!!!!! 【 这两天好像来了很多新人嘛!来报个到吧! 】 食用油的成份有哪些? 汽油装在矿泉水瓶里,被压,会不会爆炸,或者起火?比如掉在马路上,汽车压过来……会不会爆炸 把一个手机变进矿泉水瓶的魔术我看了一个魔术,那个魔术师将一个手机变进了一个完好的矿泉水瓶里,而且瓶子是完好的,现场n个观众检验过的,所以各位谁知道这么变的请教教我, 食用油的成分家里用的食用油含有什么组分啊?有没有氨基酸、蛋白质什么的? 最虎药酒配方有哪些,组成成分是什么 变魔术怎么把矿泉水瓶的盖子变到矿泉水瓶里?魔术师把瓶盖变进瓶子里边以后,观众是可以检查的?怎么变的 地沟油的有害成分有什么?为什么一般的食用油检测常用指标检查不出来? 泡制营养酒有什么要求?国家什么机构检测药酒成份最权威?包括有毒有害物质,兴奋剂,激素等 今天抽完烟扔到地上烟头立起来是什么兆头? 食用油的成分是什么 空的矿泉水瓶有什么用这个星期三是截止日期!各位帅哥美女,做考试卷要用!另:尽量多一点! 抽完烟把烟蒂丢在地上烟头树立起来有什么征兆 食用油的性质和成分 人为什么不会被大气压压扁如果解释为内外大气压平衡,产生的压力相等而相互抵消的话,那么试想用两块相同的铁板用相同大的力从相反的方向压一个人,难道由于力可以相互抵消,这个人就可 香烟烧到只剩烟头那部分后扔到地下后会继续燃烧吗?就是说抽香烟的时候抽到只剩烟头的部分,就是上面的烟草部分全都抽完了,没有弹烟灰,那扔到地上后会继续燃烧吗? 如何测试皮肤含水量 为什么大气压不会把人压扁?请真正懂的答 不要瞎编的 你有21根烟头,知道3个烟头可以卷一根烟,请问21根烟头你能抽几根烟回答7的是- 回答10的,都错了,给你们个提示答案11根,能回答对,然后说出理由的就给分 地板怎么测试含水量? 为什么人在大气压中不会被压扁,而在在水中会被压扁?人在大气压中不会被压扁,是因为人身体内部也有大气,抵消了,那么在水里,人身体里也有水,为什么不和外面的水压力抵消,而在深的地方 燃烧的烟头碰到水,水平面上得烟纸也会湿,是什么原因. 测试界限含水量的意义是什么? 为什么有大气压,房子不会被压扁 两个完全相同的矿泉水瓶,一个装满沙,另一个装满水,放在同一斜面上滑下,那个瓶 润滑油含水量怎么测试啊水太多了,润滑油的效果会降低,含水量要怎么才能测出呢 大气压那么大为什么我没被压扁?平时我用笔刺自己一下!我计算过了.压强顶多3000PA我就疼得要死大气压有10的5次方PA为什么我都不觉得疼?难道空气的压力和用笔压不同吗? 两个完全相同的矿泉水瓶,一个装满沙,另一个装满水,放在同一斜面上滑下,那个瓶子滚 含水量试验 关于魔术:矿泉水瓶盖进矿泉水瓶,我想知道如何做到?不方便在这透露的话给我留言,如果有猜想的话也可以发表下.另外还有手机进矿泉水瓶也感到好奇,有达人可指教下如何做到吗? 两个相同的矿泉水瓶,一个全装沙,一个全装水,从斜面滑,那个快? 求毒奶粉 瘦肉精 地沟油 染色馒头的作文 一个空矿泉水瓶在瓶底钻个小洞,然后装上适量的水拧紧瓶盖,为什么不会漏水?为什么拧紧瓶盖不会漏水,但是打开盖子却会漏水? 两个完全相同的矿泉水瓶,一个装沙,一个装水,放在同一斜面上滑下,哪个瓶子滚得比较快 毒奶粉,地沟油,瘦肉精,染色馒头怎么没人管?毒奶粉事件在国际上很丢脸,地沟油就不说,新闻曝光后照样舀地沟油回流餐桌,现在又搞出来瘦肉精,染色馒头,到底有关部门查的不严,还是根本就不 为何将装有适量水的矿泉水瓶打开瓶盖倒插在水槽中,水不下落?而在空中打开瓶盖,为何水下落? 木头放在水里为什么不会沉下去呢? 求除了染色馒头、瘦肉精和地沟油之外,在国内其他的食品问题!急!求官方一点的说法 要写进报告里的 谢谢! 往空矿泉水瓶中加入热水,摇晃并盖紧瓶盖,过一会儿瓶子慢慢向内凹陷,这说明什么? 什么东西放在水里会沉下去 用浸出法加工食用油时,用的是什么化学溶剂. 瘦肉精三聚氰胺染色馒头地沟油和贪官只要发现哪个企业那个窝点生产这些,贪官.死刑立即执行,没收全部财产的200%(没有任何商量的余地),什么死缓啊,终身监禁那都不可能.为什么不这么判 向矿泉水瓶倒热水后迅速倒出并改紧盖子,过一会瓶变扁了, 当电器,汽油,食用油等着火时,不能用( )扑救.选择题.A,喷水 B,喷射干粉 C,喷射二氧化碳 毒奶粉,瘦肉精,地沟油,彩色馒头,这些食品安全事件都说明了啥!诚信的缺失,道德的滑坡? 为什么把热水倒进瓶子又倒出,然后把盖子盖住,瓶子会慢慢往内缩,为什么?请给我解释关于大气压的,不要其他的条件的回答 ~三楼的,如果再没有好的答案,就是你了~ 什么食用油是采用浸出法压榨,浸出法有污染吗? 进来相继发生的“染色馒头”,“瘦肉精”,“地沟油”,“毒奶粉”等耸人听闻的食品安全事件足以表明,诚信的缺失,道德的滑坡已经到了何等严重的地步.中“耸人听闻”使用恰当吗? 刘谦矿泉水瓶倒立水不流出来的魔术怎么变的?要详细的教我 追加50分 教育我不要看魔术揭秘的滚一边去没我要的答案额 他是让人从一箱密封的矿泉水中随意抽一瓶的 所以不存在道具 如何压榨食用油是古代人工压榨的方法 , 把容积为358ml的矿泉水瓶全部按入水中不考虑瓶壁的厚度,矿泉水瓶所受到的浮力有多大? 为什么矿泉水瓶倒扣在水里,水进不去? 食用油中制作工艺:浸出和压榨,有什么区别? 矿泉水瓶如果加8公斤气压会 为什么这么冷的天气装在矿泉水瓶里的水不结冰我儿子在夏天里用矿泉水瓶装了一瓶子水,放在了院子里,现在我们河北都零下十多度了,可是它却没有结冰,我又作了实验,我找了个矿泉水瓶装 请问:压榨油和浸出油有何区别? 往矿泉水瓶里打气,它能承受多大压强才会爆? 怎么让矿泉水瓶盖进矿泉水瓶?还有手机进矿泉水瓶,有达人可指教下如何做到吗? 压榨油和浸出油的区别压榨油和浸出油哪个对身体好 汽油放到矿泉水瓶强烈震动会不会爆炸?起摩托车总是不看油,中途没有油而推着摩托车满大街跑.所以我想把汽油放到瓶子里(震动太厉害,不能使用玻璃瓶)带上,防止没油.有听说塑料瓶里装 把手机放到矿泉水瓶的魔术怎么变的?
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn