Wie konvertiere ich eine Struktur in ein Byte-Array in C #?
Ich habe eine Struktur wie diese 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 erstelle ich eine Instanz davon und weise ihr Werte zu:
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 möchte ich dieses Paket per Socket senden. Dafür muss ich die Struktur in ein Byte-Array konvertieren. Wie kann ich es tun?
Mein vollständiger Code lautet 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?
Antworten:
Dies ist mit Marshalling ziemlich einfach.
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 um es zurück zu konvertieren:
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 vor eine Zeichenfolge setzen
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string Buffer;
Und stellen Sie sicher, dass SizeConst so groß ist wie Ihre größtmögliche Zeichenfolge.
Und Sie sollten dies wahrscheinlich lesen: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx
quelle
Marshal.StructureToPtr(str, ptr, false);
. Aber ich muss erwähnen, dass ich die Funktionen verwende, die in ein Generikum eingebunden sind…Wenn Sie wirklich möchten, dass es unter Windows SCHNELL ist, können Sie dies mit unsicherem Code mit CopyMemory tun. CopyMemory ist ungefähr 5x schneller (z. B. 800 MB Daten benötigen 3 Sekunden zum Kopieren über Marshalling, während nur 0,6 Sekunden zum Kopieren über CopyMemory benötigt werden). Diese Methode beschränkt sich darauf, nur Daten zu verwenden, die tatsächlich im Struktur-Blob selbst gespeichert sind, z. B. Zahlen oder Byte-Arrays mit fester Länge.
[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; }
quelle
Schauen Sie sich diese Methoden an:
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, den ich beim Googeln gefunden habe!
Update : Weitere Informationen finden Sie in der Quelle
quelle
Variante des Vicent-Codes mit einer Speicherzuordnung weniger:
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
, um den Speicher zu "pinnen" und dann benutze ich direkt seine Adresse mith.AddrOfPinnedObject()
.quelle
where T : struct
sonstT
keine Beschwerde über das Bestehen gebennon-nullable type
.GCHandle.Alloc
wird fehlschlagen, wenn die Struktur nicht blittable Daten hat, z. B. ein Arraystring
.Da die Hauptantwort die Verwendung des CIFSPacket-Typs ist, der in C # nicht (oder nicht mehr) verfügbar ist, habe ich die richtigen Methoden geschrieben:
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 funktionieren sie.
quelle
Ich weiß, dass dies sehr spät ist, aber mit C # 7.3 können Sie dies für nicht verwaltete Strukturen oder alles andere tun, was nicht geändert wurde (int, bool usw.):
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; }
Dann verwenden Sie wie folgt:
struct MyStruct { public int Value1; public int Value2; //.. blah blah blah } byte[] bytes = ConvertToBytes(new MyStruct());
quelle
Sie können Marshal (StructureToPtr, ptrToStructure) und Marshal.copy verwenden, dies ist jedoch plataformabhängig.
Die 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 Mitglieds.
BinaryWriter und BinaryReader enthalten auch Methoden zum Speichern / Laden in Byte-Array (Stream).
Beachten Sie, dass Sie einen MemoryStream aus einem Byte-Array oder ein Byte-Array aus einem MemoryStream erstellen können.
Sie können eine Methode Speichern und eine Methode Neu in Ihrer Struktur erstellen:
Save(Bw as BinaryWriter) New (Br as BinaryReader)
Anschließend wählen Sie Mitglieder zum Speichern / Laden zum Streamen -> Byte-Array aus.
quelle
Dies kann sehr einfach erfolgen.
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 befreien,
addr
wenn Sie damit fertig sind.quelle
Ich habe mir einen anderen Ansatz ausgedacht, der jeden
struct
ohne den Aufwand der Fixierungslänge konvertieren könnte , jedoch würde das resultierende Byte-Array etwas mehr Overhead haben.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, müssten für alle diese Strukturen die Attribute mit fester Länge hinzugefügt werden. Was oft mehr Platz beanspruchte als nötig. Beachten Sie, dass dies
LayoutKind.Sequential
erforderlich ist, da die Reflexion beim Ziehen immer die gleiche Reihenfolge ergibtFieldInfo
. Meine Inspiration ist vomTLV
Typ-Länge-Wert. Schauen wir uns den Code an: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 verwendet einfach die,
BinaryFormatter
um die unbekannte Rohgröße zu serialisierenobject
, und ich verfolge einfach auch die Größe und speichere sie auch in der AusgabeMemoryStream
.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 sein Original konvertieren wollen,
struct
lesen wir einfach die Länge zurück und werfen es direkt zurück in das,BinaryFormatter
was es wiederum zurück in dasstruct
.Diese beiden Funktionen sind generisch und sollten mit allen funktionieren.
struct
Ich habe den obigen Code in meinemC#
Projekt getestet, in dem ich einen Server und einen Client habe, über dieNamedPipeStream
ich verbunden bin und kommuniziere, und ich leite meinstruct
As-Byte-Array von einem zum anderen weiter und konvertiere es zurück .Ich glaube, mein Ansatz könnte besser sein, da er nicht die Länge an sich
struct
selbst festlegt und der einzige Overhead nurint
für alle Felder gilt, die Sie in Ihrer Struktur haben. Es gibt auch ein kleines bisschen Overhead innerhalb des Byte-Arrays, das von generiert wirdBinaryFormatter
, aber ansonsten ist es nicht viel.quelle
Ich würde mir die Klassen BinaryReader und BinaryWriter ansehen. Ich musste kürzlich Daten in ein Byte-Array (und zurück) serialisieren und fand diese Klassen erst, nachdem ich sie im Grunde selbst neu geschrieben hatte.
http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx
Auf dieser Seite gibt es auch ein gutes Beispiel.
quelle
Sieht aus wie eine vordefinierte Struktur (C-Ebene) für eine externe Bibliothek. Marschall ist dein Freund. Prüfen:
http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
für den Anfang, wie man damit umgeht. Beachten Sie, dass Sie mit Attributen beispielsweise das Byte-Layout und die Behandlung von Zeichenfolgen definieren können. Eigentlich ein sehr netter Ansatz.
Weder BinaryFormatter noch MemoryStream werden dafür ausgeführt.
quelle
@Abdel Olakara Antwort donese funktioniert nicht in .net 3.5, sollte wie folgt geändert 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); }
quelle
Header header = new Header(); Byte[] headerBytes = new Byte[Marshal.SizeOf(header)]; Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);
Das sollte schnell gehen, oder?
quelle
Dieses Beispiel hier gilt nur für reine blittable Typen, z. B. Typen, die direkt in C gespeichert werden können.
Beispiel - bekannte 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 so definiert wird die Struktur automatisch als 64-Bit gepackt.
Jetzt können wir ein Volumen von Voxeln erstellen:
Voxel[,,] voxels = new Voxel[16,16,16];
Und speichern Sie sie alle in einem 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.
Da das OP jedoch wissen möchte, wie die Struktur selbst konvertiert wird, kann unsere Voxel-Struktur über die folgende Methode verfügen
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();
quelle