سؤال

ما هي أفضل طريقة لملء بنية C# من صفيف بايت[] حيث كانت البيانات من بنية C/C++؟ستبدو البنية C على هذا النحو (لغة C الخاصة بي صدئة جدًا):

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

وسوف تملأ شيئا مثل هذا:

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

ما هي أفضل طريقة للنسخ OldStuff ل NewStuff, ، لو OldStuff تم تمريره كمصفوفة بايت []؟

أقوم حاليًا بشيء مثل ما يلي، لكنه يبدو غريبًا نوعًا ما.

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

هل هناك طريقة أفضل لتحقيق ذلك؟


هل باستخدام BinaryReader تقدم الفئة أي مكاسب في الأداء مقارنة بتثبيت الذاكرة واستخدامها Marshal.PtrStructure?

هل كانت مفيدة؟

المحلول

مما أستطيع رؤيته في هذا السياق، لا تحتاج إلى النسخ SomeByteArray في المخزن المؤقت.كل ما عليك فعله هو الحصول على المقبض من SomeByteArray, ، قم بتثبيته، انسخ IntPtr البيانات باستخدام PtrToStructure ومن ثم الافراج.لا حاجة للحصول على نسخة.

ممكن حدوثه:

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

النسخة العامة:

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

إصدار أبسط (يتطلب unsafe يُحوّل):

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

نصائح أخرى

إليك نسخة آمنة استثنائية من إجابة مقبولة:

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

احترس من مشاكل التعبئة.في المثال الذي قدمته، تكون جميع الحقول في إزاحات واضحة لأن كل شيء يقع على حدود 4 بايت ولكن هذا لن يكون هو الحال دائمًا.حزم Visual C++ على حدود 8 بايت بشكل افتراضي.

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

احصل على هذا

إذا كان لديك بايت[]، فيجب أن تكون قادرًا على استخدام فئة BinaryReader وتعيين القيم على NewStuff باستخدام طرق ReadX المتاحة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top