一文了解【Windows编程】常见关键字和概念


工作了三年,都是在Linux化境下做开发,最近开始接触Windows环境开发,常常被一些Windows编程的关键字搞得很懵,于是决定,好好的总结一下平时Windows环境编程遇到的一些概念。本文会持续更新,争取将最常用的Windows编程的概念整理清楚。

1. 调用约定

含义

函数的调用约定,顾名思义就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的。它决定以下内容:

  • 函数参数的压栈顺序
  • 由调用者还是被调用者把参数弹出栈
  • 以及产生函数修饰名的方法

常见的函数定义

int function(int a, int b);

这种书写方式,没有显式的说明调用约定,编译器会默人我们使用__cdecl约定。

另一种函数定义方式

int __stdecall function(int a, int b);

这种定义方式显式的指出了函数调用约定的规则是__stdcall

接下来,具体分析一下这几种调用规则的含义:

类型

常见的调用约定有__cdecl、__stdcall、fastcall

cdecl

__cdecl 是 C Declaration 的缩写,表示 C 和 C++ 默认的函数调用约定。是C/C++和MFCX的默认调用约定。

  • 按从右至左的顺序压参数入栈。
  • 由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的,返回值在EAX中。因此对于像printf这样可变参数的函数必须用这种约定。
  • 编译器在编译的时候对这种调用规则的函数生成修饰名的时候,在输出函数名前加上一个下划线前缀,格式为_function。如函数 int add(int a, int b)的修饰名是_add。

stdcall

__stdcall是Standard Call的缩写,是C++的标准调用方式,当然这是微软定义的标准,__stdcall通常用于Win32 API中(可查看WINAPI的定义)。

  • 按从右至左的顺序压参数入栈。
  • 由被调用者把参数弹出栈。函数自己在退出时清空堆栈,返回值在EAX中。
  • __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_function@number。如函数int sub(int a, int b)的修饰名是_sub@8。

fastcall

__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的。

  • 实际上__fastcall用ECX和EDX传送前两个DWORD或更小的参数,剩下的参数仍自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈。
  • __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@number,如double multi(double a, double b)的修饰名是@multi@16。
  • __fastcall和__stdcall很象,**唯一差别就是头两个参数通过寄存器传送。**注意通过寄存器传送的两个参数是从左向右的,即第1个参数进ECX,第2个进EDX,其他参数是从右向左的入栈,返回仍然通过EAX。

thiscall

__thiscall是C++类成员函数缺省的调用约定,但它没有显示的声明形式。因为在C++类中,成员函数调用还有一个this指针参数,因此必须特殊处理,thiscall调用约定的特点:

  • 参数入栈:参数从右向左入栈
  • this指针入栈:如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入栈。
  • 栈恢复:对参数个数不定的,调用者清理栈,否则函数自己清理栈。

总结

几种调用约定的比较

项目__cdecl__stdcall__fastcall
参数传递方式右->左右->左左边开始的两个不大于4字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数自右向左压栈传送
清理栈方调用者清理被调用函数清理被调用函数清理
适用场合C/C++、MFC的默认方式; 可变参数的时候使用;Win API要求速度快,适用于对性能要求较高的场合。
C编译修饰约定_functionname_functionname@number@functionname@number

参考资料

https://blog.csdn.net/luoweifu/article/details/52425733

2. dllimport 和 dllexport

__declspec是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。

dllexport

__declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。

.def文件(模块定义文件)是包含一个或多个描述各种DLL属性的Module语句的文本文件。.def文件或__declspec(dllexport)都是将公共符号导入到应用程序或从DLL导出函数。如果不提供__declspec(dllexport)导出DLL函数,则DLL需要提供.def文件。

声明一个导出函数,是说这个函数要从本DLL导出。我要给别人用。一般用于dll中
省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类

dllimport

__declspec(dllimport)用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。

声明一个导入函数,是说这个函数是从别的DLL导入。我要用。一般用于使用某个dll的exe中
不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

比较

关键字含义
dllexport将当前动态库的接口导出,供别的程序使用
dllimport将别的动态库的接口导入到当前程序,供当前使用

用法

#define LIBSCRENDER_API __declspec (dllexport)

LIBSCRENDER_API int __stdcall sum(int a, int b)
{
	return a + b;
}

参考资料

https://docs.microsoft.com/zh-cn/cpp/cpp/dllexport-dllimport?view=vs-2019

持续更新……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值