【问题标题】:Interfacing with C/C++ routine in C# causes rip在 C# 中与 C/C++ 例程交互会导致 rip
【发布时间】:2012-10-21 01:21:08
【问题描述】:

我有一个供应商的 API 调用,我想在我的 C#Windows Forms 应用程序中使用它。问题是,无论我尝试什么P/Invoke,我都会在线路上崩溃,说明调用使堆栈不平衡。我尝试与供应商合作,但他们没有 Visual Studio 2012,也没有任何 C# 或 .NET 的经验,经过几次来回的电子邮件,他们基本上解决了这个问题。

这是他们的官方 C/C++ 声明

const char *<dll name here>(int Param1,
                            const char *Param2,
                            const char *Param3,
                            unsigned long Param4,
                            unsigned short Param5,
                            unsigned short Param6,
                            unsigned short Param7,
                            unsigned short Param8,
                            unsigned short Param9);

这是我最近尝试的 P/Invoke DLLImport 行。

[System.Runtime.InteropServices.DllImport(
    "<dll name here>", EntryPoint = "<AnsiFunctionName>", ExactSpelling = true,
    CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
public static extern String <AnsiFunctionName>(int Param1,
                                               String Param2,
                                               String Param3,
                                               ulong Param4,
                                               Int16 Param5,
                                               Int16 Param6,
                                               Int16 Param7,
                                               Int16 Param8,
                                               Int16 Param9);

这是实际的错误信息:

主题行:检测到 PInvokeStackImbalance

对 P/Invoke 函数 '' 的调用使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 P/Invoke 签名的调用约定和参数是否与目标非托管签名匹配。

我尝试了各种方法,甚至设置了一个 void 返回类型。我尝试不使用返回类型。我尝试代替 Strings、StringBuilders 和 IntPtrs。不管我怎么尝试,都没有效果。

我在阅读 StackOverflow 上的一篇文章后向供应商提到,返回字符串是危险的,永远不应该这样做。他们的回答是:

"该页面上提到的字符串返回问题不适用于 我们的职能。它们返回一个指向静态分配字符串的指针,因此您不需要释放它(也不能尝试这样做)。看起来“UnmanagedType.LPStr”应该可以工作(尽管 IntPtr 也应该可以工作,只要进行适当的转换或转换)。

更新 #1:正如评论中提到的,在这篇文章之前,我使用的是 uint,而不是 ulong。我只是认为我让比较看起来更好。 uint 和 ulong 都不起作用。

更新 #2:也许给出实际的函数名称会有所帮助。它是犰狳 CreateCodeShort3A。展望未来,我使用的是纯 .NET,但我仍然需要与之交互,只是目前还不太可能。不仅如此,这个问题让我很好奇为什么我不能解决像 P/Invoke 这样看似简单的事情。毕竟,有多少种变化? (不是一个严肃的问题)。

【问题讨论】:

  • 这可能不是主要问题,但 C++ 中的“unsigned long”通常等同于 C# 中的“uint”,而不是“ulong”。 (类似地,C++ 中的“long”通常是 C# 中的“int”)。另一方面,“unsigned long long”相当于 C# 中的“ulong”。
  • 在这篇文章之前,我使用的是 uint,而不是 ulong,只是为了写这个问题,我把它改成了 ulong,这也失败了。我尝试了无数次,或者至少,相当多的排列,到目前为止没有任何效果。
  • 您忘记了调用约定 - 您需要将 CallingConvention = CallingConvention.Cdecl 作为 DllImport 的一部分,否则它使用 StdCall 调用约定,这是不正确的并导致此错误

标签: c# dll pinvoke dllimport interopservices


【解决方案1】:

以下是我可以看到的可能性:

  1. 返回值不应为String。使用IntPtr 并使用Marshal.PtrToStringAnsi 转换为字符串。
  2. C++ 类型unsigned long 应与uint 匹配。您在 C# 中使用了 ulong,它是 64 位类型。在 Windows 上的 C++ 中,unsigned long 是无符号的 32 位类型。
  3. C++ 类型unsigned short 应与ushort 匹配。
  4. C++ DLL 使用的调用约定可能是cdecl。将CallingConvention = CallingConvention.Cdecl 添加到您的P/Invoke
  5. 不要将SetLastError 用于此类功能。这与通过GetLastError 返回错误条件的 Win32 API 函数一起使用。这个 DLL 文件几乎肯定不会。

所以 P/Invoke 应该如下所示:

[DllImport(@"mydll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr FunctionName(
    int Param1, 
    string Param2, 
    string Param3, 
    uint Param4, 
    ushort Param5, 
    ushort Param6, 
    ushort Param7, 
    ushort Param8, 
    ushort Param9
);

【讨论】:

  • 问题是缺少 CallingConvention=CallingConvention.Cdecl。这几天我一直在撞墙。我还发现该函数必须返回一个 IntPtr 但可以接受一个字符串作为参数。效果很好,我是对的。解决方案是简单而愚蠢的,我只是没有看到。感谢 Petesh 和 David。我还实现了 David 的其他 cmets,效果也很好。我不知道/意识到 SetLastError 是针对 Win32 API 的。我不确定 UInt16 和 ushort 之间的区别,但我也做到了。谢谢。
  • UInt16ushort 之间没有区别,但在你的问题中你有Int16
  • CallingConvention.StdCall 更改为 CallingConvention.Cdecl 对我有用
猜你喜欢
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 1970-01-01
  • 2011-09-25
  • 2012-02-07
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
相关资源
最近更新 更多