MarshalAs嵌套结构
我有两个C ++结构,我必须在从C#调用DLL方法时作为参数发送。
例如,让我们将它们定义为:
struct A { int data; } struct B { int MoreData; A * SomeData; }
我需要从C#调用的方法具有以下签名:
int operation (B * data);
(请注意,我无法控制这些C ++结构和方法。)
在C#中,我将这些结构定义为类:
[StructLayout(LayoutKind.Sequential)] class A { public int data; } [StructLayout(LayoutKind.Sequential)] class B { public int MoreData; [MarshalAs(UnmanagedType.Struct)] public A SomeData; }
我创建了一个“调试dll”来从C#调用,以确保在C ++方法中正确接收所有数据。 到目前为止,只有嵌套结构指针之前的数据才能正确发送。
当我尝试从嵌套结构中读取数据(B-> A-> data)时,我收到读取违规错误(AccessViolationException)。
如何编组嵌套结构,以便能够在C ++方法中读取它?
您的C#声明不等效,它会内联生成SomeData字段。 换句话说,等效的原生声明将是:
struct B { int MoreData; A SomeData; }
P / Invoke marshaller无法处理指针成员。 这是一个内存管理问题,它拥有指针并且负责删除它是非常模糊的。 要完成这项工作,你必须声明这样的结构:
struct B { public int MoreData; public IntPtr SomeData; }
并自己编组。 像这样:
var b = new B(); b.MoreData = 0x12345678; var a = new A(); a.Data = 0x789abcde; int len = Marshal.SizeOf(a); b.SomeData = Marshal.AllocCoTaskMem(len); try { Marshal.StructureToPtr(a, b.SomeData, false); someFunction(ref b); } finally { Marshal.FreeCoTaskMem(b.SomeData); }
此代码隐含的是指针由托管代码拥有,并释放内存(FreeCoTaskMem调用)。 如果本机代码复制传递的结构,那么这将是一个问题,它会在尝试取消引用指针时读取错误数据或炸弹。 不释放内存也不是一种选择,会产生不可插拔的内存泄漏。 因为本机代码不能使用free()函数来释放它。 如果您最终进入该领域,则必须使用C ++ / CLI语言编写包装器,以便可以使用CRT的malloc()函数。