構造体からバイト配列を出力できたらどれだけ楽だろうか
検証
Marshal.AllocHGlobal + Marshal.StructureToPtr/PtrToStructure
下記のような定義があるとする。
[StructLayout(LayoutKind.Sequential)] struct TestStructure { public byte param1; [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)] public byte[] array1; public int param2; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public int[] array2; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)] public string param3; [MarshalAs(UnmanagedType.Struct)] public TestStructureInternal param4; } [StructLayout(LayoutKind.Sequential)] struct TestStructureInternal { public int next; public char chr; } public partial class Form1 : Form { private void button1_Click(object sender, EventArgs e) { var item = new TestStructure(); //01 item.param1 = (byte)1; //0a 0b 0c item.array1 = new byte[] {0x0a,0x0b,0x0c}; //02 00 00 00 item.param2 = (int)2; //ff 00 00 00 00 01 00 00 item.array2 = new int[] { 0x00ff, 0x0100 }; //74 65 73 00 tes. ←MarshalAsでSizeConstに4を指定している関係でNull終端が設定されたためtが欠落しているものと思われる.ByValTStrの指定が悪いのか原因不明。 item.param3 = "test"; item.param4 = new TestStructureInternal(); //06 00 00 00 item.param4.next = (int)6; //61 item.param4.chr = 'a'; //00 00 00 ←おそらくバウンダリのための空白 string filename = "outfile.out"; if (File.Exists(filename)) { File.Delete(filename); } using (var file = new FileStream(filename, FileMode.CreateNew)) using (var writer = new BinaryWriter(file)) { ReadWriteStructWithAllocHGlobal.WriteTo(writer, item); } } }
実行結果は以下のようになった。わかりやすいように上記コードにもダンプを貼り付けておく。
00000000 01 0a 0b 0c 02 00 00 00 ff 00 00 00 00 01 00 00 |................| 00000010 74 65 73 00 06 00 00 00 61 00 00 00 |tes.....a...|
解決策
このままでは実装しても使い物にならない(文字列の終端NULLが勝手に挿入される)ので、カスタマイズで実装してみた。
https://gist.github.com/indication/6b8d85c7da8cf6fd8f5c#file-readwritestructwithreflection-cs
Structure→byte[]はできたが、逆はまだ実装できていない。
必要に応じて拡張する予定。
BigEndianにも対応してみたが、「BitConverter.IsLittleEndian」のフラグを参照していないので、考慮に入れてみる。
isBigEndian=falseの場合の結果 00000000 01 0a 0b 0c 02 00 00 00 ff 00 00 00 00 01 00 00 |................| 00000010 74 65 73 74 06 00 00 00 61 00 |test....a.| isBigEndian=trueの場合の結果 00000000 01 0a 0b 0c 00 00 00 02 00 00 00 ff 00 00 01 00 |................| 00000010 74 65 73 74 00 00 00 06 00 61 |test.....a|
ライセンス
上記検証コードについては、BinaryReader・BinaryWriterでの構造体の読み書き (構造体⇔バイト配列の変換) - Programming/.NET Framework/Tips - 総武ソフトウェア推進所 の規約に基づくものとし、私が記載したものはWTFPL Version 2.0とします。
参考文献
- BinaryReader・BinaryWriterでの構造体の読み書き (構造体⇔バイト配列の変換) - Programming/.NET Framework/Tips - 総武ソフトウェア推進所 複数の処理サンプル(完全)
- 構造体からポインタ(バイト配列)への変換 - schima.hatenablog.com 処理速度の比較等
- Marshal.StructureToPtr
- CopyMemory
- ポインタ
- C#で構造体と配列のコピーについて(ビッグエンディアンと、リトルエンディアン)
- BitConverter.IsLittleEndianフィールド
- IPAddress.NetworkToHostOrder メソッド
- IPAddress.HostToNetworkOrderメソッド