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

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

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

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

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

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

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

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

但是使用静态调用方法时,程序无法在运行时间里决定DLL的调用。在DELPHI中使用DLL时,例程的标识符必须与DLL中相应输出例程的标识符完全一致(尽管DELPHI本身大小写不敏感)。使用外部声明的缺点是程序启动时如果找不到mydll.dll将无法运行,即使没有调用其中的模块。 动态加载的方法可以避免这种情况。

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

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

  1. Type
  2. TMyProc=Procedure (Param:Pchar ) ;Stdcall;
  3.  
  4. Var MyProc: TMyproc;
  5. MyHandle:THandle;
  6. MyHandle:=LoadLibrary ('Mydll') ;
  7.  
  8. If MyHandle<= then
  9.    Raise Exception.Create( '动态链接库调用失败,错误代码是:'+Inttostr(Getlasterror))
  10. else
  11. @MyProc:=GetProcAddress(MyHandle,'demoproc');
  12.  
  13. if not Assigned(MyProc) then
  14. Raise Exception.Create('GetProcAddress 调用失败,错误代码是:'+inttostr(getlasterror))
  15. else MyProc(Pchar('a string'));
  16. Freelibrary(Myhandle); // 卸载DLL

3)、 调用方式

通过过程、函数名;

通过过程、函数别名;

通过过程、函数的顺序号

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

例:

  1. Function Getstring : string ; stdcall ; external 'Mydlls.dll' name 'Mygetstr' //name 子句指定函数名Getstring 改为Mygetstr,当程序调用这个例程时,使用Mygetstr这个名字;
  2.  
  3. Function Getstring : string ; stdcall ; external 'Mydlls.dll' index //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为数据段)。

2、创建DLL

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

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

内容实例如下:

  1. library DemoSvr;
  2.  
  3. { Important note about DLL memory management: ShareMem must be the
  4. first unit in your library's USES clause AND your project's (select
  5. Project-View Source) USES clause if your DLL exports any procedures or
  6. functions that pass strings as parameters or function results. This
  7. applies to all strings passed to and from your DLL--even those that
  8. are nested in records and classes. ShareMem is the interface unit to
  9. the BORLNDMM.DLL shared memory manager, which must be deployed along
  10. with your DLL. To avoid using BORLNDMM.DLL, pass string information
  11. using PChar or ShortString parameters. }
  12.  
  13. uses SysUtils,Classes;
  14. function Test1(a,b:integer):integer;
  15. begin
  16. Result:=a+b;
  17. end;
  18. exports
  19. Test1 index ;
  20. begin
  21. 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”:

  1. 声明部分:function Test1(a,b:integer):integer;external 'DemoSvr'; //注意此处是大小写敏感的。
  2. 运行部分:form1.caption:='1+2='+inttostr(test1(,));
3、使用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的写法如下:

  1. procedure InitDll2;
  2. begin
  3.   Application.CreateForm(TMyMainForm, MyMainForm);
  4.   MyMainForm.Show; //如果MyMainForm不可视则需要这一句.
  5. end;

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

  ④ 在单元底部的begin语句之前加入一个exports小节,然后写出所有想要输出的函数或过程的名字(最好指定索引号)。注意如果执行了第②步,一定要输出RunMyDll过程。
  ⑤ 将项目文件顶部的保留字program改为library。
  ⑥ 编译。
  现在就可以在其他程序中调用本项目中的函数和过程了,只要执行RunMyDll就可以执行这个项目,和执行原来的可执行文件一模一样。

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

  1.   unit MyImport; {Import unit for MyDll.Dll}
  2.   interface
  3.   procedure RunMyDll;
  4.   implementation
  5.   procedure RunMyDll;external 'MyDll' index ;
  6.   end.

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

Delphi 使用之dll文件生成与调用的更多相关文章

  1. C# 创建Dll文件供程序调用方法

    C# 创建Dll文件供程序调用方法 使用C#创建动态Dll文件方法: 1.  在VS2017环境下,新建-项目-选择类库类型: 2. 新创建一个.cs文件(如test.cs),编写代码如下: usin ...

  2. ASP.NET MVC+EF框架+EasyUI实现权限管理系列(16)-类库架构扩展以及DLL文件生成修改和用户的简单添加

    原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(16)-类库架构扩展以及DLL文件生成修改和用户的简单添加 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) ...

  3. Delphi 中将一些 Dll等生成资源文件打包成一个独立的EXE程序方法步骤

    资源文件一般为扩展名为res的文件,其自带的资源编译工具BRCC32.EXE(位于/Delphi/BIN目录下) 1.编写rc脚本文本 用记事本或其它文本编辑器编写一个扩展名为rc的文件,格式分别为在 ...

  4. 、Dll文件的编写 调用 说明

    1>新建Dll文件TestLib.dll 新建Unit文件U_TestFunc U_TestFunc代码如下: unit U_TestFunc; interface uses //尽可能的少us ...

  5. Delphi XE3写DLL,用Delphi7调用,报错!

    http://bbs.csdn.net/topics/390870532 用delphi xe3写的DLL,delphi7调用,参数都是PAnsiChar,DLL里的函数接收delphi7传的入参,没 ...

  6. Delphi DLL文件的动态调用

    樊伟胜

  7. Delphi DLL文件的静态调用

  8. C#Stimulator项目>>>C/C++ DLL的生成和调用,Windows下的多线程

    Windows下的多线程 http://blog.csdn.net/ganpengjin1/article/category/2541791 使用C/C++建立DLL,环境VS2013 新建Win32 ...

  9. Keil C51里面lib文件生成和调用方法

    一.包含关系 LCD1602.C里面包含LCD1602.H LCD1602.H的文件格式 二.设置生成lib文件 三.Lib文件调用 添加lib文件对话框 添加后的lib文件 呵呵^_^,这样就可以删 ...

随机推荐

  1. PHP unset 后恢复数组索引

    unset($arr[3]); $arr = array_values($arr); array_values() 函数返回一个包含给定数组中所有键值的数组,但不保留键名,被返回的数组将使用数值键,从 ...

  2. ASP.NET 访问路径 错误提示 HTTP 错误 404.8 原来路径中包含bin目录被拒绝

    HTTP 错误 404.8 - Not Found HTTP 错误 404.8 - Not Found 请求筛选模块被配置为拒绝包含 hiddenSegment 节的 URL 中的路径. 最可能的原因 ...

  3. 纯CSS实现tooltip提示框,CSS箭头及形状

    本片介绍仅用CSS做出tooltip那样的提示框及箭头等形状! 首先介绍一下CSS:after选择器 定义和用法:(参考w3school:after选择器) :after选择器在被选元素的内容后面插入 ...

  4. angularJs的ng-class切换class

    在angular中为我们提供了3种方案处理class: 1:scope变量绑定 2:字符串数组形式. 3:对象key/value处理. 第一种我们不推荐使用,看看其他两种解决方案: 字符串数组形式 字 ...

  5. DHCP中继

    DHCP中继 要求: 假设公司现在有两个部门分别为 销售部门,生产部门 对这两个部门分配不同网段, 销售部门 192.168.1.0/24 生产部门 192.168.2.0/24 为了节约使用LINU ...

  6. Rsyslog配置文件详解

    Rsyslog配置文件详解https://my.oschina.net/0757/blog/198329 # Save boot messages also to boot.log 启动的相关信息lo ...

  7. bzoj2251 [2010Beijing Wc]外星联络

    因为n很小,所以对于串s的每一个后缀,都把其加入字典树中,并且经过一个字典树节点,该节点权值就+1. 输出时因为要字典序最小,所以字典树先走0分叉,再走1分叉,如果节点权值大于等于2就输出 代码 #i ...

  8. Java中内存中的Heap、Stack与程序运行的关系

    堆和栈的内存管理 栈的内存管理是顺序分配的,而且定长,不存在内存回收问题:而堆 则是随机分配内存,不定长度,存在内存分配和回收的问题:堆内存和栈内存的区别可以用如下的比喻来看出:使用堆内存就象是自己动 ...

  9. Events in ASP.NET Master and Content Pages

    Content page PreInit event. Master page controls Init event. Content controls Init event. Master pag ...

  10. UltraISO向U盘写入镜像特别慢

    电脑:Dell INSPIRON 1416 系统:WIN7旗舰版32位 U盘:金士顿8G 镜像:CentOS7 ×86_64 问题: 开始使用"写入"功能,写入速度72k/s 后来 ...