如何将C#字节数组转换为结构化数据?

问题描述:

我通过USB传递64字节的数据包给微控制器。在微控制器的C代码的报文具有如下结构,如何将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字节的数据包作为字节[]。该函数使用Marshal.Copy将接收到的数据复制到Byte []数组中。然后我用使用Marshal.Copy的字节数组复制到该结构,这样我可以一起工作的形式

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

和再次的C#结构是作为结构化数据,例如

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

这感觉就像我在做大量的数据的副本,以及像它可能不会实现我想要的最有效的方式。我还需要将结构化数据转换回到设备命令的字节数组。我有一个方法使用相同的过程(即使用一个结构,然后将其串行化到一个字节数组并将字节数组传递给写入函数)。

有更好的选择吗?

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

+0

创造一个像数据工会联盟的代码库,我从类工作被宣布不安全(允许与Win32 USB HID dll的交互)。所以是的,我想我可以。我只需要弄清楚不安全的含义和危害是什么。我正在避免它,因为我还没有完全理解它。 – 2010-11-25 13:56:12

如果您自己调用本机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

+0

我没有跟你100%。我需要首先检查第一个字节(命令ID),然后将数据映射到适当的结构。这与您提出的解决方案如何配合?你能举出一些简单的例子(或者指点我一个)? – 2010-11-25 13:49:23