Reputation: 920
I am passing 64 byte data packets over USB to a microcontroller. In the microcontroller C code the packets have the structure,
typedef union
{
unsigned char data[CMD_SIZE];
cmd_get_t get;
// plus more union options
} cmd_t;
with
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;
and
typedef struct
{
int16_t supply;
int16_t current[4];
} cmd_get_adc_t;
On the PC side in C# I have been provided with a function which returns the 64 byte packet as a Byte[]. The function uses Marshal.Copy to copy the received data into the Byte[] array. I then used a C# struct of the form
[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;
}
and again used Marshal.Copy to copy the byte array into the struct so that I could work with is as structured data, e.g.
COMMAND_GET_ADC cmd = (COMMAND_GET_ADC)RawDeserialize(INBuffer, 1, typeof(COMMAND_GET_ADC));
short supply = cmd.Supply;
with
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;
}
This just feels like I am making lots of copies of the data and like it might not be the most productive way of achieving what I want to. I also need to convert the structured data back to a byte array for commands to the device. I have a method which uses the same process (i.e. use a struct and then serialise it to a byte array and pass the byte array to the write function).
Are there better alternatives?
Upvotes: 7
Views: 5887
Reputation: 764
If you are calling native dll yourself you could define your DllImport-s so they return and accept COMMAND_GET_ADC directly - provided that you have structs represented correctly. The framework itself should take care of it.
If you have to use byte array mandated by usage of methods provided to you - then I don't know, I never had such a constraint. I always tried to represent my interoperability data in the same way as in native dlls and I don't remember I had major issues with that.
EDIT:
[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;
}
Basically where you have FieldOffset(6) you're creating union like data union in cmd_get_t
Upvotes: 2
Reputation: 5836
If you can use unsafe code, you can cast the byte array to a pointer to your structure using the 'fixed' keyword.
Upvotes: 2