Comment convertir une structure en un tableau d’octets en C #?

Comment convertir une structure en un tableau d’octets en C #?

J’ai défini une structure comme celle-ci:

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

Dans ma méthode principale, j’en crée une instance et lui assigne des valeurs:

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

Maintenant, je veux envoyer ce paquet par socket. Pour cela, je dois convertir la structure en un tableau d’octets. Comment puis-je le faire?

Mon code complet est le suivant.

 static void Main(ssortingng[] 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); } 

Quel serait un extrait de code?

Ceci est assez facile, en utilisant le regroupement.

Haut de fichier

 using System.Runtime.InteropServices 

Fonction

 byte[] getBytes(CIFSPacket 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; } 

Et pour le reconvertir:

 CIFSPacket fromBytes(byte[] arr) { CIFSPacket str = new CIFSPacket(); int size = Marshal.SizeOf(str); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.Copy(arr, 0, ptr, size); str = (CIFSPacket)Marshal.PtrToStructure(ptr, str.GetType()); Marshal.FreeHGlobal(ptr); return str; } 

Dans votre structure, vous devrez mettre ceci avant une chaîne

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

Et assurez-vous que SizeConst est aussi grand que votre plus grande chaîne possible.

Et vous devriez probablement lire ceci: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

Si vous voulez vraiment que ce soit rapide, vous pouvez le faire en utilisant un code non sécurisé avec CopyMemory. CopyMemory est environ 5 fois plus rapide (par exemple, 800 Mo de données sont nécessaires pour copier via le marshalling, alors que seul 6 pour copier via CopyMemory). Cette méthode vous limite à utiliser uniquement les données qui sont réellement stockées dans le blob struct lui-même, par exemple les nombres ou les tableaux d’octets de longueur fixe.

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

Jetez un oeil à ces méthodes:

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

Ceci est une copie éhontée d’un autre fil que j’ai trouvé sur Google!

Mise à jour : pour plus de détails, vérifiez la source

Variante du code de Vicent avec une allocation de mémoire de moins:

 public static byte[] GetBytes(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(str, h.AddrOfPinnedObject(), false); } finally { if (h.IsAllocated) { h.Free(); } } return arr; } public static T FromBytes(byte[] arr) where T : struct { T str = default(T); GCHandle h = default(GCHandle); try { h = GCHandle.Alloc(arr, GCHandleType.Pinned); str = Marshal.PtrToStructure(h.AddrOfPinnedObject()); } finally { if (h.IsAllocated) { h.Free(); } } return str; } 

J’utilise GCHandle pour “épingler” la mémoire puis j’utilise directement son adresse avec h.AddrOfPinnedObject() .

Comme la réponse principale est d’utiliser le type CIFSPacket, qui n’est pas (ou plus) disponible en C #, j’ai écrit des méthodes correctes:

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

Testé, ils fonctionnent.

Vous pouvez utiliser Marshal (StructureToPtr, ptrToStructure) et Marshal.copy, mais cela dépend de la plataform.


La sérialisation inclut des fonctions à la sérialisation personnalisée.

 public virtual void GetObjectData(SerializationInfo info, StreamingContext context) Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext) 

SerializationInfo inclut des fonctions pour sérialiser chaque membre.


BinaryWriter et BinaryReader contiennent également des méthodes pour enregistrer / charger dans Byte Array (Stream).

Notez que vous pouvez créer un MemoryStream à partir d’un tableau d’octets ou d’un tableau d’octets à partir d’un MemoryStream.

Vous pouvez créer une méthode Save et une méthode New sur votre structure:

  Save(Bw as BinaryWriter) New (Br as BinaryReader) 

Ensuite, vous sélectionnez les membres à enregistrer / charger dans le stream -> Byte Array.

Cela peut être fait très simplement.

Définissez explicitement votre struct avec [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; } 

Ce code ne peut être écrit que dans un contexte non sécurisé. Vous devez libérer addr lorsque vous en avez fini avec.

 Marshal.FreeHGlobal(addr); 

J’ai mis au point une approche différente qui pourrait convertir n’importe quelle struct sans les tracas liés à la longueur de fixation, mais le tableau d’octets résultant aurait un peu plus de charge.

Voici un exemple de struct :

 [StructLayout(LayoutKind.Sequential)] public class HelloWorld { public MyEnum enumvalue; public ssortingng reqtimestamp; public ssortingng resptimestamp; public ssortingng message; public byte[] rawresp; } 

Comme vous pouvez le voir, toutes ces structures nécessitent l’ajout des atsortingbuts de longueur fixe. Ce qui pouvait souvent prendre plus de place que nécessaire. Notez que le LayoutKind.Sequential est requirejs, car nous voulons que la reflection nous donne toujours le même ordre lors de l’extraction de FieldInfo . Mon inspiration vient de TLV Type-Length-Value. Regardons le code:

 public static byte[] StructToByteArray(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(); } } 

La fonction ci-dessus utilise simplement le BinaryFormatter pour sérialiser l’ object brut de taille inconnue, et je garde aussi une trace de la taille et la stocke également dans la sortie MemoryStream .

 public static void ByteArrayToStruct(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)); } } } } 

Lorsque nous voulons le reconvertir en sa struct origine, nous lisons simplement la longueur et la BinaryFormatter directement dans le BinaryFormatter qui la BinaryFormatter à son tour dans la struct .

Ces 2 fonctions sont génériques et devraient fonctionner avec n’importe quelle struct , j’ai testé le code ci-dessus dans mon projet C# où j’ai un serveur et un client, connecté et communiquant via NamedPipeStream et je transfère mon struct sous forme de tableau d’octets et reconverti.

Je crois que mon approche pourrait être meilleure, car elle ne fixe pas la longueur sur la struct elle struct même et la seule surcharge est juste un int pour tous les champs que vous avez dans votre structure. Le tableau d’octets généré par BinaryFormatter également un peu de surcharge, mais à part ça, ce n’est pas beaucoup.

Je regarderais les classes BinaryReader et BinaryWriter. J’ai récemment dû sérialiser des données à un tableau d’octets (et retour) et seulement trouvé ces classes après les avoir réécrites moi-même.

http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx

Il y a un bon exemple sur cette page aussi.

Ressemble à une structure prédéfinie (niveau C) pour certaines bibliothèques externes. Marshal est votre ami. Vérifier:

http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx

pour un démarreur comment gérer cela. Notez que vous pouvez, avec des atsortingbuts, définir des éléments tels que la disposition des octets et la gestion des chaînes. Très belle approche, en fait.

Ni BinaryFormatter ni MemoryStream ne sont faits pour cela.

@Abdel Olakara answer donese not work in .net 3.5, devrait être modifié comme ci-dessous:

  public static void ByteArrayToStructure(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); 

Cela devrait faire l’affaire rapidement, non?

Cet exemple est uniquement applicable aux types purement blittables, par exemple, les types qui peuvent être mémorisés directement dans C.

Exemple – structure 64 bits bien connue

 [StructLayout(LayoutKind.Sequential)] public struct Voxel { public ushort m_id; public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom; } 

Définie exactement comme ceci, la structure sera automatiquement compressée en 64 bits.

Maintenant, nous pouvons créer du volume de voxels:

 Voxel[,,] voxels = new Voxel[16,16,16]; 

Et enregistrez-les tous dans un tableau d’octets:

 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. 

Cependant, comme l’OP veut savoir comment convertir la structure elle-même, notre struct Voxel peut avoir la méthode suivante 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();