我們知道為了讓DLL匯出一些函數,需要在每一個將要被匯出的函數前面添加標識符:_declspec(dllexport).例如在DLL中可以匯出這樣的函數(方法)
#define DLL1_API _declspec(dllexport)
DLL1_API int Add(int a,int b)
{
return a+b;
}
如果你想查看你的DLL的匯出情況可以這樣做,在你的VC安裝目錄下的VC98\BIN目錄下有一個dumpbin.exe檔案,它就是用來查看DLL檔案資訊的,你可以在命令列下(CMD)用dumpbin -exports dllname 命令來查看DLL的匯出函數列表。
上面的列表中的Add和Substract就是匯出函數。調用時可以用extern關鍵字聲時也可以用_declspec(dllimport)標識符聲明,相比前者編譯時間,編譯器產生的程式碼效率更高一些。
現在我們解決名字改編問題,C++編譯器在產生DLL時,會對匯出的函數進行名字改編,並且不同的編譯器使用的改編規則不一樣,因此改編後的名字也是不同的。這樣,如果利用不同編譯器分別產生DLL和訪問DLL的用戶端程式,後者在訪問該DLL的匯出函數時就會出現問題。如上例中函數Add在C++編譯器改編後的名字是?Add@@YAHHH@Z。我們希望編譯後的名字不發生改變,這裡有幾種方法。
第一種是定義匯出函數時加上限定符:extern "C"
#define DLL1_API extern "C" _declspec(dllexport)
但extern "C"只解決了C和C++語方之間調用的問題,它只能用於匯出全域函數這種情況而不能匯出一個類的成員函數。另外如果匯出函數的呼叫慣例發生改變,即使使用了extern "C",編譯後的函數名還是會發生改編。比如我們加入_stdcall關鍵字說明呼叫慣例為C呼叫慣例(標準呼叫慣例,也就是WINAPI呼叫慣例)。
#define DLL1_API extern "C" _declspec(dllexport)
DLL1_API int _stdcall Add(int a,int b)
{
return a+b;
}
編譯後如
函數名Add改編成了_Add@8
第二種方法是通過一個稱為模組定義檔案DEF來解決。
LIBRARY dllname
EXPORTS
Add
Subtract
LIBRARY 用來指定動態連結程式庫內部名稱。該名稱與產生的動態連結程式庫名一定在匹配,這句代碼不是必須的。EXPORTS說明了DLL將要匯出的函數,以及為這些匯出函數指定的符號名。
可以看到,通過第二種方法模組定義檔案的方式DLL編譯後匯出函數名不會發生改變。
本文寫得比較粗略,僅記錄了筆者學習和應用中碰到的問題和解決的辦法,僅為下面幾篇博文介紹動態連結程式庫的載入和Hook編程做鋪墊。希望對各位讀者的DLL編程學習有所協助,如有疑問請跟貼留言,轉貼請註明出處。