2

我在 C++ 中有以下内容:

struct TestA {
    int i;
    char text[512];
};

struct TestB {
    double n;
    char text[512];
};

union TestUnion {
    struct TestA a;
    struct TestB b;
};

int fnUnmanagedDLL(int which,TestUnion *);

我尝试像这样声明一个 C# 包装器:

public class WrapperClass
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct TestA
        {
            public int i;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 512)]
            public string text;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct TestB
        {
            public double n;
            [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 512)]
            public string text;
        }
        [StructLayout(LayoutKind.Explicit)]
        public struct TestUnion
        {
            [FieldOffset(0)]
            public TestA a;

            [FieldOffset(0)]
            public TestB b;

        }

        [DllImport("UnmanagedDLL.Dll")]
        public static extern int fnUnmanagedDLL(int which,ref TestUnion obj);
    }

一旦我运行一个引用 WrapperClass.TestUnion 的程序,我就会得到“无法从程序集 'UnmanagedToManaged,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null' 加载类型 'TestUnion',因为它在偏移量处包含一个对象字段0 未正确对齐或被非对象字段重叠。”。

这只是一个测试程序。我确实在另一个问题中看到了删除联合的建议。但是,我不知道DLL会提前填充哪个结构。任何人都可以就我做错了什么提供建议吗?

4

1 回答 1

0

默认编组器无法自动处理此问题。当从非托管函数返回时,它不知道如何编组结构,因为它无法知道TestUnion应该使用它的哪一部分。您必须通过更改 p/Invoke 定义以获取 IntPtr、分配缓冲区,然后手动编组为正确的类型来手动进行编组。

在下面的示例中,我假设 的返回值fnUnmanagedDLL是您如何确定它填充的结构类型。

public class WrapperClass
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct TestA
    {
        public int i;
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 512)]
        public string text;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct TestB
    {
        public double n;
        [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 512)]
        public string text;
    }

    public struct TestUnion  // Removed marshal attributes since they cause exceptions
    {
        public TestA a;
        public TestB b;
    }

    [DllImport("UnmanagedDLL.Dll")]
    private static extern int fnUnmanagedDLL(int which, IntPtr obj);

    public static int UnmanagedDLL(int which, ref TestUnion testUnion) {
       IntPtr buffer = Marshal.AllocHGlobal(
                Math.Max(Marshal.SizeOf(typeof(TestA)),
                         Marshal.SizeOf(typeof(TestB)));

       int returnType = fnUnmanagedDLL(which, buffer);
       switch (returnType) {
           case 1: // What ever fnUnmanagedDLL returns for TestA
              testUnion.a = (TestA)Marshal.PtrToStructure(buffer, typeof(TestA));
              break;
           case 2: // What ever fnUnmanagedDLL returns for TestB
              testUnion.a = (TestB)Marshal.PtrToStructure(buffer, typeof(TestB));
              break;
       }

       Marhsal.FreeHGlobal(buffer); // Need to manually free the allocated buffer

       return returnType;
    }
}
于 2012-09-12T16:34:11.673 回答