如何在Delphi中调用.DLL文件,小弟不懂,望大家赐教!(100分)

  • 主题发起人 主题发起人 超越
  • 开始时间 开始时间

超越

Unregistered / Unconfirmed
GUEST, unregistred user!
我有一个外部的.DLL文件,想要在delphi写的程序中调用,有什么好方法吗?
 
标题:Delphi制作Dll全集 (0分)
阿朱 (2001-5-18 8:49) 534762
用Delphi制作DLL
一 Dll的制作一般步骤
二 参数传递
三 DLL的初始化和退出清理[如果需要初始化和退出清理]
四 全局变量的使用
五 调用静态载入
六 调用动态载入
七 在DLL建立一个TForM
八 在DLL中建立一个TMDIChildForM
九 示例:
十 Delphi制作的Dll与其他语言的混合编程中常遇问题:
十一 相关资料

一 Dll的制作一般分为以下几步:
1 在一个DLL工程里写一个过程或函数
2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。
二 参数传递
1 参数类型最好与window C++的参数类型一致。不要用DELPHI的数据类型。
2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。
3 用stdcall声明后缀。
4 最好大小写敏感。
5 无须用far调用后缀,那只是为了与windows 16位程序兼容。

三 DLL的初始化和退出清理[如果需要初始化和退出清理]
1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:
procedure DllEnterPoint(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组件,就Uses Activex,在初始化时写一句CoInitialize (nil);
3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。

四 全局变量的使用
在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。

五 调用静态载入
1 客户端函数声名:
1)大小写敏感。
2)与DLL中的声明一样。
如: showform(form:Tform);Far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windows c++一样。
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 把你的Form Uses到Dll中,你的Form用到的关联的单元也要Uses进来[这是最麻烦的一点,因为你的Form或许Uses了许多特殊的单元或函数]
2 传递一个Application参数,用它建立Form.

八 在DLL中建立一个TMDIChildForM
1 Dll中的MDIForm.FormStyle不用为fmMDIChild.
2 在CreateForm后写以下两句:
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 := mainForm,因为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:PLongInt;
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;

interface

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 declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}
procedure OpenForm(mainForm:TForm);stdcall;External'project2.dll';
procedure ShowCCC;stdcall;External'project2.dll';
procedure InputCCC(Text: Pchar);stdcall;External'project2.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
Text: Pchar;
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 与PowerBuilder混合编程
在定义不定长动态数组方面在函数退出清理堆栈时老出现不可重现的地址错,原因未明,大概与PB的编译器原理有关,即使PB编译成二进制代码也如此。
sundart (2001-5-30 1:9)
呵呵。楼上兄台精神可敬。
leebons (2001-5-31 17:24)
怎么不给分啊
pcexplorer (2001-5-31 18:19)
To:leebons
你还想要分吗,知识是无价的!
楼上兄台精神可敬,改天我也来发表发表文章
:)
悲酥清风 (2001-5-31 18:29)
好啊好啊!
什么时候再来个COM制作全集、控件制作全集、专家制作全集。。。(是不是太贪心了?)
阿朱 (2001-5-31 21:31)
悲酥清风同志说的这几个全集我都在考虑。不过目前我正在写一篇关于数据库存储引擎的
文章,改天写好给大家
mikedeakins (2001-5-31 21:59)
阿朱同志,可否把全部文章发给我?谢谢。
iamfly (2001-5-31 22:7)
期待着阿朱同志的下几个全集:)
P.S 我这也有个同事我们叫他阿朱的,不过他不用DELPHI的
程云 (2001-6-8 14:51)
真是受益非浅,我正用到这方面的东西呢。
多谢了。
程云 (2001-6-8 15:3)
只是哪个在DLL中建立一个TMDIChildForM,并动态加载这个DLL,并显示这个Form时,
在关闭时,要如何才能去释放这个DLL呢?

我试过,会出错的。
9861 (2001-6-8 15:19)
我想知道怎样开发一个最简单的VCL或OCX控件。
jiceyang (2001-6-8 15:26)
真实对大富翁的高手门无私的奉贤精神,对与我正样的菜鸟真实太好。。。。。。
千中元 (2001-6-8 15:34)
手藏收藏。。。
慕容乾坤 (2001-6-8 16:4)
thanks
程云 (2001-6-8 16:22)
有人知道"阿朱"的信箱吗?
我有问题请教。
程云 (2001-6-8 16:29)
我在DLL写如下程序
function ShowForm(FHandle: THandle; mainform: TForm):LongInt;StdCall;
begin
ptr:=@(Application.MainForm);
ptr^:=LongInt(mainForm);
DllLongInt:=ptr^;
DllForm:=TDllForm.Create(mainform);
Result:=LongInt(DllForm);
end;

使用动态加载DLL见如下

procedure TMainFrm.LoadLib(LibName: String);
var
LibHandle : Thandle;
ShowForm : TShowForm;
begin
LibHandle:=LoadLibrary(LibName);

try
if LibHandle=0 then raise EDLLLoadError.Create(extractfilepath(application.ExeName)+'FrmLib/'+LibName+'文件没有发现!');
@ShowForm:=GetProcAddress(LibHandle,'ShowForm');
if Assigned(ShowForm) then
ShowForm(Application.Handle, DllCortrol.Handle, MainFrm);
except { finally }
FreeLibrary(libHandle);
end;
end;


结果,我在想释放这个DLL时就出现了错误。

FreeLibrary(DllHandle);

不知如何作?

帮帮我吧
阿朱 (2001-6-8 17:4)
我的这段代码是把DLL的句柄在程序执行时换掉了,有一段再换回的代码忘写了。你在释放
DLL时,只要把DLL句柄换回即可。
DllLongInt:=ptr^;就是此意.
程云 (2001-6-8 17:11)
我也换回来了,可是还是一样有错误

procedure TDllForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
ptr^:=DllLongInt;
end;

最好能看看你原来的代码
程云 (2001-6-8 19:3)
前的写的次序不对,在DLL中如下写

function ShowForm(FHandle: THandle; mainform: TForm):LongInt;StdCall;
begin
ptr:=@(Application.MainForm);
DllLongInt:=ptr^; //保存原句柄,多次试,发现此时为"0";
ptr^:=LongInt(mainForm); //修改
DllForm:=TDllForm.Create(mainform);
Result:=LongInt(DllForm);
end;

procedure TDllForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
ptr^:=DllLongInt; //改为原来句柄
end;

可错误依旧,真不知哪里错了
阿朱 (2001-6-8 21:29)
我不是在FormCreate和FormClose中写的,而是在DLL的函数中写的.那些变量也是dll的全局变量。
DllLongInt:=ptr^; 这两个都是.
程云 (2001-6-8 22:8)
我也是这样写的,可还是有错,我真不知是为什么,
哪些变量也全是dll的全局变量。

你有没有例程给我寄个来看看呢?

真是头疼死人了
程云 (2001-6-8 23:4)
阿朱老兄,还是不成,就是报错,说是任地址不可用。

我给你寄去个程序,你帮我看看是哪里错了。

多谢了
程云 (2001-6-9 11:41)
各位不好意思,打扰了,我的问题已经解决,
出错的原因,是我不应试图再修改回一哪个值,
这完全是画蛇添足。

多谢各位了,由其是阿朱老兄。
ynjinti (2001-6-9 14:58)
TO 阿朱:
你是DLL高手吗?我正在学DLL,并用DLL实现了MDI子窗体,创建DLL子窗体有两种方法,
一是在DLL里创建,别一个是在主程序里创建,两种方法都可行,但是有一个问题父窗体不
认识子窗体,也就是说我创建了子窗体后父窗体的MDIChildCount仍然为这0,这样的话,
父窗体无法控制子窗体比如我在主程序里发送消息给子窗体进行打印就无法进行。此外是关于
数据传输的问题,书上讲得较多的是两个应用程序共享的DLL的数据,但我现在是多个DLL共享
应用程序的通用变量(可能随时改变),如何实现,一般共享数据是用内存映射文件实现当然也可以
用流文件实现,你觉得哪种更好,当然数据类型的数据共享相对容易,如果要传送控件,对象,
如何进行。
我做的程序的思想是只有主程序是EXE,此外每个模块都是一个DLL,由主程序调用,行吗?
你有没有相关的DLL源代码,有的话麻烦发一点给我,我是说是你自己写的,不要书上抄来的。OK!
如果满意的话,我把我的分全给你,如何?
程云 (2001-6-11 14:1)
to ynjinti:

你说的在主程序里创建中创建DLL中的窗体,这是如何作的?我只知在DLL中创建。
没听说过哪个对象能在DLL之外被创建。要这样作,也得在外部进行说明,
这样哪也没有必要放入DLL中了。

我就是这样作的,主程序中只有一个主窗体,其它的都放在各DLL中,
这种方法的好处显而易见,只是有很多问题还没很好地解决,
希望能同你多多交流。
电暖气 (2001-6-11 14:15)
阿朱是个好同志
踏雪无痕 (2001-6-11 16:5)
To 程云:
在主程序中创建窗体是这样做的,Dll有一个函数返回DLL中的窗体的类,主窗体加载这个然后通过返回
的类名进行创建。一般一书上都是在DLL中进行创建的,我原来也以为不行,因为我以为和应用在不同的
进程空间运行,你也在做这方面的吗?希望以后多交流,我的EMAIL:hoboes@163.net
程云 (2001-6-12 8:6)
OK。多交流了。
踏雪无痕 (2001-6-12 8:18)
To 程云:
你的EMAIL是多少,我现在也正在做如果我最新进展我会告诉你的。
maming (2001-6-12 9:1)
学学
程云 (2001-6-12 11:33)
to 踏雪无痕:

EMail是:zzmcy@21cn.com
我上午给发了封邮件。
almeidar (2001-9-25 17:24)
[:D]今天才看到 ,呵呵 ,不好意思!
oceanwave (2001-9-25 17:43)
[gold]不错,不错。大富翁这样深入的的谈论越来越少了,向阿朱同志致敬[:D][/gold]
Ayong (2001-9-25 18:10)
真是不好意思!今天才看到。
“阿朱”。。
这个名字好亲切!!我有个大学同学,也是同居四年的室友,我们就是这样称呼他的!
你后面的全集写好了吗?千万别忘了给我一份!先谢谢了!!
dinjian (2001-9-26 9:45)
阿朱兄太棒了![^]
ctx62 (2001-9-26 13:6)
to 阿朱
有一个问题不知能否解决,就是在编两层数据库管理程序,我每一个模块都用一个
独立的exe,然后用一个主程序去调用,现在的问题是我的每一个exe文件都与server
建立一个数据库连接,如果同时执行好几个模块,再加上几个工作站都在执行
这样可能造成连接数不够的问题,能否用dll文件来实现所有的exe用一个连接。
不能没有你 (2001-9-26 13:9)
不错
值得关注
蛐蛐 (2001-9-26 13:11)
go on .
anzuo@lungcheong.com.cn
叶不归 (2001-9-26 15:21)
[:D]gua z
lliuxxingw (2001-9-26 15:47)
gz:
能否用dll文件来实现所有的exe用一个连接?
lbmjy (2001-10-6 12:26)
我遇到的问题与ynjinti一样,想在主控程序控制Dll中的MDIChildForm,
非常想知道解决问题的方法。
lbmjy@163.net
蛐蛐 (2001-10-6 12:38)
贴出来嘛,好东东,要大家分享才好。
免费得到 (2001-10-8 18:1)
洗耳恭听!
冰 原 (2001-10-8 18:56)
听课
zhuhuan (2001-10-8 20:21)
嘿嘿,其实从DLL中创建Form(无论室MDI还是其他FORM)有另外一种方法,至今不太见到,其实
DLL和EXE在同一进程空间,那和BPL有什么不一样呢?为什么BPL可以直接使用其类,而DLL就不
可以?当然可以!答案在D2 Unleashed里(后续的Unleasned里反而不见了).
刘永学 (2001-10-27 13:50)
dll学习的书籍中,有几本讲的较好,
其一为:delphi5开发人员指南《机械工业出版社》2000年//非常好,建议购买
其二为:《精通delphi》姚庭宝 电子工业出版社 1997年
其三为: 《delphi2 程序设计大全》《机械工业出版社》//好,想买也买不到

COM:
1、delphi com 深入编程 机械工业出版社
2、徐新华的书,书名忘了//深入不浅出
3、李维delphi5.x高级程序设计篇//好,有高手风范

数据库及电子商务
李维的delphi5.x丛书

控件制作、使用
1、delphi第三方控件使用大全 中国水利水电出版社//建议不要买,看看就可以了
2、delphi部件开发编程深入剖析 机械工业出版社 //其中有一个数值计算控件蛮好的

opengl
open gl 三维图形设计与制作 人民邮电出版社 //c++的例子
可视化opengl程序设计 清华大学出版社//较好,有示例
delphi 多媒体特效制作百例//中国电力出版社 ,看看就行了,不要买,将随书的光盘栲下来就可以

操纵office
网上有很多例子,查找一下就可以了


以上是我这4年的学习心得,大家可以参考一下!!
zhang12321 (2001-11-9 10:48)
太好了.我已经看到阿朱的好几个好文了.多谢了:)!!!!!!!!!!!!!!!
yxq1220 (2001-11-9 10:52)
谢谢
lop (2001-11-9 11:6)
没什么好说的了,谢谢!
tkggai (2001-11-9 11:36)
阿朱:谢谢你了:tkggai@sian.com
dcsdcs (2001-11-9 13:9)
主程序:
function dcs_logon(input:tstringlist):Tstringlist;far; external 'logondll.DLL';

var
namelist:Tstringlist;
begin
namelist:=Tstringlist.Create;
namelist.Append('dcs');
try
namelist:=dcs_logon(namelist);
except
end;
if namelist.Strings[0]='0' then
begin
label1.Caption:='用户名:'+namelist.Strings[1];
label2.Caption:='密码:'+namelist.Strings[2];
end
else
begin
label1.Caption:='用户取消登录';
label2.Caption:='';
end;
end;

dll程序:
uses
SysUtils,
Classes,
loginunit in 'loginunit.pas' {loginform};

{$R *.RES}
exports
dcs_logon index 1;
begin
end.

function dcs_logon(input:tstringlist):Tstringlist;
var
loginform: Tloginform;
begin
try
loginform:=Tloginform.Create(application);
loginform.ComboBox1.Items.Clear;
loginform.ComboBox1.Items.AddStrings(input);
loginform.ShowModal;
res.Strings[1]:=loginform.ComboBox1.Text;
res.Strings[2]:=loginform.Edit2.Text;
result:=res;
finally
loginform.Free;
end;
end;
EdwinYeah (2001-11-10 22:1)
关于制作DLL,<delphi 5开发人员指南第九章有详细介绍,以下是目录:
第9章 动态链接库 234
9.1 究竟什么是DLL 234
9.2 静态链接与动态链接 235
9.3 为什么要使用DLL 236
9.3.1 共享代码、资源和数据 236
9.3.2 隐藏实现的细节 237
9.3.3 自定义控件 237
9.4 创建和使用DLL 237
9.4.1 数美分:一个简单的DLL 237
9.4.2 显示DLL中的模式窗体 239
9.5 显示DLL中的无模式窗体 241
9.6 在Delphi应用程序中使用DLL 242
9.7 DLL的入口函数和出口函数 246
9.7.1 进程/线程初始化和终止例程 246
9.7.2 DLL入口/出口示例 246
9.8 DLL中的异常 250
9.8.1 在16位Delphi中捕捉异常 250
9.8.2 异常和Safecall指示符 250
9.9 回调函数 250
9.9.1 使用回调函数 253
9.9.2 拥有者绘制的列表框 253
9.10 从DLL中调用回调函数 253
9.11 在不同的进程间共享DLL数据 256
9.11.1 一个可以被共享数据的DLL 256
9.11.2 访问DLL中的共享数据 259
9.12 引出DLL中的对象 261
9.13 总结 265
 
标题:如何开发自己的DLL? (100分)
xjhuo (1998-12-1 23:19) 93780

我为DLL编程的问题已经苦恼好几天了,
看了几份材料,编了几个例程,仍旧不能取得预期效果,
总是编译不过去,我的具体做法如下:
先在一个FORM中写好代码;
为其建立一工程,
将工程中的PROGRAM改为LIBRARY,并将其中的
要EXPORT的过程写在PROJECT文件中的EXPORT部分,
进行编译,发现出错,出错信息不是为不能找到我的FORM,
就是需要一个宿主程序。
我真是被它搞得不耐烦了,恳请各位大侠
能够仗义直言,帮帮小弟的忙,最好能将其制作步骤
以及这当中的注意事项告之本人,
小弟在这先谢了!


huizhang (1998-12-1 23:46)
把你的工程文件贴出来让大家分析一下
jiangtao (1998-12-2 0:47)
1.创建DLl可是使用File菜单下的New then在Object repository上
选择DLl即可
(不用手工将 program 改成library )

2.提示需要host program,
是因为你按了运行键,DLl不能单独运行
但它可以调试(这是Delphi 3的new feature)
调试时,输入调用这个Dll的程序名即可


找不到Form就不知道为何了?
贴出你的文件吧?

btw:建议看《Delphi 3从入门到精通》关于DLL一章 P637 20章

aimingoo (1998-12-2 8:43)
哈哈,关于dll?我研究了不知多久了。
没有问题,这样搞定它:

1. 关于Form的问题,在view/Project Manager菜单中将相就的单元和Form删
除即可。建议在new app后,立即删除之。.dll中不会用到form.
2. 现在你可以改program为Library了。
3. 调试.dll时需要一个宿主程序,如果你编译后直接运行,也会得到这个
提示。你需要写一个调用这个.dll的.exe程序,编译后将它的路径填到Run/
Parameters菜单的Host app...输入条中。
4. 现在,你可以开以运行/调试这个.dll了。
zx (1998-12-2 15:39)
光编译,不运行就不会又要宿主程序了!
还有,最好用他给你生成dll,而不是光把
program改为library.
我觉得,您应该把具体的程序贴上来,让大家
帮你分析!
zx (1998-12-2 15:39)
光编译,不运行就不会又要宿主程序了!
还有,最好用他给你生成dll,而不是光把
program改为library.
我觉得,您应该把具体的程序贴上来,让大家
帮你分析!
huizhang (1998-12-2 17:26)
Hi xjhou,

This is the way to create a dll with a form or forms:

1. Create a new DLL project. Save the Dll project(here ia MyDll.dpr).
2. Add a new form to MyDll project(in your case, use project manager
to add your existing form). Add any components you want to the
form.
3. Name your form and save it(here is 'TDllForm').
4. Switch back to the MyDLL source unit. Add a function called
ShowForm that creates and shows the form. Use the code bellow:
function ShowForm : Integer; stdcall;
var
Form : TDLLForm;
begin
Form := TDLLForm.Create(Application);
Result := Form.ShowModal;//alt Form.Show;
Form.Free;
end;
5. Add an exports section to the MyDLL and export the ShowForm
function. Here's how the exports section should look:

exports
ShowForm;

6. Choose Project| Build MyDll to build the DLL. Save the DLL.

I think you can handel the rest include the debuging.
Good luck!
唐晓锋 (1998-12-13 9:28)
我这里有个关于DLL的比较详细的文章,由于太多
为了不影响网络的熟读邮寄给你了!
唐晓锋 (1998-12-13 9:33)
发不出去,只好贴上来了!

Delphi中DLL的创建和使用

1.DLL简介; 2.调用DLL; 3.创建DLL; 4.两个技巧; 5.初始化; 6.例外处理。


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

<Back to Top>



2、如何调用DLL
  在Delphi中有两种方法调用DLL中的函数和过程,即外部声明或者动态加载。

<1>外部声明
  在Delphi中外部声明是访问外部例程最容易和最常用的方式,有两种声明方式:通过名字、通过索引号。举例如下:在MYDLL.DLL中有两个函数和一个过程,则其外部声明可以写成:

function test1:integer;external 'mydll';
//直接通过名称调用test1(注意名称大小写敏感)。
function test11:integer;external 'mydll' name 'test1';
//通过名称调用test1,在程序中使用新名称(原名称仍然大小写敏感)。
procedure test2;external 'mydll' index 1;
//通过索引号调用TEST2。程序中可以用与DLL中不一样的名称.
  使用外部声明的缺点是程序启动时如果找不到mydll.dll将无法运行,即使没有调用其中的模块。 动态加载的方法可以避免这种情况。

<2>动态加载
  通过调用Windows API中的相关函数,将DLL调入内存并获得指向函数或过程的指针,执行完模块后释放内存。除了节约内存外,这种方法的一个很大的优点是能处理找不到dll或者在装入过程中出错的情况。这样即使某个dll有问题,应用程序的其他部分仍然能够正常运行。动态加载的例子如下:

var hDll:THandle;
  Test1:function:integer;
begin
  hDll:=LoadLibrary('mydll.dll');
  if hDll<32 then exit;//如果Dll无法加载则跳出
  @Test1:=GetProcAddress(hDll,MakeIntResource(1));
    //取得mydll中的第一个函数的地址。
  ...
  FreeLibrary(hDll);
end;

<Back to Top>



3、用Delphi创建DLL
  用Delphi创建一个DLL是十分简单的,首先需要新建一个DLL的Porject(如果使用Delphi3.0则可以在File->New对话框中选择DLL),当然也可以自己写,现在这个Project是这样的:

library Project1;
uses SysUtils,Classes;
begin
end.

  当然这是一个空DLL,现在让我们来加入一个函数,让他成为我们的第一个可以使用的DLL。完成后的文件是这样的:

library dll1;
uses SysUtils,Classes;

function Test1(a,b:integer):integer;
begin
Result:=a+b;
end;

exports
Test1 index 1;

begin
end.

  在这个DLL里我们声明了一个加法函数,然后用exports语句输出它,只有被输出的函数或过程能被其他程序调用。exports语句后的语法是:函数名 [index <n>],index <n>是为函数手工指定索引号,以便其他程序确定函数地址;也可以不指定,如果没有使用Index关键字,Delphi将按照exports后的顺序从1开始自动分配索引号。现在我们可以调用这个DLL了,下面给出一个实例,运行后form1的标题将变成“1+2=3”:

声明部分:function Test1(a,b:integer):integer;external 'dll1';
       注意此处是大小写敏感的。
运行部分:form1.caption:='1+2='+inttostr(test1(1,2));

<Back to Top>



4、使用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 1;
  end.
这样以后想要使用MyDll中的例程时,只要简单的在程序模块中的uses子句中加上MyImport即可。

<Back to Top>



5、DLL的初始化和善后工作
  一般的DLL不需要做初始化和善后工作,因此大部分读者可以跳过这一节。但如果你想让你的DLL在被载入时先作一些初始设定,或者退出时释放资源,则可以有三种方法达到目的:

<1>利用Unit的Initalization与Finalization这两个小节
  可以在Unit的这两个小节中安排Unit的进入和退出,但是Program与Library并没有这两个部分,所以只能写在Unit中。

<2>利用ExitProc变量
  在Library的begin..end.中间是可以写代码的,这里可以放置DLL初始化代码。如果想要做善后工作,则可以利用ExitProc变量。我们首先在初始化代码中把ExitProc中包含的默认的善后过程地址保存下来,然后把自定义的过程的地址赋给它,这样DLL退出时就会执行我们制定的程序;在自定义的过程的最后,把ExitProc恢复原来的默认值,以便DLL能够继续完成原来默认的善后工作。下面是示例:
  library MyDLL;
  ...
  OldExitProc: pointer;
  ...
  procedure MyExitProc;
  begin
  ... //善后程序
  ExitProc := OldExitProc;
  end;
  ...
  begin
  ... //初始化程序
  OldExitProc := ExitProc;
  ExitProc := @MyExitProc;
  end.

<3>利用DllProc变量
  和ExitProc一样,DllProc也是一个在Systemd单元中预定义的变量。在使用DLLProc时, 必须先写好一个具有以下原型的程序:
  procedure DLLHandler(Reason: integer);
并在library的begin..end.之间, 将这个DLLHandler程序的执行地址赋给DLLProc中, 这时就可以根据参数Reason的值分别作出相应的处理。另外注意要将Windows单元加入uses子句。示例如下:
  library TestDLL;
  ...
  procedure MyDLLHandler(Reason: integer);
  begin
   case Reason of
    DLL_Process_Attach: //整个DLL的初始化代码
    DLL_Process_Detach: //整个DLL的善後程序
    DLL_Thread_Attach: //当主叫端开始一个Thread时
    DLL_Thread_Detach: //当主叫端终止一个Thread时
   end;
  end;
  ...
  begin
  ... //初始化代码
  DLLProc := @MyDLLHandler;
  MyDLLHandle(DLL_Process_Attach);
  end.
由上例可以知道,当DLL支援多进程(Thread)的处理时, DllProc非常适合使用。

<Back to Top>



6、DLL中的例外处理
  在用Delphi制作DLL时, 在例外处理方面请留意以下三点:

如果uses子句中没有SysUtils话,无法使用例外处理。
如果DLL中没有对例外进行处理的话,这个例外会想完传导到主叫端的应用程序。如果该应用程序也是Delphi写的话, 这个例外可以由主叫端进行处理。
承上, 如果主叫端的程式不是Delphi或Borland C++ Builder,则例外以作业系统错误的形式来处理,例外编号是$0EEDFACE,ExceptionInformation中第一个进入点是例外发生的地址,第二个进入点是指向的Delphi例外物件的引用。
<Back to Top>



本文参考了钱达智先生(Wolfgang Chien)的技术文章,以及《Delphi for Windows Power Toolkie》([美]Harold Davis著)



出处 http://www.nease.net/~ganglion

zc (1998-12-15 1:6)
huizhang的回答是正确的,在DLL中是可以有Form的,将重用的Form做进DLL
是一个不错的想法。
yellowrose (1998-12-15 19:3)
DLL编写入门级,我最近也因使用HOOK的原因刚学起来。

动态链接库(DLL)是Windows中最重要的部分之一,Windows中的运
行程序可以共享DLL中的子例程,从而使应用程序可以共享代码,减少
内存占用。本文简要介绍用Delphi创建及使用DLL。

一、创建动态链接库

1.建立一个新的工程文件
由于此工程只是个库文件,因此并不需要Form。首先选择主菜单V
iew|Project Manage, 将自动生成的Unit1及Form1移去,然后选择Fil
e|Save Project As,将此工程取名为 Mydl.d pr存盘(命名可任意 ),
当系统提问是否保存Unit1时回答‘NO’,再选择View|Projectourc
e,将其代码修改如下:
library Mydll;
uses WinTypes;
begin
end.
此文件与一般工程文件主要不同之处在于将标识符"program"改
为"library"。
2.向动态链接库中加入被多个应用程序公用的子例程(过程及函
数 )
建立一个新的Unit,将被多个应用程序公用的子例程在这里声明
及实现。注意在这个Ui t的Interface部分声明的例程尾部要加关键
字"export",表示此例程能够被"引出",也就是能被其它应用程序调用
。例如:有一个公用函数Plus,其作用是将两个整数求和,此Unit的代
码如下:
Unit Content; {公用例程单元,命名可任意}
interface
function plus (a,b:integer):integer;export;
implementation
function plus (a,b:integer):integer;
begin
Result:=a+b;
end;
end.

3.引出公用例程
选择View|Project Source,在Mydll.dpr的uses部分之后加入:
exports
plus; {将公用例程引出}
完整的工程文件如下:
library Mydll;
uses WinTypes,
Content in ‘CONTENT.PAS’;
exports
plus;
begin
end.
以上就完成了DLL库的创建,选择Compile将其编译。
二、使用动态链接库
DLL中的资源可以被多个EXE文件公用,现在可以用Delphi建立多
个应用程序,共同使用y dll.dll中的函数Plus:

1.建立一个新的工程文件 Usedll.dpr, 移去所有的Unit及Form

2.为引用 Mydll.dll 中的函数Plus,建立一个新的Unit,其目的
是为了说明要引用的例程存在于哪个DLL库中(用关键字"external"),
此Unit的代码如下:
Unit useunit; {这是使用DLL的单元}
interface
function plus (a,b:integer):integer;
implementation
function plus; external ‘Mydll’;
{说明这个函数是在外部Mydll.dll库中实现的}
end.
可以看出,对于外部例程,在interface部分的声明与普通例程一
样,但是在implementai on部分要使用关键字"external",表示此例程
是在程序外部实现的。
3.在需要使用函数Plus的Unit中,只要在其Implementation部分
用Uses将useunit包含进来即可。
例如:新建一Form,加入Edit及Button控件,为Button增加OnClick
事件,此Form对应Uni的implementation代码如下:
implementation
{$R *.DFM}
uses useunit; {包含useunit单元}
procedure TForm1.Button1Click(Sender: TObject);
{Button1的OnClick事件}
begin
Edit1.Text:=IntToStr (Plus (10,20));
{使用DLL库中的函数}
end;
end.
将此工程编译,生成Usedll.exe,运行时按下Button即可看到Edit
控件的Text变成了30。
依上例可以建立多个EXE文件,共同使用Mydll中的函数Plus,由此
可以看出动态链接库的优越性,特别是在开发大型软件时,动态链接库
经常使用。
以上程序在Delphi 1.0/Win3.2、Delphi2.0/Win95上运行通
dwwang (1998-12-27 3:35)
分账分账,这位老兄根本不来了,
我又没时间详细看,见者有份!
得分富翁:aimingoo-14,huizhang-16,jiangtao-14,yellowrose-14,zc-14,zx-14,唐晓锋-14,
 
procedure HandleAction(Sender:TComponent);
var
PrHandle: THandle;
PuProFrmDll: TPuProFrmDll;
PuMainParaDll: TPuMainParaDll;
PrExecNameStr:string;
begin
if FileExists(Format('%SDll/%S.Dll',[PuRootPath,PrExecNameStr])) then
begin
try
if PrHandle <> 0 then
begin
@PuProFrmDll := GetProcAddress(PrHandle, 'PuProFrmDll');
if @PuProFrmDll <> nil then PuProFrmDll(Application.MainForm.Handle, PuMainParaDll);
end;
finally
_HideWait;
end;
end
else if FileExists(Format('%SBIN/%S.EXE',[PuRootPath,PrExecNameStr])) then
WinExec(PChar(Format('%SBIN/%S.EXE',[PuRootPath,PrExecNameStr])),SW_SHOW)
else if FileExists(Format('%SHELP/%S.CHM',[PuRootPath,PrExecNameStr])) then
WinExec(PChar(Format('HH.EXE %SHELP/%S.CHM',[PuRootPath,PrExecNameStr])),SW_SHOW)
else
ShellAbout(Application.Handle,'系统限制使用功能',PChar('指定功能的使用已被禁止,请与软件供应商联系!'#13#10+ReSoftCorpStr),Application.Icon.Handle);
end;
 
一个调用压缩功能dll的实例:

unit CommonDllunt;

interface

uses SysUtils, Windows;

const
CommonDLL = 'CommonDLL.dll';

function G_EncryptAndKey(const ASrc: string; Key: string): string;

implementation

uses Ras2;

type
TEncryptAndKey = function(ASrc, Key: pchar): pchar;



var
EncryptAndKey: TEncryptAndKey;


function G_EncryptAndKey(const ASrc: string; Key: string): string;
var
HLib: THandle;
begin
Result := '';
HLib := LoadLibrary(CommonDLL);
if HLib = 0 then
begin
FreeLibrary(Hlib);
MessageBox(0, pChar('找不到指定的动态连接库' + CommonDLL), '错误', mb_iconwarning);
Exit;
end;
@EncryptAndKey := GetProcAddress(HLib, 'EncryptAndKey');
if @EncryptAndKey = nil then
begin
FreeLibrary(Hlib);
exit;
end;
result := EncryptAndKey(Pchar(ASrc), pchar(Key));
FreeLibrary(Hlib);
end;
 
后退
顶部