DLL的调用 <转载自大富翁论坛> 调用DLL有两种方法,一种是在应用程序装载时调用,另一种是在应用程序运行时调用。首先介绍装载时DLL的调用: (1)装载时调用DLL 在调用DLL的Pas文件中,对DLL函数进行外部声明,声明应位于Implementation的Uses语句后,形式如下: Implementation Uses Dialogs; Function Name(argment):Boolean;far;External 'CallName'; ...... 其中External关键字后面的引号内的字串是DLL的文件名。声明以后即可在Pas文件任何地方引用DLL函数。 装载时调用DLL的优点是速度较快,程序间也可共享代码。 (2)运行时调用DLL DLL的另一种调用方法是在运行时调用。要调用到Windows的API函数:LoadLibrary,GetProcAddress等。主要用于调用 DELPHI和其它语言,特别是C++编译的DLL。 假定你的DLL包括一个函数: Function MyFunc(aparam:word):string;export; 首先在程序Type类型声明处加入一句: Type TMyfunc = function(aparam:word):string; 此句的作用如同C++中声明的函数指针。 然后定义如下变量∶ Var aptr:TFarproc; lhnd:THandle; s:string; 其中Aptr,lhnd两变量声明必须有,s是DLL函数返回值,视情况而定。 在调用DLL处加入如下语句进行DLL装载: lhnd:=Loadlibrary('路径:DLL文件名');{如lhnd:=Loadlibrary('c:\aa\bb.dll'); aptr:=GetprocAddress(lhnd,'Myfunc'); 下面可直接调用DLL了: s:=TMyfunc(bptr)(60);{根据函数填相应的变量参数} 调用完以后,用FreeLibrary释放DLL占用的内存: FreeLibrary(lhnd);
动态调用 uses Windows, ...; type TTimeRec = record Second: Integer; Minute: Integer; Hour: Integer; end; TGetTime = procedure(var Time: TTimeRec); THandle = Integer;var Time: TTimeRec; Handle: THandle; GetTime: TGetTime; ... begin Handle := LoadLibrary('DATETIME.DLL'); if Handle <> 0 then begin @GetTime := GetProcAddress(Handle, 'GetTime'); if @GetTime <> nil then begin GetTime(Time); with Time do WriteLn('The time is ', Hour, ':', Minute, ':', Second); end; FreeLibrary(Handle); end;end;
>
根据Delphi提供的有关 DLL编写和调用的帮助信息,你可以很快完成一般的 DLL编写和调用的 应用程序。本文介绍的主题是如何编写和调用能够传递各种参数(包括对象实例)的 DLL。例如, 主叫程序传递给 DLL一个ADOConnection 对象示例作为参数, DLL中的函数和过程调用通过该对象 实例访问数据库。需要明确一些基本概念。对于 DLL,需要在主程序中包含 exports子句,用于向外界提供调用 接口,子句中就是一系列函数或过程的名字。对于主叫方(调用 DLL的应用程序或其它的 DLL), 则需要在调用之前进行外部声明,即external保留字指示的声明。这些是编写 DLL和调用 DLL必须 具备的要素。另外需要了解Object Pascal 中有关调用协议的内容。在Object Pascal 中,对于过程和函数 有以下五种调用协议:指示字 参数传递顺序 参数清除者 参数是否使用寄存器
register 自左向右 被调例程 是
pascal 自左向右 被调例程 否
cdecl 自右向左 调用者 否
stdcall 自右向左 被调例程 否
safecall 自右向左 被调例程 否 这里的指示字就是在声明函数或过程时附加在例程标题之后的保留字,默认为register,即是 唯一使用 CPU寄存器的参数传递方式,也是传递速度最快的方式;pascal: 调用协议仅用于向后兼容,即向旧的版本兼容;
cdecl: 多用于 C和 C++语言编写的例程,也用于需要由调用者清除参数的例程;
stdcall: 和safecall主要用于调用Windows API 函数;其中safecall还用于双重接口。
在本例中,将使用调用协议cdecl ,因为被调用的 DLL中,使用的数据库连接是由主叫方传递 得到的,并且需要由主叫方处理连接的关闭和销毁。下面是 DLL完整源程序和主叫程序完整源程序。包括以下四个文件: Project1.DPR {主叫程序}
Unit1.PAS {主叫程序单元}
Project2.DPR {DLL}
Unit2.PAS {DLL单元}
{---------- DLL 主程序 Project2.DPR ----------}library Project2;uses
SysUtils,
Classes,
Unit2 in 'Unit2.pas' {Form1};{$R *.RES}{ 下面的语句用于向调用该 DLL的程序提供调用接口 }
exports
DoTest; { 过程来自单元Unit2 }begin
end.
{---------- DLL中的单元 Unit2.PAS ----------}unit Unit2;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, StdCtrls, Menus;type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;{ 本地数据库连接 }
Memo1: TMemo; { 用于显示信息 }
private
public
end;{ 该过程向外提供 }
procedure DoTest(H: THandle; { 获得调用者的句柄 }
AConn: TADOConnection;{ 获得调用者的数据库连接 }
S: string; { 获得一些文本信息 }
N: Integer); { 获得一些数值信息 }
cdecl; { 指定调用协议 } implementation{$R *.DFM}procedure DoTest(H: THandle; AConn: TADOConnection; S: string; N: Integer);
begin
Application.Handle := H; { 将过程的句柄赋值为调用者的句柄 }
{ 上面语句的作用在于, DLL的句柄和调用者的句柄相同,在任务栏中就不会 }
{ 各自出现一个任务标题了。 }
with TForm1.Create(Application) do try{ 创建窗体 }
Memo1.Lines.Append('成功调用'); { 显示一行信息 }
ADOConnection1 := AConn; { 获得数据库连接的实例 }
Memo1.Lines.Append(
ADOConnection1.ConnectionString +
' - ' + S + ' - ' + IntToStr(N)); { 根据得到的参数显示另一行信息 }
ShowModal; { 模式化显示窗体 }
finally
Free; { 调用结束时销毁窗口 }
end;
end;end.
{---------- 调用者 Project1.DPR,很普通的工程文件 ----------}program Project1;uses
Forms,
Unit1 in 'Unit1.pas' {Form1};{$R *.RES}begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
{---------- 调用者单元Unit1.PAS ----------}unit Unit1;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Db, ADODB;type
TForm1 = class(TForm)
Button1: TButton; { 按此按钮进行调用 }
ADOConnection1: TADOConnection; { 本地数据库连接,将传递给 DLL }
procedure Button1Click(Sender: TObject);{ 调用 DLL}
private
public
end;var
Form1: TForm1;implementation{$R *.DFM}{ 外部声明必须和 DLL中的参数列表一致,否则会运行时错误 }
procedure DoTest(H: THandle; { 传递句柄 }
AConn: TADOConnection; { 传递数据库连接 }
S: string; { 传递文本信息 }
N: Integer); { 传递数值信息 }
cdecl; { 指定调用协议 }
external 'Project2.dll';{ 指定过程来源 }{ 调用过程 }
procedure TForm1.Button1Click(Sender: TObject);
begin
DoTest(Application.Handle,
ADOConnection1,
'Call OK',
256);
end;end.
DLL的调用 <转载自大富翁论坛> 调用DLL有两种方法,一种是在应用程序装载时调用,另一种是在应用程序运行时调用。首先介绍装载时DLL的调用: (1)装载时调用DLL 在调用DLL的Pas文件中,对DLL函数进行外部声明,声明应位于Implementation的Uses语句后,形式如下: Implementation Uses Dialogs; Function Name(argment):Boolean;far;External 'CallName'; ...... 其中External关键字后面的引号内的字串是DLL的文件名。声明以后即可在Pas文件任何地方引用DLL函数。 装载时调用DLL的优点是速度较快,程序间也可共享代码。 (2)运行时调用DLL DLL的另一种调用方法是在运行时调用。要调用到Windows的API函数:LoadLibrary,GetProcAddress等。主要用于调用
DELPHI和其它语言,特别是C++编译的DLL。 假定你的DLL包括一个函数: Function MyFunc(aparam:word):string;export; 首先在程序Type类型声明处加入一句: Type
TMyfunc = function(aparam:word):string; 此句的作用如同C++中声明的函数指针。 然后定义如下变量∶ Var
aptr:TFarproc;
lhnd:THandle;
s:string; 其中Aptr,lhnd两变量声明必须有,s是DLL函数返回值,视情况而定。 在调用DLL处加入如下语句进行DLL装载:
lhnd:=Loadlibrary('路径:DLL文件名');{如lhnd:=Loadlibrary('c:\aa\bb.dll');
aptr:=GetprocAddress(lhnd,'Myfunc'); 下面可直接调用DLL了:
s:=TMyfunc(bptr)(60);{根据函数填相应的变量参数} 调用完以后,用FreeLibrary释放DLL占用的内存:
FreeLibrary(lhnd);
uses Windows, ...;
type TTimeRec = record
Second: Integer;
Minute: Integer;
Hour: Integer;
end; TGetTime = procedure(var Time: TTimeRec); THandle = Integer;var Time: TTimeRec;
Handle: THandle;
GetTime: TGetTime;
...
begin
Handle := LoadLibrary('DATETIME.DLL');
if Handle <> 0 then
begin
@GetTime := GetProcAddress(Handle, 'GetTime');
if @GetTime <> nil then
begin
GetTime(Time);
with Time do
WriteLn('The time is ', Hour, ':', Minute, ':', Second);
end;
FreeLibrary(Handle);
end;end;