首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

DLL 調用約定不匹配導致 ESP 錯誤:__stdcall vs __cdecl 解析

     編輯:關於C語言

錯誤現象

調用第三方 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 錯誤。**雙方必須完全一致**。

最佳實踐

  1. 統一約定:項目規範統一使用 __stdcall (Windows) 或 __cdecl (跨平台)。
  2. 頭文件聲明:DLL 提供頭文件,宏定義導出/導入修飾符,調用方直接包含,杜絕手寫 typedef。
  3. 檢查修飾名:用 dumpbin /exports dllname.dll 或 Dependency Walker 查看導出修飾名,推斷約定(_Func@12 → stdcall 12 字節參數)。
  4. 編譯器檢查:MSVC /Gz (默認 stdcall)、/Gd (默認 cdecl) 影響未顯式指定約定的函數。

案例還原:SetSuspendState

Windows API BOOLEAN WINAPI SetSuspendState(...) 即 __stdcall。調用方 typedef 必須加 WINAPI,否則 ESP 錯誤——這正是原文外國網友遇到的問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved