4

我有一个供应商的 API 调用,我想在我的 C# Windows 窗体应用程序中使用它。问题是,无论我尝试什么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。无论我尝试了什么,都没有任何效果。

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

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

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

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

4

1 回答 1

2

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

  1. 返回值不应该是String. 使用IntPtr并转换为字符串Marshal.PtrToStringAnsi
  2. C++ 类型unsigned long应与uint. 您ulong在 C# 中使用了 64 位类型。在 Windows 上的 C++ 中,unsigned long是无符号的 32 位类型。
  3. C++ 类型unsigned short应与ushort.
  4. C++ DLL 使用的调用约定可能是cdecl. 添加CallingConvention = CallingConvention.Cdecl到您的P/Invoke
  5. 不要SetLastError用于这样的功能。这与通过返回错误条件的 Win32 API 函数一起使用GetLastError。这个 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
);
于 2012-10-31T16:15:44.947 回答