C# P/Invoke 调用约定不匹配问题排查
本文解释了使用 Platform Invoke (P/Invoke) 在 C# 和 C 代码之间交互时的常见问题。 我们将重点关注调用约定不匹配的问题:C# 中的 CallingConvention.Cdecl
与 C 中的 __stdcall
。
调用约定解释
在 32 位环境中,调用约定决定参数传递、堆栈存储和清理。主要约定包括:
__stdcall
: 调用者推送参数;被调用者清理堆栈。由 Windows API 和 COM 使用。__cdecl
: 调用者压入参数并清理堆栈。 C.__fastcall
: 使用寄存器来存储某些参数,可能会提高速度,但会降低兼容性。__thiscall
: 与 __cdecl
类似,但处理 C 成员函数中的“this”指针。__clrcall
: 托管调用约定平衡其他元素,防止堆栈问题。代码示例分析
考虑一个 C 函数:
<code class="language-c++">extern "C" int __stdcall InvokedFunction(int);</code>
及其 C# P/Invoke 对应项:
<code class="language-csharp">[DllImport("CPlusPlus.dll", ExactSpelling = true, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] private static extern int InvokedFunction(IntPtr intArg);</code>
大会冲突
问题源于 C# 中的 CallingConvention.Cdecl
与 C 中的 __stdcall
冲突。这会导致堆栈不平衡。虽然 __stdcall
通常是 P/Invoke 默认值(由于其 Windows API 流行),但与 __cdecl
C 代码交互需要显式约定规范。
分辨率
解决方案很简单:将 C# 调用约定与 C 调用约定对齐:
<code class="language-csharp">[DllImport("CPlusPlus.dll", ExactSpelling = true, SetLastError = true, CallingConvention = CallingConvention.Stdcall)] private static extern int InvokedFunction(int intArg); // Note: IntPtr changed to int</code>
要点
理解调用约定对于成功的跨语言互操作性至关重要。 匹配约定可防止堆栈错误并确保 C# 和 C 的顺利集成。
以上是为什么我的 C# P/Invoke 使用 `CallingConvention.Cdecl` 调用 C `__stdcall` 函数时失败?的详细内容。更多信息请关注PHP中文网其他相关文章!