【问题标题】:PInvokeStackImbalance exception when using IntPtr in .NET 4? (Works in .NET 3.5)在 .NET 4 中使用 IntPtr 时出现 PInvokeStackImbalance 异常? (适用于 .NET 3.5)
【发布时间】:2012-03-27 21:19:57
【问题描述】:

可能是一个菜鸟问题,但在过去的几个小时(或几天)里,这让我陷入了困境......

我在 .NET Framework 4.0 的代码中从 DLL 调用方法

  [DllImport("xeneth.dll")]
    public static extern ErrorCode XC_GetFrame(Int32 h, FrameType type, ulong ulFlags, IntPtr buff, uint size);

然后在这里使用它:

if (XC_GetFrame(myCam, XC_GetFrameType(myCam), 0, IntPtr.Zero, (uint)fs) != ErrorCode.E_NO_FRAME)

但是,当我在 .NET 4.0 中运行它时,我得到一个 P/INVOKE 错误,但是……在 3.5 中运行它不会触发这个错误。在我和另一位程序员检查了代码之后,我们似乎将其归结为 IntPtr 在 4.0 上的运行方式不同。

我的应用程序需要在 .NET 4.0 中运行,因为应用程序所需的一些功能仅在 4.0 中可用...

有什么我可能忽略或忘记包含的内容吗?

非常感谢任何想法!

汤姆

更新:

土著宣言:

virtual ErrCode XCamera::GetFrame(FrameType type, unsigned long ulFlags, void *buffer, unsigned int size)

错误:对 PInvoke 函数 'DLLTest!DLLTest.Form1::XC_GetFrameType' 的调用使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

【问题讨论】:

  • XC_GetFrame的原生定义是什么,你遇到的具体错误是什么?
  • 请解释您遇到了什么错误?
  • 定义:虚拟 ErrCode XCamera::GetFrame(FrameType type, unsigned long ulFlags, void * buffer, unsigned int size) [纯虚拟] 错误是一个 PINVOKE Stack Imabalance: A call to PInvoke function ' DLLTest!DLLTest.Form1::XC_GetFrame' 使堆栈失衡。
  • 您的代码在 .net 2 中已经有一个错误的导入签名,但该版本过于宽松,存在某些错误。 .net 4 更严格,暴露(而不是导致)错误。

标签: c# .net c#-4.0 c#-3.0 intptr


【解决方案1】:

造成这种情况的两个常见原因:

  1. 调用约定不匹配。您的 C# 代码使用默认值 stdcall。也许本机代码使用cdecl。尝试将CallingConvention=CallingConvention.Cdecl 添加到DllImport 属性。
  2. 参数列表不匹配(见下文)。

不要自欺欺人地认为您的代码没有问题,因为 .net 3.5 不会引发此错误。 .net 4 中的错误检测更好,这就是为什么您只看到那里的错误。但是您的代码在所有 .net 版本中都损坏了。


C++ 虚方法的原生定义是:

virtual ErrCode XCamera::GetFrame(FrameType type, unsigned long ulFlags, 
    void * buffer, unsigned int size);

看起来您将对象指针作为 pinvoke 调用中的第一个参数传递。我认为这可以工作,尽管我对导出时如何处理虚函数知之甚少,不知道这是否是一个问题。大概你已经导出了一个普通的 C 函数来实例化对象。

我看到的另一个问题是,在 Windows 上,C/C++ long 是 32 位的。 C#long 是 64 位。这意味着 ulFlags 的正确声明在您的 C# 代码中为 uint

【讨论】:

  • 向 DLLImport 属性添加了调用约定,但仍然出现相同的错误。我是否在另一个答案中正确列出了本机函数声明?此外,如果框架设置为 3.5,应用程序将正常运行,但正如您所说,代码可能会被破坏。
  • 更新了原始帖子以包含声明 :) 我假设通过调用 using PInvoke 也与: using System.Runtime.InteropServices; ?
  • 当你调用一个标有 DllImport 属性的函数时,这就是 pinvoke。
  • 将 ulflags 更新为 uint 类型,但此应用程序中从同一个 DLL 调用的其他方法仍然有效;我很确定这是 C++ 方法...
  • @Haden693 我想我夸大了。我怀疑你可以通过 pinvoke 调用 C++ 方法。除了通过必须导出的辅助函数之外,您不能实例化 C++ 对象。我猜你已经这样做了。而且我真的不知道虚拟方法如何与 pinvoke 一起使用。你?否则,您需要修复长期不匹配并正确调用约定。
【解决方案2】:

你有没有检查过这两个项目的位数?如果一个是 64 位,另一个是 32 位,则会出现错误。请注意,在最近的版本中,C# 项目的默认设置从 Any CPU 更改为 x86。这在 x86 操作系统上无关紧要,但在 x64 操作系统上会产生显着差异。

此外,您通常应该尽可能多地包含错误的文本,审查以保护私人信息很好,但错误编号和文本可能会很有帮助。此外,在使用extern 时,包含任何struct 类型的两个定义以确保它们是等价的会很有帮助。 (另一个常见问题)。

【讨论】:

  • 两个项目都在 32 位下运行,所有关联的 DLL 也在 32 位下运行...调试时收到的错误是:PInvokeStackImbalance: A call to PInvoke function 'DLLTest! DLLTest.Form1::XC_GetFrame' 使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。
  • 函数定义:virtual ErrCode XCamera::GetFrame(FrameType type, unsigned long ulFlags, void * buffer, unsigned int size) [纯虚拟]
  • 这不可能。当存在位不匹配时,您不会调用该函数。
  • @DavidHeffernan:由于没有错误消息,我不确定何时将另一个 DLL 加载到内存中。
【解决方案3】:

也许更好的问题是它以前为什么有效。您提供的声明:

virtual ErrCode XCamera::GetFrame(FrameType type, unsigned long ulFlags, void * buffer, unsigned int size)

有 .NET 类型对应于:FrameType, uint, IntPtr, uint

在 Windows 上,unsigned long 是 32 位类型,而在 C# 中 ulong 是 64 位类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 2011-11-26
    • 2010-09-10
    • 1970-01-01
    • 2011-09-07
    • 2011-01-30
    • 1970-01-01
    相关资源
    最近更新 更多