Panduan Komprehensif untuk Membaca Struktur Data C/C++ di C# dari Array byte[]
Saat melakukan migrasi atau bekerja dengan struktur data di berbagai bahasa, terutama dari C/C++ ke C#, para pengembang sering menghadapi tantangan untuk menerjemahkan representasi byte dari struct C/C++ ke ekuivalen C# yang dapat dikelola. Pos blog ini membahas cara mengonversi dengan efisien array byte[]
yang berisi data struct C/C++ ke dalam struct C#, tanpa kerepotan yang sering muncul dari penyalinan data yang tidak perlu.
Memahami Masalah
Pertimbangkan definisi struct C berikut:
typedef OldStuff {
CHAR Name[8];
UInt32 User;
CHAR Location[8];
UInt32 TimeStamp;
UInt32 Sequence;
CHAR Tracking[16];
CHAR Filler[12];
}
Representasi ini terdiri dari urutan karakter dan bilangan bulat yang tetap. Saat bekerja dengan data biner seperti itu di C#, para pengembang menghadapi tantangan untuk menyelaraskan struktur dengan akurat dan mengonversinya agar dapat digunakan dalam kode yang dikelola.
Struct C# yang sesuai menggunakan StructLayout
untuk mengendalikan bagaimana data diatur dalam memori:
[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;
}
Pendekatan Konvensional
Pendekatan yang umum diambil oleh para pengembang melibatkan penyalinan array byte ke dalam buffer terpisah sebelum mencoba untuk mendeklarasikan strukturnya ke dalam struct C# yang diinginkan. Berikut adalah metode yang sering terlihat:
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();
Meskipun pendekatan ini berhasil, ia memperkenalkan overhead yang tidak perlu, terutama terkait penggunaan memori dan kinerja, dengan menciptakan salinan perantara dari data.
Solusi yang Lebih Baik
Alih-alih menduplikasi data, Anda dapat langsung memanfaatkan array byte untuk mengekstrak struct Anda. Berikut adalah metode yang lebih efisien:
Menggunakan GCHandle Langsung
NewStuff ByteArrayToNewStuff(byte[] bytes)
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));
return stuff;
}
finally
{
handle.Free();
}
}
Metode ini mengalokasikan handle untuk array byte asli dan menghilangkan kebutuhan untuk membuat buffer tambahan.
Metode Generik untuk Fleksibilitas
Jika Anda ingin mengonversi struct apa pun dari array byte, Anda dapat membuat metode generik:
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;
}
Pendekatan Tidak Aman untuk Kinerja Lebih
Bagi pengguna tingkat lanjut, memanfaatkan konteks unsafe
dapat memberikan kinerja yang lebih baik, karena memungkinkan Anda untuk bekerja langsung dengan pointer:
unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
fixed (byte* ptr = &bytes[0])
{
return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
}
}
Kesimpulan
Membaca struktur data C/C++ ke C# dari array byte[]
secara efektif tidak harus menjadi hal yang merepotkan. Dengan menggunakan GCHandle
langsung pada array byte atau menggunakan kode unsafe
, Anda meningkatkan kinerja dan menyederhanakan kode Anda. Mengadopsi pendekatan ini menghasilkan kode yang lebih bersih dan lebih mudah dirawat sambil menghindari overhead memori yang tidak perlu.
Jadi, lain kali Anda menemukan diri Anda menangani konversi data lintas bahasa, ingatlah metode ini untuk memperlancar proses Anda!