delphi 封装dll,delphi调用dll文件
制作动态链接库的一般步骤
双参数转移
DLL的三次初始化和退出清理[如果需要初始化和退出清理]
使用四个全局变量
称之为静态加载。
6.调用动态加载
在DLL中建立一个Tform。
在DLL中建立一个TMDIChildform
九个例子:
Delphi与其他语言产生的Dll混合编程中的常见问题;
XI。相关信息
Dll的生成一般分为以下几个步骤:
1在DLL项目中编写一个过程或函数
2编写一个Exports关键字,并在它下面写下进程的名称。不要写参数和调用后缀。
双参数传输
1参数类型应与窗口C一致.不要使用DELPHI数据类型。
最好有一个返回值[甚至是一个过程]来报告调用的成功或失败,或者状态。成功或失败的返回值最好是1[成功]或0[失败]。总之,是兼容Windows C的。
用stdcall声明后缀。
4最好区分大小写。
5不需要调用带far的后缀,那只是为了兼容windows 16位程序。
DLL的三次初始化和退出清理[如果需要初始化和退出清理]
sysutils unit]的1dlproc [a Pointer]是DLL的入口。
在这里,您可以用您的函数替换它的条目。但是你的函数必须满足以下要求【其实是回调函数】。如下所示:
过程DllEnterPoint(dw reason:DWORD);远;stdcall
有四种类型的dwReason参数:
当进程进入时
DLL_PROCESS_DETACH:进程退出时
DLL_THREAD_ATTACH:当线程进入
当线程退出时
在初始化部分写入:
DLLProc:=@ DLLEnterPoint;
DllEnterPoint(DLL _ PROCESS _ ATTACH);
2如果表单上有TdcomConnection组件,在初始化Uses Activex时写一个CoInitialize(nil);
3退出时,确保DCOM连接。已连接:=false,数据集已关闭。否则地址就是错的。
使用四个全局变量
在widnows 32位程序中,两个应用程序的地址空间互不相关。虽然dll是内存中的副本,但是变量是在每个进程的地址空间中,所以你不能用DLL的全局变量来实现两个应用程序之间的数据传递,除非你用内存来镜像文件。
称之为静态加载。
1客户端功能信誉:
1)区分大小写。
2)与DLL中的声明相同。
如:showform(form:t form);远;外部“y project _ dll . dll”;
3)调用过程中传递的参数类型也与Windows C相同.
4)调用时,DLL必须在windows搜索路径中,顺序为:当前目录;路径;windows寡妇系统;windows ssystem32
6.调用动态加载
1建立一个流程类型(或者一个函数)[如果你清楚流程类型的变量只是一个指针的性质,你就知道是怎么回事了]。比如:
类型
my pointer=procedure(form:t form);远;外部;
//my pointer=function(form:t form);远;外部;
hinst:Thandle;
showform:my pointer;
开始
hinst:=loadlibrary( y project _ dll );//加载一个Dll,通过文件名查找。
showform:=getprocaddress(Hinst, showform );//按函数名查找,区分大小写。如果你知道自动化物体的本质,就清楚了。
showform(application . mainform);//找到函数入口指针时调用。
免费图书馆(Hinst);
在DLL中建立一个Tform。
1把你的窗体使用放入Dll,你的窗体使用的关联单元也要使用[这是最麻烦的一点,因为你的窗体可能使用了很多特殊的单元或函数]
传递一个应用程序参数并使用它来创建一个表单。
在DLL中建立一个TMDIChildform
dll 1中的MDIform.formstyle不必是fmMDIChild。
2在Createform后写下以下两句话:
函数Showform(main form:t form):integer;调用约定
form 1:t form 1;
ptr:PLongInt;
开始
ptr:=@(应用。mainform);//先保存dll的Mainform句柄,不需要释放,替换即可。
ptr^:=longint(mainform);//用调用程序的主窗体替换DLL的主窗体。主窗体是一个特殊的窗口,它管理应用程序中的窗体资源。
//为什么不仅仅是应用程序。Mainform :=mainform,因为应用。Mainform是只读属性
form1:=Tform1。创建(主窗体);//用参数设置
注意:参数是应用程序。调用程序的主窗体。
九个例子:
DLL源代码:
图书馆项目2;
SysUtils,
班级,
对话框,
表格,
“unit 2 . pas“{ form 2 }”中的Unit2
{$R *。RES}
CCC:Pchar;
过程open form(main form:t form);stdcall
form 1:t form 1;
ptr:PLongInt;
开始
ptr:=@(应用。mainform);
ptr^:=longint(mainform);
form1:=Tform1。创建(主窗体);
过程input cc(Text:Pchar);stdcall
开始
CCC:=Text;
程序显示CCC;stdcall
开始
ShowMessage(字符串(CCC));
出口
Openform
输入CC,
ShowCCC
过程open form(main form:t form);stdcall外部“project 2 . dll”;
程序显示CCC;stdcall外部“project 2 . dll”;
过程input cc(Text:Pchar);stdcall外部“project 2 . dll”;
程序Tform1。Button1Click(发件人:to object);
正文:Pchar
开始
Text :=Pchar(Edit1。正文);
//Openform(应用程序。mainform);//调整MDICHILD
InputCCC(文本);//试验DLL中的全局变量是否在各种应用程序之间共享
程序Tform1。Button2Click(发送方:to object);
开始
ShowCCC//这说明WINDOWS 32位应用程序DLL中的全局变量也在应用程序地址空间中。16位应用可能不同,所以没有做实验。
Delphi与其他语言产生的Dll混合编程中的常见问题;
1使用PowerBuilder混合编程
在定义不定长动态数组方面,当函数退出干净堆栈时,总会出现不可恢复的地址错误。原因不明,大概和PB的编译原理有关,即使PB被编译成二进制代码。
在DELPHI中编写DLL时,如果DLL有函数创建ADO对象被调用
开始写:CoInitialize(nil);
写在最后:CoUninitialize
如果想用PChar返回一个字符串,最好用PChar用out或者var返回。
PChar的内存分配和释放是通过调用函数来处理的:GetMem(p,Size);FreeMem(p);
过程common dll(AHnd:thand le;//AApp:t application;
ADllFileName:PChar;
a constr:PChar;AUserID:整数;ABillTypeID:整数);
LPtr:PLongint;
strCon:widestring;
strdll filename:string;
开始
共初始化(无);
strCon:=StrPas(a constr);
strdll filename:=StrPas(ADllFileName);
申请。句柄:=AHnd
screen:=AScr;
LPtr:=@应用程序。主窗体;
LPtr^ :=朗恩特(AApp。mainform);
最后
初始化;
DLL中有一个窗体对象是用函数创建的,所以一定要传递Application或Application。处理
一般用formClass比较好。Create(application)在DLL中创建窗体。
我见过一个DLL把屏幕对象转移到DLL。不知道为什么,也希望有朋友告诉我转移屏幕对象有什么好处和坏处。
Screen是让主程序主窗体的MDIChildCount正常增加,
否则,无论打开多少个DLL中的mdichildcount窗体,MDIChildCount都不会增加。
第1章为什么要使用动态链接库(DLL) top
你必须熟悉DLL。Windows中存在大量带有DLL后缀的文件,这是保证Windows正常运行、维护和升级的重要保证。(比如我的Win95系统目录下有500多个DLL文件。实际上,DLL是一个特殊的可执行文件。特殊主要是因为一般不能直接运行,需要*等宿主程序的动态调用。EXE程序或其他要使用的dll。简单来说,一般来说,DLL就是编译好的函数和过程的集合。
使用DLL技术有几个主要原因:
首先,减小可执行文件的大小。
使用DLL技术的很大一部分原因是为了减小可执行文件的大小。当操作系统进入Windows时代,其规模已经达到了几十甚至几百兆。试想一下,如果你还在使用DOS时代的单执行文件系统,一个可执行文件的大小可能会达到几十兆,这是所有人都无法接受的。解决方法是使用动态链接技术将一个大的可执行文件分成许多小的可执行程序。
第二,实现资源共享。
这里的资源共享包括很多方面,大部分是内存共享,代码共享等等。早期的程序员经常会遇到这种事情,在不同的编程任务中编写相同的代码。这种方法显然浪费了很多时间,人们编写了各种库来解决这个问题。但由于编程语言和环境的差异,这些库一般不具有通用性,用户在运行程序时需要这些库,极其不方便。DLL的出现就像树立了一个标准,让这些库有了统一的规范。这样,不同编程语言的程序员可以方便地使用其他编程语言编写的dll。另外,DLL的一个突出特点是只在内存中加载一次,可以节省有限的内存,同时服务于多个进程。
第三,易于维护和升级。
细心的朋友可能会发现,有些DLL文件有版本描述。(看DLL文件的属性就知道了,但不是每个DLL文件都有。)这是为了方便维护和升级。比如Win95早期有个BUG,就是闰年2月29日无法正确显示。后来微软发布了补丁修正了这个BUG。值得一提的是,我们并没有重装Win95,而是用新版本替换了旧版本的DLL。(具体是哪个DLL文件我一时想不起来了。另一个常见的例子是驱动程序的升级。比如著名的DirectX,经过多次升级,现在已经发展到6.0版本。更妙的是,当我们试图安装一个较低版本的DLL时,系统会提示我们避免人为错误。比如我们在升级某个硬件的驱动时,经常会遇到Windows提醒当前安装的驱动比原来的驱动老。
第四,更安全。
这里说的安全也包括很多方面。比如DLL文件被病毒攻击的概率比普通EXE文件低很多。另外,由于是动态链接,给一些搞破坏的“高手”带来了一定的拆卸难度。
第二章用Delphi编写DLL。
注意:这里,作者假设读者使用的是Delphi 3或Delphi 4。开场白说了这么多,该说正题了。其实写DLL并不是一件很难的事情,只需要注意一些事项就可以了。为了便于说明,我们先举个例子。
图书馆Delphi
使用
SysUtils,
班级;
函数TestDll(I:integer):integer;stdcall
开始
结果:=I;
结束;
出口
TestDll
开始
结束。
只要编译上面的代码,你就可以得到一个名为Delphi.dll的动态链接库。
现在,我们来看看需要注意什么。
首先,用DLL编写的函数或过程必须添加stdcall参数。
其次,编写的函数和过程应该用exports语句声明为外部函数。
3.当使用长字符串类型的参数和变量时,应该参考ShareMem。
Delphi中的字符串类型非常强大。我们知道一个普通字符串的最大长度是256个字符,但是Delphi中的字符串类型默认可以达到2G。(是的,你没看错,确实是两万亿。)这时,如果你坚持使用参数、变量甚至是string类型的记录信息,就必须引用ShareMem单元,而且必须是第一个这么做的。它是uses语句后第一个被引用的单元。示例:
使用
ShareMem
SysUtils,
班级;
还有一点,你应该在你的工程文件(*)里做同样的工作。dpr)而不是单元文件(*。pas)。这一点在Delphi自己的帮助文件中没有说清楚,造成了很多误解。如果你不这样做,你很可能会付出崩溃的代价。避免使用string类型的方法是将string类型的参数和变量声明为Pchar或ShortString(如s:string[10])。使用动态数组也会出现同样的问题,解决方法如上所述。
第三章在Delphi中静态调用DLL。
调用DLL比编写DLL容易。首先,我们将介绍静态调用方法。稍后,我们将介绍动态调用方法,并对两种方法进行比较。同样,我们先举一个静态调用的例子。
unit Unit1
连接
使用
窗口、消息、系统、类、图形,
控件、窗体、对话框、标准控件。
类型
Tform1=类(Tform)
edit 1:TEdit;
button 1:t button;
过程按钮1Click(发送方:to object);
私人的
{私人声明}
公众的
{公开声明}
结束;
form 1:t form 1;
履行
{$R *。DFM}
//下面这个银行里的代码是我们实际写的代码。
函数TestDll(I:integer):integer;stdcall外部“Delphi . dll”;
程序Tform1。Button1Click(发件人:to object);
开始
编辑1。text:=IntToStr(TestDll(1));
结束;
结束。
首先,用stdcall调用参数。
正如前面提到的,在引用DLL中的函数和过程时,应该使用stdcall参数,原因与前面提到的相同。
其次,使用外部语句指定被调用的DLL文件的路径和名称。
如您所见,我们指定了要在外部语句中调用的DLL文件的名称。没有写入路径,因为DLL文件与调用它的主程序在同一个目录中。如果DLL文件在C:,我们可以把上面的引用语句写成外部的‘C: Delphi . DLL’。注意后缀。必须写入文件的dll。
三。不能从DLL调用全局变量。
如果我们在DLL中声明某种全局变量,比如:var s:byte。这样,全局变量S可以在DLL中正常使用,但S不能被调用程序使用,即S不能作为全局变量传递给调用程序。但是,调用程序中声明的变量可以作为参数传递给DLL。
四。被调用的DLL必须存在。
第四章在Delphi中动态调用DLL。
调用动态dll相对复杂,但是非常灵活。为了充分说明问题,这次我们举一个调用c写的DLL的例子,首先用c编译下面的DLL源程序。
#包括
extern "C" _declspec(dllexport)
int WINAPI TestC(int i)
{
返回I;
}
编译后生成一个DLL文件,这里叫做Cpp.dll。在这个DLL中只有一个返回整数类型的函数TestC。为了便于解释,我们还是参考上面的调用程序,只是用下面的代码替换了原来Button1Click过程中的语句。
程序Tform1。Button1Click(发件人:to object);
类型
tint func=function(I:integer):integer;stdcall
th:Thandle;
TF:tint func;
TP:TFarProc;
开始
th:=LoadLibrary( CPP . dll );{加载DLL}
如果Th 0那么
Tp:=GetProcAddress(Th,PChar( TestC ));
如果Tp为零,则开始
TF:=tint func(Tp);
编辑1。text:=IntToStr(Tf(1));{调用TestC函数}
目标
其他
ShowMessage(“未找到TestC函数”);
最后
免费图书馆(Th);{发布DLL}
其他
显示消息(“找不到CPP . dll”);
结束;
如你所见,这种动态调用技术非常复杂,但你可以通过改变参数来动态改变被调用的DLL,比如将LoadLibrary (Cpp.dll )中的DLL名称改为 Delphi.dll 。
首先,定义要调用的函数或过程的类型。
在上面的代码中,我们定义了一个TIntFunc类型,它对应于我们将要调用的函数TestC。在其他情况下,应该做同样的定义。并且还添加了stdcall调用参数。
第二,释放被调用的DLL。
我们用LoadLibrary动态调用了一个DLL,但是要记住DLL在使用后一定要用FreeLibrary手动释放,否则DLL会一直占用内存,直到你退出Windows或者关机。
两种调用DLL方法的优缺点。
静态方法实现简单,容易掌握,一般快一点,安全可靠一点;
但是,静态方法不能在运行时灵活地加载和卸载所需的DLL。相反,它在主程序开始时加载指定的DLL,并在程序结束时释放它。另外,只有基于编译器和链接器的系统(如Delphi)才能使用这种方法。
动态方法解决了静态方法的缺点,可以方便地访问DLL中的函数和过程,甚至是旧DLL中一些新添加的函数或过程。
然而,动态方法很难完全掌握,因为必须为不同的函数或过程定义许多复杂的类型和调用方法。对于新手,我建议你先用静态的方法,等你熟练了再用动态调用的方法。
第五章使用动态链接库的实用技巧
一、写作技巧。
1.为了保证DLL的正确性,可以把它写成普通应用程序的一部分,经过调试后再从主程序中分离出来,编译成DLL。
2.为了保证DLL的通用性,在自己编写的DLL中要避免使用可视化控件的名字,比如Edit1中的Edit1的名字。文本;或者自定义非Windows定义的类型,如某种记录。
3.为了方便调试,每个函数和过程都要尽量简短明了,并有详细的注释。
4.try-finally应该用于处理可能的错误和异常,注意此时应该引用SysUtils单元。
5.尽可能少的引用单元来减小DLL的大小,尤其不要引用可视化单元,比如对话框单元。例如,一般情况下,我们可以不引用Classes单元,这样可以减少大约16Kb的编译DLL。
第二,通话技巧。
1.使用静态方法时,可以重命名被调用的函数或过程。在前面提到的C写的DLL例子中,如果去掉extern“C”语句,C会编译出一些奇怪的函数名,原来的TestC函数会被命名为@TestC$s等等,这是由于C采用了C名mangling技术。这个函数名在Delphi中是非法的。我们可以这样解决这个问题:
参考函数是
函数TestC(i:integer):整数;stdcall
外部“CPP . dll”;名称“@ TestC $ s”;
名字的作用就是给它改名。
2.你可以把我们写的DLL放在Windows目录或者Windowssystem目录下。通过这样做,您可以在外部语句或LoadLibrary语句中写入DLL的名称,而不是路径。但是,这有问题。这两个目录里有很多重要的系统dll。如果你编译的DLL和他们同名,后果不堪设想。况且你的编程技术还不足以把自己的dll放到系统目录里!
第三,调试技巧。
1.我们知道DLL在编写的时候是不能一步一步运行调试的。一种方法是在运行参数菜单中设置一个主机程序。在本地页面的宿主应用程序栏中添加宿主程序的名称,进行单步调试、断点观察和操作。
2.添加DLL的版本信息。简介中提到版本信息对于DLL来说非常重要。如果包含版本信息,DLL的大小将增加2Kb。加这么一点空间还是值得的。遗憾的是,我们不能直接使用Projectoptions菜单中的Version选项,Delphi的帮助文件中没有提到。根据作者的研究,加一行代码就够了。示例:
图书馆Delphi
使用
SysUtils,
班级;
{$R *。RES}
//注意,上面那行代码必须加在这个位置。
函数TestDll(I:integer):integer;stdcall
开始
结果:=I;
结束;
出口
TestDll
开始
结束。
3.为了避免与其他DLL重名,最好在命名自己的DLL时混合使用字符、数字和下划线。例如jl_try16.dll.
4.如果你在Delphi 1或Delphi 2中编译过一些dll,你最初编译的dll是16位的。只要在新的Delphi 3或Delphi 4环境下重新编译源代码,就可以得到一个32位的DLL。
【后记】:除了上面介绍的DLL最常用的方法,DLL还可以作为资源的载体。例如,在Windows中更改图标就是使用DLL中的资源。另外,掌握DLL的设计技术有利于使用更高级的OLE、COM、ActiveX编程。
如何在Delphi中调用DLL
立即浮现在脑海中的指令如下:
1.需要动态链接的DLL必须放在与可执行文件相同的目录中,或者放在Windows系统目录中。2.确认dll导出的函数原型。目前只能得到c语言的函数原型。这时候注意c和object Pascal对应的类型。如有必要,在接口部分定义所需的数据类别。
3.声明要在实现部分使用的函数。语法大致如下:
过程名称(参数.);远;外部“DLL文件名”;
索引n;
函数FuncName(Argr.):数据类型;远;
外部“DLL文件名”;索引n;
你在声明的时候,如果不写index n,就是参考资料里所谓的按名字导入的方法。此时因为需要从DLL的名称表中查找这个函数,所以链接执行速度比按序数导入略慢。此外,还有另一个由新名字。由于我没用过,你可以查一下参考资料,大意是你可以用另一个程序名导入后调用这个函数。
4.然后,调用和使用和一般Delphi一样。5.以上直接写入调用DLL函数的程序单元。另外,DLL的调用声明也可以集中在一个程序单元(导入单元)中。Delphi附带的WinTypes、WinProcs就是一个例子。
可以参考一下,同时观察一下C和Pascal对应的数据类型。6.除了上面提到的静态导入方法,还有一种方法就是写动态导入。首先,声明一个过程类型。当程序被执行时,用loadlibrary () API加载它。然后使用GetProcAddress() API获取链接调用的函数的地址。Object Pascal语言指南P.132-133有个例子,可以参考一下。
如果要举例的话,下面是我之前节目的节选:
(*对于CWindows 3.1 *)
单元Ime31
连接
SysUtils、WinTypes、WinProcs、Dialogs
(*必需的数据类型声明*)
tDateNTime=记录
wYear,wMonth,wDay:word;
wHour,wMin,wSec:word;
TImePro=记录
HWnd ime:HWnd;IME汉德尔
dtInstDate:tDateNTime;{安装日期和时间}
wVersion:word;{ the的版本}
szDescription:数组[0.49]的字节;{ IME模块的描述}
szName: array[0.79]的字节;name的模块名称}
szOptions: array[0.29]的字节;{ IME启动时的选项}
fEnable:boolean;{ IME地位;True=激活,False=停用}
ptimepro=^timepro;
函数SetIme(const sime filename:string):boolean;远;
履行
(* begin调用winnls.dll导出函数的声明*)
函数ImpSetIme(hwn time:HWND;lpImePro:pTImePro):boolean;远;外部“win nls . dll”;
(* end调用winnls.dll出口函数的声明*)
(* - *)
(* SetIme(const sime filename:string):boolean;
(*======
(*切换到特定的输入法。
(*传入参数:
(* sImeFileName:输入法IME文件名,例如:phon.ime
(*空字符串:字母数字输入法。
(*返回值:
(*真:切换成功。
(* False:失败。
(* - *)
函数SetIme(const sime filename:string):boolean;
皮美普罗:pTImePro
开始
结果:=假;
如果MaxAvail SizeOf(TImePro)则
开始
MessageDlg(内存不足,mtWarning,[mbOk],0);
退出;
开始
新的(皮梅罗);
如果sImeFileName= 则(*空字符串,恢复到字母数字输入法*)
pImePro^.szName[0] :=0
StrPCopy(@pImePro^.szName,sime filename);
结果:=ImpSetIme(0,pime pro);(*调用ImpSetIme *)
最后
Dispose(皮美普罗);
结束;{尝试次数}
结束;{的设置时间}
DELPHI中动态链接库开发常见问题的探讨:
http://www.delphibbs.com/delphibbs/DispQ.asp?LID=3685176
2007-4-6 19:25:51如果要返回一个字符串,最好使用PChar在out或var模式下返回。PChar的内存分配和释放由调用函数处理:GetMem(p,Size);FreeMem(p);
而编写被调用函数的方式应该是:
application . messagebox(pchar( DLL加载错误,DLL可能不存在!),PChar (error ),
MB_ICONWARNING或MB _ OK);
最后
free library(dll hnd);
结束;