调用封装在DLL内的子窗体

摘 要 使用动态连接库(DLL)可以将资源、函数、过程等封装,同样也可以将系统需要调用的子窗体封装起来,在需要时调用。不仅能够节省系统资源,同时也有利于整个软件的模块化处理。Delphi是Windows操作系统中流行的快速开发工具,可以方便的创建和调用动态连接库。本文中,介绍了使用Delphi来完成在动态连接库中封装及调用子窗体的完整过程。

关键词 封装 动态连接库 子窗体 Delphi

 

在Windows的应用程序中,MDI类型的应用程序非常普遍,对于子窗体的调用,则是各有其法。在Windows操作系统中,大量使用到了DLL(动态连接库),通过这种方式可以将程序设计模块化,在系统更新时较为方便。

1 Delphi中对于DLL的支持

Delphi是Borland公司推出的基于Windows操作系统的快速开发工具,它能够很轻松的完成Windows各种应用的开发,同时,也具有编写、调用DLL的能力。

在Delphi中调用DLL哪谌萦辛街址绞剑 蔡 饔糜攵 饔谩?lt;/DIV>

静态调用是指在Delphi中事先声明该函数或者过程的定义,然后既可以按照声明的名称来调用该函数。例如,假如我们要调用Windows的kernel32.dll中的Sleep和SleepEx这样的两个方法,那么就可以事先声明如下:

procedure Sleep; external kernel32 name 'Sleep';

function SleepEx; external kernel32 name 'SleepEx';

这样我们就可以直接使用Sleep和SleepEx来直接调用kernel.dll中的“Sleep”和“SleepEx”方法了。

静态调用使用比较简单,在对于Windows系统自身的DLL中的函数或者过程的调用时,使用较为普遍。上诉所举的两个方法,实际上就是在Delphi的Windows.pas单元中的静态调用声明。

实际上,Windows还提供了另外一种较为复杂的调用方式,即动态调用。

使用动态调用有两个步骤:

⑴ 声明将要调用的DLL内部方法类型为一个对象,例如:

TDllBegin = function(Info : TDllParam; var FormInfo : TFormInfo) : Integer ; StdCall;

⑵ 使用Windows API来动态调用DLL中的方法。

需要使用到三个Windows API函数,分别是:

加载库文件:LoadLibrary;

寻找指定方法的地址:GetProcAddress;

释放库文件资源:FreeLibrary。

下面是一个简单的例子:

 
  
1 var
2
3 H : THandle;
4
5 Proc : TDllBegin;
6
7 pProc : TFarProc;
8
9 ADllFormInfo : TFormInfo;
10
11   begin
12
13 H : = SafeLoadLibrary( ' F001.dll ' ); // 假设F001.DLL为目标DLL文件
14
15   if H <> 0 then
16
17 pProc : = GetProcAddress(H, pAnsiChar( ' DllBeginFunction ' ));
18
19   if pProc <> nil then
20
21   begin
22
23 Proc : = TDllBegin (pProc);
24
25 if Proc(GetDllParam, ADllFormInfo) = 1 then
26
27 FrmList.Add(ADllFormInfo); // 将返回的DLL子窗体信息保存到链表中
28
29   end ;
30
31   end ;

 

在Delphi中提供了一个封装的加载DLL的函数——SafeLoadLibrary,其声明如下:

function SafeLoadLibrary(const Filename: string; ErrorMode: UINT = SEM_NOOPENFILEERRORBOX): HMODULE;

可以使用该函数来替代Windows API LoadLibrary,按照Borland公司的说法,它可以“安全而简单的加载库文件”,其返回值与LoadLibrary一致。

相对于静态调用,动态调用较为复杂,而且还要涉及到过程指针等概念,使用者需要有一定的经验方能使用自如;但是相对于静态调用,动态调用却也有较为灵活的优点,应根据实际情况灵活选用。

在本文中,则主要介绍使用动态调用的方法来调用DLL中的子窗体。

2 Delphi中对于窗体(Form)的显示方法

一般而言,在Delphi中调用窗体有两种方式:模态调用与非模态调用。对应的方法是ShowModal与Show。

在MDI应用程序中,一般对于子窗体均使用Show的方式来显示,即非模态调用。这样做的好处是,即使同时打开了多个子窗体,在窗体之间的切换也非常容易,不会像模态调用那样,出现对一个窗体的操作不结束就无法继续的现象。

因此,在本文中,主要介绍使用非模态方式调用DLL中的子窗体。

3 编写包含子窗体的DLL文件

在Delphi中编写DLL是比较简单的,新建一个DLL项目之后,可以看到在该项目文件(*.DPR)中包含了如下的内容:

 
  
1 library Project2;
2
3
4 uses
5
6 SysUtils,
7
8 Classes;
9
10
11 { $R *.res }
12
13
14 begin
15
16 end .

 

需要注意的是,如果在传递给DLL中方法的参数中包含有String类型的变量(包括结构体中的String变量,以及单独作为参数的String变量),需要在Uses子句的第一位添加上ShareMem单元文件,同时在调用该DLL的主程序项目文件的Uses子句第一位添加上该单元文件。在发布该程序的时候,需要将BorlandMM.DLL文件同时发布。

当然,也可以通过将String类型替换为PChar类型来避免这个问题,这样还可以保证其他的语言调用该DLL内部的方法。其实就是做到与C/C++相兼容。

在建立完一个DLL的项目文件之后,向该项目添加窗体,要将该项目的主窗体的FormStyle社定为fsMDIForm,这样才能保证在主程序中调用该子窗体。

接下来的工作则和普通的Delphi程序设计完全一样,诸如添加控件、编写事件等等。

4 调用DLL中的子窗体

前面的所有工作完成以后,就可以开始从主程序调用封装在DLL中的子窗体。

在调用之前,首先需要了解一个事实:主程序的Application与DLL的Application实际上是两个不同的东西。这就是说,直接使用DLL自身的Application变量肯定得不到预期的结果。

解决的方法有两个:

⑴      主程序的Application句柄传递给DLL,并覆盖之。

⑵      主程序的Application直接传递给DLL,并覆盖之。

同理,也可以把主程序的Screen等全局变量传递给DLL,这样DLL实际使用的就是主程序的全局变量,也只有这样才能顺利的调用封装在DLL中的子窗体。

除此之外,前面介绍的在动态调用DLL内方法的时候,指出了在调用完毕要使用FreeLibirary来释放资源,但是在调用子窗体的时候,却不能立刻就对获得的资源进行释放,否则就会出现错误。

因此,对于这种调用,要使用另外一种方法,即将使用LoadLibirary返回的句柄保存起来,在需要释放的时候(例如,制定关闭某个打开的子窗体、关闭所有的子窗体等等),才是用FreeLibirary来释放打开的DLL资源。

为了完成上诉的任务,需要声明两个结构体,分别保存传递给DLL的参数,以及保存调用的DLL的信息,如下:

TDllParam = packed record

Application : TApplication;//此处直接将Application变量传递给DLL

Screen : TScreen;

   …

end;

 

TFormInfo = packed record

DllForm : THandle;//保存使用LoadLibrary返回的值

end;

 

在完成结构体定义之后,最后再来看看DLL项目文件中对外导出的方法定义。因为在调用子窗体的时候,实际上就是调用该方法。

function DllBegin (SysInfo: TDllParam; var FormInfo: TFormInfo): Integer; stdcall;

begin

Application := SysInfo.Application;//将DLL的Application对象强行设为主程序的Application变量

try

    Result := 0;

    if Application.FindComponent('TInfoDllMainForm') = nil then   

    begin

      ADllForm := TTInfoDllMainForm.Create(Application, SysInfo);

      ADllForm.Show;

      …

      Result := 1;

    end;

    ADllForm.BringToFront;

except

    Result := -1;

end;

end;

exports

DllBegin,

 

对于MDI应用的子窗体,如果在其主窗体的FormClose事件中声明代码:

Action := caFree;

那么在关闭此子窗体的时候,就会自动的销毁该窗体,否则,关闭该子窗体只能让该窗体最小化。

假如在FormClose事件中加入了该行代码,则出现了一个问题,用户关闭了某个子窗体,但是在主程序中仍然保存了调用该子窗体的DLL句柄等信息,那么就非常容易产生异常,解决的方法同样有两个:

⑴ 在FormClose事件中不使用Action := caFree;的代码,所有子窗体的关闭操作全部由主程序来完成。显然,这是一个非常简单的处理方法,用户也无法真正的关闭子窗体,只能使其最小化。

⑵ 使用消息。自己定义一个消息,在子窗体关闭的时候触发这个消息,在主程序定义该消息的处理事件,主要是使用FreeLibirary来释放相对应的资源等操作,这样就可以解决前面提及的问题。

至此,调用封装在DLL内的子窗体整个过程结束。由于所有打开的子窗体信息均保存在链表中,可以对该链表进行各种操作,包括关闭指定的子窗体(同时释放相关的DLL资源等等)。

5 结束语

使用DLL封装窗体,不仅仅对于软件的模块化有益处,而且还对于软件的编写、调试、维护以及升级均有裨益。当然,相对于将主程序、子窗体编写在一起的方式,这种方式显得较为复杂,而且编写并不是那么直观。但是,对于大型程序的编写,多人并行开发,这种方式则又有其独到的优势所在:只要编写好调用的主程序,其下的各个子模块均封装在各自的DLL中,则可以多人同时开始并行开发,独立完成单元测试,这将大大加快整个软件的开发过程。

转载于:https://www.cnblogs.com/RoyGo/archive/2009/08/26/1906995.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值