How to Convert a Structure to a Byte Array in C#

How to convert a structure to a byte array in C#?

This is fairly easy, using marshalling.

Top of file

using System.Runtime.InteropServices

Function

byte[] getBytes(CIFSPacket str) {
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];

IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return arr;
}

And to convert it back:

CIFSPacket fromBytes(byte[] arr)
{
CIFSPacket str = new CIFSPacket();

int size = Marshal.SizeOf(str);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);

Marshal.Copy(arr, 0, ptr, size);

str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType());
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return str;
}

In your structure, you will need to put this before a string

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;

And make sure SizeConst is as big as your biggest possible string.

And you should probably read this:
http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

How to convert a structure that contains an array to a byte array?

The problem lies with wrong assumption about how structure is represented in C#

// 8 + (numDi*4) bytes
[Serializable]
public struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;
}

The assumption that size of public DiObject[] Di member is numDi * 4 is not true. In place of this field there is a pointer to the array of structures. Array is a class in .NET and is not included in place in structure declaration.

To solve this problem one can use fixed arrays. I understand that the idea behind the design is to get variable length array and it is presented in next code listing.

This code does not raise AccessViolationException during executin:

using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;

namespace StructToBytes
{
// 4 bytes
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DiObject
{
[FieldOffset(0)]
public byte Command;

[FieldOffset(1)]
public byte ErrorClass;

[FieldOffset(2)]
public byte Reserved;

[FieldOffset(3)]
public byte Flags;
}

// 8 + (numDi*4) bytes
[Serializable]
public unsafe struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public fixed byte Di[2 * 4];
}

internal unsafe class Program
{
private static byte[] GetBytes(MyPacket packet, int packetSize)
{
var data = new byte[packetSize];
var ptr = Marshal.AllocHGlobal(packetSize);

// ==== Access violation exception occurs here ====
Marshal.StructureToPtr(packet, ptr, true);

Marshal.Copy(ptr, data, 0, packetSize);
Marshal.FreeHGlobal(ptr);
return data;
}

private static MyPacket FromBytes(byte[] data)
{
var packet = new MyPacket();
var dataSize = Marshal.SizeOf(packet);
var ptr = Marshal.AllocHGlobal(dataSize);
Marshal.Copy(data, 0, ptr, dataSize);
packet = (MyPacket)Marshal.PtrToStructure(ptr, packet.GetType());
Marshal.FreeHGlobal(ptr);
return packet;
}

private static void Main(string[] args)
{
const string queuePath = @".\private$\test_msmq";

// Create the packet
var packet = new MyPacket();

// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 2;

// 8 bytes
// packet.Di = new DiObject[packet.NumDi];
packet.Di[0] = 2;
packet.Di[1] = 3;
packet.Di[2] = 4;
packet.Di[3] = 5;
packet.Di[4] = 6;
packet.Di[5] = 7;
packet.Di[6] = 8;
packet.Di[7] = 9;

// Convert the struct in bytes
int packetSize = Marshal.SizeOf<MyPacket>();
var packetBytes = GetBytes(packet, packetSize);

// Create the message

var msg = new Message();
msg.BodyStream = new MemoryStream(packetBytes);

// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);

// Open the queue
var q = new MessageQueue(queuePath); // {Formatter = new BinaryMessageFormatter()};

// Send the message to the queue
q.Send(msg);

}
}
}

Code below provides efficient conversion to byte array and from byte array for MyPacket struct with variable internal array size. Implementation avoids casts and bounds checks by using unsafe pointer arithmetic.

using System;
using System.IO;
using System.Messaging;
using System.Runtime.InteropServices;

namespace StructToBytes
{
// 4 bytes
[Serializable]
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DiObject
{
[FieldOffset(0)]
public byte Command;

[FieldOffset(1)]
public byte ErrorClass;

[FieldOffset(2)]
public byte Reserved;

[FieldOffset(3)]
public byte Flags;
}

[Serializable]
public unsafe struct MyPacket
{
public uint ProtocolIdentifier;
public uint NumDi;
public DiObject[] Di;

public byte[] ToBytes()
{
byte[] buffer = new byte[NumDi];

fixed(DiObject* pDi = Di)
fixed(byte* pBuff = buffer)
{
var pBuffDi = (DiObject*)pBuff;
var pDiPtr = pDi;
for (int i = 0; i < NumDi; i++)
*pBuffDi++ = *pDiPtr++;
}
return buffer;
}

public static MyPacket Create(byte[] buffer)
{
// argument checking code here

var packet = new MyPacket();
packet.ProtocolIdentifier = buffer[0];
packet.NumDi = buffer[1];
packet.Di = new DiObject[packet.NumDi];

fixed (byte* pBuf = buffer)
fixed (DiObject* pDi = packet.Di)
{
byte* pBufPtr = pBuf;
pBufPtr += 2;
var pBufDi = (DiObject*)pBufPtr;
var pDiPtr = pDi;

for (int i = 0; i < packet.NumDi; i++)
*pDiPtr++ = *pBufDi++;
}

return packet;
}
}

internal unsafe class Program
{

private static void Main(string[] args)
{
const string queuePath = @".\private$\test_msmq";

// Create the packet
var packet = new MyPacket();

// 8 bytes
packet.ProtocolIdentifier = 1;
packet.NumDi = 5;

// 8 bytes
packet.Di = new DiObject[packet.NumDi];
packet.Di[0].Command = 2;
packet.Di[0].ErrorClass = 3;
packet.Di[0].Flags = 4;
packet.Di[0].Reserved = 5;
packet.Di[1].Command = 6;
packet.Di[1].ErrorClass = 7;
packet.Di[1].Flags = 8;
packet.Di[1].Reserved = 9;
packet.Di[2].Command = 6;
packet.Di[2].ErrorClass = 7;
packet.Di[2].Flags = 8;
packet.Di[2].Reserved = 9;
packet.Di[3].Command = 6;
packet.Di[3].ErrorClass = 7;
packet.Di[3].Flags = 8;
packet.Di[3].Reserved = 9;

// Create the message

var msg = new Message();
msg.BodyStream = new MemoryStream(packet.ToBytes());

// Open or create the message queue
if (!MessageQueue.Exists(queuePath))
MessageQueue.Create(queuePath);

// Open the queue
var q = new MessageQueue(queuePath);

// Send the message to the queue
q.Send(msg);

}
}
}

Converting a structure to byte array, converting to big endian and sending over UDP

0x430A3333 is 138.2 under IEEE754. Why do you say its garbage?



Related Topics



Leave a reply



Submit