參考:http://blog.csdn.net/ninetowns2008/article/details/6311663
結合這篇博客:http://www.cnblogs.com/xumenger/p/4372289.html
再結合:http://blog.csdn.net/lailai186/article/details/8770487
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'; //或者external 'mydll.dll'; //直接通過名稱調用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'); //注意LoadLibrary函數的參數是一個PChar類型的,不是string,該參數是指要加載的DLL的DLL名 if hDll < 32 then exit; //如果Dll無法記載則跳出 @Test1 := GetProcAddress(hDll, MakeIntResource(1)); //取得mydll.dll中的第一個函數的地址。 //取得mydll 中的第一個函數的地址 .... FreeLibrary(hDll); end;
3.用Delphi創建DLL
用Delphi創建一個DLL是十分簡單的,首先需要新建一個DLL的Project(如果使用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語句的語法是
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而不是可執行文件,重新寫一遍顯然是沒有必要的,只要按照下面的步驟對已有的項目文件進行修改就可以了
(1)打開項目文件(.DPR),刪除單元底部 begin 和 end. 之間的所有語句(一般情況下這些語句是有Delphi 自動生成的)。如果項目中沒有用到Form,則從uses 子句中刪除表單單元(Form),然后轉向(2)
(2)對項目進行修改,令除了 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;
(3)如果想要輸出其他函數或者過程,而原來的項目中沒有,則可以在單元底部的begin語句之前加上這些代碼
(4)在單元底部的begin 語句之前加上一個 exports小節,然后寫出所有想輸出的函數或過程的名字(最好指定索引號)。注意如果執行了第(2)補,一定要輸出 RunMyDll的過程
(5)將項目文件頂部的保留字program改為library
(6)編譯
現在就可以在其他程序中調用本項目中的函數和過程了,只要執行 RunMyDll 就可以執行這個項目,和執行原來的可執行文件一模一樣
<2>創建一個引入文件
如果DLL比較復雜,則為它的聲明專門創建一個引入程序單元將是十分有意義的,並且會使這個DLL變得更加容易維護。引入單元的格式如下
unit MyInport; {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 的Initialization 與 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.
以上的代碼同樣是關於Delphi的指針和函數指針的一個很好的應用的例子,值得研究一下
<3>利用DllProc變量
和ExitProc 一樣,DllProc也是一個在Systemd單元里預定義的變量。在使用DLLProc時,必須先寫好一個具有以下原型的程序: 騙人哦測讀熱 DLLHandler(Reason : Integer);
並在Library 的 begin ... end.之間,將這個DLLHandle程序的執行地址賦值給DLLProc,這時候就可以根據參數Reason的值分別作出相應的處理。另外注意將 windows單元加入uses子句中。示例
library TestDLL; ... procedure MyDLLHandler(Reason : Integer); begn case Reason of DLL_Process_Attach; //整個DLL的初始化代碼 DLL_Process_Detach; //整個DLL的善后程序 DLL_Thread_Attach; //當主叫端開始一個Thread時 DLL_Thread_Detach; //當主叫端終止一個Thread時 end; end; ... begin .. //初始化代碼 DLLProc := @MyDLLHandler; MyDLLHandler(DLL_Process_Attach); end;
由上例可知,當DLL支持多進程的處理是,DLLProc非常適合使用
6.DLL中的例外處理
在用Delphi制作DLL的時候,在例外處理方面請注意以下三點
<1>如果uses子句中沒有SysUtils的話,無法使用例外處理
<2>如果DLL中沒有對例外進行處理的話,這個例外會向外傳導到主叫端的應用程序。如果該應用程序也是Delphi寫的話,這個例外可以由主叫端進行處理
<3>承上,如果主叫端的程序不是Delphi 或Borland C++ Builder,則例外以作業系統錯誤的形式來處理,例外編號是$0EEDFACE,ExceptionInformation中地第一個進入點是例外發生的地址,第二個進入點是指向Delphi 例外物件的引用