1、概述 1)静态链接与动态链接 静态链接是指在编译期把要调用的函数或过程链接到可执行文件中,成为可执行文件的一部分。也就是说,函数和过程的代码就在程序的.exe文件中。动态链接指在编译时,连接器只使用子例程external声明中的信息,在可执行文件中建立一数据表格,被调用的函数是在运行期才链接到可执行文件中。 2)动态链接库 动态链接库(Dynamic Link Library DLL)是一个程序模块,它包含代码、数据或资源,可以被其他应用程序共享。Delphi的应用程序经常调用DLL中的函数,每当直接访问Window API函数时,其实是在访问DLL。一个动态链接库与一个可执行文件(.exe)类似,它们之间的主要差别,DLL不能单独执行。动态链接库的文件扩展名一般是.dll,也有可能是.drv(设备驱动程序)、.sys(系统文件)和.fon(字体文件)。 3)使用DLL的优点 A、有利于程序的模块化。当需要对应用程序进行修改时,只需要修改其中的一个模块,而不是整个应用程序。我们可以提供DLL的不同版本,代替当前的DLL。 B、共享代码、资源和数据。使用DLL的主要目的是为了共享代码。Delphi中的代码也可以共享,介只限于Delphi程序,而DLL的代码可以被所有的Windows应用程序共享。 C、节省内存。如果不同的程序使用相同的DLL,只需将DLL在内存中装载一次。 D、适合于复杂的应用程序开发。当开发一个庞大而且需不断更新或改正错误的应用程序,可以将其划分为多个执行部分和DLL,这样我们只对需要改变的部分进行操作,而不是对整个大执行文件进行改动。 2、在Delphi中创建DLL 在编写DLL时应遵守以下规则:输出子例程必须列在DLL的exports子句中,使子例程可以在DLL外部可以看到;输出函数必须被声明为stdcall,以使用标准的Win32参数来代替优化的register参数传递技术;DLL可以使用全局数据,该数据将不会通过调用应用程序来共享。 1)在DLL中创建函数 具体步骤如下: 单击菜单file|new,选择New Items窗体中New页中DLL图标,保存项目dllDemo.dpr。 添加一个新单元calc.pas,在单元中声明两个函数并实现; function Add(val1,val2:integer):integer;stdcall; function Substract(val1,val2:integer):integer;stdcall; implementation …… 2)在DLL创建类 *添加一个新单元UseClass.pas * 定义一个基类 TDllCalc=class public function Multiply(val1,val2:integer):integer;virtual;abstract; function Divide(val1,val2:integer):integer;virtual;abstract; end; * 定义一派生类并实现其函数
TDllCalcImpl=class(TDllCalc) public function Multiply(val1,val2:integer):integer;override; function Divide(val1,val2:integer):integer;override; end; implementation …… * 声明一个实现函数
function ClassCalcImpl:TDllCalc;stdcall; function ClassCalcImpl:TDllCalc; begin result:=TDllCalcImpl.Create; end; 3)在项目dllDemo.dpr单元中添加exports子句,并在其中声明输出函数 exports Add,Substract,ClassCalcImpl; 4)编译生成一个.dll文件(dllDemo.dll) 3、在Delphi中使用DLL 建立一个应用程序这prj1.dpr,与DLL在同一目录下。在form中有5个button,2个spinedit,2个bevel,7个label。 在Delphi中调用DLL有两种方式:隐式调用和显式调用。 1)隐式调用 ①直接对函数的调用 在单元implementation部分声明两个函数,必须声明为stdcall,同时加上external dllName
function Add(val1,val2:integer):integer ;stdcall;external 'dllDemo.dll'; function Substract(val1,val2:integer) :integer;stdcall;external 'dllDemo.dll'; 两个button的click事件为: procedure TfrmDemo1.GetVal(var val1, val2: integer); begin val1:=sedt1.Value; val2:=sedt2.Value; end; procedure TfrmDemo1.btnAddClick(Sender: TObject); begin GetVal(FVal1,FVal2); label1.Caption:=inttostr(add(FVal1,FVal2)); end; procedure TfrmDemo1.btnSubstractClick(Sender: TObject); begin GetVal(FVal1,FVal2); label2.Caption:=inttostr(Substract(FVal1,FVal2)); end; ②对类的引用 * 在引用单元中声明一个基类; TDllCalc=class public function Multiply(val1,val2:integer):integer;virtual;abstract; function Divide(val1,val2:integer):integer;virtual;abstract; end; * 在单元实现部分声明一个引用函数; function ClassCalcImpl:TDllCalc;stdcall;external 'dllDemo.dll'; * 然后定义一个对象。 private { Private declarations } FVal1,FVal2:integer; NewObject:TDLLCalc; procedure GetVal(var val1,val2:integer); Implementation procedure TfrmDemo1.FormCreate(Sender: TObject); begin NewObject:=ClassCalcImpl; end; procedure TfrmDemo1.btnMultiplyClick(Sender: TObject); begin GetVal(FVal1,FVal2); label3.Caption:=inttostr(NewObject.Multiply(FVal1,FVal2)); end; procedure TfrmDemo1.btnDivideClick(Sender: TObject); begin GetVal(FVal1,FVal2); label4.Caption:=inttostr(NewObject.Divide(FVal1,FVal2)); end; procedure TfrmDemo1.FormDestroy(Sender: TObject); begin NewObject.Free; end;
2)显式调用 虽然隐式调用DLL比较方便,但这并不是最好的方式。假若一个DLL包含许多例程,其中大部分例程可能根本用不着,因此把整个DLL都调入内存显然是浪费,尤其是当一个应用程序需要用到多个动态链接库。另一种情况是,假设有一组标准的函数有多个版本,分别由多个DLL实现。在这种情况下,最好在需要哪个版本的时候就调入哪个DLL,这就是指显式调用DLL。 定义一个函数类型 TAdd=function(val1,val2:integer):integer;stdcall; const DllName='DllDemo.dll'; procedure TfrmDemo1.bntDynaCallClick(Sender: TObject); var HInst:THandle; FPointer:TFarProc; MyFunc:TAdd; begin HInst:=LoadLibrary(DllName); //调用DLL if HInst>0 then //如果成功 try FPointer:=GetProcAddress(HInst,PChar('Add')); //获得函数地址 if FPointer< >nil then begin GetVal(FVal1,FVal2); MyFunc:=TAdd(FPointer); label5.caption:=inttostr(MyFunc(FVal1,FVal2)); end else messagebox(0,'Funtion Add not found','Warning',MB_OK); finally FreeLibrary(HInst); end else messagebox(0,'DllName not found','Warning',MB_OK); end;
project->Build project1
你可以看看这个
http://www.yesky.com/20011206/208621.shtml
1)静态链接与动态链接
静态链接是指在编译期把要调用的函数或过程链接到可执行文件中,成为可执行文件的一部分。也就是说,函数和过程的代码就在程序的.exe文件中。动态链接指在编译时,连接器只使用子例程external声明中的信息,在可执行文件中建立一数据表格,被调用的函数是在运行期才链接到可执行文件中。
2)动态链接库
动态链接库(Dynamic Link Library DLL)是一个程序模块,它包含代码、数据或资源,可以被其他应用程序共享。Delphi的应用程序经常调用DLL中的函数,每当直接访问Window API函数时,其实是在访问DLL。一个动态链接库与一个可执行文件(.exe)类似,它们之间的主要差别,DLL不能单独执行。动态链接库的文件扩展名一般是.dll,也有可能是.drv(设备驱动程序)、.sys(系统文件)和.fon(字体文件)。
3)使用DLL的优点
A、有利于程序的模块化。当需要对应用程序进行修改时,只需要修改其中的一个模块,而不是整个应用程序。我们可以提供DLL的不同版本,代替当前的DLL。
B、共享代码、资源和数据。使用DLL的主要目的是为了共享代码。Delphi中的代码也可以共享,介只限于Delphi程序,而DLL的代码可以被所有的Windows应用程序共享。
C、节省内存。如果不同的程序使用相同的DLL,只需将DLL在内存中装载一次。
D、适合于复杂的应用程序开发。当开发一个庞大而且需不断更新或改正错误的应用程序,可以将其划分为多个执行部分和DLL,这样我们只对需要改变的部分进行操作,而不是对整个大执行文件进行改动。
2、在Delphi中创建DLL
在编写DLL时应遵守以下规则:输出子例程必须列在DLL的exports子句中,使子例程可以在DLL外部可以看到;输出函数必须被声明为stdcall,以使用标准的Win32参数来代替优化的register参数传递技术;DLL可以使用全局数据,该数据将不会通过调用应用程序来共享。
1)在DLL中创建函数
具体步骤如下:
单击菜单file|new,选择New Items窗体中New页中DLL图标,保存项目dllDemo.dpr。
添加一个新单元calc.pas,在单元中声明两个函数并实现;
function Add(val1,val2:integer):integer;stdcall;
function Substract(val1,val2:integer):integer;stdcall;
implementation
……
2)在DLL创建类
*添加一个新单元UseClass.pas
* 定义一个基类
TDllCalc=class
public
function Multiply(val1,val2:integer):integer;virtual;abstract;
function Divide(val1,val2:integer):integer;virtual;abstract;
end;
* 定义一派生类并实现其函数
TDllCalcImpl=class(TDllCalc)
public
function Multiply(val1,val2:integer):integer;override;
function Divide(val1,val2:integer):integer;override;
end;
implementation
……
* 声明一个实现函数
function ClassCalcImpl:TDllCalc;stdcall;
function ClassCalcImpl:TDllCalc;
begin
result:=TDllCalcImpl.Create;
end;
3)在项目dllDemo.dpr单元中添加exports子句,并在其中声明输出函数
exports
Add,Substract,ClassCalcImpl;
4)编译生成一个.dll文件(dllDemo.dll)
3、在Delphi中使用DLL
建立一个应用程序这prj1.dpr,与DLL在同一目录下。在form中有5个button,2个spinedit,2个bevel,7个label。
在Delphi中调用DLL有两种方式:隐式调用和显式调用。
1)隐式调用
①直接对函数的调用
在单元implementation部分声明两个函数,必须声明为stdcall,同时加上external dllName
function Add(val1,val2:integer):integer
;stdcall;external 'dllDemo.dll';
function Substract(val1,val2:integer)
:integer;stdcall;external 'dllDemo.dll';
两个button的click事件为:
procedure TfrmDemo1.GetVal(var val1, val2: integer);
begin
val1:=sedt1.Value;
val2:=sedt2.Value;
end;
procedure TfrmDemo1.btnAddClick(Sender: TObject);
begin
GetVal(FVal1,FVal2);
label1.Caption:=inttostr(add(FVal1,FVal2));
end;
procedure TfrmDemo1.btnSubstractClick(Sender: TObject);
begin
GetVal(FVal1,FVal2);
label2.Caption:=inttostr(Substract(FVal1,FVal2));
end;
②对类的引用
* 在引用单元中声明一个基类;
TDllCalc=class
public
function Multiply(val1,val2:integer):integer;virtual;abstract;
function Divide(val1,val2:integer):integer;virtual;abstract;
end;
* 在单元实现部分声明一个引用函数;
function ClassCalcImpl:TDllCalc;stdcall;external 'dllDemo.dll';
* 然后定义一个对象。
private
{ Private declarations }
FVal1,FVal2:integer;
NewObject:TDLLCalc;
procedure GetVal(var val1,val2:integer);
Implementation
procedure TfrmDemo1.FormCreate(Sender: TObject);
begin
NewObject:=ClassCalcImpl;
end;
procedure TfrmDemo1.btnMultiplyClick(Sender: TObject);
begin
GetVal(FVal1,FVal2);
label3.Caption:=inttostr(NewObject.Multiply(FVal1,FVal2));
end;
procedure TfrmDemo1.btnDivideClick(Sender: TObject);
begin
GetVal(FVal1,FVal2);
label4.Caption:=inttostr(NewObject.Divide(FVal1,FVal2));
end;
procedure TfrmDemo1.FormDestroy(Sender: TObject);
begin
NewObject.Free;
end;
2)显式调用
虽然隐式调用DLL比较方便,但这并不是最好的方式。假若一个DLL包含许多例程,其中大部分例程可能根本用不着,因此把整个DLL都调入内存显然是浪费,尤其是当一个应用程序需要用到多个动态链接库。另一种情况是,假设有一组标准的函数有多个版本,分别由多个DLL实现。在这种情况下,最好在需要哪个版本的时候就调入哪个DLL,这就是指显式调用DLL。
定义一个函数类型
TAdd=function(val1,val2:integer):integer;stdcall;
const
DllName='DllDemo.dll';
procedure TfrmDemo1.bntDynaCallClick(Sender: TObject);
var
HInst:THandle;
FPointer:TFarProc;
MyFunc:TAdd;
begin
HInst:=LoadLibrary(DllName); //调用DLL
if HInst>0 then //如果成功
try
FPointer:=GetProcAddress(HInst,PChar('Add')); //获得函数地址
if FPointer< >nil then
begin
GetVal(FVal1,FVal2);
MyFunc:=TAdd(FPointer);
label5.caption:=inttostr(MyFunc(FVal1,FVal2));
end
else
messagebox(0,'Funtion Add not found','Warning',MB_OK);
finally
FreeLibrary(HInst);
end
else
messagebox(0,'DllName not found','Warning',MB_OK);
end;