Delphi 使用之dll文件生成与调用
http://www.cnblogs.com/IceKernel/articles/2848848.html
在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为数据段)。
用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(,));
(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文件生成与调用的更多相关文章
- C# 创建Dll文件供程序调用方法
C# 创建Dll文件供程序调用方法 使用C#创建动态Dll文件方法: 1. 在VS2017环境下,新建-项目-选择类库类型: 2. 新创建一个.cs文件(如test.cs),编写代码如下: usin ...
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(16)-类库架构扩展以及DLL文件生成修改和用户的简单添加
原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(16)-类库架构扩展以及DLL文件生成修改和用户的简单添加 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) ...
- Delphi 中将一些 Dll等生成资源文件打包成一个独立的EXE程序方法步骤
资源文件一般为扩展名为res的文件,其自带的资源编译工具BRCC32.EXE(位于/Delphi/BIN目录下) 1.编写rc脚本文本 用记事本或其它文本编辑器编写一个扩展名为rc的文件,格式分别为在 ...
- 、Dll文件的编写 调用 说明
1>新建Dll文件TestLib.dll 新建Unit文件U_TestFunc U_TestFunc代码如下: unit U_TestFunc; interface uses //尽可能的少us ...
- Delphi XE3写DLL,用Delphi7调用,报错!
http://bbs.csdn.net/topics/390870532 用delphi xe3写的DLL,delphi7调用,参数都是PAnsiChar,DLL里的函数接收delphi7传的入参,没 ...
- Delphi DLL文件的动态调用
樊伟胜
- Delphi DLL文件的静态调用
- C#Stimulator项目>>>C/C++ DLL的生成和调用,Windows下的多线程
Windows下的多线程 http://blog.csdn.net/ganpengjin1/article/category/2541791 使用C/C++建立DLL,环境VS2013 新建Win32 ...
- Keil C51里面lib文件生成和调用方法
一.包含关系 LCD1602.C里面包含LCD1602.H LCD1602.H的文件格式 二.设置生成lib文件 三.Lib文件调用 添加lib文件对话框 添加后的lib文件 呵呵^_^,这样就可以删 ...
随机推荐
- PHP unset 后恢复数组索引
unset($arr[3]); $arr = array_values($arr); array_values() 函数返回一个包含给定数组中所有键值的数组,但不保留键名,被返回的数组将使用数值键,从 ...
- ASP.NET 访问路径 错误提示 HTTP 错误 404.8 原来路径中包含bin目录被拒绝
HTTP 错误 404.8 - Not Found HTTP 错误 404.8 - Not Found 请求筛选模块被配置为拒绝包含 hiddenSegment 节的 URL 中的路径. 最可能的原因 ...
- 纯CSS实现tooltip提示框,CSS箭头及形状
本片介绍仅用CSS做出tooltip那样的提示框及箭头等形状! 首先介绍一下CSS:after选择器 定义和用法:(参考w3school:after选择器) :after选择器在被选元素的内容后面插入 ...
- angularJs的ng-class切换class
在angular中为我们提供了3种方案处理class: 1:scope变量绑定 2:字符串数组形式. 3:对象key/value处理. 第一种我们不推荐使用,看看其他两种解决方案: 字符串数组形式 字 ...
- DHCP中继
DHCP中继 要求: 假设公司现在有两个部门分别为 销售部门,生产部门 对这两个部门分配不同网段, 销售部门 192.168.1.0/24 生产部门 192.168.2.0/24 为了节约使用LINU ...
- Rsyslog配置文件详解
Rsyslog配置文件详解https://my.oschina.net/0757/blog/198329 # Save boot messages also to boot.log 启动的相关信息lo ...
- bzoj2251 [2010Beijing Wc]外星联络
因为n很小,所以对于串s的每一个后缀,都把其加入字典树中,并且经过一个字典树节点,该节点权值就+1. 输出时因为要字典序最小,所以字典树先走0分叉,再走1分叉,如果节点权值大于等于2就输出 代码 #i ...
- Java中内存中的Heap、Stack与程序运行的关系
堆和栈的内存管理 栈的内存管理是顺序分配的,而且定长,不存在内存回收问题:而堆 则是随机分配内存,不定长度,存在内存分配和回收的问题:堆内存和栈内存的区别可以用如下的比喻来看出:使用堆内存就象是自己动 ...
- Events in ASP.NET Master and Content Pages
Content page PreInit event. Master page controls Init event. Content controls Init event. Master pag ...
- UltraISO向U盘写入镜像特别慢
电脑:Dell INSPIRON 1416 系统:WIN7旗舰版32位 U盘:金士顿8G 镜像:CentOS7 ×86_64 问题: 开始使用"写入"功能,写入速度72k/s 后来 ...