¿Cómo convierto una estructura en una matriz de bytes en C #?
He definido una estructura como esta:
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;
}
En mi método principal, creo una instancia y le asigno valores:
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";
Ahora quiero enviar este paquete por socket. Para eso, necesito convertir la estructura en una matriz de bytes. ¿Cómo puedo hacerlo?
Mi código completo es el siguiente.
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);
}
¿Qué sería un fragmento de código?
Respuestas:
Esto es bastante fácil, usando la clasificación.
Parte superior del archivo
using System.Runtime.InteropServices
Función
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; }
Y para convertirlo de nuevo:
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; }
En su estructura, deberá poner esto antes de una cadena
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)] public string Buffer;
Y asegúrese de que SizeConst sea tan grande como su cuerda más grande posible.
Y probablemente debería leer esto: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx
fuente
Marshal.StructureToPtr(str, ptr, false);
. Pero debo mencionar que estoy usando las funciones envueltas en un genérico, aunque…Si realmente desea que sea RÁPIDO en Windows, puede hacerlo utilizando un código inseguro con CopyMemory. CopyMemory es aproximadamente 5 veces más rápido (p. Ej., 800 MB de datos tardan 3 segundos en copiarse mediante clasificación, mientras que sólo se tarda 0,6 segundos en copiar mediante CopyMemory). Este método lo limita a usar solo datos que están realmente almacenados en la estructura del blob, por ejemplo, números o matrices de bytes de longitud fija.
[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; }
fuente
Eche un vistazo a estos métodos:
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); }
¡Esta es una copia descarada de otro hilo que encontré al buscar en Google!
Actualización : para obtener más detalles, consulte la fuente
fuente
Variante del código de Vicent con una asignación de memoria menos:
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; }
Utilizo
GCHandle
para "fijar" la memoria y luego uso directamente su dirección conh.AddrOfPinnedObject()
.fuente
where T : struct
contrario, se quejará de queT
está aprobadonon-nullable type
.GCHandle.Alloc
fallará si la estructura tiene datos no blittables, por ejemplo, una matrizstring
.Como la respuesta principal es usar el tipo CIFSPacket, que no está (o ya no está) disponible en C #, escribí los métodos correctos:
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; }
Probados, funcionan.
fuente
Sé que es muy tarde, pero con C # 7.3 puede hacer esto para estructuras no administradas o cualquier otra cosa que no esté administrada (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; }
Entonces usa así:
struct MyStruct { public int Value1; public int Value2; //.. blah blah blah } byte[] bytes = ConvertToBytes(new MyStruct());
fuente
Puede usar Marshal (StructureToPtr, ptrToStructure) y Marshal.copy, pero esto depende de la plataforma.
La serialización incluye funciones para la serialización personalizada.
public virtual void GetObjectData(SerializationInfo info, StreamingContext context) Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
SerializationInfo incluye funciones para serializar cada miembro.
BinaryWriter y BinaryReader también contienen métodos para Guardar / Cargar en Byte Array (Stream).
Tenga en cuenta que puede crear un MemoryStream desde un Byte Array o un Byte Array desde un MemoryStream.
Puede crear un método Guardar y un método Nuevo en su estructura:
Save(Bw as BinaryWriter) New (Br as BinaryReader)
Luego, selecciona miembros para Guardar / Cargar en Stream -> Byte Array.
fuente
Esto se puede hacer de manera muy sencilla.
Defina su estructura explícitamente con
[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; }
Este código solo se puede escribir en un contexto inseguro. Tienes que liberar
addr
cuando termines.fuente
Se me ocurrió un enfoque diferente que podría convertir cualquiera
struct
sin la molestia de fijar la longitud, sin embargo, la matriz de bytes resultante tendría un poco más de sobrecarga.He aquí una muestra
struct
:[StructLayout(LayoutKind.Sequential)] public class HelloWorld { public MyEnum enumvalue; public string reqtimestamp; public string resptimestamp; public string message; public byte[] rawresp; }
Como puede ver, todas esas estructuras requerirían agregar los atributos de longitud fija. Lo que a menudo podría terminar ocupando más espacio del requerido. Tenga en cuenta que
LayoutKind.Sequential
es obligatorio, ya que queremos que la reflexión siempre nos dé el mismo orden al tirar deFieldInfo
. Mi inspiración es deTLV
Tipo-Longitud-Valor. Echemos un vistazo al código: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(); } }
La función anterior simplemente usa
BinaryFormatter
para serializar el tamaño desconocido sin procesarobject
, y simplemente hago un seguimiento del tamaño también y lo guardo dentro de la salidaMemoryStream
también.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)); } } } }
Cuando queremos convertirlo de nuevo a su original
struct
, simplemente leemos la longitud y lo volcamos directamente de nuevo en elBinaryFormatter
que a su vez lo volcamos de nuevo en el archivostruct
.Estas 2 funciones son genéricas y deberían funcionar con cualquiera
struct
, probé el código anterior en miC#
proyecto donde tengo un servidor y un cliente, me conecto y me comunico a travésNamedPipeStream
y reenvío mistruct
matriz de bytes de uno a otro y lo convertí .Creo que mi enfoque podría ser mejor, ya que no fija la longitud en
struct
sí mismo y la única sobrecarga es soloint
para cada campo que tiene en su estructura. También hay una pequeña sobrecarga dentro de la matriz de bytes generada porBinaryFormatter
, pero aparte de eso, no es mucho.fuente
Echaría un vistazo a las clases BinaryReader y BinaryWriter. Recientemente tuve que serializar datos en una matriz de bytes (y viceversa) y solo encontré estas clases después de que básicamente las reescribí yo mismo.
http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx
También hay un buen ejemplo en esa página.
fuente
Parece una estructura predefinida (nivel C) para alguna biblioteca externa. Marshal es tu amigo. Cheque:
http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
para empezar, cómo lidiar con esto. Tenga en cuenta que puede, con atributos, definir cosas como el diseño de bytes y el manejo de cadenas. MUY buen enfoque, de hecho.
Ni BinaryFormatter ni MemoryStream están hechos para eso.
fuente
@Abdel Olakara respuesta donese no funciona en .net 3.5, debe modificarse de la siguiente manera:
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); }
fuente
Header header = new Header(); Byte[] headerBytes = new Byte[Marshal.SizeOf(header)]; Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);
Esto debería funcionar rápidamente, ¿verdad?
fuente
Este ejemplo aquí solo es aplicable a tipos puros blittable, por ejemplo, tipos que se pueden memorizar directamente en C.
Ejemplo: estructura conocida de 64 bits
[StructLayout(LayoutKind.Sequential)] public struct Voxel { public ushort m_id; public byte m_red, m_green, m_blue, m_alpha, m_matid, m_custom; }
Definida exactamente así, la estructura se empaquetará automáticamente como de 64 bits.
Ahora podemos crear volumen de vóxeles:
Voxel[,,] voxels = new Voxel[16,16,16];
Y guárdelos todos en una matriz de bytes:
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.
Sin embargo, dado que el OP quiere saber cómo convertir la estructura en sí, nuestra estructura Voxel puede tener el siguiente método
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();
fuente