请教一个菜问题:怎么样(静态、动态)调用DLL?(50分)

  • 主题发起人 主题发起人 winslow
  • 开始时间 开始时间
W

winslow

Unregistered / Unconfirmed
GUEST, unregistred user!
希望能有简单的代码说明,如何声明,如何使用,有什么要注意的。
有点急,希望DX能够指点一下!
 
转贴:

调用DLL有两种方法,一种是在应用程序装载时调用,另一种是在应用程序运行时调用。
首先介绍装载时DLL的调用:

  (1)装载时调用DLL

  在调用DLL的Pas文件中,对DLL函数进行外部声明,
声明应位于Implementation的Uses语句后,形式如下:

  Implementation

  Uses Dialogs;

  Function Name(argment):Boolean;far;External 'Call?Name';

; ; 其中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);
 
在Delphi中使用DLL
南京
王永红
---- 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;

---- 结果见图。

;
---- 图:运行结果

---- 4、注意事项

---- 1)动态连接库文件必须放在调用文件的同一目录下或Windows的System目录下;

---- 2)在显示调用时,在GetProcAddress()函数中的变量lpProcName必须与动态连接库中的输出函数名完全一样(大小写相同)。

---- 以上程序在Delphi5.0中调试通过。
 
谢谢两位大侠,我消化一下
另这个TFarproc是不是一个系统有的专门的类?它是用来做什么的?
 
分儿不多,聊表谢意,谢谢
 
后退
顶部