什么是stdcall?

什么是stdcall?,c,winapi,calling-convention,stdcall,C,Winapi,Calling Convention,Stdcall,我正在学习Win32编程,WinMain的原型如下所示: int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show ) 我对这个WINAPI标识符的用途感到困惑,并发现: #define WINAPI __stdcall 这有什么用?我对返回类型之后的内容感到困惑。\uu stdcall的作用是什么?当返回类型和函数名之间存在某种差异时,这意味着什么?\

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

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )
我对这个
WINAPI
标识符的用途感到困惑,并发现:

#define WINAPI      __stdcall

这有什么用?我对返回类型之后的内容感到困惑。
\uu stdcall
的作用是什么?当返回类型和函数名之间存在某种差异时,这意味着什么?

\uu stdcall
是函数使用的调用约定。这将告诉编译器用于设置堆栈、推送参数和获取返回值的规则

还有许多其他的调用约定,
\uuuuuu cdecl
\uuuuuu thiscall
\uuuuuuu fastcall
,以及名字奇妙的
\uuuuuu declspec(裸体)
\uu stdcall
是Win32系统调用的标准调用约定

维基百科涵盖了这一领域


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

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

以下是文档,但除非您理解第一部分,否则意义不大:

C或C++本身并不定义这些标识符。它们是编译器扩展,代表某些调用约定。这决定了参数的放置位置、顺序、被调用函数将在何处找到返回地址等等。例如,_fastcall意味着函数的参数通过寄存器传递


提供了在那里找到的不同呼叫约定的概述。

到目前为止的答案已经涵盖了详细信息,但是如果您不打算下拉到汇编,那么您需要知道的是,呼叫者和被呼叫者必须使用相同的呼叫约定,否则你会发现很难找到的bug。

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

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

void __stdcall fnname ( int, int* )
{
    ...
}

int main()
{
    CreateThread ( NULL, 0, fnname, int, int*...... )
}
在这里,fnname将参数直接推入堆栈。

请查看:


直到今天,我以前从未使用过这个。这是因为在我的代码中我使用多线程,而我使用的多线程API是windows one(_beginthreadex)

要启动线程,请执行以下操作:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);
ExecuteCommand函数必须在方法签名中使用uu stdcall关键字,以便beginthreadex调用它:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
unsigned int\uu stdcall场景::ExecuteCommand(void*命令)
{
返回系统(静态_-cast(命令));
}

而且它不能与_standard _call混淆,因为它是standard-c!如果你不知道更多的细节,你可能会认为这就是uu stdcall的要点:有一些Windows API使用u cdecl而不是u stdcall——通常是采用可变数量参数的API,比如wsprintf()。你说得对。它的命名看起来像一个CRT函数,但它是一个API。你知道如何从C#中P/调用它吗?我还没有测试过这个,但是pinvoke.net给出了这个签名:“静态外部int wsprintf([Out]StringBuilder lpOut,string lpFmt,…)”我的直觉告诉我,C#编译器不知道在这个问题上使用#U cdecl约定。请看这个问题,了解非常starnge崩溃的示例如果我没有弄错的话,那么这些调用约定控制编译器如何生成汇编代码。当与汇编代码交互时,注意调用约定以防止堆栈问题是至关重要的。这里有一个很好的表格记录了一些约定:我们可以说这会禁用某些非法优化吗?@AndrewProck在这种情况下,“虚拟”更改是可以的,但一般来说,您可以使用
s来绕过愚蠢的(并且事与愿违的)最少6个字符。