无法封送包含union的结构

时间:2010-01-01 00:07:09

标签: c# c++ struct marshalling unions

我有一个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;
}

然而,当我尝试将数据加载到我的结构中时,只有偏移数组的前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);

如果您需要更多信息,请与我们联系。

3 个答案:

答案 0 :(得分:1)

我对此并不是100%肯定,但我相信联盟意味着两个成员都使用相同的内存。在C ++结构的情况下,int []或posedesc结构。所以结构的大小将是sizeof(int)+ sizeof(posedisc)。意思是,Union并不意味着你将拥有一个int []和一个posedisc,你将拥有共享内存,这些内存可以是C ++土地中的那些类型,但只有一个或另一个在管理的土地上。

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

答案 1 :(得分:0)

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

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

现在,在您的C#结构中,只要您使用MarshalAs属性,就不会指定ArraySubType参数。

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

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

答案 2 :(得分:0)

有点迟到了,但我最好的猜测是,当编组人员到达该字段时,offset数组被pos数组覆盖,并且它将是引用被覆盖,而不是实际的元素。因此,offset的长度始终为3(并在其上尝试GetType,它应返回Single[]。这是重叠参考的结果。)。

因此,您可以删除q并设置pos&#39;大小为7,或者你可以使用fixed数组(虽然我不确定如何编组)。

相关问题