【问题标题】:What is __stdcall?什么是__stdcall?
【发布时间】:2010-09-22 19:07:23
【问题描述】:

我正在学习 Win32 编程,WinMain 原型如下:

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )

我对这个 WINAPI 标识符的用途感到困惑并找到了:

#define WINAPI      __stdcall

这是做什么的?我对在返回类型之后有什么东西感到困惑。 __stdcall 是干什么用的?返回类型和函数名之间有什么意思?

【问题讨论】:

  • @AndrewProck 在这种情况下,“虚拟”更改是可以的,但通常你可以使用 s 来解决这个愚蠢的问题(并且适得其反——例如)至少 6 个字符.

标签: c winapi calling-convention stdcall


【解决方案1】:

__stdcall 是函数的调用约定。这告诉编译器适用于设置堆栈、压入参数和获取返回值的规则。

还有许多其他的调用约定,__cdecl__thiscall__fastcall 和奇妙的命名__declspec(naked)__stdcall 是 Win32 系统调用的标准调用约定。

维基百科涵盖了details

当您在代码之外调用函数(例如 OS API)或操作系统正在调用您(如 WinMain 的情况)时,这主要是重要的。如果编译器不知道正确的调用约定,那么您可能会遇到非常奇怪的崩溃,因为堆栈将无法正确管理。

【讨论】:

【解决方案2】:

直到今天我才用过这个。这是因为在我的代码中我使用的是多线程,而我使用的多线程 API 是 windows 之一(_beginthreadex)。

启动线程:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);

ExecuteCommand 函数必须在方法签名中使用 __stdcall 关键字,以便 beginthreadex 调用它:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}

【讨论】:

    【解决方案3】:

    __stdcall 用于将函数参数放入堆栈。 函数完成后,它会自动释放内存。 这用于固定参数。

    void __stdcall fnname ( int, int* )
    {
        ...
    }
    
    int main()
    {
        CreateThread ( NULL, 0, fnname, int, int*...... )
    }
    

    这里的 fnname 有 args 它直接压入堆栈。

    【讨论】:

      【解决方案4】:
      【解决方案5】:

      我同意到目前为止所有的答案都是正确的,但这就是原因。 Microsoft 的 C 和 C++ 编译器为应用程序的 C 和 C++ 函数中的(预期)函数调用速度提供了各种调用约定。在每种情况下,调用者和被调用者必须就使用哪种调用约定达成一致。现在,Windows 本身提供了函数(API),而且这些函数已经编译好了,所以当你调用它们时,你必须遵守它们。对 Windows API 的任何调用以及来自 Windows API 的回调都必须使用 __stdcall 约定。

      【讨论】:

      • 而且它不能与 _standard_call 混淆,因为它是标准 c !如果一个人不知道更好,有人可能会认为这将是 __stdcall 的重点
      • 一个小问题:有一些 Windows API 使用 __cdecl 而不是 __stdcall - 通常采用可变数量的参数,例如 wsprintf()。
      • 你是对的。它的名字看起来像一个 CRT 函数,但它是一个 API。您是否知道如何从 C# 中 P/Invoke 它?
      • 我没有测试过这个,但是 pinvoke.net 给出了这个签名:“static extern int wsprintf([Out] StringBuilder lpOut, string lpFmt, ...);”
      • 我的直觉说 C# 编译器不知道在那个编译器上使用 __cdecl 约定。
      【解决方案6】:

      到目前为止的答案已经涵盖了细节,但是如果你不打算下拉到汇编,那么你所需要知道的是调用者和被调用者必须使用相同的调用约定,否则你会获取难以找到的错误。

      【讨论】:

        【解决方案7】:

        C 或 C++ 本身不定义这些标识符。它们是编译器扩展,代表某些调用约定。这决定了将参数放在哪里,以什么顺序,被调用函数将在哪里找到返回地址,等等。例如,__fastcall 表示函数的参数通过寄存器传递。

        Wikipedia Article 概述了现有的不同调用约定。

        【讨论】:

          【解决方案8】:

          这与函数的调用方式有关 - 基本上是事物放入堆栈的顺序以及谁负责清理。

          这是文档,但除非您了解第一部分,否则它没有多大意义:
          http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

          【讨论】:

            猜你喜欢
            • 2020-02-07
            • 2010-11-21
            • 1970-01-01
            • 2013-03-27
            • 1970-01-01
            • 2015-01-02
            • 2014-08-12
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多