[c#] C #에서 구조를 바이트 배열로 변환하는 방법은 무엇입니까?

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

내 주요 방법에서는 인스턴스를 만들고 값을 할당합니다.

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

코드 조각은 무엇입니까?



답변

마샬링을 사용하면 상당히 쉽습니다.

파일 상단

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


답변

Windows에서 FAST를 정말로 원한다면 CopyMemory와 함께 안전하지 않은 코드를 사용하여 수행 할 수 있습니다. CopyMemory는 약 5 배 더 빠릅니다 (예 : 800MB의 데이터는 마샬링을 통해 복사하는 데 3 초가 걸리고 CopyMemory를 통해 복사하는 데는 .6s 만 걸립니다). 이 방법은 실제로 구조체 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;
    }


답변

다음 방법을 살펴보십시오.

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

이것은 인터넷 검색에서 찾은 다른 스레드의 뻔뻔한 사본입니다!

업데이트 : 자세한 내용은 소스 확인


답변

메모리 할당이 하나 더 적은 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().


답변

주된 대답은 C #에서 사용할 수 없거나 더 이상 사용할 수없는 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;
    }

테스트를 거쳐 작동합니다.


답변

나는 이것이 정말 늦었다는 것을 알고 있지만 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;
    }

그런 다음 다음과 같이 사용하십시오.

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

    byte[] bytes = ConvertToBytes(new MyStruct());


답변

Marshal (StructureToPtr, ptrToStructure) 및 Marshal.copy를 사용할 수 있지만 이것은 plataform에 따라 다릅니다.


직렬화에는 사용자 정의 직렬화에 대한 함수가 포함됩니다.

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

SerializationInfo에는 각 멤버를 직렬화하는 함수가 포함됩니다.


BinaryWriter 및 BinaryReader에는 또한 바이트 배열 (Stream)에 저장 /로드하는 메서드가 포함되어 있습니다.

바이트 배열에서 MemoryStream을 만들거나 MemoryStream에서 바이트 배열을 만들 수 있습니다.

구조에 Save 메소드와 New 메소드를 생성 할 수 있습니다.

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

그런 다음 Save / Load to Stream-> Byte Array 할 멤버를 선택합니다.