Win32函数调用约定(Calling Convention)

2024-09-08 09:44

本文主要是介绍Win32函数调用约定(Calling Convention),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

平常我们在C#中使用DllImportAttribute引入函数时,不指明函数调用约定(CallingConvention)这个参数,也可以正常调用。如FindWindow函数

[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

在DllImportAttribute类中, CallingConvention属性可以用于指明函数调用约定,默认值是Winapi(__stdcall),而Windows API函数默认的调用约定是Winapi(__stdcall),这也是为什么平常我们不指定CallingConvention值也能调用成功的原因。

函数调用约定

函数调用约定(Calling Convention)是指在函数调用时关于函数的多个参数入栈和出栈顺序的约定。

通俗地讲就是关于堆栈的一些说明,首先是函数参数压栈顺序,其次是压入堆栈的内容由谁来清除,调用者还是函数自己。如果函数调用约定不一致,在使用库中的导出函数时会调用异常。

下面介绍一下在Windows的库编程中的5种调用约定

__cdecl

__cdecl(_cdecl)调用约定又称为C调用约定,是C默认的调用约定,也是C++全局函数的默认调用约定,通常省略。在未指定调用约定时就默认为__cdecl,如下面两个写法是一样的

1 int sum(int num1,int num2);
2 int __cdecl sum(int num1,int num2);

在__cdecl调用约定下,函数的多个参数由调用者按从右到左的顺序压入堆栈,被调函数获得参数的序列是从左到右的,清理堆栈的工作由调用者负责,因为函数参数的个数是可变的。如果是被调函数清理堆栈,则参数个数必须确定,否则由于被调用函数事先无法知道参数的个数,事后的清除工作也将无法正常进行。

__stdcall

__stdcall(_stdcall)调用约定又称为Pascal调用约定,也是Pascal语言的调用约定,如下:

1 int __stdcall sum(int num1,int num2);

__stdcall调用约定下,函数的多个参数由调用者从右到左的顺序压入堆栈,被调函数获得参数的序列是从左到右的,清理堆栈的工作由被调用函数负责。

常用宏WINAPICALLBACK来表示 __stdcall调用约定。

1 #define CALLBACK __stdcall
2 #define WINAPI __stdcall

Windows API函数大部分是__stdcall约定,如文章最开头提到的FindWindow函数。

1 HWND
2 WINAPI
3 FindWindowA(
4     _In_opt_ LPCSTR lpClassName,
5     _In_opt_ LPCSTR lpWindowName);
6 WINUSERAPI

__fastcall调用约定

__fastcall调用约定称为快速调用约定,前两个双字(DWORD)参数或更小尺寸的参数通过寄存器ECX和EDX来传递,剩下的参数按照自右向左的顺序压栈传递,清理堆栈的工作由被调用者函数来完成。

1 int __fastcall sum(int num1,int num2);

thiscall调用约定

thiscall调用约定是C++中的非静态类成员函数的默认调用约定。thiscall只能被编译器使用,没有相应的关键字。因此不能手动指定。采用thiscall约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈。ECX寄存器传送一个额外的参数,this指针

naked call调用约定

naked call调用约定也称为裸调,是一个不大用的调用约定,不建议使用。编译器不会给这样的函数增加初台化和清理的代码。naked call不是类型修饰符,它必须和__declspec共同使用。

1 __declspec(naked) int sum(int num1,int num2);

未指定函数调用约定或函数调用约定错误的情况

如果函数调用约定错误,会出现如下这种:

Run-Time Check Failure #0- The value of ESP was not properly saved across a function call. Thie is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

我最近就遇到了这个问题,在C++动态去调用一个库中的函数,运行时就报这个错,后面给函数指针加上了__stdcall就调用成功了,类似下面这样

1 typedef void (__stdcall *FUNC)(int num1, int num2);

这篇关于Win32函数调用约定(Calling Convention)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1147771

相关文章

AutoGen Function Call 函数调用解析(一)

目录 一、AutoGen Function Call 1.1 register_for_llm 注册调用 1.2 register_for_execution 注册执行 1.3 三种注册方法 1.3.1 函数定义和注册分开 1.3.2 定义函数时注册 1.3.3  register_function 函数注册 二、实例 本文主要对 AutoGen Function Call

PHP7扩展开发之函数调用

前言 在这篇文章中我们将演示如何在扩展中调用函数,和调用对象的方法。代码示例如下: <?phpclass demo {public function get_site_name ($prefix) {return $prefix."信海龙的博客\n";}}function get_site_url ($prefix) {return $prefix."www.bo56.com\n";}

C++中类的构造函数调用顺序

当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的 构造函数,依次类推,直至到达派生类次数最多的派生次数最多的类的构造函数为止。 简而言之,对象是由“底层向上”开始构造的。因为,构造函数一开始构造时,总是 要调用它的基类的构造函数,然后才开始执行其构造函数体,调用直接基类构造函数时, 如果无专门说明,就调用直接基类的默认构造函数。在对象析构时,其顺序正好相反。

【AI大模型应用开发】2.1 Function Calling连接外部世界 - 入门与实战(1)

Function Calling是大模型连接外部世界的通道,目前出现的插件(Plugins )、OpenAI的Actions、各个大模型平台中出现的tools工具集,其实都是Function Calling的范畴。时下大火的OpenAI的GPTs,原理就是使用了Function Calling,例如联网检索、code interpreter。 本文带大家了解下Function calling,看

Ollama Qwen2 支持 Function Calling

默认 Ollama 中的 Qwen2 模型不支持 Function Calling,使用默认 Qwen2,Ollama 会报错。本文将根据官方模板对 ChatTemplate 进行改进,使得Qwen2 支持 Tools,支持函数调用。 Ollama 会检查对话模板中是否存在 Tools,如果不存在就会报错,下面的代码是 Ollama 解析模板的代码。 Ollama 3.1 是支持 Tools

控制台、win32 、mfc 、QT区别

控制台程序主要用于早期dos(disk operate system)编程。win32 在windows95系统以前,c++还未流行起来,面向c语言(面向过程)的窗口编程,c语言直接使用API进行开发,大多数API都需带入windows句柄作为参数。mfc基于win32添加了c++特性,基于面向对象编程,实际是对windows 大多API的封装库,但内容复杂混乱。包涵一个应用程序的框架,为减少

SOMEIP_ETS_089: SD_Calling_same_ports_before_and_after_suspendInterface

测试目的: 验证设备(DUT)是否能够在一个请求完成后,对相同的SOME/IP端口恢复监听和分派请求。 描述 本测试用例旨在检查DUT在执行了SuspendInterface操作后,是否仍然能够使用与之前相同的源端口和SOME/IP端口来响应方法调用。 测试拓扑: 具体步骤: TESTER:第一个TestFieldUINT8 Getter和SetterDUT:正常响应TESTER:

不同驱动模块间的函数调用

linux层的处理 函数注册:     if (ECNT_REGISTER_SUCCESS != ecnt_register_hook(&phy_api_dispatch_hook_ops) ){                                                                   panic("Register hook function

函数调用的开销

函数调用的开销‌ 函数调用时,‌会产生一定的开销,‌主要包括:‌ 1.‌栈帧创建与销毁‌:‌ 调用函数时,‌需为函数局部变量、‌参数等创建栈帧。‌函数返回时,‌栈帧被销毁,‌恢复调用前的栈状态。‌ 2.‌参数传递‌:‌ 实参值需传递给形参,‌涉及数据复制或地址传递。‌对于大型数据结构,‌复制开销可能较大。‌ 3.‌返回值处理‌:‌ 函数执行完毕后,‌需将返回值传递给调用者。‌返回值传

Win32中的字符串

ANSI字符和Unicode字符 在Visual C++中,用CHAR来表示8位ANSI字符,用WCHAR来表示16位Unicode字符(宽字符) 1 typedef char CHAR2 typedef wchar_t WCHAR 一般常用的定义如下 TypedefDefinitionCHARcharPSTR or LPSTRchar*PCSTR or LPCSTRconst ch