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

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

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

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

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

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

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

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

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

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<= 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  //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 按钮。

内容实例如下:

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 ;
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(,));
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的写法如下:

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 ;
  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. Javascript模块化编程(一):模块的写法 作者: 阮一峰

    声明:转载自阮一峰的网络日志 随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂. 网页越来越像桌面程序,需要一个团队分工协作.进度管理. ...

  2. Java 一维数组 二维数组 三维数组

    二维数组包含一位数组  三维数组就是在二维数组的基础上,再加一层.把二维数组看做是一维数组就可以了,按照上述理解类推.   下面是 一维 二维 三维数组例子   一维数组: int[] array1 ...

  3. php判断闰年

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  4. MySQL导入.sql文件及常用命令

    在MySQL Qurey   Brower中直接导入*.sql脚本,是不能一次执行多条sql命令的,在mysql中执行sql文件的命令: mysql> source d:/myprogram/d ...

  5. tcpip

    netstat -anp | grep 8099 kill -9 8099 服务端端口状态 1.LISTENING状态 FTP服务启动后首先处于侦听(LISTENING)状态. 2.ESTABLISH ...

  6. iOS解析JSON字符串报错Error Domain=NSCocoaErrorDomain Code=3840 "Invalid escape sequence around character 586."

    将服务器返回的JSON string转化成字典时报错: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid escape sequence ...

  7. svg学习(四)circle

    <circle> 标签 < <?xml version="1.0" standalone="no"?> <!DOCTYPE ...

  8. [原创]java WEB学习笔记92:Hibernate学习之路-- -QBC 检索和本地 SQL 检索:基本的QBC 查询,带 AND 和 OR 的QBC,统计查询,排序,分页

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. JavaScript 设计模式之工厂模式

  10. C#函数过载

    什么是method?函数也.overloading,是过载的意思.为什么会过载呢?因为一个函数,本来后面拖着两个参数的,现在拖着三个参数了,那不是过载是什么? 为什么同一个函数,后面可以跟两个参数,也 ...