如何在C#中将结构转换为字节数组?

Swa*_*pta 72 c# struct

如何在C#中将结构转换为字节数组?

我已经定义了这样的结构:

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;
}
Run Code Online (Sandbox Code Playgroud)

在我的main方法中,我创建了一个实例并为其赋值:

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";
Run Code Online (Sandbox Code Playgroud)

现在我想通过套接字发送这个数据包.为此,我需要将结构转换为字节数组.我该怎么做?

我的完整代码如下.

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);
}
Run Code Online (Sandbox Code Playgroud)

代码片段是什么?

Vin*_*abb 112

这很容易,使用编组.

文件顶部

using System.Runtime.InteropServices
Run Code Online (Sandbox Code Playgroud)

功能

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;
}
Run Code Online (Sandbox Code Playgroud)

并将其转换回来:

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;
}
Run Code Online (Sandbox Code Playgroud)

在您的结构中,您需要将它放在字符串之前

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string Buffer;
Run Code Online (Sandbox Code Playgroud)

并确保SizeConst与您最大的字符串一样大.

你应该读这个:http: //msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

  • 在fromBytes方法中,不需要两次分配CIFSPacket.Marshal.SizeOf很乐意将Type作为参数,Marshal.PtrToStructure将分配一个新的托管对象. (3认同)
  • 请注意,在某些情况下,函数 «StructureToPtr» 会引发异常。这可以通过将“false”而不是“true”传递给“Marshal.StructureToPtr(str, ptr, false);”来解决。但需要提到的是,我正在使用包装到泛型中的函数,不过…… (2认同)

Pau*_*aul 28

如果你真的希望它是快速的,你可以使用CopyMemory使用不安全的代码.CopyMemory的速度提高了约5倍(例如800MB的数据通过编组复制需要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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 作为对那些正在阅读此答案的人的提示。.这不是跨平台友好的(它仅使用Windows的kernel32.dll)。但是话又说回来,它是在2014年编写的。 (2认同)
  • 另外要求结构是连续的。 (2认同)

Abd*_*oof 22

看看这些方法:

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);
}
Run Code Online (Sandbox Code Playgroud)

这是我在谷歌上发现的另一个线程的无耻副本!

更新:有关详细信息,请查看来源

  • -1,您应该提供原始线程的链接. (5认同)
  • 此选项取决于平台 - 注意 Grand Endian 和 Little endian 以及大约 32 位 / 64 位。 (2认同)

xan*_*tos 15

使用较少内存分配的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;
}
Run Code Online (Sandbox Code Playgroud)

GCHandle用来"固定"内存,然后直接使用它的地址h.AddrOfPinnedObject().


pbi*_*ies 5

由于主要答案是使用CIF中没有(或不再)可用的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;
    }
Run Code Online (Sandbox Code Playgroud)

经过测试,他们的工作.


小智 5

我知道这真的很晚了,但是使用 C# 7.3,您可以对非托管结构或其他任何非托管结构(int、bool 等...)执行此操作:

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;
    }
Run Code Online (Sandbox Code Playgroud)

然后像这样使用:

struct MyStruct {
        public int Value1;
        public int Value2;
        //.. blah blah blah
    }

    byte[] bytes = ConvertToBytes(new MyStruct());
Run Code Online (Sandbox Code Playgroud)