通过Marshal.PtrToStructure将IntPtr映射到Struct,导致“访问冲突异常”

问题描述 投票:0回答:2

我正在尝试使用.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 inicialization以及在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);

但是,仍然得到相同的例外。

c# exception marshalling access-violation intptr
2个回答
1
投票

您正在使用Marshal.PtrToStructure错误。

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

返回值包含结果结构。

所以解决方案应该是:

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

0
投票

我的猜测是你在C#中错误地声明了第一个参数。您已将其声明为ref IntPtr,它相​​当于C ++中的EMI **。但我敢打赌C ++声明,你遗憾地没有包括,读取EMI *。所以只需删除参考,一切都应该很好。

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

© www.soinside.com 2019 - 2024. All rights reserved.