【问题标题】:How do I convert a C# byte array into structured data?如何将 C# 字节数组转换为结构化数据?
【发布时间】:2010-11-25 07:26:21
【问题描述】:

我通过 USB 将 64 字节数据包传递给微控制器。在微控制器 C 代码中,数据包具有以下结构,

typedef union
{
    unsigned char data[CMD_SIZE];
    cmd_get_t get;
    // plus more union options
} cmd_t;

typedef struct
{
    unsigned char cmd;          //!< Command ID
    unsigned char id;           //!< Packet ID
    unsigned char get_id;       //!< Get identifier
    unsigned char rfu[3];       //!< Reserved for future use
    union
    {
        unsigned char data[58];     //!< Generic data
        cmd_get_adc_t adc;          //!< ADC data
        // plus more union options
    } data;                     //!< Response data
} cmd_get_t;

typedef struct
{
    int16_t supply;
    int16_t current[4];
} cmd_get_adc_t;

在 C# 的 PC 端,我提供了一个函数,该函数将 64 字节数据包作为 Byte[] 返回。该函数使用 Marshal.Copy 将接收到的数据复制到 Byte[] 数组中。然后我使用了一个 C# 结构体

[StructLayout(LayoutKind.Sequential, Pack=1)]
    public struct COMMAND_GET_ADC
    {
        public byte CommandID;
        public byte PacketID;
        public byte GetID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
            public byte[] RFU;
        public short Supply;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
            public short[] Current;
    }

并再次使用 Marshal.Copy 将字节数组复制到结构中,以便我可以将其用作结构化数据,例如

COMMAND_GET_ADC cmd = (COMMAND_GET_ADC)RawDeserialize(INBuffer, 1, typeof(COMMAND_GET_ADC));
short supply = cmd.Supply;

public static object RawDeserialize(Byte[] rawData, int position, Type anyType)
{
    int rawsize = Marshal.SizeOf(anyType);
    if(rawsize > rawData.Length)
    {
        return null;
    }
    IntPtr buffer = Marshal.AllocHGlobal(rawsize);
    Marshal.Copy(rawData, position, buffer, rawsize);
    object retobj = Marshal.PtrToStructure(buffer, anyType);
    Marshal.FreeHGlobal(buffer);
    return retobj;
}

这感觉就像我正在制作大量数据副本,并且好像它可能不是实现我想要的最有效的方式。我还需要将结构化数据转换回字节数组以向设备发送命令。我有一个使用相同过程的方法(即使用结构,然后将其序列化为字节数组并将字节数组传递给写入函数)。

还有更好的选择吗?

【问题讨论】:

    标签: c# struct


    【解决方案1】:

    如果您可以使用不安全的代码,您可以使用 'fixed' 关键字将字节数组转换为指向您的结构的指针。

    【讨论】:

    • 在代码库中,我正在使用该类被声明为不安全(以允许与 Win32 USB HID dll 进行交互)。所以是的,我想我可以。我只需要弄清楚 unsafe 的含义和危害是什么。我一直在避免它,因为我还没有完全理解它。
    【解决方案2】:

    如果您自己调用本机 dll,您可以定义您的 DllImport-s,以便它们直接返回并接受 COMMAND_GET_ADC - 前提是您的结构正确表示。框架本身应该处理它。

    如果您必须使用提供给您的方法所要求的字节数组 - 那么我不知道,我从未有过这样的限制。我总是尝试以与原生 dll 相同的方式来表示我的互操作性数据,但我不记得我在这方面遇到过重大问题。

    编辑:

    [StructLayout(LayoutKind.Explicit)]
    public struct COMMAND_GET
    {
        [FieldOffset(0)]
        public byte CommandID;
        [FieldOffset(1)]
        public byte PacketID;
        [FieldOffset(2)]
        public byte GetID;
        [FieldOffset(3)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
        public byte[] RFU;
        [FieldOffset(6)]
        public ADC_Data Adc_data;
        [FieldOffset(6)]
        public SomeOther_Data other_data;
        [FieldOffset(6)]
        ....
    }
    
    
    [StructLayout(LayoutKind.Sequential, Pack=1)]
    public struct ADC_Data
    {
        public short Supply;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
        public short[] Current;
    }
    

    基本上你有 FieldOffset(6) 你在 cmd_get_t 中创建类似数据联合的联合

    【讨论】:

    • 我不会 100% 关注你。我需要首先检查第一个字节(命令 ID),然后将数据映射到适当的结构。这将如何适合您提出的解决方案?你能举个简单的例子吗(或者给我一个例子)?
    • 我实际上尝试过这个,但由于某种原因,当 LayoutKind 设置为显式并且我使用 MarshalAs 作为数组时,线程似乎只是崩溃了。我还没有弄清楚。谢谢你的例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    相关资源
    最近更新 更多