การอ่านโครงสร้างข้อมูล C / C ++ ใน C # จากอาร์เรย์ไบต์


86

อะไรคือวิธีที่ดีที่สุดในการเติมโครงสร้าง 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ถูกส่งผ่านเป็น byte [] อาร์เรย์?

ตอนนี้ฉันกำลังทำสิ่งต่อไปนี้ แต่มันรู้สึกอึดอัด

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หรือไม่


1
FYI หากโปรแกรมของคุณทำงานบนเครื่องต่างๆคุณอาจต้องจัดการกับ endian น้อยและใหญ่
KPexEA

1
คุณจะจัดการกับระดับของโครงสร้างได้อย่างไรโดยไม่ต้องย้อนกลับไบต์สำหรับแต่ละค่าในโครงสร้าง
พาท

คำตอบ:


114

จากสิ่งที่ฉันเห็นในบริบทนั้นคุณไม่จำเป็นต้องคัดลอก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));
    }
}

CS0411 อาร์กิวเมนต์ประเภทสำหรับเมธอด 'ByteArrayToStructure <T> (byte [], int)' ไม่สามารถอนุมานได้จากการใช้งาน ลองระบุประเภทอาร์กิวเมนต์อย่างชัดเจน (ฉันเพิ่มดัชนี int ของอาร์เรย์ไบต์) เข้าไป
SSpoke

จะรั่วไหลหน่วยความจำหากมีข้อยกเว้น ดู: stackoverflow.com/a/41836532/184528สำหรับเวอร์ชันที่ปลอดภัยยิ่งขึ้น
cdiggins

2
ณ วันที่ 4.5.1 มี PtrToStructure เวอร์ชันทั่วไปดังนั้นบรรทัดที่สองในเวอร์ชันทั่วไปด้านบนจึงกลายเป็น: var stuff = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());

10

นี่คือคำตอบที่ยอมรับในเวอร์ชันปลอดภัยยกเว้น:

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

3
@ Ben-Collins คำตอบที่ยอมรับได้รับการแก้ไขหลังจากที่ฉันเพิ่มคำตอบ
cdiggins

5

ระวังปัญหาการบรรจุหีบห่อ ในตัวอย่างที่คุณให้ฟิลด์ทั้งหมดอยู่ในค่าออฟเซ็ตที่ชัดเจนเนื่องจากทุกอย่างอยู่ในขอบเขต 4 ไบต์ แต่จะไม่เป็นเช่นนั้นเสมอไป Visual C ++ บรรจุในขอบเขต 8 ไบต์โดยค่าเริ่มต้น


1
"Visual C ++ บรรจุในขอบเขต8 ไบต์โดยค่าเริ่มต้น" สิ่งนี้ช่วยแก้ปัญหาของฉันได้ขอบคุณมาก!
Chris L

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

มีสิ่งนี้


0

หากคุณมีไบต์ [] คุณควรจะสามารถใช้คลาส BinaryReader และตั้งค่าบน NewStuff โดยใช้เมธอด ReadX ที่มี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.