【问题标题】:Cannot marshal a struct that contains a union无法封送包含联合的结构
【发布时间】:2010-12-31 13:07:47
【问题描述】:

我有一个如下所示的 C++ 结构:

struct unmanagedstruct
{
    int             flags;
    union
    {
        int             offset[6];
        struct
        {
            float           pos[3];
            float           q[4];
        } posedesc;
    } u;
};

我正试图在 C# 中像这样编组它:

[StructLayout(LayoutKind.Explicit)]
public class managedstruct {
    [FieldOffset(0)]
    public int flags;

    [FieldOffset(4), MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6)]
    public int[] offset;

    [StructLayout(LayoutKind.Explicit)]
    public struct posedesc {
        [FieldOffset(0), MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3)]
        public float[] pos;

        [FieldOffset(12), MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
        public float[] q;
    }

    [FieldOffset(4)]
    public posedesc pose;
}

但是,当我尝试将数据加载到我的结构中时,只有 offset 数组的前 3 个元素存在(数组的长度为 3)。我可以确认它们的值是正确的——但我仍然需要其他 3 个元素。我是不是做错了什么?

我正在使用这些函数来加载结构:

private static IntPtr addOffset(IntPtr baseAddress, int byteOffset) {
    switch (IntPtr.Size) {
        case 4:
            return new IntPtr(baseAddress.ToInt32() + byteOffset);
        case 8:
            return new IntPtr(baseAddress.ToInt64() + byteOffset);
        default:
            throw new NotImplementedException();
    }
}

public static T loadStructData<T>(byte[] data, int byteOffset) {
    GCHandle pinnedData = GCHandle.Alloc(data, GCHandleType.Pinned);
    T output = (T)Marshal.PtrToStructure(addOffset(pinnedData.AddrOfPinnedObject(), byteOffset), typeof(T));
    pinnedData.Free();
    return output;
}

加载示例:

managedstruct mystruct = loadStructData<managedstruct>(buffer, 9000);

如果您需要更多信息,请告诉我。

【问题讨论】:

  • 编组代码应该如何知道何时将联合编组为 int[]float[] ?它应该为每个数组元素选择,还是总是一个或另一个?

标签: c# c++ struct marshalling unions


【解决方案1】:

我对此不是 100% 确定,但我相信联盟意味着两个成员使用相同的内存。在 C++ 结构的情况下,一个 int[] 或一个 posedesc 结构。所以结构的大小将是 sizeof(int) + sizeof(posedisc)。意思是,Union 并不意味着您将同时拥有 int[] 和posedisc,您将拥有共享内存,在 C++ 领域中可以是这两种类型中的任何一种,但在托管领域中只能是其中一种。

所以我认为您可能需要两个托管结构,一个具有偏移量,一个具有posedisc。您可以在调用 LoadStruct 时选择其中一个。或者,您可以创建一个 byte[] 字段并具有将这些字节转换为所需类型的计算属性。

【讨论】:

    【解决方案2】:

    问题的一个可能原因是,在 C++ 中,int 的大小取决于架构。

    换句话说,在某些情况下,您在 C++ 结构中的 offset 数组实际上可能是一个 64 位值的数组。

    现在,在您的 C# 结构中,无论何时使用 MarshalAs 属性,您都无需指定 ArraySubType 参数。

    根据文档,当您省略 ArraySubType 时,该结构应该根据托管数组的类型进行编组,但可能是因为 C++ 结构中的 offset 数组没有在 48 范围内传播字节而不是 24,导致您遇到的问题。

    我的建议是尝试将 C++ 结构中的所有 int 更改为 long 以确保大小始终相同,并在数组的所有 MarshalAs 属性中添加 ArraySubType 参数。

    【讨论】:

    • long 的大小也取决于架构。我建议int32_tstdint.h 中的任何其他固定大小类型。
    【解决方案3】:

    聚会有点晚了,但我最好的猜测是,当编组器到达现场时,您的 offset 数组会被 pos 数组覆盖,而 reference 将被覆盖,而不是实际的元素。因此,offset 的长度始终为 3(尝试GetType,它应该返回Single[]。这就是重叠引用所得到的。)。

    因此,您可以删除q 并将pos' 的大小设置为7,或者您可以使用fixed 数组(虽然我不确定它会如何编组)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-13
      • 2019-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多