探Delphi7中的插件编程.docx

上传人:b****7 文档编号:11324700 上传时间:2023-02-26 格式:DOCX 页数:19 大小:127.67KB
下载 相关 举报
探Delphi7中的插件编程.docx_第1页
第1页 / 共19页
探Delphi7中的插件编程.docx_第2页
第2页 / 共19页
探Delphi7中的插件编程.docx_第3页
第3页 / 共19页
探Delphi7中的插件编程.docx_第4页
第4页 / 共19页
探Delphi7中的插件编程.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

探Delphi7中的插件编程.docx

《探Delphi7中的插件编程.docx》由会员分享,可在线阅读,更多相关《探Delphi7中的插件编程.docx(19页珍藏版)》请在冰豆网上搜索。

探Delphi7中的插件编程.docx

探Delphi7中的插件编程

探Delphi7中的插件编程

caishaoting2005-07-10

1前言

1.1写作目的

我写Delphi程序是从MIS系统入门的,开始尝试子系统划分的时候采用的是MDI窗体的结构。

随着系统功能的扩充,不断有新的子系统加入系统中,单个工程会变得非常大,每次做一点修改都要重新编译,单个工程的形式也不利于团队协作。

为了提高工作效率,我希望利用DLL动态链接库的形式实现插件结构的编程。

 

插件结构的编程需要一个插件容器来控制各DLL的运行情况,将划分好的每个子系统安排到一个DLL库文件中。

对每个DLL程序需要为容器预留接口函数,一般接口函数包括:

启动调用DLL库的函数、关闭DLL库的函数。

通过接口函数,插件容器可以向DLL模块传递参数实现动态控制。

具体实现细节我将在下文说明并给出响应代码。

 

1.2阅读对象

您可能需要先了解一下DELPHI中UNIT的结构,工程的结构。

本文没有深入讨论DLL编程的理论细节,只是演示了一些实用的代码,我当时学习的是刘艺老师的《DELPHI深入编程》一书。

我也处于DELPHI的入门阶段,只是觉得这次的DLL开发有一些值得讨论的地方,所以写这篇文章,希望各位能对我做的不好的地方慷慨建议。

 

2示例程序简介

为了便于阅读我将使用一个MIS系统的部分程序代码演示插件编程的一些方法。

示例程序是典型的C/S结构DBMS应用程序,我们关注的部分将是框架程序(下文简称Hall)的控制语句和dll插件程序的响应控制。

 

2.1程序结构

插件容器Hall使用一个独立的工程创建,Hall的主窗口的作用相当于MDI程序中的MDI容器窗体,Hall中将显式调用Dll中的接口函数。

每个插件程序独立使用各自的工程,与普通工程不同的是,DLL工程创建的是DllWizard,相应编译生成的文件是以DLL为后缀。

 

2.2接口设计

实例程序Narcissus中我们预留两个接口函数:

ShowDLLForm

该函数将应用程序的句柄传递给DLL子窗口,DLL程序将动态创建DLL窗体的实例。

还可以将一些业务逻辑用参数的形式传递给DLL子窗口,比如窗体名称、当前登陆的用户名等。

初次调用一个DLL窗体实例时使用此函数创建。

 

FreeDLLForm

该函数将显示释放DLL窗口实例,在退出应用程序时调用每个DLL窗体的FreeDLLForm方法来释放创建的实例,不然会引起内存只读错误。

同样,也可以将一些在释放窗体时需要做的业务逻辑用参数的形式传递给DLL窗体。

 

2.3调试方式

DLL窗体程序无法直接执行,需要有一个插件容器来调用。

应此我们需要先实现一个基本的Hall程序,然后将Hall.exe保存在一个固定的目录中。

对每个DLL工程做如下设置:

1.       打开DLL工程

2.       选择菜单Run–Parameters

3.       在弹出的窗口中浏览到我们的容器Hall.exe

这样在调试DLL程序时将会自动调用Hall程序,利用Hall中预留的调用接口调试DLL程序。

 

3插件程序的基本实现

DLL程序的设计方式和普通WINAPP没有很大的区别,只是所有的窗口都是作为一种特殊的“资源”保存在DLL库中,需要手动调用,而不像WINAPP中会有工程自动创建。

声明接口函数的方法很简单

1.       在Unit的Implementation部分中声明函数

2.       在函数声明语句的尾部加上stdcall标记

3.       在工程代码(Project–ViewSource)的begin语句之前,用exports语句声明函数接口

为了使代码简洁,我个人喜欢在工程中独立添加一个Unit单元(File–New--Unit),然后将所有要输出的函数体定义在此单元中,不要忘记将引用到的窗体的Unit也uses进来。

我命名这个单元为UnitEntrance,在ShowDLLForm函数中初始化了要显示的窗口并调用Show方法显示,HALL会将登陆的用户名用参数传递过来,得到用户名后就可以进行一些权限控制,表现在界面初始化上。

其代码如下

 

unitUnitOfficeEntrance;

 

interface

uses

Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms;

 

functionShowDLLForm(AHandle:

THandle;ACaption:

string;AUserID:

string):

boolean;stdcall;

functionFreeDLLForm(AHandle:

THandle;ACaption:

string;AUserID:

string):

boolean;stdcall;

 

implementation

 

usesUnitOfficialMainForm;//改成MAINFORM的unit

 

var

DLL_Form:

TFormOfficialMain;//改成MAINFORM的NAME

 

//-----------------------------------------

//Name:

ShowDLLForm

//Func:

DLL插件调用入口函数

//Para:

AHandle挂靠程序句柄;ACaption本窗体标题

//Rtrn:

N/A

//Auth:

CST

//Date:

2005-6-3

//-----------------------------------------

functionShowDLLForm(AHandle:

THandle;ACaption:

string;AUserID:

string):

boolean;

begin

result:

=true;

try

Application.Handle:

=AHandle;//挂靠到主程序容器

DLL_Form:

=TFormOfficialMain.Create(Application);//改成MAINFORM的NAME

try

withDLL_Formdo

begin

Caption:

=ACaption;

StatusBar.Panels.Items[0].Text:

=AUserID;

//ConfigureUI

Show;

end;

except

one:

exceptiondo

begin

dll_form.Free;

end;

end;

except

result:

=false;

end;

end;

 

//-----------------------------------------

//Name:

FreeDLLForm

//Func:

DLL插件调用出口函数

//Para:

AHandle挂靠程序句柄

//Rtrn:

true/false

//Auth:

CST

//Date:

2005-6-11

//-----------------------------------------

functionFreeDLLForm(AHandle:

THandle;ACaption:

string;AUserID:

string):

boolean;

begin

Application.Handle:

=AHandle;//挂靠到主程序容器

 

ifDLL_Form.ShowingthenDLL_Form.Close;//如果窗口打开先关闭,触发FORM.CLOSEQUERY可取消关闭过程

ifnotDLL_Form.Showingthen

begin

DLL_Form.Free;

result:

=true;

end//仍然打开状态,说明CLOSEQUERY.CANCLOSE=FALSE

else

begin

result:

=false;

end;

end;

 

end.

 

DLL工程文件代码如下:

 

libraryOfficial;

 

{ImportantnoteaboutDLLmemorymanagement:

ShareMemmustbethe

firstunitinyourlibrary'sUSESclauseANDyourproject's(select

Project-ViewSource)USESclauseifyourDLLexportsanyproceduresor

functionsthatpassstringsasparametersorfunctionresults.This

appliestoallstringspassedtoandfromyourDLL--eventhosethat

arenestedinrecordsandclasses.ShareMemistheinterfaceunitto

theBORLNDMM.DLLsharedmemorymanager,whichmustbedeployedalong

withyourDLL.ToavoidusingBORLNDMM.DLL,passstringinformation

usingPCharorShortStringparameters.}

 

uses

SysUtils,

Classes,

UnitOfficialDetailFormin'UnitOfficialDetailForm.pas'{FormOfficialDetail},

UnitOfficialMainFormin'UnitOfficialMainForm.pas'{FormOfficialMain},

UnitOfficeEntrancein'UnitOfficeEntrance.pas',

UnitOfficialClassin'..\..\Public\Library\UnitOfficialClass.pas',

UnitMyDataAdatperin'..\..\Public\Library\UnitMyDataAdatper.pas',

UnitMyHeadersin'..\..\Public\Library\UnitMyHeaders.pas';

 

{$R*.res}

exportsShowDLLForm,FreeDLLForm;//接口函数

begin

end.

 

插件程序一旦调用了DLL窗口,窗口实例将会保持在HALL窗口的上层,因此不用担心遮挡的问题。

 

 

4容器程序的实现。

4.1接口函数的引入

调用DLL库中的函数有显式和隐式两种方式,显式调用更灵活,因此我们使用显示调用。

在Delphi中需要为接口函数申明函数类型,然后实例化函数类型的实例,该实例实际是一个指向函数的指针,通过指针我们可以访问到函数并传递参数、获取返回值。

在单元文件的Interface部分加入函数类的申明:

 

type

//定义接口函数类型,接口函数来自DLL接口

TShowDLLForm=Function(AHandle:

THandle;ACaption:

String;AUserID:

string):

Boolean;stdcall;

TFreeDLLForm=Function(AHandle:

THandle;ACaption:

String;AUserID:

string):

boolean;stdcall;

 

显示调用库函数需要如下几个步骤

1.       载入DLL库文件

2.       获得函数地址

3.       执行函数

4.       释放DLL库

接下来我们将详细讨论这几个步骤。

 

4.2   载入DLL库文件

通过调用API函数LoadLibrary可以将DLL库载入到内存中,在此我们不讨论DLL对内存管理的影响。

LoadLibrary的参数是DLL文件的地址路径,如果载入成功会返回一个CARDINAL类型的变量作为DLL库的句柄;如果目标文件不存在或其他原因导致载入DLL文件失败会返回一个0。

 

4.3   实例化接口函数

获得接口函数指针的API函数为GetProcAddress(库文件句柄,函数名称),如果找到函数则会返回该函数的指针,如果失败则返回NIL。

使用上文定义的函数类型定义函数指针变量,然后使用@操作符获得函数地址,这样就可以使用指针变量访问函数。

主要代码如下:

……

var

ShowDLLForm:

TShowDLLForm;//DLL接口函数实例

FreeDLLForm:

TFreeDLLForm;

begin

try

begin

APlugin.ProcAddr:

=LoadLibrary(PChar(sPath));

APlugin.FuncFreeAddr:

=GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');

APlugin.FuncAddr:

=GetProcAddress(APlugin.ProcAddr,'ShowDLLForm');

 

@ShowDLLForm:

=APlugin.FuncAddr;

@FreeDLLForm:

=APlugin.FuncFreeAddr;

ifShowDllForm(Self.Handle,APlugin.Caption,APlugin.UserID)then

Result:

=True

……

 

4.4   一个具体的实现方法

为了结构化管理插件,方便今后的系统扩充,我们可以结合数据库记录可用的DLL信息,然后通过查询数据库记录动态访问DLL程序。

4.4.1       系统模块表设计

对于MIS系统,可以利用已有的DBS条件建立一个系统模块表,记录DLL文件及映射到系统模块中的相关信息

字段名

作用

类型

AutoID

索引

INT

modAlias

模块别称

VARCHAR

modName

模块名称

VARCHAR

modWndClass

窗体唯一标识

VARCHAR

modFile

DLL路径

VARCHAR

modMemo

备注

TEXT

⏹        模块别称是用来在编程设计阶段统一命名的规则,特别是团队开发时可以供队员参考。

⏹        模块名称将作为ACAPTION参数传递给SHOWDLLFORM函数作为DLL窗口的标题。

⏹        窗体唯一标识是DLL子模块中主窗口的CLASSNAME,用来在运行时确定要控制的窗口。

⏹        DLL路径保存DLL文件名称,程序中将转换为绝对路径。

 

4.4.2       插件信息数据结构

定义一个记录插件相关信息的数据接口可以集中控制DLL插件。

在Interface部分加入如下代码:

type

//定义插件信息类

TMyPlugins=class

Caption:

String;//DLL窗体标题

DllFileName:

String;//DLL文件路径

WndClass:

String;//窗体标识

UserID:

string;//用户名

ProcAddr:

THandle;//LOADLIBRARY载入的库句柄

FuncAddr:

Pointer;//SHOWDLLFORM函数指针

FuncFreeAddr:

Pointer;//FREEDLLFORM函数指针

end;

……

为每个插件创建一个TMyPlugins的实例,下文会讨论对这些实例的初始化方法。

 

4.4.3       插件载入函数

在本示例中DLL窗口是在HALL中触发打开子窗口的事件中载入并显示的。

按钮事件触发后,先根据插件结构体实例判断DLL是否已经加载,如果已经加载,则控制窗口的显示或关闭;如果没有加载则访问数据表将字段赋值到插件结构体中,然后执行载入、获得指针的工作。

局部代码如下

……

//-----------------------------------------

//Name:

OpenPlugin

//Func:

插件信息类控制过程:

初始化==》设置权限==》载入DLL窗口

//Para:

APlugin-TMyPlugins;sAlias别名;iFuncValue权限值

//Rtrn:

N/A

//Auth:

CST

//Date:

2005-6-2

//-----------------------------------------

procedureTFormHall.OpenPlugin(AFromActn:

TAction;APlugin:

TMyPlugins;sAlias:

string;sUserID:

string);

varhWndPlugin:

HWnd;

begin

 

//判断插件窗口是否已经载入

hWndPlugin:

=FindWindow(PChar(APlugin.WndClass),nil);

ifhWndPlugin<>0then//插件窗口已经载入

begin

ifnotIsWindowVisible(hWndPlugin)then

begin

AFromActn.Checked:

=True;

ShowWindow(hWndPlugin,SW_SHOWDEFAULT);//显示

end

else

begin

AFromActn.checked:

=False;

ShowWindow(hWndPlugin,SW_HIDE);

end;

Exit;//离开创建插件过程

end;

 

 

//初始化插件类实例

ifnotInitializeMyPlugins(APlugin,sAlias)then

begin

showmessage('初始化插件类错误。

');

exit;

end;

 

 

//获得当前权限值

APlugin.UserID:

=sUserID;

//载入DLL窗口

ifnotLoadShowPluginForm(APlugin)then

begin

showmessage('载入中心插件出错。

');

exit;

end;

end;

 

 

//-----------------------------------------

//Name:

InitializeMyPlugins

//Func:

初始化MYPLUGIN实例(Caption|DllFileName|IsLoaded)

//Para:

APlugin-TMyPlugins

//Rtrn:

N/A

//Auth:

CST

//Date:

2005-6-2

//-----------------------------------------

functionTFormHall.InitializeMyPlugins(APlugin:

TMyPlugins;sAlias:

String):

Boolean;

var

strSQL:

string;

myDA:

TMyDataAdapter;

begin

Result:

=False;

myDA:

=TMyDataAdapter.Create;

strSQL:

='SELECT*FROMSystemModuleListWHEREmodAlias='+QuotedStr(sAlias);

 

try

myDA.RetrieveData(strSQL);

except

onE:

Exceptiondo

begin

result:

=false;

myDA.Free;

exit;

end;

end;

 

try

begin

withmyDA.MyDataSetdo

begin

ifNotIsEmptythen

begin

APlugin.Caption:

=FieldByName('modName').Value;

APlugin.DllFileName:

=FieldByName('modFile').Value;

APlugin.WndClass:

=FieldByName('modWndClass').Value;

result:

=True;

end;

Close;

end;//endofwith...do...

end;//endoftry

except

onE:

Exceptiondo

begin

Result:

=False;

myDA.Free;

Exit;

end;//endofexception

end;//endoftry...except

myDA.Free;

end;

 

 

//-----------------------------------------

//Name:

LoadShowPluginForm

//Func:

载入DLL插件并显示窗口

//Para:

APlugin-TMyPlugins

//Rtrn:

true-创建成功

//Auth:

CST

//Date:

2005-6-2

//-----------------------------------------

functionTFormHall.LoadShowPluginForm(constAPlugin:

TMyPlugins):

boolean;

var

ShowDLLForm:

TShowDLLForm;//DLL接口函数实例

FreeDLLForm:

TFreeDLLForm;

sPath:

string;//DLL文件的完整路径

begin

try

begin

sPath:

=ExtractFilepath(Application.ExeName)+'plugins\'+APlugin.DllFileName;

APlugin.ProcAddr:

=LoadLibrary(PChar(sPath));

APlugin.FuncFreeAddr:

=GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');

APlugin.FuncAddr:

=GetProcAddress(APlugin.Pro

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 冶金矿山地质

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1