网站公告列表

  没有公告

加入收藏
设为首页
联系站长
您现在的位置: 网络学院 >> 程序设计 >> Delphi编程 >> 文章正文
  初探delphi 7 中的插件编程          【字体:
初探delphi 7 中的插件编程
作者:佚名    文章来源:不详    点击数:    更新时间:2007-7-1    

初探Delphi 7 中的插件编程

caishaoting 2005-07-10

1 前言

1.1 写作目的

我写Delphi程序是从MIS系统入门的,开始尝试子系统划分的时候采用的是MDI窗体的结构。
正在装载数据……
随着系统功能的扩充,不断有新的子系统加入系统中,单个工程会变得非常大,每次做一点修改都要重新编译,单个工程的形式也不利于团队协作。为了提高工作效率,我希望利用
DLL动态链接库的形式实现插件结构的编程

 

插件结构的编程需要一个插件容器来控制各DLL的运行情况,将划分好的每个子系统安排到一个DLL库文件中。对每个DLL程序需要为容器预留接口函数,一般接口函数包括:启动调用DLL库的函数、关闭DLL库的函数。通过接口函数,插件容器可以向DLL模块传递参数实现动态控制。具体实现细节我将在下文说明并给出响应代码。

 

1.2 阅读对象

您可能需要先了解一下DELPHIUNIT的结构,工程的结构。本文没有深入讨论DLL编程的理论细节,只是演示了一些实用的代码,我当时学习的是刘艺老师的《DELPHI深入编程》一书。

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

 

2 示例程序简介

为了便于阅读我将使用一个MIS系统的部分程序代码演示插件编程的一些方法。示例程序是典型的C/S结构DBMS应用程序,我们关注的部分将是框架程序(下文简称Hall)的控制语句和dll插件程序的响应控制。

 

2.1 程序结构

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

每个插件程序独立使用各自的工程,与普通工程不同的是,DLL工程创建的是Dll Wizard,相应编译生成的文件是以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.        UnitImplementation部分中声明函数

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

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

为了使代码简洁,我个人喜欢在工程中独立添加一个Unit单元(File – New -- Unit),然后将所有要输出的函数体定义在此单元中,不要忘记将引用到的窗体的Unituses进来。我命名这个单元为UnitEntrance,在ShowDLLForm函数中初始化了要显示的窗口并调用Show方法显示,HALL会将登陆的用户名用参数传递过来,得到用户名后就可以进行一些权限控制,表现在界面初始化上。

其代码如下

 

unit UnitOfficeEntrance;

 

interface

uses

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

 

function ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;

function FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;

 

implementation

 

uses UnitOfficialMainForm;                // 改成MAINFORMunit

 

var

    DLL_Form:TFormOfficialMain;      //改成MAINFORMNAME

 

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

//Name: ShowDLLForm

//Func: DLL插件调用入口函数

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

//Rtrn: N/A

//Auth: CST

//Date: 2005-6-3

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

function ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;

begin

    result:=true;

    try

        Application.Handle:=AHandle;    //挂靠到主程序容器

        DLL_Form:=TFormOfficialMain.Create(Application);  //改成MAINFORMNAME

        try

            with DLL_Form do

            begin

                Caption := ACaption;

                StatusBar.Panels.Items[0].Text := AUserID;

                //Configure UI

                Show ;

            end;

        except

            on e:exception do

            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

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

function FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;

begin

    Application.Handle:=AHandle;    //挂靠到主程序容器

 

    if DLL_Form.Showing then DLL_Form.Close;  //如果窗口打开先关闭,触发FORM.CLOSEQUERY可取消关闭过程

    if not DLL_Form.Showing then

        begin

            DLL_Form.Free;

            result:=true;

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

    else

        begin

            result:=false;

        end;

end;

 

end.

 

DLL工程文件代码如下:

 

library Official;

 

{ Important note about DLL memory management: ShareMem must be the

  first unit in your library's USES clause AND your project's (select

  Project-View Source) USES clause if your DLL exports any procedures or

  functions that pass strings as parameters or function results. This

  applies to all strings passed to and from your DLL--even those that

  are nested in records and classes. ShareMem is the interface unit to

  the BORLNDMM.DLL shared memory manager, which must be deployed along

  with your DLL. To avoid using BORLNDMM.DLL, pass string information

  using PChar or ShortString parameters. }

 

uses

  SysUtils,

  Classes,

  UnitOfficialDetailForm in 'UnitOfficialDetailForm.pas' {FormOfficialDetail},

  UnitOfficialMainForm in 'UnitOfficialMainForm.pas' {FormOfficialMain},

  UnitOfficeEntrance in 'UnitOfficeEntrance.pas',

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

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

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

 

{$R *.res}

exports ShowDLLForm,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;

            if ShowDllForm(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

站内文章搜索 高级搜索
文章录入:admin    责任编辑:admin 
  • 上一篇文章:

  • 下一篇文章:
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    最新热点 最新推荐 相关文章
     在delphi中使用xml文档有…
     初探delphi 7 中的插件编…
     delphi 2006(dexter) & …
     获得windows的版本信息。
     “序列号输入助手”源代…
     rs232串口通讯模块
     ado方式下判断数据表是否…
  • Ant入门-配置和使用     选…

  • 浅析Spring框架下PropertyPl…

  • MVP——Model-Viewer-Presen…

  • C++ Object Model

  • constructor and destructor

  • 绑定HGE到AngelScript脚本系…

  • delegate C#关键字 (委托类型…

  • 【游戏制作基础】网络游戏设…

  • Archive for the Linux Cate…

  • CString, BSTR, LPCTSTR之间…

  •   网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    网络学院©2007 www.23book.net
    为您提供web编程,vb编程,vc编程,服务器架设管理,数据库设计等方面的知识 站长:David