.net - Lettura di una struttura di dati C / C ++ in C # da un array di byte

original title: ".net - Reading a C/C++ data structure in C# from a byte array"


Translate

What would be the best way to fill a C# struct from a byte[] array where the data was from a C/C++ struct? The C struct would look something like this (my C is very rusty):

typedef OldStuff {
    CHAR Name[8];
    UInt32 User;
    CHAR Location[8];
    UInt32 TimeStamp;
    UInt32 Sequence;
    CHAR Tracking[16];
    CHAR Filler[12];
}

And would fill something like this:

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)]
public struct NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(0)]
    public string Name;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(8)]
    public uint User;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(12)]
    public string Location;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(20)]
    public uint TimeStamp;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(24)]
    public uint Sequence;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    [FieldOffset(28)]
    public string Tracking;
}

What is best way to copy OldStuff to NewStuff, if OldStuff was passed as byte[] array?

I'm currently doing something like the following, but it feels kind of clunky.

GCHandle handle;
NewStuff MyStuff;

int BufferSize = Marshal.SizeOf(typeof(NewStuff));
byte[] buff = new byte[BufferSize];

Array.Copy(SomeByteArray, 0, buff, 0, BufferSize);

handle = GCHandle.Alloc(buff, GCHandleType.Pinned);

MyStuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));

handle.Free();

Is there better way to accomplish this?


Would using the BinaryReader class offer any performance gains over pinning the memory and using Marshal.PtrStructure?



Quale sarebbe il modo migliore per riempire una struttura C # da una matrice di byte [] in cui i dati provenivano da una struttura C / C ++? La struttura C sarebbe simile a questa (la mia C è molto arrugginita): typedef OldStuff {...

Questo è il riepilogo dopo la traduzione, se è necessario visualizzare la traduzione completa, fare clic sull'icona "traduci"


Tutte le risposte
  • Translate

    From what I can see in that context, you don't need to copy SomeByteArray into a buffer. You simply need to get the handle from SomeByteArray, pin it, copy the IntPtr data using PtrToStructure and then release. No need for a copy.

    That would be:

    NewStuff ByteArrayToNewStuff(byte[] bytes)
    {
        GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        try
        {
            NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));
        }
        finally
        {
            handle.Free();
        }
        return stuff;
    }
    

    Generic version:

    T ByteArrayToStructure<T>(byte[] bytes) where T: struct 
    {
        T stuff;
        GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        try
        {
            stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        }
        finally
        {
            handle.Free();
        }
        return stuff;
    }
    

    Simpler version (requires unsafe switch):

    unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
    {
        fixed (byte* ptr = &bytes[0])
        {
            return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
        }
    }
    

  • Translate

    Here is an exception safe version of the accepted answer:

    public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
    {
        var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        try {
            return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        }
        finally {
            handle.Free();
        }
    }
    

  • Translate

    Watch out for packing issues. In the example you gave all fields are at the obvious offsets because everything is on 4 byte boundaries but this will not always be the case. Visual C++ packs on 8 byte boundaries by default.


  • Translate
    object ByteArrayToStructure(byte[] bytearray, object structureObj, int position)
    {
        int length = Marshal.SizeOf(structureObj);
        IntPtr ptr = Marshal.AllocHGlobal(length);
        Marshal.Copy(bytearray, 0, ptr, length);
        structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType());
        Marshal.FreeHGlobal(ptr);
        return structureObj;
    }   
    

    Have this


  • Translate

    If you have a byte[] you should be able to use the BinaryReader class and set values on NewStuff using the available ReadX methods.