如何用delphi制作DLL动态库方法.docx
《如何用delphi制作DLL动态库方法.docx》由会员分享,可在线阅读,更多相关《如何用delphi制作DLL动态库方法.docx(16页珍藏版)》请在冰豆网上搜索。
![如何用delphi制作DLL动态库方法.docx](https://file1.bdocx.com/fileroot1/2023-2/3/ef12e312-f11d-43d1-8039-fecff0b95582/ef12e312-f11d-43d1-8039-fecff0b955821.gif)
如何用delphi制作DLL动态库方法
用Delphi制作DLL的方法
一Dll的制作一般步骤
二参数传递
三DLL的初始化和退出清理[如果需要初始化和退出清理]
四全局变量的使用
五调用静态载入
六调用动态载入
七在DLL建立一个TForM
八在DLL中建立一个TMDIChildForM
九示例:
十Delphi制作的Dll与其他语言的混合编程中常遇问题:
十一相关资料
一Dll的制作一般分为以下几步:
1在一个DLL工程里写一个过程或函数
2写一个Exports关键字,在其下写过程的名称。
不用写参数和调用后缀。
二参数传递
1参数类型最好与windowC++的参数类型一致。
不要用DELPHI的数据类型。
2最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。
成功或失败的返回值最好为1[成功]或0[失败].一句话,与windowsc++兼容。
3用stdcall声明后缀。
4最好大小写敏感。
5无须用far调用后缀,那只是为了与windows16位程序兼容。
三DLL的初始化和退出清理[如果需要初始化和退出清理]
1DLLProc[SysUtils单元的一个Pointer]是DLL的入口。
在此你可用你的函数替换了它的入口。
但你的函数必须符合以下要求[其实就是一个回调函数]。
如下:
procedureDllEnterPoint(dwReason:
DWORD);far;stdcall;
dwReason参数有四种类型:
DLL_PROCESS_ATTACH:
进程进入时
DLL_PROCESS_DETACH进程退出时
DLL_THREAD_ATTACH线程进入时
DLL_THREAD_DETACH线程退出时
在初始化部分写:
DLLProc:
=@DLLEnterPoint;
DllEnterPoint(DLL_PROCESS_ATTACH);
2如Form上有TdcomConnection组件,就UsesActivex,在初始化时写一句CoInitialize(nil);
3在退出时一定保证DcomConnection.Connected:
=False,并且数据集已关闭。
否则报地址错。
四全局变量的使用
在widnows32位程序中,两个应用程序的地址空间是相互没有联系的。
虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。
五调用静态载入
1客户端函数声名:
1)大小写敏感。
2)与DLL中的声明一样。
如:
showform(form:
Tform);Far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windowsc++一样。
4)调用时DLL必须在windows搜索路径中,顺序是:
当前目录;Path路径;windows;widows\system;windows\ssystem32;
六调用动态载入
1建立一种过程类型[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。
如:
type
mypointer=procedure(form:
Tform);Far;external;
var
Hinst:
Thandle;
showform:
mypointer;
begin
Hinst:
=loadlibrary('yproject_dll');//Load一个Dll,按文件名找。
showform:
=getprocaddress(Hinst,'showform');//按函数名找,大小写敏感。
如果你知道自动化对象的本质就清楚了。
showform(application.mainform);//找到函数入口指针就调用。
Freelibrary(Hinst);
end;
七在DLL建立一个TForM
1把你的FormUses到Dll中,你的Form用到的关联的单元也要Uses进来[这是最麻烦的一点,因为你的Form或许Uses了许多特殊的单元或函数]
2传递一个Application参数,用它建立Form.
八在DLL中建立一个TMDIChildForM
1Dll中的MDIForm.FormStyle不用为fmMDIChild.
2在CreateForm后写以下两句:
functionShowForm(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:
=mainForm,因为Application.MainForm是只读属性
Form1:
=TForm1.Create(mainForm);//用参数建立
end;
备注:
参数是主调程序的Application.MainForm
九示例:
DLL源代码:
libraryProject2;
uses
SysUtils,
Classes,
Dialogs,
Forms,
Unit2in'Unit2.pas'{Form2};
{$R*.RES}
var
ccc:
Pchar;
procedureOpenForm(mainForm:
TForm);stdcall;
var
Form1:
TForm1;
ptr:
PLongInt;
begin
ptr:
=@(Application.MainForm);
ptr^:
=LongInt(mainForm);
Form1:
=TForm1.Create(mainForm);
end;
procedureInputCCC(Text:
Pchar);stdcall;
begin
ccc:
=Text;
end;
procedureShowCCC;stdcall;
begin
ShowMessage(String(ccc));
end;
exports
OpenForm;
InputCCC,
ShowCCC;
begin
end.
调用方源代码:
unitUnit1;
interface
uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,
StdCtrls;
type
TForm1=class(TForm)
Button1:
TButton;
Button2:
TButton;
Edit1:
TEdit;
procedureButton1Click(Sender:
TObject);
procedureButton2Click(Sender:
TObject);
private
{Privatedeclarations}
public
{Publicdeclarations}
end;
var
Form1:
TForm1;
implementation
{$R*.DFM}
procedureOpenForm(mainForm:
TForm);stdcall;External'project2.dll';
procedureShowCCC;stdcall;External'project2.dll';
procedureInputCCC(Text:
Pchar);stdcall;External'project2.dll';
procedureTForm1.Button1Click(Sender:
TObject);
var
Text:
Pchar;
begin
Text:
=Pchar(Edit1.Text);
//OpenForm(Application.MainForm);//为了调MDICHILD
InputCCC(Text);//为了实验DLL中的全局变量是否在各个应用程序间共享
end;
procedureTForm1.Button2Click(Sender:
TObject);
begin
ShowCCC;//这里表明WINDOWS32位应用程序DLL中的全局变量也是在应用程序地址空间中,16位应用程序或许不同,没有做实验。
end;
十Delphi制作的Dll与其他语言的混合编程中常遇问题:
1与PowerBuilder混合编程
在定义不定长动态数组方面在函数退出清理堆栈时老出现不可重现的地址错,原因未明,大概与PB的编译器原理有关,即使PB编译成二进制代码也如此。
Windows的执行文件可以划分为两种形式程序和动态连接库
(DLLs)。
一般程序运行是用.EXE文件,但应用程序有时也可以
调用存储在DLL中的函数。
当我们调用Windows中的API函数的时候,实际上就是调用
存储在DLL中的函数。
在如下几种情况下,调用DLL是合理的:
1)不同的程序使用相同的DLL,这样只需要将DLL在内存
中装载一次,节省了内存的开销。
2)当某些内容需要升级的时候,如果使用DLL只需要改变
DLL就可以了,而不需要把整个程序都进行变动。
3)由于DLL是独立于语言的,所以,当不同语言习惯的人
共同开发一个大型项目的时候,使用DLL便于程序系统的交流,
当然,Delphi开发的DLL也可以在诸如VisualBASIC,C++等系
统中使用。
下面通过几个例子,说明Delphi开发动态连接库的方法和规
范。
第一节动态连接库的构建和调用方法
一、动态连接库构建
File---New---Other---DLLWizard
这就创建了一个动态连接库的基本模块
libraryProject2;
uses
SysUtils,
Classes;
{$R*.res}
begin
end.
把工程名改为Mydll,并写入必要的函数
librarymydll;
uses
SysUtils,Classes,Dialogs,windows;
functionTriple(N:
Integer):
integer;stdcall;
begin
result:
=N+3;
end;
functionDouble(N:
Integer):
integer;stdcall;
begin
result:
=N+2;
end;
functionTriple1(N:
Integer):
integer;stdcall;
begin
showmessage('计算N+3');
result:
=N+3;
end;
functionDouble1(N:
Integer):
integer;stdcall;
begin
messagebox(0,'计算N+2','计算N+2',mb_ok);
result:
=N+2;
end;
exports
Triplename'Tr',
Doublename'Do',
Triple1name'TrM',
Double1name'DoM';
Triple,Double,Triple1,Double1;
{$R*.RES}
begin
end.
其中函数:
Triple:
把传入值加三
Double:
把传入值加二
Triple1:
把传入值加三并显示提示
Double1:
把传入值加二并显示提示
从这个例子中可以看出DLL程序的几个规则:
1)在DLL程序中,输出函数必须被声明为stdcall,以使用标
准的Win32参数传递技术来代替优化的Register。
(说明:
在Delphi中Register方式是缺省的调用约定,这个约
定尽量采用寄存器来传递参数,传递次序从左到右,最多可用到3
个CPU的寄存器,如果参数多于3个,剩下的就通过栈来传送,使
用寄存器传送可保证参数传递的速度最快。
而stdcall方式是通过Windows的标准调用来传递参数,传递
秩序从左到右,这种方式适合调用Windows的API,在DLL中,当
然要使用这种方式)。
2)所有的输出函数都必须列在exports子句下面,这使的子例程
在DLL外部就可以看到。
exports
Triplename'Tr',
Doublename'Do',
Triple1name'TrM',
Double1name'DoM';
列出了用户使用这个函数的接口名字。
虽然别名不是必须的,但
最好给个别名,以便用户程序更容易找到这个函数,同时还要指出,
Delphi6.0取消了Delphi5.0中允许使用的index,如果还用Index
来指明接口名字,Delphi6.0中将提示错误。
实例中给出了两种提示方法,主要想说明一个问题:
showmessage(''),是VCL提供的函数,由于多次编译VCL,做出
的程序会比较大。
而messagebox(0,'','',mb_ok)是Windows提供的API函数,做
出的程序会比较小。
这就是说,编写DLL程序的时候,要尽量避免多次编译VCL。
作
为一个实例,这里把两种方法都列出来了。
保存
编译:
Projrct---BuildMydll
这就完成了一个简单的动态连接库的编写。
二、动态连接库的调用
首先在implementation下做调用声明
const
gdi32='mydll.dll';
functiontriple(n:
integer):
integer;stdcall;externalgdi32name'Tr';
functionDouble(N:
Integer):
integer;stdcall;externalgdi32name'Do';
functiontriple1(n:
integer):
integer;stdcall;externalgdi32name'TrM';
functionDouble1(N:
Integer):
integer;stdcall;externalgdi32name'DoM';
以后程序中就可以作为普通的函数使用了,例如:
procedureTForm1.Button1Click(Sender:
TObject);
varN:
integer;
begin
N:
=updown1.position;
edit1.text:
=inttostr(triple(N));
end;
第二节DLL中的Delphi窗体
一、在DLL中放置窗的的方法
在DLL中,除了放置标准的函数和过程以外,也可以放置
已经做好的的delphi窗体,也可以把做好的窗体供其它程序使
用,方法是:
1)首先按普通方法制作窗体,不过在interface区域,对接
口函数做如下声明
functionCreateform(capt:
string):
string;stdcall;
2)在implementation下加入接口函数
functionCreateform(capt:
string):
string;stdcall;
varForm1:
TForm1;
begin
form1:
=Tform1.Create(application);
form1.show;
form1.caption:
=capt;
end;
3)制作DLL动态连接库,但要声明:
uses
unit1in'unit1.pas';
exports
{写入接口标示符}
Createformname'Myform';
4)调用窗体的程序按普通方法制作,但是在implementation下首
先声明要调用的DLL函数
const
gdi32='myFormdll.dll';
functionCreateform(capt:
string):
string;stdcall;externalgdi32name'Myform';
procedureTForm3.Button1Click(Sender:
TObject);
varn,m:
string;
begin
m:
='我的窗体';
Createform(m);varn,m:
string;
end;
二、DLL中的调用窗体时的数据传递
在窗体调用时,可以用普通的函数方法传递数据,下面举
个例子。
1)建立窗体
做一个改变颜色窗体,放在DLL中,可以用普通的方法来
做,但要作如下声明:
functionmycolor(col:
longint):
longint;stdcall;
functionGetcolor:
longint;stdcall;
其中,mycolor为构造窗体;Getcolor为传递颜色数据。
在implementation区声明一个窗体内全局的变量
varcolor1:
longint;
下面写出相应的程序
functionmycolor(col:
longint):
longint;stdcall;
varForm1:
TForm1;
begin
form1:
=Tform1.Create(application);
form1.show;
form1.panel1.Color:
=col;
form1.edit1.Text:
=inttostr(form1.panel1.Color);
result:
=color1;
end;
functionGetcolor:
longint;stdcall;
begin
result:
=color1;
end;
procedureTForm1.ScrollBar1Change(Sender:
TObject);
begin
panel2.Color:
=RGB(ScrollBar1.Position,ScrollBar2.Position,ScrollBar3.Position);
edit2.Text:
=inttostr(panel2.Color);
color1:
=panel2.Color;
end;
procedureTForm1.Button2Click(Sender:
TObject);
begin
Free;//析构Form1
end;
2)建立动态连接库
运行成功后,再建立动态连接库:
libraryFormDLL;
{从文件调入}
uses
unit1in'unit1.pas';
exports
{写入接口标示符}
Mycolorname'My',
Getcolorname'Get';
begin
end.
3)建立调用的程序
首先声明要调用的DLL函数
const
gdi32='formDll.dll';
functionMycolor(col:
longint):
longint;stdcall;externalgdi32name'My';
functionGetcolor:
longint;stdcall;externalgdi32name'Get';
然后写出相应的程序
procedureTForm1.Button1Click(Sender:
TObject);
begin
Mycolor(color);
end;
procedureTForm1.Button2Click(Sender:
TObject);
begin
color:
=getcolor;
end;
我们可以看到,在改变颜色的窗体中做了颜色变化后,当前窗
体的颜色将发生变化。
handle