Delphi中DLL的創建和使用


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;   
  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. 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM