摘要:本文阐述了
Windows
环境下动态链接库的概念和特点,对静态调用和动态调用两种调用方式作出了比较,并给出了
Delphi
中应用动态链接库的实例。
一、动态链接库的概念
动态链接库(
Dynamic Link Library
,缩写为
DLL
)是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。动态链接库文件的扩展名一般是
dll
,
也有可能是
drv
、
sys
和
fon
,它和可执行文件(
exe
)非常类似,区别在于
DLL
中虽然包含了可执行代码却不能单独执行,而应由
Windows
应用
程序直接或间接调用。
动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件
的一部分。换句话说,函数和过程的代码就在程序的
exe
文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数
的多个拷贝,这样就浪费了宝贵的内存资源。而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信
息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在
Windows
的管理下,才在应用程序与相应的
DLL
之间建立链接关系。当要执行所调
用
DLL
中的函数时,根据链接产生的重定位信息,
Windows
才转去执行
DLL
中相应的函数代码。
一般情况下,如果一个应用程序使
用了动态链接库,
Win32
系统保证内存中只有
DLL
的一份复制品,这是通过内存映射文件实现的。
DLL
首先被调入
Win32
系统的全局堆栈,然后映射到
调用这个
DLL
的进程地址空间。在
Win32
系统中,每个进程拥有自己的
32
位线性地址空间,如果一个
DLL
被多个进程调用,每个进程都会收到该
DLL
的
一份映像。与
16
位
Windows
不同,在
Win32
中
DLL
可以看作是每个进程自己的代码。
二、动态链接库的优点
1
.
共享代码、资源和数据
使用
DLL
的主要目的就是为了共享代码,
DLL
的代码可以被所有的
Windows
应用程序共享。
2
.
隐藏实现的细节
DLL
中的例程可以被应用程序访问,而应用程序并不知道这些例程的细节。
3
.
拓展开发工具如
Delphi
的功能
由于
DLL
是与语言无关的,因此可以创建一个
DLL
,被
C++
、
VB
或任何支持动态链接库的语言调用。这样如果一种语言存在不足,就可以通过访问另一种语言创建的
DLL
来弥补。
三、动态链接库的实现方法
1
.
Load-time Dynamic
Linking
这种用法的前提是在编译之前已经明确知道要调用
DLL
中的哪几个函数,编译时在目标文件中只保留必要的链接信息,而不含
DLL
函数的代码;当程序执行时,利用链接信息加载
DLL
函数代码并在内存中将其链接入调用程序的执行空间中,其主要目的是便于代码共享。
2
.
Run-time Dynamic
Linking
这种方式是指在编译之前并不知道将会调用哪些
DLL
函数,完全是在运行过程中根据需要决定应调用哪个函数,并用
LoadLibrary
和
GetProcAddress
动态获得
DLL
函数的入口地址。
四、DLL
的两种调用方式在Delphi
中的比较
编写DLL
的目的是为了输出例程供其他程序调用,因此在DLL
的工程文件中要把输出的例程用Exports
关键字引出。在调用DLL
的应用程序中,需要 声明用到的DLL
中的方法,声明格式要和DLL
中的声明一样。访问DLL
中的例程有静态调用和动态调用两种方式。静态调用方式就是在单元的 Interface
部分用External
指示字列出要从DLL
中引入的例程;动态调用方式就是通过调用Windows
的API
包括 LoadLibrary
函数、GetProcAddress
函数以及FreeLibrary
函数动态的引入DLL
中的例程。
静态调用 方式所需的代码较动态调用方式所需的少,但存在着一些不足,一是如果要加载的DLL
不存在或者DLL
中没有要引入的例程,这时候程序就自动终止运行;二是 DLL
一旦加载就一直驻留在应用程序的地址空间,即使DLL
已不再需要了。动态调用方式就可解决以上问题,它在需要用到DLL
的时候才通过 LoadLibrary
函数引入,用完后通过FreeLibrary
函数从内存中卸载,而且通过调GetProcAddress
函数可以指定不同的例程。 最重要的是,如果指定的DLL
出错,至多是API
调用失败,不会导致程序终止。以下将通过具体的实例说明说明这调用方式的使用方法。
1
. 静态调用方式
示例程序创建了一个DLL
,其中仅包含一个求两个整数的和的函数,在主程序中输入两个整数,通过调用该DLL
,即可求出两个整数的和,如图1
所示。
该DLL
的程序代码如下:
library AddNum; |
主程序在调用该DLL
时,首先在interface
部分声明要调用的函数:
function
AddNum(Num1,Num2:integer):integer;stdcall;external 'AddNum.dll' |
然后在按钮控件的事件中写入如下代码:
procedure TForm1.Button1Click(Sender: TObject); |
2
.动态调用方式
这个示例程序创建了一个显示日期的DLL
,其中包含一个窗体,如图2
所示。
程序中定义了一个ShowCalendar
函数,返回在这个窗体中设定的日期。函数定义如下:
function ShowCalendar(AHandle: THandle; ACaption:
String): TDateTime; |
在DLL
的工程文件中用exports
ShowCalendar;
语句引出该函数。下面通过一个简单的应用程序测试一下该DLL
文件。新建一个工程文件,在窗体中放置一个Label
控件和一个按钮控件,在按钮控件的OnClick
事件中编写如下代码:
procedure TMainForm.Button1Click(Sender: TObject); |
从以上程序中可以看到DLL
的动态调用方式比静态调用方式的优越之处。DLL
例程在用到时才被调入,用完后就被卸载,大大减少了系统资源的占用。在调用
LoadLibrary
函数时可以明确指定DLL
的完整路径,如果没有指定路径,运行时首先查找应用程序载入的目录,然后是Windows
系统的 System
目录和环境变量Path
设定的路径。
五、结束语
由于动态链接库可以实现代码和资源的共享,大大减少系统资源的占用,因此在当今的应用程序开发中起着非常重要的作用。Delphi
是现今流行的应用软件开发工具,本文就如何在Delphi
中使用动态链接库给出了一定程度上的阐述。