程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi Dll 創建和使用(2)

Delphi Dll 創建和使用(2)

編輯:Delphi

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是這樣的:  
   


[delphi]
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.      

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

[delphi]
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.  

 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.

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved