請問有關 calling convention options 的問題 |
尚未結案
|
arisaka_matsuri
高階會員 ![]() ![]() ![]() ![]() 發表:25 回覆:205 積分:231 註冊:2003-10-19 發送簡訊給我 |
我有一個關於BCB函數(function)宣告時所用的修飾字的問題,請教各位大大
一般來說,BCB中函數都會加上 __fastcall 修飾字,但是最近我在寫程式時,會有寫好的函數卻沒辦法真正傳入、傳出值的問題。trace了好幾次,都是同樣的情況,比方說:
void __fastcall function_name(Graphics::TBitmap* pBMP, long index) { ... }呼叫function_name(pBitmap, 0),但是index的值卻沒有真正傳入(index的值會是一個不確定的值,但不是0),怎麼會這樣? 如果把修飾字__fastcall拿掉,或是改用__stdcall,就一切正常。於是我查了BCB的說明,有關calling convention options的部分。看是看的懂,不過為什麼會出現上述的問題呢?在__fastcall的說明中提到,__fastcall使用暫存器EAX, EDX, ECX傳遞前3個參數,除了速度快和使用上的限制,會有可能造成參數無法傳遞的情況嗎?能否解釋一下呢? |
ENIX007
高階會員 ![]() ![]() ![]() ![]() 發表:28 回覆:274 積分:185 註冊:2003-11-27 發送簡訊給我 |
arisaka_matsuri您好:
這是之前在網路上找到的文章,希望能有所幫助,由於我已經無從得知它原始
連結,因此用引言的方式節錄出來:
引言: C Builder的命名規則 除了前面提到的 __declspec編譯指令之外,在C Builder尚有几种修飾字會影響到函數的命名, 它們就是 __cdecl,__stdcall,__pascal,__fastcall四個修飾字。為了了解該修飾字對於函式命名的影響,我們可以用以下的程式來測試之:程式迷人之處,在於邏輯思考,然而卻也是惱人之處~~ 發表人 - taishyang 於 2003/12/22 18:48:08#ifndef _DLLNAME01_H_ #define _DLLNAME01_H_ #ifndef DLLNAME #define EXTERN __declspec(dllimport) #else #define EXTERN __declspec(dllexport) #endif EXTERN void DllName01(void); EXTERN void _stdcall DllName02(void); EXTERN void _cdecl DllName03(void); EXTERN void _pascal DllName04(void); EXTERN void _fastcall DllName05(void); }; #endif 以上為程式的定義,同時我們可以在 .CPP檔中撰寫相對應的空函式,然後將其編譯成DLL檔,再利用TDUMP.EXE或是VC 內的DUMPBIN.EXE來觀察其內容,由於TDUMP會將函式命名解碼,反而會使混淆原來的名稱,因此以下的輸出是由DUMPBIN.EXE得來。 函式定義 DLL內的函式名 摘要說明 void DllName01(void) @DllName01$qv 因為是CPP程式碼 void _stdcall DllName02(void) @DllName02$qqsv 所以函式名都被修 void _cdecl DllName03(void) @DllName03$qv 飾過。 void _pascal DllName04(void) @DLLNAME04$QV void _fastcall DllName05(void) @DllName05$qqrv 以上結果是否令你丈二金鋼、摸不著頭緒。這是因為我們的程式名稱若以CPP為延伸名,C Builder會以C 特有的命名方式來為函式命名,這种命名方式會在函式名稱後加上其使用參數的性質,如參數類別等。這在C 中有一個特別的名稱,叫做mangled name,這是一种為了要實作出多載函式所發出的命名規則。(注:在C 中Add(int) 和Add(double) 可以同時存在,因此必須在object code區分之)。同時這种命名方式由於各個編譯器厂商使用的方式各不相同,因此在撰寫DLL時要避免使用之。為了要避開以上問題,我們改以下列的宣告方式: #define _DLLNAME01_H_ #ifndef DLLNAME #define EXTERN __declspec(dllimport) #else #define EXTERN __declspec(dllexport) #endif extern "C" { EXTERN void DllName011(void); EXTERN void _stdcall DllName022(void); EXTERN void _cdecl DllName033(void); EXTERN void _pascal DllName044(void); EXTERN void _fastcall DllName055(void); }; #endif 其中extern "C" {櫋櫋.}; 是用來告訴編譯器使用C的命名方式,不要使用C 的mangled name。若是其中只有一個函式時,你可以直接以下列方式宣告之: extern "C" void __stdcall ShowImage(); 現在我們可以檢視除去mangled name後的函式名稱: 函式定義 DLL內的函式名 摘要說明 void DllName01(void) _DllName01 名稱加底線 void _stdcall DllName02(void) DllName02 名稱未變 void _cdecl DllName03(void) _DllName03 名稱加底線 void _pascal DllName04(void) DLLNAME04 名稱大寫 void _fastcall DllName05(void) @DllName05 名稱加@ 以上我們可得知,在未加修飾字時和使用_cdecl修飾字時的名稱是一樣的。而 _pascal修飾字所產生的函式名則和16位元的標准DLL 函式名相同(這在VC 是不被接受的),__fastcall的函式名稱則加上 @。 其中在WIN32中使用最多的是 _stdcall修飾字,這也是你要撰寫一個可以和其他語言共同使用時所使用的修飾字,其次則為 __cdecl修飾字,這是用來傳送不定參數型別的函式如printf、sprintf等使用的。其餘兩者几乎在DLL沒有机會使用。 結論:由上可知,在C Builder中撰寫DLL時必須注意以下事項: 使用 __declspec(dllimport)及 __declspec(dllexport)的標准型式。 注意C 的函式名稱編碼(mangled name)。 注意修飾字的使用。除非使用不定參數的函式,否則必使用 __stdcall修 飾字。 (4) 不要把 __declspec的使用和 __stdcall混淆了。此二者并沒有絕對的相關性。即使是程式老手都可能栽在此處,切記,切記! 怎麼樣,在看完了以上的介紹後,是否有晃然大悟的感覺。在了解以上的規則後,今後不論在撰寫或是使用DLL時遭遇連結的問題時,應該難不倒你吧!
------
程式迷人之處,在於邏輯思考,然而卻也是惱人之處~~ |
ENIX007
高階會員 ![]() ![]() ![]() ![]() 發表:28 回覆:274 積分:185 註冊:2003-11-27 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |