1、如何用delphi制作DLL动态库方法用Delphi制作DLL的方法 一 Dll的制作一般步骤 二 参数传递 三 DLL的初始化和退出清理如果需要初始化和退出清理 四 全局变量的使用 五 调用静态载入 六 调用动态载入 七 在DLL建立一个TForM 八 在DLL中建立一个TMDIChildForM 九 示例: 十 Delphi制作的Dll与其他语言的混合编程中常遇问题: 十一 相关资料 一 Dll的制作一般分为以下几步: 1 在一个DLL工程里写一个过程或函数 2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。 二 参数传递 1 参数类型最好与window C+的参数
2、类型一致。不要用DELPHI的数据类型。 2 最好有返回值即使是一个过程,来报出调用成功或失败,或状态。成功或失败的返回值最好为1成功或0失败.一句话,与windows c+兼容。 3 用stdcall声明后缀。 4 最好大小写敏感。 5 无须用far调用后缀,那只是为了与windows 16位程序兼容。 三 DLL的初始化和退出清理如果需要初始化和退出清理 1 DLLProcSysUtils单元的一个Pointer是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求其实就是一个回调函数。如下: procedure DllEnterPoint(dwReason: DWO
3、RD);far;stdcall; dwReason参数有四种类型: DLL_PROCESS_ATTACH:进程进入时 DLL_PROCESS_DETACH进程退出时 DLL_THREAD_ATTACH 线程进入时 DLL_THREAD_DETACH 线程退出时 在初始化部分写: DLLProc := DLLEnterPoint; DllEnterPoint(DLL_PROCESS_ATTACH); 2 如Form上有TdcomConnection组件,就Uses Activex,在初始化时写一句CoInitialize (nil); 3 在退出时一定保证DcomConnection.Conne
4、cted := False,并且数据集已关闭。否则报地址错。 四 全局变量的使用 在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。 五 调用静态载入 1 客户端函数声名: 1)大小写敏感。 2)与DLL中的声明一样。 如: showform(form:Tform);Far;externalyproject_dll.dll; 3)调用时传过去的参数类型最好也与windows c+一样。 4)调用时DLL必须在windows搜索路径中,
5、顺序是:当前目录;Path路径;windows;widowssystem;windowsssystem32; 六 调用动态载入 1 建立一种过程类型如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了。如: type mypointer=procedure(form:Tform);Far;external; var Hinst:Thandle; showform:mypointer; begin Hinst:=loadlibrary(yproject_dll);/Load一个Dll,按文件名找。 showform:=getprocaddress(Hinst,showform)
6、;/按函数名找,大小写敏感。如果你知道自动化对象的本质就清楚了。 showform(application.mainform);/找到函数入口指针就调用。 Freelibrary(Hinst); end; 七 在DLL建立一个TForM 1 把你的Form Uses到Dll中,你的Form用到的关联的单元也要Uses进来这是最麻烦的一点,因为你的Form或许Uses了许多特殊的单元或函数 2 传递一个Application参数,用它建立Form. 八 在DLL中建立一个TMDIChildForM 1 Dll中的MDIForm.FormStyle不用为fmMDIChild. 2 在CreateF
7、orm后写以下两句: function ShowForm(mainForm:TForm):integer;stdcall var Form1: TForm1; ptr:PLongInt; begin ptr:=(Application.MainForm);/先把dll的MainForm句柄保存起来,也无须释放,只不过是替换一下 ptr:=LongInt(mainForm);/用主调程序的mainForm替换DLL的MainForm。MainForm是特殊的WINDOW,它专门管理Application中的Forms资源. /为什么不直接Application.MainForm := mainF
8、orm,因为Application.MainForm是只读属性 Form1:=TForm1.Create(mainForm);/用参数建立 end; 备注:参数是主调程序的Application.MainForm 九 示例: DLL源代码: library Project2; uses SysUtils, Classes, Dialogs, Forms, Unit2 in Unit2.pas Form2; $R *.RES var ccc: Pchar; procedure OpenForm(mainForm:TForm);stdcall; var Form1: TForm1; ptr:PLo
9、ngInt; begin ptr:=(Application.MainForm); ptr:=LongInt(mainForm); Form1:=TForm1.Create(mainForm); end; procedure InputCCC(Text: Pchar);stdcall; begin ccc := Text; end; procedure ShowCCC;stdcall; begin ShowMessage(String(ccc); end; exports OpenForm; InputCCC, ShowCCC; begin end. 调用方源代码: unit Unit1; i
10、nterface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private Private declarations public Public
11、declarations end; var Form1: TForm1; implementation $R *.DFM procedure OpenForm(mainForm:TForm);stdcall;Externalproject2.dll; procedure ShowCCC;stdcall;Externalproject2.dll; procedure InputCCC(Text: Pchar);stdcall;Externalproject2.dll; procedure TForm1.Button1Click(Sender: TObject); var Text: Pchar;
12、 begin Text := Pchar(Edit1.Text); / OpenForm(Application.MainForm);/为了调MDICHILD InputCCC(Text);/为了实验DLL中的全局变量是否在各个应用程序间共享 end; procedure TForm1.Button2Click(Sender: TObject); begin ShowCCC;/这里表明WINDOWS 32位应用程序DLL中的全局变量也是在应用程序地址空间中,16位应用程序或许不同,没有做实验。 end; 十 Delphi制作的Dll与其他语言的混合编程中常遇问题: 1 与PowerBuilde
13、r混合编程 在定义不定长动态数组方面在函数退出清理堆栈时老出现不可重现的地址错,原因未明,大概与PB的编译器原理有关,即使PB编译成二进制代码也如此。Windows 的执行文件可以划分为两种形式程序和动态连接库(DLLs)。一般程序运行是用.EXE文件,但应用程序有时也可以调用存储在DLL 中的函数。 当我们调用Windows 中的API 函数的时候,实际上就是调用存储在DLL 中的函数。 在如下几种情况下,调用DLL 是合理的: 1) 不同的程序使用相同的DLL ,这样只需要将DLL 在内存中装载一次,节省了内存的开销。 2) 当某些内容需要升级的时候,如果使用DLL 只需要改变DLL 就可
14、以了,而不需要把整个程序都进行变动。 3) 由于DLL 是独立于语言的,所以,当不同语言习惯的人共同开发一个大型项目的时候,使用DLL 便于程序系统的交流,当然,Delphi开发的DLL 也可以在诸如Visual BASIC,C+ 等系统中使用。 下面通过几个例子,说明Delphi开发动态连接库的方法和规范。 第一节 动态连接库的构建和调用方法 一、动态连接库构建 File-New-Other-DLL Wizard 这就创建了一个动态连接库的基本模块 library Project2; uses SysUtils, Classes; $R *.res begin end. 把工程名改为Mydl
15、l,并写入必要的函数 library mydll; uses SysUtils,Classes,Dialogs,windows; function Triple(N:Integer):integer;stdcall; begin result:=N+3; end; function Double(N:Integer):integer;stdcall; begin result:=N+2; end; function Triple1(N:Integer):integer;stdcall; begin showmessage(计算N+3); result:=N+3; end; function D
16、ouble1(N:Integer):integer;stdcall; begin messagebox(0,计算N+2,计算N+2,mb_ok); result:=N+2; end; exports Triple name Tr, Double name Do, Triple1 name TrM, Double1 name DoM; Triple,Double,Triple1,Double1; $R *.RES begin end. 其中函数:Triple:把传入值加三 Double:把传入值加二 Triple1:把传入值加三并显示提示 Double1:把传入值加二并显示提示 从这个例子中可以
17、看出DLL 程序的几个规则: 1) 在DLL 程序中,输出函数必须被声明为stdcall,以使用标准的Win32 参数传递技术来代替优化的Register。 (说明:在Delphi中Register方式是缺省的调用约定,这个约定尽量采用寄存器来传递参数,传递次序从左到右,最多可用到3个CPU 的寄存器,如果参数多于3 个,剩下的就通过栈来传送,使用寄存器传送可保证参数传递的速度最快。 而stdcall 方式是通过Windows 的标准调用来传递参数,传递秩序从左到右,这种方式适合调用Windows 的API ,在DLL 中,当然要使用这种方式)。 2)所有的输出函数都必须列在exports子句
18、下面,这使的子例程在DLL外部就可以看到。 exports Triple name Tr, Double name Do, Triple1 name TrM, Double1 name DoM; 列出了用户使用这个函数的接口名字。虽然别名不是必须的,但最好给个别名,以便用户程序更容易找到这个函数,同时还要指出,Delphi 6.0取消了Delphi 5.0中允许使用的index ,如果还用Index来指明接口名字,Delphi 6.0中将提示错误。 实例中给出了两种提示方法,主要想说明一个问题: showmessage(),是VCL 提供的函数,由于多次编译VCL,做出的程序会比较大。 而me
19、ssagebox(0,mb_ok) 是Windows提供的API 函数,做出的程序会比较小。 这就是说,编写DLL 程序的时候,要尽量避免多次编译VCL 。作为一个实例,这里把两种方法都列出来了。 保存 编译:Projrct-Build Mydll 这就完成了一个简单的动态连接库的编写。 二、动态连接库的调用 首先在implementation下做调用声明 const gdi32=mydll.dll; function triple(n:integer):integer;stdcall;external gdi32 name Tr;function Double(N:Integer):inte
20、ger;stdcall;external gdi32 name Do;function triple1(n:integer):integer;stdcall;external gdi32 name TrM;function Double1(N:Integer):integer;stdcall;external gdi32 name DoM; 以后程序中就可以作为普通的函数使用了,例如: procedure TForm1.Button1Click(Sender: TObject);var N:integer;begin N:=updown1.position; edit1.text:=intto
21、str(triple(N);end; 第二节 DLL 中的Delphi窗体 一、在DLL 中放置窗的的方法 在DLL 中,除了放置标准的函数和过程以外,也可以放置已经做好的的delphi窗体,也可以把做好的窗体供其它程序使用,方法是: 1)首先按普通方法制作窗体,不过在interface区域,对接口函数做如下声明 function Createform(capt:string):string;stdcall; 2)在implementation下加入接口函数 function Createform(capt:string):string;stdcall;var Form1: TForm1;be
22、gin form1:=Tform1.Create(application); form1.show; form1.caption:=capt;end; 3)制作DLL 动态连接库,但要声明: uses unit1 in unit1.pas; exports 写入接口标示符Createform name Myform; 4)调用窗体的程序按普通方法制作,但是 在implementation下首先声明要调用的DLL函数 const gdi32=myFormdll.dll; function Createform(capt:string):string;stdcall;external gdi32
23、name Myform; procedure TForm3.Button1Click(Sender: TObject);var n,m:string;begin m:=我的窗体; Createform(m);var n,m:string;end; 二、DLL 中的调用窗体时的数据传递 在窗体调用时,可以用普通的函数方法传递数据,下面举个例子。 1)建立窗体 做一个改变颜色窗体,放在DLL 中,可以用普通的方法来做,但要作如下声明: function mycolor(col:longint):longint;stdcall; function Getcolor:longint;stdcall;
24、其中,mycolor为构造窗体;Getcolor为传递颜色数据。 在implementation区声明一个窗体内全局的变量 var color1:longint; 下面写出相应的程序 function mycolor(col:longint):longint;stdcall;var Form1: TForm1;begin form1:=Tform1.Create(application); form1.show; form1.panel1.Color:=col; form1.edit1.Text:=inttostr(form1.panel1.Color); result:=color1;end
25、; function Getcolor:longint;stdcall;begin result:=color1;end; procedure TForm1.ScrollBar1Change(Sender: TObject);begin panel2.Color:=RGB(ScrollBar1.Position,ScrollBar2.Position,ScrollBar3.Position); edit2.Text:=inttostr(panel2.Color); color1:=panel2.Color;end; procedure TForm1.Button2Click(Sender: T
26、Object);begin Free; /析构Form1end; 2)建立动态连接库 运行成功后,再建立动态连接库: library FormDLL; 从文件调入uses unit1 in unit1.pas; exports 写入接口标示符Mycolor name My,Getcolor name Get; beginend. 3)建立调用的程序 首先声明要调用的DLL函数 const gdi32=formDll.dll; function Mycolor(col:longint):longint;stdcall;external gdi32 name My; function Getcolor:longint;stdcall;external gdi32 name Get; 然后写出相应的程序 procedure TForm1.Button1Click(Sender: TObject);begin Mycolor(color);end; procedure TForm1.Button2Click(Sender: TObject);begin color:=getcolor;end; 我们可以看到,在改变颜色的窗体中做了颜色变化后,当前窗体的颜色将发生变化。 handle
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1