利用 System.Runtime.InteropServices.Marshal 將結構體與字節數組互轉,再通過 FileStream 讀寫文件。關鍵在於結構體佈局屬性與字段封送屬性的精確定義。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TestStruct
{
[MarshalAs(UnmanagedType.R8)] // double
public double dTest;
[MarshalAs(UnmanagedType.U2)] // UInt16
public ushort uTest;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 固定長度 byte[]
public byte[] bTest;
}
// 結構體 → byte[]
public static byte[] RawSerialize(object obj)
{
int size = Marshal.SizeOf(obj);
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(obj, ptr, false);
byte[] bytes = new byte[size];
Marshal.Copy(ptr, bytes, 0, size);
return bytes;
}
finally { Marshal.FreeHGlobal(ptr); }
}
// byte[] → 結構體
public static TestStruct RawDeserialize(byte[] bytes)
{
int size = Marshal.SizeOf(typeof(TestStruct));
if (size > bytes.Length) return default;
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, ptr, size);
return (TestStruct)Marshal.PtrToStructure(ptr, typeof(TestStruct));
}
finally { Marshal.FreeHGlobal(ptr); }
}
private void BtnWrite_Click(object sender, EventArgs e)
{
string path = Application.StartupPath + @"\test.dat";
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
var ts = new TestStruct
{
dTest = double.Parse(txtDouble.Text),
uTest = ushort.Parse(txtUshort.Text),
bTest = Encoding.Default.GetBytes(txtString.Text)
};
fs.Write(RawSerialize(ts), 0, Marshal.SizeOf(ts));
}
}
private void BtnRead_Click(object sender, EventArgs e)
{
string path = Application.StartupPath + @"\test.dat";
if (!File.Exists(path)) { MessageBox.Show("文件不存在"); return; }
byte[] bytes = new byte[Marshal.SizeOf(typeof(TestStruct))];
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
fs.Read(bytes, 0, bytes.Length);
var ts = RawDeserialize(bytes);
txtDouble.Text = ts.dTest.ToString();
txtUshort.Text = ts.uTest.ToString();
txtString.Text = Encoding.Default.GetString(ts.bTest);
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 字段偏移錯誤 | 結構體對齊與字段順序不匹配 | 使用 LayoutKind.Sequential,按聲明順序;必要時用 FieldOffset 顯式指定 |
| 字符串亂碼 | CharSet 不一致(Ansi/Unicode) | 結構體與 MarshalAs 統一 CharSet,或改用固定長度 byte[] 手動編碼 |
| 數組長度不固定 | ByValArray 需 SizeConst | 確定最大長度,讀取後按實際有效長度解碼 |
| 跨平台失效 | 結構體大小/對齊在 32/64 位不同 | 避免在跨平台持久化場景使用,改用 Protobuf/JSON/BinaryFormatter |