dll檔案中包含乙個匯出函式表。這些匯出函式由它們的符號名和稱為標識號的整數與外界聯絡起來。函式表中還包含了dll中函式的位址。當應用程式載入dll模組時時,它並不知道呼叫函式的實際位址,但它知道函式的符號名和標識號。動態鏈結過程在載入的dll模組時動態建立乙個函式呼叫與函式位址的對應表。如果重新編譯和重建dll檔案,並不需要修改應用程式,除非你改變了匯出函式的符號名和引數序列。
簡單的dll檔案只為應用程式提供匯出函式,比較複雜的dll檔案除了提供匯出函式以外,還呼叫其它dll檔案中的函式。這樣,乙個特殊的dll可以既有匯入函式,又有匯入函式。這並不是乙個問題,因為動態鏈結過程可以處理交叉相關的情況。
在dll**中,必須像下面這樣明確宣告匯出函式:
__declspec(dllexport) int myfunction(int n);
但也可以在模組定義(def)檔案中列出匯出函式,不過這樣做常常引起更多的麻煩。在應用程式方面,要求像下面這樣明確宣告相應的輸入函式:
__declspec(dllimport) int myfuncition(int n);
僅有匯入和匯出宣告並不能使應用程式內部的函式呼叫鏈結到相應的dll檔案上。應用程式的專案必須為鏈結程式指定所需的輸入庫(lib檔案)。而且應用程式事實上必須至少包含乙個對dll函式的呼叫。
二、與dll模組建立鏈結
應用程式匯入函式與dll檔案中的匯出函式進行鏈結有兩種方式:隱式鏈結和顯式鏈結。所謂的隱式鏈結是指在應用程式中不需指明dll檔案的實際儲存路徑,程式設計師不需關心dll檔案的實際裝載。而顯式鏈結與此相反。
採用隱式鏈結方式,程式設計師在建立乙個dll檔案時,鏈結程式會自動生成乙個與之對應的lib匯入檔案。該檔案包含了每乙個dll匯出函式的符號名和可選的標識號,但是並不含有實際的**。lib檔案作為dll的替代檔案被編譯到應用程式專案中。當程式設計師通過靜態鏈結方式編譯生成應用程式時,應用程式中的呼叫函式與lib檔案中匯出符號相匹配,這些符號或標識號進入到生成的exe檔案中。lib檔案中也包含了對應的dll檔名(但不是完全的路徑名),鏈結程式將其儲存在exe檔案內部。當應用程式執行過程中需要載入dll檔案時,windows根據這些資訊發現並載入dll,然後通過符號名或標識號實現對dll函式的動態鏈結。
顯式鏈結方式對於整合化的開發語言(例如vb)比較適合。有了顯式鏈結,程式設計師就不必再使用匯入檔案,而是直接呼叫win32 的loadlibary函式,並指定dll的路徑作為引數。loadlibary返回hinstance引數,應用程式在呼叫getprocaddress函式時使用這一引數。getprocaddress函式將符號名或標識號轉換為dll內部的位址。假設有乙個匯出如下函式的dll檔案:
extern "c" __declspec(dllexport) double squareroot(double d);
下面是應用程式對該匯出函式的顯式鏈結的例子:
typedef double(sqrtproc)(double);
hinstance hinstance;
sqrtproc* pfunction;
verify(hinstance=::loadlibrary("c://winnt//system32//mydll.dll"));
verify(pfunction=(sqrtproc*)::getprocaddress(hinstance,"squareroot"));
double d=(*pfunction)(81.0);//呼叫該dll函式
在隱式鏈結方式中,所有被應用程式呼叫的dll檔案都會在應用程式exe檔案載入時被載入在到記憶體中;但如果採用顯式鏈結方式,程式設計師可以決定dll檔案何時載入或不載入。顯式鏈結在執行時決定載入哪個dll檔案。例如,可以將乙個帶有字串資源的dll模組以英語載入,而另乙個以西班牙語載入。應用程式在使用者選擇了合適的語種後再載入與之對應的dll檔案。
三、使用符號名鏈結與標識號鏈結
在win16環境中,符號名鏈結效率較低,所有那時標識號鏈結是主要的鏈結方式。在win32環境中,符號名鏈結的效率得到了改善。microsoft現在推薦使用符號名鏈結。但在mfc庫中的dll版本仍然採用的是標識號鏈結。乙個典型的mfc程式可能會鏈結到數百個mfc dll函式上。採用標識號鏈結的應用程式的exe檔案體相對較小,因為它不必包含匯入函式的長字串符號名。
四、編寫dllmain函式
dllmain函式是dll模組的預設入口點。當windows載入dll模組時呼叫這一函式。系統首先呼叫全域性物件的建構函式,然後呼叫全域性函式dllmain。dllmain函式不僅在將dll鏈結載入到程序時被呼叫,在dll模組與程序分離時(以及其它時候)也被呼叫。下面是乙個框架dllmain函式的例子。
hinstance g_hinstance;
extern "c" int apientry dllmain(hinstance hinstance,dword dwreason,lpvoid lpreserved)
else if(dwreason=dll_process_detach)
return 1;//成功
}如果程式設計師沒有為dll模組編寫乙個dllmain函式,系統會從其它執行庫中引入乙個不做任何操作的預設dllmain函式版本。在單個執行緒啟動和終止時,dllmain函式也被呼叫。正如由dwreason引數所表明的那樣。
五、模組控制代碼
程序中的每個dll模組被全域性唯一的32位元組的hinstance控制代碼標識。程序自己還有乙個hinstance控制代碼。所有這些模組控制代碼都只有在特定的程序內部有效,它們代表了dll或exe模組在程序虛擬空間中的起始位址。在win32中,hinstance和hmodule的值是相同的,這個兩種型別可以替換使用。程序模組控制代碼幾乎總是等於0x400000,而dll模組的載入位址的預設控制代碼是0x10000000。如果程式同時使用了幾個dll模組,每乙個都會有不同的hinstance值。這是因為在建立dll檔案時指定了不同的基位址,或者是因為引導程式對dll**進行了重定位。
模組控制代碼對於載入資源特別重要。win32 的findresource函式中帶有乙個hinstance引數。exe和dll都有其自己的資源。如果應用程式需要來自於dll的資源,就將此引數指定為dll的模組控制代碼。如果需要exe檔案中包含的資源,就指定exe的模組控制代碼。
但是在使用這些控制代碼之前存在乙個問題,你怎樣得到它們呢?如果需要得到exe模組控制代碼,呼叫帶有null引數的win32函式getmodulehandle;如果需要dll模組控制代碼,就呼叫以dll檔名為引數的win32函式getmodulehandle。
六、應用程式怎樣找到dll檔案
如果應用程式使用loadlibrary顯式鏈結,那麼在這個函式的引數中可以指定dll檔案的完整路徑。如果不指定路徑,或是進行隱式鏈結,windows將遵循下面的搜尋順序來定位dll:
1. 包含exe檔案的目錄,
2. 程序的當前工作目錄,
3. windows系統目錄,
4. windows目錄,
5. 列在path環境變數中的一系列目錄。
這裡有乙個很容易發生錯誤的陷阱。如果你使用vc++進行專案開發,並且為dll模組專門建立了乙個專案,然後將生成的dll檔案拷貝到系統目錄下,從應用程式中呼叫dll模組。到目前為止,一切正常。接下來對dll模組做了一些修改後重新生成了新的dll檔案,但你忘記將新的dll檔案拷貝到系統目錄下。下一次當你執行應用程式時,它仍載入了老版本的dll檔案,這可要當心!
七、除錯dll程式
microsoft 的vc++是開發和測試dll的有效工具,只需從dll專案中執行除錯程式即可。當你第一次這樣操作時,除錯程式會向你詢問exe檔案的路徑。此後每次在除錯程式中執行dll時,除錯程式會自動載入該exe檔案。然後該exe檔案用上面的搜尋序列發現dll檔案,這意味著你必須設定path環境變數讓其包含dll檔案的磁碟路徑,或者也可以將dll檔案拷貝到搜尋序列中的目錄路徑下。
Win32 動態鏈結庫 二
1.檔案 新建 空專案 include windows.h include stdio.h pragma comment lib,debug xxp dll test.lib extern c declspec dllimport int addfun int a,int b 匯入動態鏈結庫的add...
MFC動態鏈結庫和WIN32動態鏈結庫
1 dll的構成 每個應用程式都有乙個入口函式winmain,而每個dll也有乙個入口函式dllmain。dll跟應用程式一樣都含有資源 資料段和 段。dll跟應用程式的差別主要是dll有符號輸入表和符號輸出表,以方便應用程式呼叫dll中的函式。因為dll是由應用程式載入的,所以它本身沒有虛擬的記憶...
Win32下動態鏈結庫 DLL 程式設計
主題 dll是windows最重要的組成要素,windows中的許多新功能 新特性都是通過dll來 實現的,因此掌握它 應用它是非常重要的。動態鏈結庫不僅可以作為乙個執行模組,包括函式 而且可以包含程式以外的任何資料 或資源 位圖 圖示等等 動態鏈結庫就是給應用程式提供函式或者資源。dll是一種磁碟...