zoukankan      html  css  js  c++  java
  • Windows中的库编程(三、函数调用约定 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);

    life runs on code

    作者: zhaotianff

    转载请注明出处

  • 相关阅读:
    元素的隐藏和显示
    dateformat-参数表
    HTTP缓存控制小结
    Shell 快捷键
    PHP中的cURL库
    选择排序法
    双系统重装windows后如何恢复ubuntu启动项
    dell 3420 独立显卡黄色感叹号不能用问题
    YUY数据转换为RGB数据,并进行灰度化处理显示
    ubuntu 15.04安装显卡驱动,出现登录界面闪烁得解决方案(dell 3420 )
  • 原文地址:https://www.cnblogs.com/zhaotianff/p/15229035.html
Copyright © 2011-2022 走看看