加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Delphi中DLL的创建和使用

发布时间:2020-12-15 10:00:17 所属栏目:大数据 来源:网络整理
导读:Delphi中DLL的创建和使用??? ? 1.DLL简介;?? 2.调用DLL;?? 3.创建DLL;?? 4.两个技巧;?? 5.初始化;?? 6.例外处理。????? ???? ?? 1、DLL简介??? ? DLL是Dynamic-Link?? Libraries(动态链接库)的缩写,库里面是一些可执行的模块以及资源(如位图、图标等)
Delphi中DLL的创建和使用???
? 1.DLL简介;?? 2.调用DLL;?? 3.创建DLL;?? 4.两个技巧;?? 5.初始化;?? 6.例外处理。?????
????
?? 1、DLL简介???
?   DLL是Dynamic-Link?? Libraries(动态链接库)的缩写,库里面是一些可执行的模块以及资源(如位图、图标等)。可以认为DLL和EXE基本上是一回事,只是DLL不能直接执行,而必须由应用程序或者其他DLL调用。DLL为应用程序间的资源共享提供了方便,同时也是多语言混合编程的重要手段。由此可见学习使用DLL是Windows程序员必须掌握的一项重要技术。???
??
? 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;?????
? 3、用Delphi创建DLL???
?   用Delphi创建一个DLL是十分简单的,首先需要新建一个DLL的Porject(如果使用Delphi3.0则可以在File->New对话框中选择DLL),当然也可以自己写,现在这个Project是这样的:???
? library?? Project1;???
? uses?? SysUtils,Classes;???
? end.?????
?   当然这是一个空DLL,现在让我们来加入一个函数,让他成为我们的第一个可以使用的DLL。完成后的文件是这样的:???
? library?? dll1;???
? function?? Test1(a,b:integer):integer;???
? Result:=a+b;???
? end;???
? exports???
? Test1?? index?? 1;???
?   在这个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));?????
??????
? 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即可。???
? 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;???
?   ...?? //初始化程序???
?   OldExitProc?? :=?? ExitProc;???
?   ExitProc?? :=?? @MyExitProc;???
? <3>利用DllProc变量???
?   和ExitProc一样,DllProc也是一个在Systemd单元中预定义的变量。在使用DLLProc时,?? 必须先写好一个具有以下原型的程序:???
?   procedure?? DLLHandler(Reason:?? integer);???
? 并在library的begin..end.之间,?? 将这个DLLHandler程序的执行地址赋给DLLProc中,?? 这时就可以根据参数Reason的值分别作出相应的处理。另外注意要将 Windows单元加入uses子句 。示例如下:???
?   library?? TestDLL;???
?   procedure?? MyDLLHandler(Reason:?? integer);???
?    case?? Reason?? of???
?     DLL_Process_Attach:?? //整个DLL的初始化代码???
?     DLL_Process_Detach:?? //整个DLL的善後程序???
?     DLL_Thread_Attach:?? //当主叫端开始一个Thread时???
?     DLL_Thread_Detach:?? //当主叫端终止一个Thread时???
?    end;???
?   ...?? //初始化代码???
?   DLLProc?? :=?? @MyDLLHandler;???
?   MyDLLHandle(DLL_Process_Attach);???
? 由上例可以知道,当DLL支援多进程(Thread)的处理时,?? DllProc非常适合使用。???
? 6、DLL中的例外处理???
?   在用Delphi制作DLL时,?? 在例外处理方面请留意以下三点:?????
? 如果uses子句中没有SysUtils话,无法使用例外处理。?????
? 如果DLL中没有对例外进行处理的话,这个例外会想完传导到主叫端的应用程序。如果该应用程序也是Delphi写的话,?? 这个例外可以由主叫端进行处理。?????
? 承上,?? 如果主叫端的程式不是Delphi或Borland?? C++?? Builder,则例外以作业系统错误的形式来处理,例外编号是$0EEDFACE,ExceptionInformation中第一个进入点是例外发生的地址,第二个进入点是指向的Delphi例外物件的引用。?????
?????
? {?? 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,51); font-family:Arial; font-size:14px; line-height:26px">????? Unit1?? in?? 'Unit1.pas';???
????? Exports???
????? EnableMouseHook,?? //只要把这两个函数输出就可以了,???
????? DisableMouseHook;//不会不懂函数的意思吧^_^。???
? {$R?? *.res}???
? end.???
? unit1???
? unit?? Unit1;???
? interface???
? Uses?? Messages,Windows;???
? var???
? hHk:?? HHOOK;//钩子的句柄值。???
? function?? MouseHookProc(nCode:?? Integer;WParam:?? WPARAM;LParam:?? LPARAM):?? LRESULT;stdcall;???
? //鼠标钩子的回调函数,即是用它来处理得到消息后要干什么。这里我只是发送一个//WM_PASTE消息。???
? //nCode参数是Hook的标志,一般只关心小于0时。看下面的详细说明???
? //WParam参数表示鼠标消息的类型???
? //LParam参数是一个指向?? TMOUSEHOOKSTRUCT?? 结构的指针。结构包含了鼠标消息的状态,我只用了hwnd一个???
? //即鼠标消息要传递给的窗口句柄。???
? //返回值如果不是0的话windows就把这个消息丢掉,其它的程序就不会再收到这个消息了。???
? function?? EnableMouseHook:Boolean;?? stdcall;?? export;???
? function?? DisableMouseHook:Boolean;?? stdcall;?? export;//两个函数都是Boolean类型,成功都是返回True???
? implementation???
????????? MouseHookStruct:?? ^TMOUSEHOOKSTRUCT;//这个结构Delphi在Windows单元有定义,直接用就可以了。??
????????? nState:?? SHORT;//得到键盘状态的GetKeyState函数的返回值。这是一个16位的数。???
????????? Result?? :=?? 0;?? //最好首先给他一个返回值,不然会有警告的!记住这可不是C语言。???
????????? //当nCode小于0时表示还有其它的Hook,必须把参数传给他。???
????????? //此时就要用Api函数CallNextHookEx让他调用下一个Hook!!!当然不用好像也可以。???
????????? if?? nCode?? <?? 0?? then???
????????? Result?? :=?? CallNextHookEx(hHk,nCode,WParam,LParam)//参数是现成的,直接用就可以了,???
????????? //详细的说明可以参考Win32?? SDK???
????????? else?? if?? wParam?? =?? WM_LBUTTONDBLCLK?? then?? //判断是不是鼠标左键双击事件???
????????? begin???
????????? nState?? :=?? GetKeyState(VK_CONTROL);//这个函数只有一个参数,就是要得到的键的???
????????? //键值,这里用windows的虚拟键值表示ctrl键。???
????????? if?? (nState?? and?? $8000)?? =?? $8000?? then//如果按下了,那么返回值的最高位为1???
????????? begin?? //即是16进制的8000,如果没有按下就返回0???
????????? MouseHookStruct?? :=?? Pointer(LParam);//转换指针并付值给MouseHookStruct变量。???
????????? SendMessage(MouseHookStruct.hwnd,WM_PASTE,0);//如果条件都满足了就发送WM_PASTE(粘贴)消息???
????????? end;???
????????? if?? hHk?? =?? 0?? then?? //为了安全,必须判断一下再设置钩子。???
????????? Begin?????
????????? //?? 第三个参数的Hinstance?? 在Delphi中有定义,用就可以了。第四个参数必须为0???
????????? hHk?? :=?? SetWindowsHookEx(WH_MOUSE,@MouseHookProc,Hinstance,0);???
????????? Result?? :=?? True;???
????????? end???
????????? else???
????????? Result?? :=?? False;???
? function?? DisableMouseHook:Boolean;?? stdcall;?? export;???
????????? if?? hHk?? <>?? 0?? then?? //如果有钩子就卸掉他。???
????????? UnHookWindowsHookEx(hHk);???
????????? hHk?? :=?? 0;???
? end.?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读