通過GetProcAddress函數動態調用dll中的函數,是否必須通過extern "C"聲明匯出函數? [已結貼,結貼人:darongtou]
如題,網上搜了N多資料,一直找不到確定的答案,目前我的答案是“是”。 晚上因為一個程式,好好研究了一下。 很多資料上都只是說明“如果沒有 extern "C" 修飾,輸出函數僅僅能從 C++ 代碼中調用。” 卻並沒有明確這個調用是通過顯式調用還是隱式調用,我也一直沒有看到過有代碼是通過顯示調用沒有extern "C" 修飾的匯出函數。 MSDN上也只是說 The spelling and case of the function name pointed to by lpProcName must be identical to that in the EXPORTS statement of the source DLL's module-definition (.DEF) file. The exported names of Win32 API functions may differ from the names you use when calling these functions in your code. 下面再從理論方面進行一些分析: GetProcAddress函式宣告是: FARPROC GetProcAddress( HMODULE hModule, // handle to DLL module LPCSTR lpProcName // name of function ); C++是支援函數重載的,也就是說允許多個不同的函數可以有同樣的函數名,如果不通過extern "C"修飾,就可以輸出相同的函數名。 這樣,就和GetProcAddress函式宣告不一致了,所以推斷不能動態調用沒有extern "C"修飾的匯出函數,因為GetProcAddress函數是通過函數名來唯一確定被調用函數的地址的。 歡迎大家討論! 100 第1個回答 顯示調用必須使用extern "C"修飾符。隱式調用可以使用任何類型,但只有C++能調用沒有extern "C"修飾的匯出函數。 GetProcAddress是一種通用的擷取函數進入點的API,能被任何語言調用,所以限制一定比較多,比如它的參數一定是一個ANSI串(作業系統並未提供UNICODE版本)。
第2個回答 我的理解是這樣的:GetProcAddress實際上跟你直接調用myfunc()一樣,都是查詢Export表來得到函數地址再去調用,因此你修飾符不對就會造成找不到entry,是不行的。當然我沒試過,沒有完全的把握。
第3個回答 C函數和C++函數的名稱是不一樣的,可以使用工具來查看,比如Dependency Walker。如果你想實驗,可以根據工具看到的名稱來調用GetProcAddress試試
第4個回答 主要是就是名字的問題 有兩種例外情況可以不加extern “C”: 1。如果不是用C++編譯器而是用C編譯DLL,名字不會變,可以不加extern "C" 2。如果DLL的使用者知道是用C++編譯器編譯DLL,不加extern “C”也可以,因為他知道名字改變的規則。調用GetProcAddress,把函數名字改了就是了
第5個回答 C++編譯器和C編譯器編譯後產生的函數名不一樣。 GetProcAddress認為是cdecl的函數,而 編譯DLL的是VC++,所以要加一個extern “C"的修飾符 指明以cdecl的方式產生函數。
第6個回答 發現不用加extern "C"也是可以的,只要在調用端用修飾過的函數名即可,不能用原函數名。 例子關鍵代碼如下: ---------------------------- DLL部分: // This is an example of an exported function. DLL1_API int __cdecl fnDll1(void) { return 42; } 輸出的修飾函數名為?fnDll1@@YAHXZ DLL1_API int __cdecl fnDll1(int a) { return 42+a; } 輸出的修飾函數名為?fnDll1@@YAHH@Z ----------------------------- EXE部分: HINSTANCE hModule = LoadLibrary("dll1.dll"); ASSERT(hModule); typedef int (*fnDll1)(); fnDll1 pfnDll1 = NULL; //VERIFY(pfnDll1 = (fnDll1)::GetProcAddress(hModule, "fnDll1")); VERIFY(pfnDll1 = (fnDll1)::GetProcAddress(hModule, "?fnDll1@@YAHXZ")); ASSERT(pfnDll1() == 42); typedef int (*fnDll2)(int); fnDll2 pfnDll2 = NULL; VERIFY(pfnDll2 = (fnDll2)::GetProcAddress(hModule, "?fnDll1@@YAHH@Z")); ASSERT(pfnDll2(3) == 45); --------------------------- 這事暫時可以告一段落了,實驗還是最有力的證明。 |