調用第三方 DLL 導出函數時,運行期拋出:Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call。
調用約定不一致:DLL 導出函數使用 __stdcall(Callee 清理棧),調用方聲明為默認 __cdecl(Caller 清理棧),導致棧指針 ESP 失衡。
| 特性 | __cdecl (C 默認) | __stdcall (WinAPI) |
|---|---|---|
| 參數壓棧順序 | 右至左 | 右至左 |
| 棧清理責任 | **調用者** | **被調用者** (函數內 ret N) |
| 函數名修飾 | 前綴 _ | 前綴 _ + 後綴 @N (N=參數字節數) |
| 可變參數支持 | 支持 | 不支持 |
// DLL 導出(__stdcall) extern "C" __declspec(dllexport) int __stdcall myAdd(int, int); // 應用程序錯誤聲明(默認 __cdecl) typedef int (*LPADD)(int, int); // 缺少 __stdcall LPADD pAdd = (LPADD)GetProcAddress(hDll, "myAdd"); pAdd(1, 2); // ESP 錯誤
// 方式 1:顯式 __stdcall typedef int (__stdcall *LPADD)(int, int); // 方式 2:使用 WINAPI 宏(定義為 __stdcall) typedef int (WINAPI *LPADD)(int, int);
若 DLL 導出為 __cdecl,調用方聲明加 __stdcall/WINAPI 同樣會導致 ESP 錯誤。**雙方必須完全一致**。
__stdcall (Windows) 或 __cdecl (跨平台)。dumpbin /exports dllname.dll 或 Dependency Walker 查看導出修飾名,推斷約定(_Func@12 → stdcall 12 字節參數)。/Gz (默認 stdcall)、/Gd (默認 cdecl) 影響未顯式指定約定的函數。SetSuspendStateWindows API BOOLEAN WINAPI SetSuspendState(...) 即 __stdcall。調用方 typedef 必須加 WINAPI,否則 ESP 錯誤——這正是原文外國網友遇到的問題。