在 Windows 程式設計中,WINAPI 是一個常見卻常被忽視的關鍵字。它出現在 WinMain、回呼函式與各種 API 宣告中,實際上決定了函式的呼叫慣例。本文將從巨集定義、呼叫慣例差異、名稱修飾規則等層面,完整梳理 WINAPI 及相關 C/C++ 函式呼叫機制。
在 windef.h 中定義:
#define WINAPI __stdcall
#define APIENTRY WINAPI
#define CALLBACK __stdcall
因此 WINAPI、APIENTRY、CALLBACK、PASCAL 在 x86 平台上皆為 __stdcall 的別名。
__cdecl)。WINAPI,呼叫端與被呼叫端對堆疊清理責任的認知不一致,將導致堆疊損壞。| 慣例 | 參數入堆順序 | 堆疊清理者 | 名稱修飾 | 可變參數 |
|---|---|---|---|---|
| __stdcall (WINAPI) | 右至左 | 被呼叫者 | _func@n | 不支援 |
| __cdecl (預設) | 右至左 | 呼叫者 | _func | 支援 |
| __fastcall | 前兩個 DWORD 經 ECX/EDX,其餘右至左入堆 | 被呼叫者 | @func@n | 不支援 |
| thiscall | 右至左,this 指標經 ECX 或壓堆 | 視參數固定與否而定 | C++ name mangling | 不支援 |
| naked call | 完全由程式設計師控制 | 程式設計師 | 無修飾 | 視實作而定 |
ret n 時一次彈回參數空間,適合固定參數的 API。call 後執行 add esp, n,才能支援 printf 這類可變參數函式。__stdcall:_FunctionName@ParamBytes__cdecl:_FunctionName__fastcall:@FunctionName@ParamBytes採用 ?FunctionName@@YG...@Z 形式,關鍵差異在於呼叫慣例標識:
__stdcall:@@YG__cdecl:@@YA__fastcall:@@YI參數編碼範例:H=int, PAD=char*, K=unsigned long。
int WINAPI WinMain(
HINSTANCE hInstance, // 當前執行個體控制代碼
HINSTANCE hPrevInstance, // 16 位元相容性保留,永為 NULL
LPSTR lpCmdLine, // 命令列參數字串
int nCmdShow // 視窗顯示方式 (SW_SHOWDEFAULT 等)
);
extern "C" 可抑制 C++ name mangling,配合 __stdcall 產生乾淨的 _Func@n 符號,利於動態連結。理解 WINAPI 本質是 __stdcall,並掌握各呼叫慣例的堆疊行為與名稱修飾,是撰寫穩健 Windows 程式、除錯堆疊損壞、實作跨語言介面的基石。