WINAPI和APIENTRY,C/C++函数调用的方式, 函数名字修饰规则

新博客地址: vonsdite.cn

新博客地址: vonsdite.cn

文章目录

  • 新博客地址: vonsdite.cn
  • 新博客地址: vonsdite.cn
    • 1. WINAPI 和 APIENTRY
    • 2. VC的两种调用方式
          • __stdcall
          • __cdecl
    • 3. 其它调用方式
          • __fastcall
          • __thiscall
          • naked call
    • 4. 函数名字修饰规则

1. WINAPI 和 APIENTRY

  • windef.h头文件中有如下定义
#define WINAPI      __stdcall
#define APIENTRY    WINAPI
  • 所以实际上 WINAPIAPIENTRY是一样的

2. VC的两种调用方式

  1. __stdcallPASCAL调用方式,Windows系统规定由系统调用的函数都遵守PASCAL调用方式,但VC中的函数缺省调用方式为__cdecl,所以Windows编程很多时候都需要声明调用方式
  2. __cdeclC调用方式

主要区别在于: __stdcall是被调用函数自己恢复堆栈, __cdecl是调用者恢复堆栈

__stdcall
  • __stdcall调用方式又被称为Pascal调用方式。在Microsoft C++系列的C/C++编译器中,使用PASCAL宏WINAPI宏CALLBACK宏来指定函数的调用方式为__stdcall
  • stdcall调用方式的函数声明为:
int __stdcall function(int a, int b);
  • __stdcall的调用方式意味着:
    (1) 参数从右向左依次压入堆栈
    (2) 由被调用函数自己来恢复堆栈
    (3) 函数名自动加前导下划线,后面紧跟着一个@,其后紧跟着参数的尺寸

  • 上面那个函数翻译成汇编语言将变成:
    push b 先压入第二个参数
    push a 再压入第一个参数
    call function 调用函数
    在编译时,此函数的名字被翻译为_function@8

__cdecl
  • __cdecl调用方式又称为C调用方式,是C语言缺省的调用方式,它的语法为:
int function(int a, int b) // 不加修饰符就是C调用方式
int _cdecl function(int a, int b) // 明确指定用C调用方式
  • cdecl的调用方式决定了:
    (1) 参数从右向左依次压入堆栈
    (2) 由调用者恢复堆栈
    (3) 函数名自动加前导下划线

  • 由于是由调用者来恢复堆栈,因此C调用方式允许函数的参数个数是不固定的,这是C语言的一大特色。

  • 上面那个函数被翻译为:
    push b // 先压入第二个参数
    push a // 在压入第一个参数
    call funtion // 调用函数
    add esp, 8 // 清理堆栈
    在编译时,此方式的函数被翻译成:_function

3. 其它调用方式

__fastcall
  • __fastcall ,它是一种快速调用方式。

  • 此方式的函数的第一个和第二个DWORD参数通过ecxedx传递

  • 后面的参数从右向左的顺序压入栈。

  • 被调用函数清理堆栈。

  • 函数名修饰规则同__stdcall

  • 其声明语法为:

int fastcall function(int a, int b);
__thiscall
  • __thiscall 调用方式是唯一一种不能显示指定的修饰符;
  • 它是c++类成员函数缺省的调用方式;
  • 由于成员函数调用还有一个this指针,因此必须用这种特殊的调用方式;
  • __thiscall调用方式意味着:
    • 参数从右向左压入栈。
    • 如果参数个数确定,this指针通过ecx传递给被调用者; 由被调用函数自己清理堆栈。
    • 如果参数个数不确定,this指针在所有参数压入栈后被压入栈; 由调用者清理堆栈,

可以看到,对于参数个数固定的情况,它类似于__stdcall参数个数不定时则类似于__cdecl

naked call
  • 是一种比较少见的调用方式,一般高级程序设计语言中不常见。
  • 函数的声明调用方式和实际调用方式必须一致,必然编译器会产生混乱。

4. 函数名字修饰规则

  • C编译时函数名修饰约定规则:

    • __stdcall调用:

      • 函数名前加上一个下划线前缀
      • 函数后面加上一个“@”符号和其参数的字节数,如_function@8
    • __cdecl调用

      • 仅在函数名前加上一个下划线前缀,如_function
    • __fastcall调用

      • 函数名前加上一个“@”符号
      • 函数名后面也是一个“@”符号和其参数的字节数,如@function@8
    • 注:

      • 它们均不改变输出函数名中的字符大小写
      • 这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写
  • C++编译时函数名修饰约定规则:

    • __stdcall调用
      • 以**“?”标识函数名的开始**,后跟函数名;
      • 函数名后面以**“@@YG”标识参数表的开始,后跟参数表**;
      • 参数表以代号表示:
        X–void ,
        D–char,
        E–unsigned char,
        F–short,
        H–int,
        I–unsigned int,
        J–long,
        K–unsigned long,
        M–float,
        N–double,
        _N–bool,

        PA–表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代
        表一次重复;
      • 参数表的第一项为该函数的返回值类型其后依次为参数的数据类型, 指针标识在其所指数据类型前
      • 参数表后以**“@Z”标识整个名字的结束**,如果该函数无参数,则以“Z”标识结束
      • 其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",例如
    		int Test1(char *var1,unsigned long) // ----->"?Test1@@YGHPADK@Z"
    		void Test2() // -----> "?Test2@@YGXXZ"
    
    • __cdecl调用
      • 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为**“@@YA”**。
    • __fastcall调用
      • 规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为**“@@YI”**。

VC++对函数的缺省声明是"__cdecl", 将只能被C/C++调用

你可能感兴趣的:(windows)