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

C++ WINAPI 與呼叫慣例深度解析

     編輯:C++ 入門

前言

在 Windows 程式設計中,WINAPI 是一個常見卻常被忽視的關鍵字。它出現在 WinMain、回呼函式與各種 API 宣告中,實際上決定了函式的呼叫慣例。本文將從巨集定義、呼叫慣例差異、名稱修飾規則等層面,完整梳理 WINAPI 及相關 C/C++ 函式呼叫機制。

一、WINAPI 的真實身分

在 windef.h 中定義:

#define WINAPI   __stdcall
#define APIENTRY WINAPI
#define CALLBACK __stdcall

因此 WINAPI、APIENTRY、CALLBACK、PASCAL 在 x86 平台上皆為 __stdcall 的別名。

為什麼 WinMain 必須加 WINAPI?

  • Windows 系統規定:由系統呼叫的函式必須遵循 Pascal(__stdcall)呼叫慣例。
  • MSVC 預設採用 C 呼叫慣例(__cdecl)。
  • 若不加 WINAPI,呼叫端與被呼叫端對堆疊清理責任的認知不一致,將導致堆疊損壞。

二、主流呼叫慣例比較

慣例參數入堆順序堆疊清理者名稱修飾可變參數
__stdcall (WINAPI)右至左被呼叫者_func@n不支援
__cdecl (預設)右至左呼叫者_func支援
__fastcall前兩個 DWORD 經 ECX/EDX,其餘右至左入堆被呼叫者@func@n不支援
thiscall右至左,this 指標經 ECX 或壓堆視參數固定與否而定C++ name mangling不支援
naked call完全由程式設計師控制程式設計師無修飾視實作而定

關鍵差異:誰負責清理堆疊?

  • __stdcall:被呼叫函式在 ret n 時一次彈回參數空間,適合固定參數的 API。
  • __cdecl:呼叫端在 call 後執行 add esp, n,才能支援 printf 這類可變參數函式。

三、名稱修飾規則速查

C 語言編譯

  • __stdcall:_FunctionName@ParamBytes
  • __cdecl:_FunctionName
  • __fastcall:@FunctionName@ParamBytes

C++ 編譯(含類別、多載、命名空間)

採用 ?FunctionName@@YG...@Z 形式,關鍵差異在於呼叫慣例標識:

  • __stdcall:@@YG
  • __cdecl:@@YA
  • __fastcall:@@YI

參數編碼範例:H=int, PAD=char*, K=unsigned long。

四、WinMain 參數詳解

int WINAPI WinMain(
    HINSTANCE hInstance,      // 當前執行個體控制代碼
    HINSTANCE hPrevInstance,  // 16 位元相容性保留,永為 NULL
    LPSTR     lpCmdLine,      // 命令列參數字串
    int       nCmdShow        // 視窗顯示方式 (SW_SHOWDEFAULT 等)
);

五、實務建議

  1. 撰寫 DLL 匯出函式或系統回呼時,務必加上 WINAPI / CALLBACK。
  2. 跨模組傳遞函式指標時,雙方必須對呼叫慣例達成共識,否則堆疊必壞。
  3. 使用 extern "C" 可抑制 C++ name mangling,配合 __stdcall 產生乾淨的 _Func@n 符號,利於動態連結。

結語

理解 WINAPI 本質是 __stdcall,並掌握各呼叫慣例的堆疊行為與名稱修飾,是撰寫穩健 Windows 程式、除錯堆疊損壞、實作跨語言介面的基石。

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