Delphi 使用之dll文件生成与调用

DLL是Dynamic-Link Libraries(动态连接库)的缩写,库里面是一些可执行的模块以及资源(如位图、图标等)。能够认为DLL和EXE基本上是一回事,只是DLL不能直接执行,而必须由应用程序或者其余DLL调用。DLL为应用程序间的资源共享提供了方便,同时也是多语言混合编程的重要手段。因而可知学习使用DLL是Windows程序员必须掌握的一项重要技术。
 

 

一、DLL文件调用

在DELPHI中,有两种方法调用一个储存在DLL(动态连接库)中的函数和过程,即静态调用或者动态调用。app

1)、 静态调用或显式装载函数

使用一个外部声明子句,使DLL在应用程序开始执行前即被装入。例如:学习

Function instring (sourcestr: Pchar ; check: char): integer; far; external 'demostr'

这种方式要在单元的interface 部分用external 指示字列出要从DLL中调用的例程。Far 指令代表能够被其余段(例如其余单元)调用的子例程。全部在单元接口中声明的子例程在缺省状况下都是Far类型的,其相反的指令是near。spa

若是external 后什么也不跟,必须用 {$ L } 编译指令预先指定一个DLL名字,如:.net

{ $ L Mydlls.dll }
Procedure setstring(var str: string) ;
stdcall ; external

可是使用静态调用方法时,程序没法在运行时间里决定DLL的调用。在DELPHI中使用DLL时,例程的标识符必须与DLL中相应输出例程的标识符彻底一致(尽管DELPHI自己大小写不敏感)。使用外部声明的缺点是程序启动时若是找不到mydll.dll将没法运行,即便没有调用其中的模块。 动态加载的方法能够避免这种状况。 3d

 

2)、 动态调用或隐式装入指针

使用WINDOWS API 函数 Loadlibrary 和GetprocAddress能够实如今运行时间里的动态装载DLL,并调用其中的过程。将DLL调入内存并得到指向函数或过程的指针,执行完模块后释放内存。除了节约内存外,这种方法的一个很大的优势是能处理找不到dll或者在装入过程当中出错的状况。这样即便某个dll有问题,应用程序的其余部分仍然可以正常运行。动态加载的例子以下: 

Type 
    TMyProc=Procedure (Param:Pchar ) ;Stdcall;

Var MyProc: TMyproc;
    MyHandle:THandle;
    MyHandle:=LoadLibrary ('Mydll') ;
        
    If MyHandle<=0 then
      Raise Exception.Create( '动态连接库调用失败,错误代码是:'+Inttostr(Getlasterror))
    else 
        @MyProc:=GetProcAddress(MyHandle,'demoproc');
        
    if not Assigned(MyProc) then
        Raise Exception.Create('GetProcAddress 调用失败,错误代码是:'+inttostr(getlasterror))
    else MyProc(Pchar('a string'));
        Freelibrary(Myhandle); // 卸载DLL   

 

3)、 调用方式

经过过程、函数名;

经过过程、函数别名;

经过过程、函数的顺序号

举例以下:在MYDLL.DLL中有两个函数和一个过程,则其外部声明能够写成:

例:

Function Getstring : string ; stdcall ; external 'Mydlls.dll' name 'Mygetstr' //name 子句指定函数名Getstring 改成Mygetstr,当程序调用这个例程时,使用Mygetstr这个名字;

Function Getstring : string ; stdcall ; external 'Mydlls.dll' index 5 //Index 子句经过索引号引入例程能够减小DLL的加载时间。

 

4)、 调用约定

调用约定,是指调用例程时参数的传递顺序。DELPHI中DLL支持的调用约定有:

调用约定 参数传递顺序
Register  从左到右
Pascal  从左到右
Stdcall  从右到左
Cdecl  从右到左
Safecall  从右到左

使用Stdcall 方式,能保证不一样语言写的DLL的兼容性,同时它也是WINDOWS API的约定方式; Delphi 3.0、4.0的默认调用方式为Register; Cdecl是采用 C/C++的调用约定,适用于DLL是由C++语言编写的; Safecall 是适合于声明OLE对象中的方法。

 

5)、 DLL中的变量和段

一个DLL声明的任何变量都为本身私有 ,调用它的模块不能直接使用它定义的变量。要使用时必须经过过程或函数界面才能完成,对DLL来讲,它永远都没有机会使用调用它的模块中的声明的变量。一个DLL没有本身的SS(堆栈段),它使用调用它的应用程序的堆栈。所以在DLL中的过程、函数不要假定DS=SS(DS为数据段)。

 

二、建立DLL


  用Delphi建立一个DLL是十分简单的,首先须要新建一个DLL的Porject:选择File/New/Others...,以下:

选择“Dll Wizard”,点击 OK 按钮。

内容实例以下:

library DemoSvr;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses SysUtils,Classes;
function Test1(a,b:integer):integer;
begin
Result:=a+b;
end;
exports
Test1 index 1;
begin
end.

在这个DLL里咱们声明了一个加法函数,而后用exports语句输出它,只有被输出的函数或过程能被其余程序调用。

调试(编译/Ctrl+F9)成功之后 会生成一个DemoSvr.dll 文件。能够设置编译的路径,选择"Project/Options..."菜单:

 

在DLL项目中,能够指定一个宿主程序来调试,具体方法为选择 "Run/Parameters..."菜单:

 

另外,能够设置跟踪调试:

 

 

exports语句的语法是:

函数名 [index <n>],

index <n>是为函数手工指定索引号,以便其余程序肯定函数地址;也能够不指定,若是没有使用Index关键字,Delphi将按照exports后的顺序从1开始自动分配索引号。如今咱们能够调用这个DLL了,下面给出一个实例,运行后form1的标题将变成“1+2=3”: 

 

声明部分:function Test1(a,b:integer):integer;external 'DemoSvr'; //注意此处是大小写敏感的。 
运行部分:form1.caption:='1+2='+inttostr(test1(1,2)); 

 

三、使用DLL的两个技巧


(1)把现有的项目改为DLL
  学会制做DLL之前,大多数程序员手中都积攒下来很多已经完成了的项目,若是如今须要把这些项目作成DLL而不是可执行文件,从新写一遍显然是没有必要的,只要按照下面的步骤对已有的项目文件进行修改就能够了:
  ① 打开项目文件(.DPR),删除单元底部begin和end.之间的全部语句(通常状况下这些语句是由Delphi自动生成的)。若是项目中没有用到Form,则从uses子句中删除表单单元(Form),而后转到第③步。
  ② 对项目进行修改,令除Main Form以外的全部Form都是动态生成的,这样咱们只要在DLL输出的一个函数或者过程当中生成Main Form,便可调用执行整个项目。咱们假设Main Form的名字是MyMainForm,项目的名字是MyDll,如今在单元底部的begin语句以前加入一个过程,过程的名字为RunMyDll,这个过程将动态生成Main Form,从而运行整个项目。RunMyDll的写法以下:

procedure InitDll2; 
begin 
  Application.CreateForm(TMyMainForm, MyMainForm); 
  MyMainForm.Show; //若是MyMainForm不可视则须要这一句. 
end; 

  ③ 若是想要输出其余函数或者过程,而原来的项目中没有,则能够在单元底部的begin语句以前加入这些代码。 

  ④ 在单元底部的begin语句以前加入一个exports小节,而后写出全部想要输出的函数或过程的名字(最好指定索引号)。注意若是执行了第②步,必定要输出RunMyDll过程。
  ⑤ 将项目文件顶部的保留字program改成library。
  ⑥ 编译。
  如今就能够在其余程序中调用本项目中的函数和过程了,只要执行RunMyDll就能够执行这个项目,和执行原来的可执行文件如出一辙。

(2)建立一个引入文件
  若是DLL比较复杂,则为它的声明专门建立一个引入程序单元将是十分有意义的,而且会使这个DLL变得更加容易维护。引入单元的格式以下:

  unit MyImport; {Import unit for MyDll.Dll} 
  interface 
  procedure RunMyDll; 
  implementation 
  procedure RunMyDll;external 'MyDll' index 1; 
  end. 

这样之后想要使用MyDll中的例程时,只要简单的在程序模块中的uses子句中加上MyImport便可。 

相关文章
相关标签/搜索