动态链接库的建立与调用实验分析(14.动态链接库编程)

4.动态链接库的动态调用

动态调用也称为显式加载方式,动态调用不需要在单元体 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,则卸载该模块。

示例:使用上一节中的角度转换弧度的动态链接库,实现输入角度,输出弧度。界面如下:

动态链接库的建立与调用实验分析(14.动态链接库编程)(1)

代码如下:

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.

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页