读书人

Delphi Dll 创造和使用(2)

发布时间: 2013-04-09 16:45:09 作者: rapoo

Delphi Dll 创建和使用(2)
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));

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;
  ...
  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非常适合使用。

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,         Unit1   in   'Unit1.pas';         Exports         EnableMouseHook,   //只要把这两个函数输出就可以了,         DisableMouseHook;//不会不懂函数的意思吧^_^。         {$R   *.res}         begin     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     function   MouseHookProc(nCode:   Integer;WParam:   WPARAM;LParam:   LPARAM):   LRESULT;stdcall;     var             MouseHookStruct:   ^TMOUSEHOOKSTRUCT;//这个结构Delphi在Windows单元有定义,直接用就可以了。             nState:   SHORT;//得到键盘状态的GetKeyState函数的返回值。这是一个16位的数。     begin             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,0);//如果条件都满足了就发送WM_PASTE(粘贴)消息             end;     end;         end;         function   EnableMouseHook:Boolean;   stdcall;   export;     begin             if   hHk   =   0   then   //为了安全,必须判断一下再设置钩子。             Begin               //   第三个参数的Hinstance   在Delphi中有定义,用就可以了。第四个参数必须为0             hHk   :=   SetWindowsHookEx(WH_MOUSE,@MouseHookProc,Hinstance,0);             Result   :=   True;             end             else             Result   :=   False;     end;         function   DisableMouseHook:Boolean;   stdcall;   export;     begin             if   hHk   <>   0   then   //如果有钩子就卸掉他。             begin             UnHookWindowsHookEx(hHk);             hHk   :=   0;             Result   :=   True;             end             else             Result   :=   False;     end;             end. 


读书人网 >.NET

热点推荐