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

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代码内的断点处。

谁说WP仍然不行?意大利WP份额已超iOS MDCC之移动娱乐游戏峰会:百家手游齐逐金玩奖 专访Mozilla宫力:基于Web是Firefox OS最大的资本 如何设计一款Java框架? AngularJS优化最佳实践:从1200ms缩减到35ms Linus Torvalds宣布Linux kernel3.12正式发布! 硅谷的技术文化——渴求独处! MDCC为移动开发者服务:一看、一聊、一聚 做华为合伙人:华为开发者联盟沙龙&#183;北京站纪实 Q3全球智能手机报告:Android和iOS基本不变 WP最得意 Google Apps不再支持IE9 IE9用户或将无法使用Gmail等服务 [开源推荐]CecOS:企业级的云操作系统和虚拟化平台 Avangate SaaS模式开启全球软件新营销之门 Android平台的两大移动威胁现状与变化趋势 推荐五款优秀的PHP代码重构工具 11个值得注意的安全问题及应对,第12个高端黑 微软新举措:打通Windows和WP开发者账户,下调注册费用 合并两应用商店的前兆?微软开始合并Windows Store和WP开发者账户 最难忘的Bug调试经历 LiveCode:让每个人都能开发APP的开发平台 17173总经理赵佳:媒体移动化需抓住三大要素 Django 1.6发布 支持持久数据库链接 如何优化单页面网站搜索引擎? TechCrunch国际创新峰会将于上海举行 极路由:一场由路由器引发的概念盛宴 14nm制程:三星64bit处理器 Exynos 6/S曝光 从Oracle到MySQL,余额宝云实践分享 从南水北调东线工程看物联网,大数据的另一面 不只是技术!成为IT经理必备的十大软技能 看看传说5亿行代码的Healthcare.gov网站的架构 如何走出选择PHP还是ASP.NET的两难境地? CString的find函数 哪位高手能解决Visual C++ Extensions for ADO之AddNew(&rs)的问题?(可惜无分可赠!!!) 在一个对话框中怎么得到另外一个对话框的某个控件的指针? 谁有delphi连接sql server的技术资料文章 谁有delphi6网络编程一书的代码???????????? 超级简单的问题!答了就给分!快快来! 谁有《delphi6网络编程》一书的原代码??????????? AMD xp cpu的性能是怎么样的 请大家帮忙,一个LPTCSTR的数据问题 散分,散分,高手在哪?新买的165上网卡无法拨号上网,救命,喔,救命 请问一个问题。 赛扬1.7G,256M内存打“传奇”有点卡!!!!!!!!帮我,快 如何 将数字字符串 转换为整数? 千万火急----如何把从组件中取的记录集在VB中赋给FPSpread控件? 探讨方法 有些不懂,请帮帮忙,有分。 大哥哥进来看看小妹这个问题好吗? socket高手请进!可以解决吗? java 的帮助文件哪儿可以找到? 很多页面都链接到同一页面,在返回时如何判断返回到原先的页面?(不用histroy.back())? jbuilder9中开发web services,import wsdl文件出错 很多页面都链接到同一页面,在返回时如何判断返回到原先的页面?(不用histroy.back()) 要调用某个DLL中函数,应如何申明? 有问题急? jb8中类找不到了??? 哪位可以告诉我在哪里可以改动网论坛首页上方的banner广告的路径? 救命啊!!为什么在DBASE4里执行“ALTER TABLE TableName ADD COLUMN AAA C(10)”发生异常?!,该语句不兼容啊! 如果当初你没遇到Java,你会选择什么 欢迎参加微软新闻组 & Diyinside Windows User Group离线技术沙龙 访问access数据库怎么不能查日期? Dll问题! 这个异常是什么回事? 小小问题。。。 為深麼我的Wizard模式的PreportyPage的OnWizardNext函數實現後,不能出現第二個頁面,how to. 那一个有的高程模拟试题 简单问题,请指点 在线等待求助:win2000缠人问题 ****郁闷,从来没有遇到这样的问题!!!为什么报表显示的时候会闪一下!而且显示出来后其窗体还没有自动关闭!******** 请大家发表一下自己的意见 问一个消息? 欢迎参加微软新闻组 & Diyinside Windows User Group离线技术沙龙 求助:chart使用Lineseries颜色问题 ?在C#的如何运用线程 关于安装Websphere Portal的问题。希望大家帮我解决一下。 非法字符的问题 转让一优派PT813 21寸显示器(适合专业作图) 北京 一个小问题,还请多帮忙!谢谢!! 怎么用命令看到一个进程的内存数? 用VC怎样获知当前Window是否支持某国语言(如中文)? 投诉: 有关通用查询组件—— Teachers apparently expect a certain amont of aggressive from boys.为什么 apparently 可以修饰expect.而且在这里,expect不加ed呢 我国古代所称的诗三百指的是? 三天大雨两天晒网是什么意思 ''Social Studies" Means? 这场大雨( )下了三天,地上水流成河.续组词填空 “交代”与“交待”的区别如题.这两个词到底有什么样的区别? 三天前下了一场大雨 英语如题 “交代”和“交待”有什么区别?哪个正确? 金星木星,能看到吗 唯一和惟一有什么区别?交代和交待有什么区别? 朝鲜海军数十名官兵在执行战斗任务时身奶源大省河北出现乳品抢购风 乳品涨价CNN恶意报道北京\"10·28\"德城市疏散两万居民拆除二战炸弹中国当代雕塑入选法国卢浮宫非遗展德国西部城市多特蒙德疏散两万居民拆除广西以制度给高官划\"红线\"标\"记协召开“打击新闻敲诈、加强行业自律尼泊尔发生180多起“反选举”暴力活李克强:改革已进深水区 必须敢于打破伊朗最高领袖对核谈“不乐观”省领导会见香港新界地区图片新闻标题导航一载有陆客的大客车在苗栗侧翻七岁男童落水 两位教师勇救敢于担当清正廉洁的政法队伍标题导航标题导航让无人机成为民造福的工具全国台企联举行第四届会员代表大会出国“躲债”官场如歌穿梭于地球和火影世界万乘风流巨星学校北境传奇诡墓魅影古楼迷影诡谲事件私人侦探公司财宝无双末日之神黑夜行天生桥瀑布群旅游普贤寺旅游大兴善寺旅游含光门遗址博物馆旅游重阳宫旅游咸阳古渡旅游张裕卡斯特酒庄旅游半月湾旅游林海烽山森林公园旅游五马祠街旅游世界风筝都纪念广场旅游
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘