2010-10-12 253 views
2

我有两个C++结构,我必须从C#调用DLL方法时作为参数发送。MarshalAs嵌套结构

例如,让我们将它们定义为:

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->数据)读取数据时,出现读取违例错误(AccessViolationException)。

如何编组嵌套结构以便我可以在C++方法中读取它?

回答

4

您的C#声明不等效,它会内联生成SomeData字段。换句话说,等效的原生声明是:

struct B 
{ 
    int MoreData; 
    A SomeData; 
} 

P/Invoke编组器不能处理指针的成员。这是一个内存管理问题,拥有指针并负责删除它是非常模糊的。为了使这项工作在所有的,你必须声明的结构是这样的:

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()函数。

+0

这来得太迟了,刚完成自己的封送。不过,这是正确的答案。不管怎样,谢谢你! – 2010-10-12 14:44:08