VC 動態鏈結庫 DLL 程式設計深入淺出 二

2021-05-22 17:40:47 字數 3779 閱讀 5672

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 中需將函式宣告...