i have a struct define as:
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi,Pack = 1)]
internal struct Message
{
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
public byte age;
[MarshalAs(UnmanagedType.U2, SizeConst = 2)]
public ushort length;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1,SizeConst = 502)]
public byte[] data;
}
payload received via udp,when received byte[],need to be converted to struct.
data length specified as 502,but actually it should be the length member value indicating the data length,if remove the SizeConst attr, the code will throw Marshal exception at Marshal.SizeOf(typeof(T)).
public static T ToStruct<T>(this byte[] buf)
{
var lth = Marshal.SizeOf(typeof(T));
if (lth > buf.Length) return default(T);
var ptr = Marshal.AllocHGlobal(lth);
Marshal.Copy(buf, 0, ptr, lth);
var structure = Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return (T)structure;
}
exception info:
System.ArgumentException: Type 'Itms.Agent.IotBox.TieTa.Entity.Message' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
i know this is very simple to handle in c/c++, but C# doesn't have a similar approach.
any help?
when received byte[],need to be converted to struct. data length specified as 502,but actually it should be the length member value indicating the data length
that's too complex for simple marshal operations. You'll probably have to serialize/deserialize manually, i.e. something like:
byte[] payload = ...
var age = payload[0];
var len = (payload[1] << 8) | payload[2]; // invert depending on endianness
byte[] data = len == 0 ? Array.Empty<byte>() : new byte[len];
Buffer.BlockCopy(payload, 3, data, len);
var msg = new Message(age, len, data);
This would mean you could remove all the attributes, as you're not using any marshalling features.
Also... the length field seems kinda redundant, since it is simply duplicating the array's length.
I am doing a project in which I need to write structured data into a binary file. First I need to write a header, then fetch data from somewhere, populate and write the structured data blocks to the said file. I am porting C structs to C# as follows:
C header struct:
typedef struct
{
DWORD uSignature;
DWORD uRecordLength;
} Header;
C data struct:
typedef struct
{
DWORD uCode; // a two character identifier
char uLabel[10];
int uDate;
float uData[37];
} MyData;
Here is the C# header struct:
struct Header
{
public uint uSignature;
public uint uRecordLength;
}
and here is the C# data struct:
struct MyData
{
public MyData (int Count) : this ()
{
uData = new Single[Count];
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] uCode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] uLabel;
public int uDate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)]
public Single [] uData;
}
This file will be read by another application which can read from the binary file if data correctly formatted. I printed out the size of the two struct types and they look good. However, the output file can not be read by the said application.
So I have two questions:
Are the data types and Marshals I used correctly in C to C# conversion?
I use FileStream and BinaryWriter to write to the binary file. All data (header and subsequent data) must be in sequence (contiguous) . As I create and write data structs on the fly, I am not sure how to allocate continuous memory using something like:
public static byte[] GetData(object obj)
{
var size = Marshal.SizeOf(obj.GetType());
var data = new byte[size];
IntPtr pnt = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(obj, pnt, true);
// Copy the array to unmanaged memory.
Marshal.Copy(pnt, data, 0, size);
return data;
}
finally
{
// Free the unmanaged memory.
Marshal.FreeHGlobal(pnt);
}
}
Any help would be greatly appreciated!
[EDIT]
I added two methods to convert specific struct data into byte array, but the file is still unreadable:
private byte[] DataToByteArray(MyData data)
{
int len = 0;
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
data.uCode.CopyTo(barray, 0);
len += data.uCode.Length;
data.uLabel.CopyTo(barray, len);
len += data.uLabel.Length;
BitConverter.GetBytes(0).CopyTo(barray, len);
len += data.uData.Length;
Buffer.BlockCopy(data.uData, 0, barray, len, data.uData.Length);
return barray;
}
private byte[] HeadToByteArray(Header data)
{
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
BitConverter.GetBytes(data.uSignature).CopyTo(barray, 0);
BitConverter.GetBytes(data.uRecordLength).CopyTo(barray, 4);
return barray;
}
【EDIT2】
Here is how it works in C:
#define NQ_EX 'QN'
FILE *fout;
fopen_s(&fout, "path_to_the_file", "wb");
Header head = { val1, sizeof(MyData) };
fwrite(&head, sizeof(Header), 1, fout);
while (!stop && data_is_coming)
{
MyData data;
memset(&data, 0, sizeof(data));
data.uCode = NQ_EX;
sprintf_s(data.uLabel, "%s", getVal("field1"));
data.uData[0] = getVal("field2");
data.uData[1] = getVal("field3");
....
fwrite(&data, sizeof(MyData), 1, fout);
}
The endianness seems to be fine. After some changes and testing with the help of Jeroen and others, I am able to make it work. The problem was due to the Block.copy method. I change it to Array.copy as follows:
private byte[] DataToByteArray(MyData data)
{
int len = 0;
var size = Marshal.SizeOf(data.GetType());
var barray = new byte[size];
data.uCode.CopyTo(barray, 0);
len += data.uCode.Length;
data.uLabel.CopyTo(barray, len);
len += data.uLabel.Length;
BitConverter.GetBytes(0).CopyTo(barray, len);
len += data.uData.Length;
for (int i = 0; i < data.uData.Length; i++)
Array.Copy(BitConverter.GetBytes(data.uData[i]), 0, barray, len+i * 4, 4);
return barray;
}
I have a struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct FieldIndex {
public fixed byte Meta[16];
}
The first byte is TypeCode. The remaining 15 bytes is a UTF8-encoded string.
I'm using the following to fill the nameBuf byte array:
private static string GetMetaName (FieldIndex meta) {
var idx = 0;
var bytesOut = 1;
var nameBuf = new byte[Indexer.MAXFIELDNAMELENGTH]; // 15
while (bytesOut < Indexer.MAXFIELDLENGTH) { // 16
nameBuf[idx] = *(byte*)((byte*)&meta + bytesOut);
++bytesOut;
++idx;
}
//var src = (byte*)&field.meta + 1;
//fixed (byte* b = nameBuf) {
// *((byte*)b) = (byte)src;
//}
return BinaryConverter.ToString(nameBuf, 0, Indexer.MAXFIELDNAMELENGTH, Indexer.DefaultEncoding);
}
In the commented code above, I was trying to accomplish the same task without the while iteration but it does not work (no compile error, just the wrong interpolation). Can I assign nameBuf without the while-loop?
Update
I'd also prefer using the (byte*) rather than Marshal.Copy.
You could try to use the Marshal.Copy static method in unsafe context:
unsafe
{
Marshal.Copy(new IntPtr(meta), nameBuf, 1, nameBuf.Length);
}
There isn't much point in doing this the hard unsafe way when you can do it just easily this way:
[StructLayout(LayoutKind.Sequential)]
struct FieldIndex {
public byte Typecode;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
private byte[] Utf8string;
public string GetString() {
return Encoding.UTF8.GetString(Utf8string, 0, 15);
}
}
No while-loop, mission accomplished. That pointer is still there and is just as efficient, you just don't see it anymore. If you fret over calling the GetString() method too often then simply declare another public class and keep this one internal.
I need to convert a byte array to my structure type. To do this I use the following code for desktop project application:
var str = new SFHeader();
int size = Marshal.SizeOf(str);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(buffer, 0, ptr, size);
str = (SFHeader)Marshal.PtrToStructure(ptr, typeof(SFHeader));
Marshal.FreeHGlobal(ptr);
return str;
Where SFHeader is my structure.
The problem is that the line:
str = (SFHeader)Marshal.PtrToStructure(ptr, typeof(SFHeader));
throws a NotSupportedException when I run this code from a smart device project. Are there others methods to do this work in the Compact Framework?
[StructLayout(LayoutKind.Sequential)]
public struct SFHeader
{
internal const int MAX_FILENAME_LENGTH = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_FILENAME_LENGTH)]
public string FileName;
public int Offset;
public short Size;
public byte Flags;
public byte Source;
public long LastWriteTime;
}
Marshal.PtrToStructure works and I've used it many times in the compact framework. it looks like you are using it correctly. Therefore, the problem must be your struct definition (something might not be supported in the CF for the struct)
The following code runs just fine on my device using Windows CE 5.0 and .NET CF 3.5
[StructLayout(LayoutKind.Sequential)]
public struct SFHeader
{
internal const int MAX_FILENAME_LENGTH = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_FILENAME_LENGTH)]
public string FileName;
public int Offset;
public short Size;
public byte Flags;
public byte Source;
public long LastWriteTime;
}
private static void Test()
{
var str = new SFHeader();
int size = Marshal.SizeOf(str);
byte[] buffer = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(buffer, 0, ptr, size);
str = (SFHeader)Marshal.PtrToStructure(ptr, typeof(SFHeader));
Marshal.FreeHGlobal(ptr);
}
I would check to make sure you are following these guidelines Marshaling Structures in the .NET Compact Framework
Another option is to copy the fields from your buffer to your structure manually byte by byte. You could write a function that returns a SFHeader and takes a byte[].
How do I convert a structure to a byte array in C#?
I have defined a structure like this:
public struct CIFSPacket
{
public uint protocolIdentifier; //The value must be "0xFF+'SMB'".
public byte command;
public byte errorClass;
public byte reserved;
public ushort error;
public byte flags;
//Here there are 14 bytes of data which is used differently among different dialects.
//I do want the flags2. However, so I'll try parsing them.
public ushort flags2;
public ushort treeId;
public ushort processId;
public ushort userId;
public ushort multiplexId;
//Trans request
public byte wordCount;//Count of parameter words defining the data portion of the packet.
//From here it might be undefined...
public int parametersStartIndex;
public ushort byteCount; //Buffer length
public int bufferStartIndex;
public string Buffer;
}
In my main method, I create an instance of it and assign values to it:
CIFSPacket packet = new CIFSPacket();
packet.protocolIdentifier = 0xff;
packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
packet.errorClass = 0xff;
packet.error = 0;
packet.flags = 0x00;
packet.flags2 = 0x0001;
packet.multiplexId = 22;
packet.wordCount = 0;
packet.byteCount = 119;
packet.Buffer = "NT LM 0.12";
Now I want to send this Packet by socket. For that, I need to convert the structure to a byte array. How can I do it?
My full code is as follows.
static void Main(string[] args)
{
Socket MyPing = new Socket(AddressFamily.InterNetwork,
SocketType.Stream , ProtocolType.Unspecified ) ;
MyPing.Connect("172.24.18.240", 139);
//Fake an IP Address so I can send with SendTo
IPAddress IP = new IPAddress(new byte[] { 172,24,18,240 });
IPEndPoint IPEP = new IPEndPoint(IP, 139);
//Local IP for Receiving
IPEndPoint Local = new IPEndPoint(IPAddress.Any, 0);
EndPoint EP = (EndPoint)Local;
CIFSPacket packet = new CIFSPacket();
packet.protocolIdentifier = 0xff;
packet.command = (byte)CommandTypes.SMB_COM_NEGOTIATE;
packet.errorClass = 0xff;
packet.error = 0;
packet.flags = 0x00;
packet.flags2 = 0x0001;
packet.multiplexId = 22;
packet.wordCount = 0;
packet.byteCount = 119;
packet.Buffer = "NT LM 0.12";
MyPing.SendTo(It takes byte array as parameter);
}
What would a code snippet be?
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
If you really want it to be FAST on Windows, you can do it using unsafe code with CopyMemory. CopyMemory is about 5x faster (e.g. 800MB of data takes 3s to copy via marshalling, while only taking .6s to copy via CopyMemory). This method does limit you to using only data which is actually stored in the struct blob itself, e.g. numbers, or fixed length byte arrays.
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
private static unsafe extern void CopyMemory(void *dest, void *src, int count);
private static unsafe byte[] Serialize(TestStruct[] index)
{
var buffer = new byte[Marshal.SizeOf(typeof(TestStruct)) * index.Length];
fixed (void* d = &buffer[0])
{
fixed (void* s = &index[0])
{
CopyMemory(d, s, buffer.Length);
}
}
return buffer;
}
Have a look at these methods:
byte [] StructureToByteArray(object obj)
{
int len = Marshal.SizeOf(obj);
byte [] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
void ByteArrayToStructure(byte [] bytearray, ref object obj)
{
int len = Marshal.SizeOf(obj);
IntPtr i = Marshal.AllocHGlobal(len);
Marshal.Copy(bytearray,0, i,len);
obj = Marshal.PtrToStructure(i, obj.GetType());
Marshal.FreeHGlobal(i);
}
This is a shameless copy of another thread which I found upon Googling!
Update : For more details, check the source
Variant of the code of Vicent with one less memory allocation:
public static byte[] GetBytes<T>(T str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(arr, GCHandleType.Pinned);
Marshal.StructureToPtr<T>(str, h.AddrOfPinnedObject(), false);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return arr;
}
public static T FromBytes<T>(byte[] arr) where T : struct
{
T str = default(T);
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(arr, GCHandleType.Pinned);
str = Marshal.PtrToStructure<T>(h.AddrOfPinnedObject());
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return str;
}
I use GCHandle to "pin" the memory and then I use directly its address with h.AddrOfPinnedObject().
I know this is really late, but with C# 7.3 you can do this for unmanaged structs or anything else that's unmanged (int, bool etc...):
public static unsafe byte[] ConvertToBytes<T>(T value) where T : unmanaged {
byte* pointer = (byte*)&value;
byte[] bytes = new byte[sizeof(T)];
for (int i = 0; i < sizeof(T); i++) {
bytes[i] = pointer[i];
}
return bytes;
}
Then use like this:
struct MyStruct {
public int Value1;
public int Value2;
//.. blah blah blah
}
byte[] bytes = ConvertToBytes(new MyStruct());
As the main answer is using CIFSPacket type, which is not (or no longer) available in C#, I wrote correct methods:
static byte[] getBytes(object str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
static T fromBytes<T>(byte[] arr)
{
T str = default(T);
int size = Marshal.SizeOf(str);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
str = (T)Marshal.PtrToStructure(ptr, str.GetType());
Marshal.FreeHGlobal(ptr);
return str;
}
Tested, they work.
You can use Marshal (StructureToPtr, ptrToStructure), and Marshal.copy but this is plataform dependent.
Serialization includes Functions to Custom Serialization.
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
SerializationInfo include functions to serialize each member.
BinaryWriter and BinaryReader also contains methods to Save / Load to Byte Array (Stream).
Note that you can create a MemoryStream from a Byte Array or a Byte Array from a MemoryStream.
You can create a method Save and a method New on your structure:
Save(Bw as BinaryWriter)
New (Br as BinaryReader)
Then you select members to Save / Load to Stream -> Byte Array.
Almost all of the answers here use Marshal.StructureToPtr, which might be good for P/Invoke but it is very slow, and doesn't even always represent the actual raw content of the value. #Varscott128's answer is much better but it also contains an explicit byte copying, which is not necessary.
For unmanaged structs (structs without managed references) all you need is to reinterpret the allocated result array so a simple assignment does the trick (works even for huge structs):
.NET (Core) Solution:
If you can use the Unsafe class, then the solution is really easy. The unsafe modifier is required only due to sizeof(T).
public static unsafe byte[] SerializeValueType<T>(in T value) where T : unmanaged
{
byte[] result = new byte[sizeof(T)];
Unsafe.As<byte, T>(ref result[0]) = value;
return result;
}
// Note: Validation is omitted for simplicity
public static T DeserializeValueType<T>(byte[] data) where T : unmanaged
=> return Unsafe.As<byte, T>(ref data[0]);
.NET Framework/Standard Solution:
public static unsafe byte[] SerializeValueType<T>(in T value) where T : unmanaged
{
byte[] result = new byte[sizeof(T)];
fixed (byte* dst = result)
*(T*)dst = value;
return result;
}
// Note: Validation is omitted for simplicity
public static unsafe T DeserializeValueType<T>(byte[] data) where T : unmanaged
{
fixed (byte* src = data)
return *(T*)src;
}
See the complete code with validations here.
Remarks:
The OP's example contains a string, which is a reference type so the solution above cannot be used for that. And if you can't use generic methods for some reason things start to get more complicated, especially for .NET Framework (but non-generic size calculation is a pain also on the Core platform). If performance does not matter, then you can revert to Marshal.SizeOf and StructureToPtr as suggested by several other answers, or feel free to use the BinarySerializer.SerializeValueType method from my library that I linked also for the examples above (NuGet).
This can be done very straightforwardly.
Define your struct explicitly with [StructLayout(LayoutKind.Explicit)]
int size = list.GetLength(0);
IntPtr addr = Marshal.AllocHGlobal(size * sizeof(DataStruct));
DataStruct *ptrBuffer = (DataStruct*)addr;
foreach (DataStruct ds in list)
{
*ptrBuffer = ds;
ptrBuffer += 1;
}
This code can only be written in an unsafe context. You have to free addr when you're done with it.
Marshal.FreeHGlobal(addr);
I've come up with a different approach that could convert any struct without the hassle of fixing length, however the resulting byte array would have a little bit more overhead.
Here is a sample struct:
[StructLayout(LayoutKind.Sequential)]
public class HelloWorld
{
public MyEnum enumvalue;
public string reqtimestamp;
public string resptimestamp;
public string message;
public byte[] rawresp;
}
As you can see, all those structures would require adding the fixed length attributes. Which could often ended up taking up more space than required. Note that the LayoutKind.Sequential is required, as we want reflection to always gives us the same order when pulling for FieldInfo. My inspiration is from TLV Type-Length-Value. Let's have a look at the code:
public static byte[] StructToByteArray<T>(T obj)
{
using (MemoryStream ms = new MemoryStream())
{
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inms = new MemoryStream()) {
bf.Serialize(inms, info.GetValue(obj));
byte[] ba = inms.ToArray();
// for length
ms.Write(BitConverter.GetBytes(ba.Length), 0, sizeof(int));
// for value
ms.Write(ba, 0, ba.Length);
}
}
return ms.ToArray();
}
}
The above function simply uses the BinaryFormatter to serialize the unknown size raw object, and I simply keep track of the size as well and store it inside the output MemoryStream too.
public static void ByteArrayToStruct<T>(byte[] data, out T output)
{
output = (T) Activator.CreateInstance(typeof(T), null);
using (MemoryStream ms = new MemoryStream(data))
{
byte[] ba = null;
FieldInfo[] infos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo info in infos)
{
// for length
ba = new byte[sizeof(int)];
ms.Read(ba, 0, sizeof(int));
// for value
int sz = BitConverter.ToInt32(ba, 0);
ba = new byte[sz];
ms.Read(ba, 0, sz);
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inms = new MemoryStream(ba))
{
info.SetValue(output, bf.Deserialize(inms));
}
}
}
}
When we want to convert it back to its original struct we simply read the length back and directly dump it back into the BinaryFormatter which in turn dump it back into the struct.
These 2 functions are generic and should work with any struct, I've tested the above code in my C# project where I have a server and a client, connected and communicate via NamedPipeStream and I forward my struct as byte array from one and to another and converted it back.
I believe my approach might be better, since it doesn't fix length on the struct itself and the only overhead is just an int for every fields you have in your struct. There are also some tiny bit overhead inside the byte array generated by BinaryFormatter, but other than that, is not much.
I would take a look at the BinaryReader and BinaryWriter classes. I recently had to serialize data to a byte array (and back) and only found these classes after I'd basically rewritten them myself.
http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx
There is a good example on that page too.
Looks like a predefined (C level) structure for some external library. Marshal is your friend. Check:
http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
for a starter how to deal with this. Note that you can - with attributes - define things like byte layout and string handling. VERY nice approach, actually.
Neither BinaryFormatter Nor MemoryStream are done for that.
#Abdel Olakara answer donese not work in .net 3.5, should be modified as below:
public static void ByteArrayToStructure<T>(byte[] bytearray, ref T obj)
{
int len = Marshal.SizeOf(obj);
IntPtr i = Marshal.AllocHGlobal(len);
Marshal.Copy(bytearray, 0, i, len);
obj = (T)Marshal.PtrToStructure(i, typeof(T));
Marshal.FreeHGlobal(i);
}
Header header = new Header();
Byte[] headerBytes = new Byte[Marshal.SizeOf(header)];
Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);
This should do the trick quickly, right?
This example here is only applicable to pure blittable types, e.g., types that can be memcpy'd directly in C.
Example - well known 64-bit struct
[StructLayout(LayoutKind.Sequential)]
public struct Voxel
{
public ushort m_id;
public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom;
}
Defined exactly like this, the struct will be automatically packed as 64-bit.
Now we can create volume of voxels:
Voxel[,,] voxels = new Voxel[16,16,16];
And save them all to a byte array:
int size = voxels.Length * 8; // Well known size: 64 bits
byte[] saved = new byte[size];
GCHandle h = GCHandle.Alloc(voxels, GCHandleType.Pinned);
Marshal.Copy(h.AddrOfPinnedObject(), saved, 0, size);
h.Free();
// now feel free to save 'saved' to a File / memory stream.
However, since the OP wants to know how to convert the struct itself, our Voxel struct can have following method ToBytes:
byte[] bytes = new byte[8]; // Well known size: 64 bits
GCHandle h = GCHandle.Alloc(this, GCHandleType.Pinned);
Marshal.Copy(hh.AddrOfPinnedObject(), bytes, 0, 8);
h.Free();