4.2 宣告匯出函式
dll 中匯出函式的宣告有兩種方式:一種為4.1節例子中給出的在函式宣告中加上__declspec(dllexport),這裡不再舉例說明;另外一種方式 是採用模組定義(.def) 檔案宣告,.def檔案為鏈結器提供了有關被鏈結程式的匯出、屬性及其他方面的資訊。
下面的**演示了 怎樣同.def檔案將函式add宣告為dll匯出函式(需在dlltest工程中新增lib.def檔案):
; lib.def : 匯出dll函式
library dlltest
exports
add @ 1
.def檔案的規則為:
(1)library語句說明.def檔案相應的dll;
(2)exports語句後列出要匯出函式的名稱。可以在.def檔案中的匯出函式名後加@n,表示要匯出函式的序號為n(在進行函式呼叫時,這個序號將 發揮其作用);
(3).def 檔案中的注釋由每個注釋行開始處的分號 (;) 指定,且注釋不能與語句共享一行。
由此可以看出,例子中lib.def檔案的含義為生成名為「dlltest」的動態鏈結庫,匯出其中的add函式,並指定add函式的序號為1。
4.3 dll的呼叫方式
在4.1節的例子中我們看到了由「loadlibrary- getprocaddress-freelibrary」系統api提供的三位一體「dll載入-dll函式位址獲取-dll釋放」方式,這種呼叫方式稱 為dll的動態呼叫。
動態呼叫方式的特點是完全由程式設計者用 api 函式載入和解除安裝 dll,程式設計師可以決定 dll 檔案何時載入或不載入,顯式鏈結在執行時決定載入哪個 dll 檔案。
與動態呼叫方式相對應的就是靜態呼叫方式,「有動必有靜」,這 **於物質世界的對立統一。「動與靜」,其對立與統一竟無數次在技術領域裡得到驗證,譬如靜態ip與dhcp、靜態路由與動態路由等。從前文我們已經知 道,庫也分為靜態庫與動態庫dll,而想不到,深入到dll內部,其呼叫方式也分為靜態與動態。「動與靜」,無處不在。《周易》已認識到有動必有靜的動靜 平衡觀,《易.繫辭》曰:「動靜有常,剛柔斷矣」。哲學意味著一種普遍的真理,因此,我們經常可以在枯燥的技術領域看到哲學的影子。
靜態呼叫方式的特點是由編譯系統完成對dll的載入和應用程式結束時 dll 的解除安裝。當呼叫某dll的應用程式結束時,若系統中還有其它程式使用該 dll,則windows對dll的應用記錄減1,直到所有使用該dll的程式都結束時才釋放它。靜態呼叫方式簡單實用,但不如動態呼叫方式靈活。
附件),將編譯dlltest工程所生成的.lib和.dll檔案拷入dllcall工程所在 的路徑,dllcall執行下列**:
#pragma comment(lib,"dlltest.lib")
//.lib檔案中僅僅 是關於其對應dll檔案中函式的重定位資訊
extern "c" __declspec(dllimport) add(int x,int y);
int main(int argc, char* argv)
由上述**可以看出,靜態呼叫方式的順利進行需要完成兩個動作:
(1)告訴編譯器與dll相對應的.lib文 件所在的路徑及檔名,#pragma comment(lib,"dlltest.lib")就是起這個作用。
程式設計師在建立乙個 dll檔案時,聯結器會自動為其生成乙個對應的.lib檔案,該檔案包含了dll 匯出函式的符號名及序號(並不含有實際的**)。在應用程式裡,.lib檔案將作為dll的替代檔案參與編譯。
(2)宣告匯入函 數,extern "c" __declspec(dllimport) add(int x,int y)語句中的__declspec(dllimport)發揮這個作用。
靜態呼叫方式不再需要使用系統api來載入、解除安裝dll以及 獲取dll中匯出函式的位址。這是因為,當程式設計師通過靜態鏈結方式編譯生成應用程式時,應用程式中呼叫的與.lib檔案中匯出符號相匹配的函式符號將進入 到生成的exe 檔案中,.lib檔案中所包含的與之對應的dll檔案的檔名也被編譯器儲存在 exe檔案內部。當應用程式執行過程中需要載入dll檔案時,windows將根據這些資訊發現並載入dll,然後通過符號名實現對dll 函式的動態鏈結。這樣,exe將能直接通過函式名呼叫dll的輸出函式,就象呼叫程式內部的其他函式一樣。
4.4 dllmain函式
windows在載入dll的時候,需要乙個入口函式,就如同控制台或dos程式需要main 函式、win32程式需要winmain函式一樣。在前面的例子中,dll並沒有提供dllmain函式,應用工程也能成功引用dll,這是因為 windows在找不到dllmain的時候,系統會從其它執行庫中引入乙個不做任何操作的預設dllmain函式版本,並不意味著dll可以放棄 dllmain函式。
根據編寫規範,windows必須查詢並執行dll裡的dllmain函式作為載入dll的依據,它使得dll 得以保留在記憶體裡。這個函式並不屬於匯出函式,而是dll的內部函式。這意味著不能直接在應用工程中引用dllmain函式,dllmain是自動被呼叫 的。
附件)。
bool apientry dllmain( handle hmodule,
dword ul_reason_for_call,
lpvoid lpreserved )
return true; }
dllmain函式在dll被載入和解除安裝時被呼叫,在單個執行緒啟動和終止時,dllmain函式也被調 用,ul_reason_for_call指明了被呼叫的原因。原因共有4種,即process_attach、process_detach、 thread_attach和thread_detach,以switch語句列出。
來仔細解讀一下dllmain的函式頭bool apientry dllmain( handle hmodule, word ul_reason_for_call, lpvoid lpreserved )。
apientry被定義為__stdcall,它意味著這個函式以標準pascal的方式進行呼叫,也就 是winapi方式;
程序中的每個dll模組被全域性唯一的32位元組的hinstance控制代碼標識,只有在特定的程序內部有效,控制代碼代 表了dll模組在程序虛擬空間中的起始位址。在win32中,hinstance和hmodule的值是相同的,這兩種型別可以替換使用,這就是函式引數 hmodule的來歷。
執行下列**:
hdll = loadlibrary("..//debug//dlltest.dll");
if (hdll != null)
freelibrary(hdll); }
我們看到輸出順序為:
process attach of dll
call add in dll:5
process detach of dll
這一輸出順序驗證了dllmain被呼叫 的時機。
**中的getprocaddress ( hdll, makeintresource ( 1 ) )值得留意,它直接通過.def檔案中為add函式指定的順序號訪問add函式,具體體現在makeintresource ( 1 ),makeintresource是乙個通過序號獲取函式名的巨集,定義為(節選自winuser.h):
#define makeintresourcea(i) (lpstr)((dword)((word)(i)))
#define makeintresourcew(i) (lpwstr)((dword)((word)(i)))
#ifdef unicode
#define makeintresource makeintresourcew
#else
#define makeintresource makeintresourcea
VC 動態鏈結庫 DLL 程式設計深入淺出 一
1.概論 先來闡述一下dll dynamic linkable library 的概念,你可以簡單的把dll看成一種倉庫,它提供給你一些可以直接拿來用的變數 函式或類。在倉庫的發展史上經歷了 無庫 靜態鏈結庫 動態鏈結庫 的時代。2.靜態鏈結庫 對靜態鏈結庫的講解不是本文的重點,但是在具體講解dll...
VC 動態鏈結庫 DLL 程式設計深入淺出 一
1.概論 先來闡述一下dll dynamic linkable library 的概念,你可以簡單的把dll看成一種倉庫,它提供給你一些可以直接拿來用的變數 函式或類。在倉庫的發展史上經歷了 無庫 靜態鏈結庫 動態鏈結庫 的時代。圖1 建立乙個靜態鏈結庫 並新建lib.h和lib.cpp兩個檔案,l...
VC 動態鏈結庫 DLL 程式設計深入淺出 三
4.5 stdcall約定 如果通過vc 編寫的dll欲被其他語言編寫的程式呼叫,應將函式的呼叫方式宣告為 stdcall方式,winapi都採用這種方式,而c c 預設的呼叫方式卻為 cdecl。stdcall方式與 cdecl對函式名最終生成符號的方式不同。若採用c編譯方式 在c 中需將函式宣告...