Как преобразовать массив байтов C# в структурированные данные?

Я передаю 64 байтовые пакеты данных через USB на микроконтроллер. В коде микроконтроллера 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# мне была предоставлена функция, которая возвращает 64-байтовый пакет как байт[]. Функция использует Маршал.Копировать для копирования полученных данных в массив 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;
    }

и снова использовать Маршал.Скопируйте, чтобы скопировать массив байтов в структуру, чтобы я мог работать с структурированными данными, например

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;
}

это просто кажется, что я делаю много копий данных и как это может быть не самым продуктивным способом достижения того, что я хочу. Мне также нужно преобразовать структурированные данные обратно в массив байтов для команд на устройство. У меня есть метод, который использует тот же процесс (т. е. использует структуру, а затем сериализует ее в массив байтов и передайте массив байтов функции write).

есть ли лучшие альтернативы?

2 ответов


Если вы можете использовать небезопасный код, вы можете привести массив байтов к указателю на вашу структуру, используя ключевое слово "fixed".


Если вы сами вызываете собственную 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