Как преобразовать структуру в массив байтов в С#?

Как преобразовать структуру в массив байтов в С#?

Я определил такую ​​структуру:

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

В моем основном методе я создаю его экземпляр и присваиваю ему значения:

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

Теперь я хочу отправить этот пакет по сокету. Для этого мне нужно преобразовать структуру в массив байтов. Как я могу это сделать?

Мой полный код выглядит следующим образом.

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

Каким будет фрагмент кода?

Ответ 1

Это довольно просто, используя сортировку.

Верх файла

using System.Runtime.InteropServices

Функция

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

И для его возврата:

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

В вашей структуре вам нужно будет поставить это перед строкой

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

И убедитесь, что SizeConst такой же большой, как ваша самая большая возможная строка.

И вы должны, вероятно, прочитать следующее: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

Ответ 2

Посмотрите на эти методы:

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

Это бесстыдная копия другого потока, который я нашел в Googling!

Обновить. Подробнее см. источник

Ответ 3

Если вы действительно хотите, чтобы он был FAST, вы можете сделать это, используя небезопасный код с CopyMemory. CopyMemory примерно в 5 раз быстрее (например, 800 МБ данных занимает 3 с для копирования через маршаллинг, а только для копирования с помощью CopyMemory.6s). Этот метод ограничивает использование только данных, которые фактически хранятся в самой структуре struct blob, например. числа или массивы байтов с фиксированной длиной.

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

Ответ 4

Вариант кода Vicent с меньшим объемом выделения памяти:

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

Я использую GCHandle для "привязки" к памяти, а затем я использую непосредственно ее адрес с помощью h.AddrOfPinnedObject().

Ответ 5

В качестве основного ответа используется тип CIFSPacket, который не является (или больше не доступен) на С#, я написал правильные методы:

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

Протестировано, они работают.

Ответ 6

Вы можете использовать маршал (StructureToPtr, ptrToStructure) и Marshal.copy, но это зависит от формы.


Сериализация включает функции для пользовательской сериализации.

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

SerializationInfo включает функции для сериализации каждого члена.


BinaryWriter и BinaryReader также содержат методы для сохранения/загрузки в байт-массив (поток).

Обратите внимание, что вы можете создать MemoryStream из байт-массива или байт-массива из MemoryStream.

Вы можете создать метод Сохранить и метод Новый в вашей структуре:

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

Затем вы выбираете элементы для сохранения/загрузки в поток → Байт-массив.

Ответ 7

Это можно сделать очень просто.

Определите свою структуру явно с помощью [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;
}

Этот код может быть написан только в небезопасном контексте. Вы должны освободить addr, когда закончите с ним.

Marshal.FreeHGlobal(addr);

Ответ 8

Я придумал другой подход, который мог бы преобразовать любой struct без проблем с длиной исправления, однако полученный массив байтов имел бы немного больше служебных данных.

Вот пример struct:

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

Как вы можете видеть, для всех этих структур потребуется добавить атрибуты фиксированной длины. Который часто мог занять больше места, чем требовалось. Обратите внимание, что требуется LayoutKind.Sequential, так как мы хотим, чтобы отражение всегда давало нам тот же порядок, когда тянул за FieldInfo. Мое вдохновение - от TLV Type-Length-Value. Давайте посмотрим на код:

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

Вышеуказанная функция просто использует BinaryFormatter для сериализации неизвестного размера raw object, и я просто отслеживаю размер, а также сохраняю его внутри выходного файла MemoryStream.

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

Когда мы хотим преобразовать его обратно в исходный struct, мы просто прочтем длину и вернем его обратно в BinaryFormatter, который, в свою очередь, вернет его обратно в struct.

Эти 2 функции являются общими и должны работать с любым struct, я протестировал вышеуказанный код в моем проекте C#, где у меня есть сервер и клиент, подключенный и обменивающийся через NamedPipeStream, и я пересылаю свои struct как массив байтов от одного и к другому и преобразовал его обратно.

Я считаю, что мой подход может быть лучше, поскольку он не фиксирует длину самого struct, и только накладные расходы - это всего лишь int для всех полей, которые у вас есть в вашей структуре. Кроме того, в массиве байтов, генерируемых BinaryFormatter, есть немного незначительных накладных расходов, но кроме этого, это немного.

Ответ 9

Я бы посмотрел на классы BinaryReader и BinaryWriter. Недавно мне пришлось сериализовать данные в массив байтов (и обратно) и только найти эти классы после того, как я в основном переписал их сам.

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

На этой странице также есть хороший пример.

Ответ 10

Похож на предопределенную структуру (C-уровень) для некоторой внешней библиотеки. Маршал - ваш друг. Проверьте:

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

для стартера, как справиться с этим. Обратите внимание, что вы можете - с атрибутами - определять такие вещи, как макет макета и обработка строк. На самом деле очень хороший подход.

Для этого не выполняются ни BinaryFormatter, ни MemoryStream.

Ответ 11

@Abdel Olakara ответ donese не работает в .net 3.5, следует изменить, как показано ниже:

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

Ответ 12

        Header header = new Header();
        Byte[] headerBytes = new Byte[Marshal.SizeOf(header)];
        Marshal.Copy((IntPtr)(&header), headerBytes, 0, headerBytes.Length);

Это должно сделать трюк быстро, правильно?

Ответ 13

Этот пример применим только к чистым типам blittable, например типам, которые могут быть memcpy'd непосредственно в C.

Пример - хорошо известная 64-битная структура

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

Определенный точно так же, структура будет автоматически упакована как 64-разрядная.

Теперь мы можем создать объем вокселей:

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

И сохраните их все в массив байтов:

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.

Однако, поскольку OP хочет знать, как преобразовать собственно структуру, наша структура Voxel может иметь следующий метод 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();