动态调用也称为显式加载方式,动态调用不需要在单元体 Interface 部分把需要调用的所有 dll 过程全部列出,只要在调用前引用,并且使用 LoadLibrary 函数指定需要加载的 dll 文件,使用 GetProcAddress 函数指定所调用的过程或函数,并返回该过程或函数的入口地址,使用 FreeLibrary 函数实现将该函数从内存中移除。
如果指定的 dll 文件出错,不会导致程序终止运行。
动态调用 dll 主要用到三个 Win32 API 函数:
1.LoadLibrary() - 把指定库模块装入内存,语法:
function LoadLibrary(LibFileName: PChar): THandle;
LibFileName 指定了要装载 dll 的文件名,如果 LibFileName 没有包含一个路径,则 Windows 按如下顺序进行查找:
- 当前目录
- Windows 目录
- Windows 系统目录
- 当前可执行文件所在目录
- PATH 环境变量中的目录以及网络的映像目录列表
如果函数执行成功,则返回装载库的实例句柄。否则,返回一个小于 HINSTANCE_ERROR 的错误代码以指明错误的种类。
如果应用程序中使用 LoadLibrary 调用某一模块前,其他应用程序已经把该模块装入内存,则 LoadLibrary 并不会装载该模块的另一实例,而是使该模块的“引用计数”加 1 。这样就能够保证在内存中只加载一次,所有应用程序都共享这个单一的拷贝。
2.GetProcAddress() - 获取给定模块中函数的地址,语法:
function GetProcAddress(Module: THandle; ProcName: PChar): TFarChar;
Module 包含被调用的函数库模块的句柄,该值由 LoadLibrary 返回。如果将 module 设置为 nil,则表示要引用当前模块。
ProcName 是指向含有函数名的以 nil 结尾的字符串指针,该字符串指针中所含有的函数名的拼写必须与动态链接库文件 exports 中的对应拼写一致,否则会出错。
如果该函数执行成功,则返回模块中函数入口处的地址,否则返回 nil。
3.FreeLibrary() - 从内存中移出库模块,语法:
procedure FreeLibrary(Module: THandle);
Module 为库模块的句柄,由 LoadLibrary 返回。由于库模块在内存中只装载一次,因而调用该过程首先库模块的引用计数减 1。如果引用计数为 0,则卸载该模块。
示例:使用上一节中的角度转换弧度的动态链接库,实现输入角度,输出弧度。界面如下:
代码如下:
unit Unit1;
{$mode objfpc}{$H }
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Spin, Windows;
type
{ TForm1 }
TForm1 = class(TForm)
AngFloatSpinEdit: TFloatSpinEdit;
Button1: TButton;
Label1: TLabel;
Label2: TLabel;
ArcLabel: TLabel;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
type
// 照着DLL(在源文件中)所使用的定义的方式,定义将要调用的子程序
TFunAngToArc=function (a: double):double; stdcall;
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
// 为DLL子程序创建一个合适的变量(数据字段)
pfunc: TFunAngToArc;
// 为DLL创建一个处理程序/句柄
hdl: THandle;
begin
// 获取将要使用的库的处理程序/句柄
hdl:=LoadLibrary(PChar('laz1401.dll'));
// 检查是否成功加载DLL文件
if hdl>32 then
begin
// 分配子程序调用的存储器地址给变量
Pointer(pfunc):=GetProcAddress(hdl, 'FunAngToArc');
// 检查是否已经返回一个有限的存储器内存地址
if @pfunc <> nil then
ArcLabel.Caption:=floattostr(pfunc(AngFloatSpinEdit.Value));
end;
// 释放存储器内存
pfunc:=nil;
FreeLibrary(hdl);
end;
end.