MarshalAs嵌套结构

时间:2010-10-12 12:44:13

标签: c# marshalling

我有两个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->数据),我收到读取违规错误(AccessViolationException)。

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

1 个答案:

答案 0 :(得分:5)

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