關於DLL函數名導出名字命名規則

http://blog.csdn.net/yscdaxian/article/details/5933379

使用Dependency看DLL的導出函數的名字,會發現有一些有意思的東西,這大多是和編譯DLL時候指定DLL導出函數的導出符有關係。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
當你使用extern "C"的情況下:   
  __stdcall會使導出函數名字前面加一個下劃線,後面加一個@再加上參數的字節數,比如_Fun@4就是4個字節  
  __fastcall類似__stdcall,不過前面沒有下劃線,_fastcall應該前面還有一個@,比如@LoadaDir@4 
  __cdecl則是前面僅僅有一個下劃線
如果不用extern "C"話則使用C++命名機制,涉及到C++ Name Mangling,比較複雜,編譯器之間也不太一樣。
另外,__declspec(dllexport)僅會對__cdecl進行處理,去掉前面的下劃線(對於一般全局函數來說缺省就是__cdecl),而對於其他兩種不會處理。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern "C"的作用是(防止C++編譯器的“名字破壞”特性),使編譯器按照C的方式生成函數名,C的方式實際的函數名和你寫的一樣。如果沒有這個,則按照C++的方式生成函數名,這樣實際的函數名(LoadLibrary方式GetProcAddress傳入的函數名)和你寫得函數名不一樣,這樣你用LoadLibrary、GetProcAddress這種方式調用dll就不成功。   
但是用引入庫(*.LIB)的方式調用,則編譯器自動轉換函數名,所以總是沒有問題。   
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
我們知道爲了讓DLL導出一些函數,需要在每一個將要被導出的函數前面添加標識符:_declspec(dllexport)。例如在DLL中可以導出這樣的函數(方法)
#define DLL1_API _declspec(dllexport)
DLL1_API int Add(int a,int b)
{
  return a+b;
}
現在我們解決名字改編問題,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編譯後導出函數名不會發生改變。
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
傳統的導出 DLL 函數的方法是使用模塊定義文件 (.def),Visual C++ 提供了更簡潔方便的方法,即 “__declspec(dllexport)” 關鍵字,例如:
__declspec(dllexport) int __stdcall MyExportFunction(int iTest);
但是通過查看工具我們可以發現,DLL 導出的函數名字實際上是 _MyExportFunction@4。還好,VC 提供了一個預處理指示符 “#pragma” 來指定鏈接選項,可以通過它達到我們的目的,如下:
#pragma comment(linker, "/EXPORT:MyExportFunction=_MyExportFunction@4")
這樣再看,就會發現導出的函數名字已經成爲了想要的MyExportFunction。
終於知道了,應該把函數前面的 __declspec() 修飾去掉,也就是說,只需要第二條 pragma 指令即可。而且還可以使如下形式:
#pragma comment(linker, "/EXPORT:MyExportFunction=_MyExportFunction@4,PRIVATE")
PRIVATE 的作用與其在 def 文件中的作用一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章