__Windows上的cdecl或u stdcall? 我正在开发一个Windows的C++库,它将作为DLL分发。我的目标是最大化二进制互操作性;更准确地说,我的DLL中的函数必须可以从使用多个版本的MSVC++和MinGW编译的代码中使用,而无需重新编译DLL。然而,我不知道哪种呼叫约定是最好的,或者
有时我会听到这样的语句“C调用约定是唯一一个保证相同的accross编译器”,这与诸如“”之类的语句形成了对比。这似乎并没有阻止某些库开发人员(比如)在他们分发的DLL中使用C调用约定,而没有任何明显的问题 另一方面,__Windows上的cdecl或u stdcall? 我正在开发一个Windows的C++库,它将作为DLL分发。我的目标是最大化二进制互操作性;更准确地说,我的DLL中的函数必须可以从使用多个版本的MSVC++和MinGW编译的代码中使用,而无需重新编译DLL。然而,我不知道哪种呼叫约定是最好的,或者,c++,windows,dll,calling-convention,binary-compatibility,C++,Windows,Dll,Calling Convention,Binary Compatibility,有时我会听到这样的语句“C调用约定是唯一一个保证相同的accross编译器”,这与诸如“”之类的语句形成了对比。这似乎并没有阻止某些库开发人员(比如)在他们分发的DLL中使用C调用约定,而没有任何明显的问题 另一方面,stdcall调用约定似乎定义得很好。据我所知,所有Windows编译器基本上都需要遵循它,因为它是Win32和COM使用的约定。这是基于这样的假设,即没有Win32/COM支持的Windows编译器将不会非常有用。论坛上发布的许多代码片段都将函数声明为stdcall,但我似乎找不
stdcall
调用约定似乎定义得很好。据我所知,所有Windows编译器基本上都需要遵循它,因为它是Win32和COM使用的约定。这是基于这样的假设,即没有Win32/COM支持的Windows编译器将不会非常有用。论坛上发布的许多代码片段都将函数声明为stdcall
,但我似乎找不到一篇文章能够清楚地解释原因
那里有太多相互冲突的信息,我运行的每个搜索都会给我不同的答案,这并不能真正帮助我在两者之间做出决定。我正在寻找一个清晰、详细、有争议的解释,解释为什么我应该选择一个而不是另一个(或者为什么两者是等价的)
请注意,这个问题不仅适用于“经典”函数,也适用于虚拟成员函数调用,因为大多数客户端代码将通过“接口”、纯虚拟类(如和所述的模式)与我的DLL进行接口。两种调用约定的最大区别是“\uu cdecl”将平衡堆栈的负担放在调用方的函数调用之后,这允许函数具有可变数量的参数。“_stdcall”公约在性质上是“简单的”,但在这方面不太灵活 另外,我认为托管语言默认使用stdcall约定,因此,如果使用cdecl,任何在其上使用p/Invoke的人都必须显式地声明调用约定
因此,如果所有函数签名都是静态定义的,我可能会倾向于stdcall,如果不是cdecl。我只是做了一些实际测试(用MSVC++和MinGW编译DLL和应用程序,然后混合它们)。看起来,我使用
cdecl
调用约定获得了更好的结果
更具体地说:stdcall
的问题是MSVC++会损坏DLL导出表中的名称,即使在使用extern“C”
时也是如此。例如foo
变成_foo@4
。这仅在使用\uuu declspec(dllexport)
时发生,而不是在使用DEF文件时发生;然而,在我看来,DEF文件是一个维护麻烦,我不想使用它们
MSVC++名称损坏带来两个问题:
- 在DLL上使用会变得稍微复杂一些李>
- 默认情况下,MinGW不会在修饰名称前加上未加分数(例如,MinGW将使用
而不是foo@4
),这会使链接复杂化。此外,它还带来了看到DLL的“非下划线版本”以及在野外弹出的与“下划线版本”不兼容的应用程序的风险_foo@4
cdecl
约定:MSVC++和MinGW之间的互操作性非常好,开箱即用,并且名称在DLL导出表中保持不变。它甚至适用于虚拟方法
基于这些原因,
cdecl
显然是我的赢家。就安全性而言,\uu cdecl
约定是“更安全的”,因为调用方需要释放堆栈。在\uu stdcall
库中可能发生的情况是,开发人员可能忘记了正确释放堆栈,或者攻击者可能会通过破坏DLL的堆栈(例如通过API挂钩)而注入一些代码,调用方随后不会检查这些堆栈。
我没有任何CVS安全示例表明我的直觉是正确的。“cdecl的解释有一些变化,特别是在如何返回值方面”-这大致意味着在x86上使用cdecl的不同操作系统可能会使用不同的cdecl变体,即他们都称为“cdecl”的不同ABI。Windows锁定了变体,任何在Windows上运行且不尊重Windows选择的实现都无法调用Windows cdecl函数,因此正如您所说的stdcall,它对Windows编程毫无用处,还有一些标准的C函数很难实现。调用Win32 API函数时使用了u stdcall调用约定。值得注意的是,这并不适用于64位DLL,因为通常都忽略了O.StdCalk和Ay-CCDL,因此在导出的C函数上没有名字命名。@ JTBR如何导出导出的C++函数(即,没有<代码>外)C“< /COD>”?@ ELA782C++总是有一些名称的修饰,以便支持函数重载。但这不是标准化的,因此编译器之间可能会有所不同。@jtbr对不起,我不是指mangling这个名字。我的意思是,在输出C++功能的64位DLL中,是否也忽略了Ay-StdCalk和Ay-CCDL。我相信在所有x86-64编译中都会忽略u stdcall和u cdecl。看见