一般來說,從dll匯出函式有兩種方法。一種是使用.def檔案;另一種是使用__declspec(dllexport)。
使用上面兩種方法各有優缺點。使用.def檔案就是需要額外維護,當匯出函式更改名字或者追加匯出函式。而使用__declspec(dllexport)則需要注意使用的呼叫約定,在使用c++編譯器時。
在使用c++或者在常見的windows api的宣告標頭檔案常見的winapi,而winapa就是__stdcall。在c++還有__cdecl。而__pascal和__thiscall都已忽略了。巨集pascal也用的是__stdcall。
一般來說,呼叫約定只關乎引數的壓棧和最後的清理工作。還有就是名字修飾。比如:
void func(char, short, int, double)使用__cdecl呼叫:
使用c編譯器編譯後的修飾名字為:_func。清理棧的工作由呼叫方做。
使用__stdcall和__thiscall呼叫:
使用c編譯器編譯後的修飾名字為_func@20。清理棧的工作由被調方做。
使用__fastcall呼叫:
使用c編譯器編譯後的修飾名字為@func@20。清理棧的工作由被調方做。
從上面的圖也可看見,壓棧都是從右到左,而名字修飾的方法卻不一樣。c編譯器的修飾方法相對來說比較簡單。以_func@20為例,_是追加的,func就是函式的名字,@是追加的,20為引數的位元組數。c++的修飾方法相對來說比較複雜。下圖是使用c++編譯器產生的名字,使用__declspec(dllexport)和__stdcall。
下圖是使用__cdecl編譯:
大致一看都差不多,只是在名字後的有個字母不一樣,__stdcall得是yg,而__cdecl的是ya。而這也正好指明了c++的名字修飾的規則,簡單來說就是:
・名字(可以包含命名空間)
・呼叫約定
・返回值
・引數列表
呼叫約定修飾規則:
代號呼叫約定
@@yg
__stdcall
@@ya
__cdecl
@@yi
__fastcall
型別修飾規則:
代號含義
xvoid
dchar
eunsigned char
fshort
hint
iunsigned int
jlong
kunsigned long(dword)
mfloat
ndouble
_nbool
ustruct
papointer
pbconst pointer
參數列後以」@z」標示整個名字的結束,如果該函式無引數,則以」z」標識結束。
最後一點:如果我們直接使用__declspec(dllexport)匯出函式的話,最好使用c編譯器。如果直接用c++編譯器,匯出的函式名就像上面一樣。在動態使用dll時呼叫getprocaddress使用原始函式名稱一般來說會失敗。例如:
getprocaddress(handle, "所以一般使用c編譯器,比如這樣:func
");
#ifdef __cplusplus //這時,修飾後的名字就是這樣:if used by c++ code
extern"c
" #ifdef __cplusplus
}#endif
一般匯入函式時也建議這樣:
#ifdef __cplusplus第一種寫法在效率上來說會更好點。extern"c
"#endif
參考:
DLL 中呼叫約定和名稱修飾
呼叫約定 calling convention 是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式 引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。在c 中,為了允許操作符過載和函式過載,c 編譯器往往按照某種規則改寫每乙個入口點的符號名...
DLL中呼叫約定和名稱修飾
dll中呼叫約定和名稱修飾 一 呼叫約定 calling convention 是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式 引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。在c 中,為了允許操作符過載和函式過載,c 編譯器往往按照...
函式呼叫約定與名字修飾約定
在windows下,由於很多語言支援動態鏈結庫技術,因此動態鏈結庫是一種很好的混合程式設計方法。語言對函式的約定有兩種 函式呼叫約定和名字修飾約定。不同語言預設的呼叫呼叫約定和函式的命名方式是不同的,要想不同的語言開發的動態鏈結庫能夠相互呼叫,那麼開發動態鏈結庫的語言和呼叫鏈結庫的語言的函式約定必須...