I’m trying to send a network packet of data to a hardware device. I would like to use a slick, object oriented approach so I can work with it at a high level. The packet has several fields of variable length. Obviously the byte layout is very particular.
Here’s a struct representing the packet I need to send:
[StructLayout(LayoutKind.Sequential)]
public struct Packet
{
public UInt16 Instruction;
public UInt16 Length; //length of data field
public UInt32 SessionHandle;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] SenderContext;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] MessageData;
}
MessageData[] can be any length, I’m fixing it to 8 bytes as a starter.
Here’s my attempt at creating a byte[] to send it via Socket:
public static byte[] ToBytes(Packet ep)
{
Byte[] bytes = new Byte[Marshal.SizeOf(typeof(Packet))];
GCHandle pinStructure = GCHandle.Alloc(ep, GCHandleType.Pinned);
try
{
Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
return bytes;
}
finally
{
pinStructure.Free();
}
}
But I receive:
ArgumentException : Object contains non-primitive or non-blittable data.
I thought setting SizeConst in the struct would take care of this? Anyways, I’m more lost than this because the hardware device is expecting a variable length packet, and I’d like to take advantage of that.
I can manually put together the packet byte-by-byte and everything works great. But I know there has got to be a better way, and that I must be going down the wrong path.
Any ideas?
The CLR will not allow you to use a struct that has a field that overlaps a reference type. The two arrays in your case. It is quite incompatible with the garbage collector, it cannot keep track of the object reference. And would be quite unsafe as it allows a backdoor into the heap, manipulating the value of the object reference directly.
There’s no point in doing so in your specific case as both arrays overlap and are exactly the same size. One will get the job done just fine.