【问题标题】:Mapping IntPtr into Struct via Marshal.PtrToStructure causing "Access Violation Exception"通过 Marshal.PtrToStructure 将 IntPtr 映射到 Struct 导致“访问冲突异常”
【发布时间】:2011-08-23 10:03:49
【问题描述】:

我正在尝试在 .NET 中使用 C++ DLL(实现 EMI 协议的第三方库,具有可用的源代码)。我已成功完成编组、调用函数并让一切正常。

当我想将 IntPtr 编组回 .NET Struct 时,会出现问题,这里是代码(按照建议修改 - 删除了“ref”并更改了 AllocHGlobal 以仅分配 emiStruct 的大小):

private EMI emiStruct;
private IntPtr emiIntPtr;

emiIntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(emiStruct));
Marshal.StructureToPtr(emiStruct, emiIntPtr, false);
EMIStruct.Error result = emi_init(emiIntPtr, hostname, portNumber, password, shortNumber, windowSize, throughput);
Marshal.PtrToStructure(emiIntPtr, emiStruct);

最后一行 (PtrToStructure) 导致异常“尝试读取或写入受保护的内存。这通常表明其他内存已损坏”。

另外,我可以看到调试输出:

A first chance exception of type 'System.AccessViolationException' occurred in mscorlib.dll
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0xc3fffff8.
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0x01fffff7.
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0x00001f1d.

我认为问题出在指针 emiIntPtr 的内存分配中。尽管如此,当我运行代码时,连接到服务器时出现问题(例如,找不到服务器),随后对 Struct emiStruct 的编组正确完成(没有例外)。只有在成功建立连接并且服务器发送响应时才会出现此问题。

另外,我使用我试图在 .NET 中使用的同一个 DLL 库编写了一个 C++ 示例应用程序,并且这个应用程序(当我编译它时)运行得很好——这意味着,C++ DLL 应该没问题而不是导致崩溃。

此外,我发现了一些提示来检查/取消选中项目编译器的几个属性(使用 JIT,为 x86 cpu 编译它等),不幸的是,这些都没有帮助。

您对问题可能出在哪里或如何在 .NET 中进行正确的 IntPtr 初始化以及 IntPtr 和 Struct 之间的映射有什么建议?

感谢大家的回复:

这里我添加了 emi_init 函数的 C++ 标头:

FUNC( init)( EMI*           emi,         /* out */
const char*    hostname,    /* in  */
unsigned short port,        /* in  */
const char*    password,    /* in  */
const char*    origin_addr, /* in  */
int            window_sz,   /* in  */
         int            throughput); /* in  */

这里是 C# emi_init 声明(我已经按照建议删除了 emiPtr 的“ref”属性):

[System.Runtime.InteropServices.DllImport("emi.dll", EntryPoint = "_emi_init")]
    public static extern EMIStruct.Error emi_init(
    System.IntPtr emiPtr,
    [System.Runtime.InteropServices.InAttribute()]  [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string hostname,
    ushort port,
    [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string password,
    [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string origin_addr,
    int window_sz, int throughput);

但是,仍然遇到同样的异常。

【问题讨论】:

  • 你为什么要通过 ref 传递 ptr?这似乎很奇怪。很高兴看到 c++ 标头。
  • @David Heffernan:有趣的地方:)
  • @frankie 为什么不向我们展示界面的 C++ 端?

标签: c# exception marshalling access-violation intptr


【解决方案1】:

您错误地使用了Marshal.PtrToStructure

第二个参数需要一个类型,iow typeof(EMI)

返回值包含结果结构。

所以解决方案应该是:

var s = (EMI) Marshal.PtrToStructure(emiIntPtr, typeof(EMI));

【讨论】:

  • 我以前也有过这种情况,但没有成功。我刚刚使用您建议的语法尝试过,但仍然遇到相同的异常:(
  • 尝试只分配一个“实例”而不是 100000 的范围。
  • @leppie 有两个重载,frankie 正在使用另一个。
  • @David Heffernan:您不能在该重载上使用装箱值类型,请参阅文档。但是,您可以为该重载使用顺序或固定布局引用类型。
  • @David Heffernan:这是我几个月来听到的最天真的事情!
【解决方案2】:

我的猜测是您在 C# 中错误地声明了第一个参数。您已将其声明为 ref IntPtr,相当于 C++ 中的 EMI**。但我敢打赌,很遗憾您没有包含的 C++ 声明是 EMI*。因此,只需删除 ref,一切都会好起来的。

我希望 emi_init 不会从 EMI 参数中读取,即它没有语义。在这种情况下,您不需要在调用 emi_init 之前调用 StructureToPtr。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多