2010-07-19 5 views
63

Wie konvertiere ich eine Struktur in ein Byte-Array in C#?Wie konvertiert man eine Struktur in ein Byte-Array in C#?

Ich habe eine Struktur wie folgt definiert:

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 meiner Hauptmethode, ich eine Instanz davon erstellen und Werte zuweisen es:

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

Jetzt mag ich dieses Paket senden nach Steckdose. Dazu muss ich die Struktur in ein Byte-Array konvertieren. Wie kann ich es tun?

Mein vollständiger Code ist wie folgt.

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

Was wäre ein Code-Snippet?

+2

Schlüsselwort - 'Serialisierung' –

+0

Eine Korrektur in der letzten Zeile MyPing.Send (Es dauert Byte-Array als Parameter); Es ist nicht SendTo senden ...... –

+0

Hallo Petar, Ich habe dich nicht bekommen ... –

Antwort

97

Das ist ziemlich einfach, Rangier- verwenden.

Anfang der Datei

using System.Runtime.InteropServices 

Funktion

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

Und es zu konvertieren zurück:

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

In Ihrer Struktur, müssen Sie dies setzen, bevor eine Zeichenfolge

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

Und stellen Sie sicher, SizeConst ist so groß wie Ihre größtmögliche Zeichenfolge.

Und Sie sollten dies wahrscheinlich lesen: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

+0

Danke Vincet. GetBytes() sollte nach dem Senden des Bytes [] aufgerufen werden ?? und frombytes() -Methode sendet die Bytes? Ich bin ein wenig verwirrt Kumpel? –

+1

GetBytes konvertiert von Ihrer Struktur in ein Array. FromBytes konvertiert von den Bytes zurück in Ihre Struktur. Dies ergibt sich aus den Funktionssignaturen. –

+0

@Vincet: Das ist in Ordnung Vincet.Now ich möchte die Antwort zurück bekommen. Wie kann ich das tun? Wie kann ich meine Antwort überprüfen? –

0

Sieht für eine externe Bibliothek wie eine vordefinierte Struktur (C-Ebene) aus. Marshal ist dein Freund. Check:

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

für einen Starter, wie man damit umgehen. Beachten Sie, dass Sie mit Attributen Dinge wie Byte-Layout und String-Handling definieren können. Sehr nette Annäherung, wirklich.

Weder BinaryFormatter noch MemoryStream sind dafür gemacht.

18

Werfen Sie einen Blick auf diese Methoden:

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

Dies ist eine schamlose Kopie eines anderen Threads, die ich auf Googeln gefunden!

aktualisieren: Weitere Informationen erhalten Sie im source

+0

@Abdel: Warum Shamless Copy? –

+4

-1, sollten Sie einen Link zum ursprünglichen Thread bereitstellen. –

+0

Ich habe die Struktur in Byte-Array umgewandelt mit Marshalling jetzt, wie ich überprüfen kann, ob ich die Antwort von Socket bekomme? Wie überprüft man das? –

2

Sie können Marshal (StructureToPtr, ptrToStructure) verwenden, und Marshal.Copy aber dies ist abhängig plataform.


Serialisierung umfasst Funktionen zur benutzerdefinierten Serialisierung.

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

SerializationInfo enthält Funktionen zum Serialisieren jedes Elements.


BinaryWriter und BinaryReader enthält auch Methoden zum Speichern/Laden in Byte-Array (Stream).

Beachten Sie, dass Sie einen MemoryStream aus einem Byte-Array oder einem Byte-Array aus einem MemoryStream erstellen können.

Sie können eine Methode speichern erstellen und ein Verfahren Neu bei Ihrer Struktur:

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

Dann wählen Sie Mitglieder Speichern/Laden Stream -> Byte-Array.

1

Dies kann sehr einfach getan werden.

Definieren Sie Ihre Struktur explizit mit [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; 
} 

Dieser Code kann nur in einem unsicheren Kontext geschrieben werden. Sie müssen addr freigeben, wenn Sie damit fertig sind.

+0

Wenn Sie explizit geordnete Operationen für eine Sammlung fester Größe ausführen, sollten Sie wahrscheinlich ein Array und for-Schleife verwenden. Das Array, weil es eine feste Größe hat, und die for-Schleife, weil foreach nicht garantiert in der von Ihnen erwarteten Reihenfolge ist, es sei denn, Sie kennen die zugrundeliegende Implementierung Ihres Listentyps und seines Enumerators und werden sich niemals ändern. Man könnte den Aufzähler so definieren, dass er am Ende beginnt und zum Beispiel rückwärts geht. –

19

Wenn Sie wirklich wollen, dass es schnell ist, können Sie es mit unsicheren Code mit CopyMemory tun. CopyMemory ist ungefähr 5x schneller (z. B. benötigen 800MB der Daten 3s, um über Marshalling zu kopieren, während nur 0,6s über CopyMemory kopiert werden). Diese Methode beschränkt Sie darauf, nur Daten zu verwenden, die tatsächlich im Struct-Blob selbst gespeichert sind, z. Zahlen oder feste Länge 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; 
    } 
0

@Abdel Olakara Antwort donese nicht in .net 3.5 zu arbeiten, sollten wie unten modifiziert werden:

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); 
    } 
0
 Header header = new Header(); 
     Byte[] headerBytes = new Byte[Marshal.SizeOf(header)]; 
     Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length); 

Dies sollte den Trick schnell tun, nicht wahr?

+0

Die GCHandle-Version ist viel besser. –

3

Als Haupt Antwort CIFSPacket Typen verwendet, die nicht (oder nicht mehr) ist in C# verfügbar, ich richtige Methoden schrieb:

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

getestet, sie arbeiten.

6

Variante des Codes von Vicent mit einer weniger Speicherzuweisung:

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

Ich benutze GCHandle auf „Pin“ das Gedächtnis und dann verwende ich direkt seine Adresse mit h.AddrOfPinnedObject().

+0

Dies sollte die akzeptierte Antwort sein – Julien

+0

Sollte 'where T: struct' entfernt werden, sonst wird es sich über' T', pass ist kein 'non-nullable type'. – codenamezero

0

Dieses hier Beispiel gilt nur für reine blitfähig Typen, zB Typen, die direkt in C memcpy'd werden können

Beispiel - auch bekannt 64-Bit-Struktur

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

genau definiert wie Dadurch wird die Struktur automatisch als 64-Bit gepackt.

Jetzt können wir Volumen von Voxel erstellen:

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

Und sie alle zu einer Byte-Array speichern:

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. 

Da jedoch das OP wissen will, wie die Struktur zu konvertieren selbst, können unsere Voxel-Struktur folgende Methode haben 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(); 
1

ich habe kommen mit ein anderer Ansatz, das jedenstruct ohne lästiges Befestigungslänge jedoch der resultierende Byte-Array würde ein wenig mehr Aufwand umwandeln könnte. Hier

ist ein Beispiel struct:

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

Wie Sie sehen können, all diese Strukturen erfordern würde die feste Länge Attribute hinzufügen. Das konnte oft mehr Platz einnehmen als nötig. Beachten Sie, dass die LayoutKind.Sequential erforderlich ist, wie wir Reflexion wollen gibt uns immer die gleiche Reihenfolge, wenn für FieldInfo ziehen. Meine Inspiration ist von TLV Type-Length-Wert. Schauen wir uns den Code einen Blick:

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

Die obige Funktion einfach die BinaryFormatter verwendet die unbekannte Größe roh object, und ich einfach den Überblick über die Größe als auch und speichern sie in der Ausgabe MemoryStream zu serialisiert.

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

Wenn wir es wieder in seinen ursprünglichen struct wir einfach lesen Sie die Länge zurück und es direkt Dump zurück in die BinaryFormatter die es wiederum Dump der struct zurück konvertieren möchten in.

Diese zwei Funktionen sind allgemeiner Natur und sollte mit jedem struct arbeiten, habe ich den obigen Code in meinem C# Projekt getestet, wo ich einen Server und einen Client, verbunden und kommunizieren über NamedPipeStream und ich meine struct als Byte-Array weiterleiten aus eins und zu anderem und wandelte es zurück.

Ich glaube, mein Ansatz könnte besser sein, da es nicht die Länge auf struct selbst fixiert und der einzige Aufwand ist nur ein int für alle Felder, die Sie in Ihrer Struktur haben. Es gibt auch einig klein wenig Overhead in dem Byte-Array von BinaryFormatter erzeugt, aber anders als das, ist nicht viel.

Verwandte Themen