一动态链接库的概念
动态链接库(Dynamic Link Library缩写为DLL)是一个可以被其它应用程序共享的程序模块其中封装了一些可以被共享的例程和资源动态链接库文件的扩展名一般是dll也有可能是drvsys和fon它和可执行文件(exe)非常类似区别在于DLL中虽然包含了可执行代码却不能单独执行而应由Windows应用程序直接或间接调用
动态链接是相对于静态链接而言的所谓静态链接是指把要调用的函数或者过程链接到可执行文件中成为可执行文件的一部分换句话说函数和过程的代码就在程序的exe文件中该文件包含了运行时所需的全部代码当多个程序都调用相同函数时内存中就会存在这个函数的多个拷贝这样就浪费了宝贵的内存资源而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)仅当应用程序被装入内存开始运行时在Windows的管理下才在应用程序与相应的DLL之间建立链接关系当要执行所调用DLL中的函数时根据链接产生的重定位信息Windows才转去执行DLL中相应的函数代码
一般情况下如果一个应用程序使用了动态链接库Win系统保证内存中只有DLL的一份复制品这是通过内存映射文件实现的DLL首先被调入Win系统的全局堆栈然后映射到调用这个DLL的进程地址空间在Win系统中每个进程拥有自己的位线性地址空间如果一个DLL被多个进程调用每个进程都会收到该DLL的一份映像与位Windows不同在Win中DLL可以看作是每个进程自己的代码
二动态链接库的优点
. 共享代码资源和数据
使用DLL的主要目的就是为了共享代码DLL的代码可以被所有的Windows应用程序共享
. 隐藏实现的细节
DLL中的例程可以被应用程序访问而应用程序并不知道这些例程的细节
. 拓展开发工具如Delphi的功能
由于DLL是与语言无关的因此可以创建一个DLL被C++VB或任何支持动态链接库的语言调用这样如果一种语言存在不足就可以通过访问另一种语言创建的DLL来弥补
三动态链接库的实现方法
. Loadtime Dynamic Linking
这种用法的前提是在编译之前已经明确知道要调用DLL中的哪几个函数编译时在目标文件中只保留必要的链接信息而不含DLL函数的代码当程序执行时利用链接信息加载DLL函数代码并在内存中将其链接入调用程序的执行空间中其主要目的是便于代码共享
. Runtime 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调用失败不会导致程序终止以下将通过具体的实例说明说明这调用方式的使用方法
. 静态调用方式
示例程序创建了一个DLL其中仅包含一个求两个整数的和的函数在主程序中输入两个整数通过调用该DLL即可求出两个整数的和
该DLL的程序代码如下
library AddNum;
uses
SysUtils
Classes;
{$R *res}
function AddNumber(NumNum:integer):integer;stdcall; file://定义求和函数
begin
result:=Num+Num;
end;
exports
AddNumber; file://引出求和函数
begin
end
主程序在调用该DLL时首先在interface部分声明要调用的函数
function AddNum(NumNum:integer):integer;stdcall;external AddNumdll
name AddNumber;
然后在按钮控件的事件中写入如下代码
procedure TFormButtonClick(Sender: TObject);
var
NumberNumber:integer;
Sum:integer;
begin
Number:=strtoint(EditText);
Number:=strtoint(EditText);
Sum:=AddNum(NumberNumber); file://调用求和函数计算结果
EditText:=inttostr(Sum);
end;
.动态调用方式
这个示例程序创建了一个显示日期的DLL其中包含一个窗体
程序中定义了一个ShowCalendar函数返回在这个窗体中设定的日期函数定义如下
function ShowCalendar(AHandle: THandle; ACaption: String): TDateTime;
var
DLLForm: TDLLForm;
begin
ApplicationHandle := AHandle;
DLLForm := TDLLFormCreate(Application); file://创建并显示窗体
try
DLLFormCaption := ACaption;
DLLFormShowModal; file://显示方式为模式化
Result := DLLFormcalDLLCalendarCalendarDate; file://返回设定日期
finally
DLLFormFree; file://用完后卸载该窗体
end;
end;
在DLL的工程文件中用exports ShowCalendar; 语句引出该函数下面通过一个简单的应用程序测试一下该DLL文件新建一个工程文件在窗体中放置一个Label控件和一个按钮控件在按钮控件的OnClick事件中编写如下代码
procedure TMainFormButtonClick(Sender: TObject);
var
OneHandle : THandle; file://定义一个句柄变量
begin
OneHandle := LoadLibrary(Clendardll); file://动态载入DLL并返回其句柄
try
if OneHandle <> then file://如果载入成功则获取ShowCalendar函数的地址
@ShowCalendar := GetProcAddress(OneHandle ShowCalendar);
if not (@ShowCalendar = nil) then
file://如果找到该函数则在主窗体的Label中显示DLL窗体中设定的日期
LabelCaption := DateToStr(ShowCalendar(ApplicationHandle Caption))
else
RaiseLastWinError;
finally
FreeLibrary(OneHandle); file://调用完毕收回DLL占用的资源
end;
end;
从以上程序中可以看到DLL的动态调用方式比静态调用方式的优越之处DLL例程在用到时才被调入用完后就被卸载大大减少了系统资源的占用在调用LoadLibrary函数时可以明确指定DLL的完整路径如果没有指定路径运行时首先查找应用程序载入的目录然后是Windows系统的System目录和环境变量Path设定的路径
五结束语
由于动态链接库可以实现代码和资源的共享大大减少系统资源的占用因此在当今的应用程序开发中起着非常重要的作用Delphi是现今流行的应用软件开发工具本文就如何在Delphi中使用动态链接库给出了一定程度上的阐述