Как преобразовать массив байтов 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