構造体 Byte 配列 構造体とコピーする方法 構造体とバイト配列の変換を行うには System.Runtime.InteropServices 名前空間をインポートして置くと便利で有る Imports System.Runtime.InteropServices using System.Runtime.InteropServices; 下記の 3 種類の構造体にバイト配列の値を格納した場合に付いて検証する Type1 構造体は総ての要素が Short 型の場合で Type2 と Type3 は最初の要素が Short 型 2 番目の要素が Integer 型 3 番目と 4 番目の要素が Bute 型で 構造体の長さ ( 総バイト数 ) は孰れも 8 バイトで有る 猶 3 番目の構造体は System.Runtime.InteropServices.StructLayout 属性で構造体のメモリ内での配置をカスタマイズして居る Private Structure Type1 Dim Item1 As Short Dim Item2 As Short Dim Item3 As Short Dim Item4 As Short End Structure Private Structure Type2 Dim Item1 As Short Dim Item2 As Integer Dim Item3 As Byte Dim Item4 As Byte End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Structure Type3 Dim Item1 As Short Dim Item2 As Integer Dim Item3 As Byte Dim Item4 As Byte End Structure private struct type1 public short item1; public short item2; public short item3; public short item4; -1-
private struct type2 public short item1; public int item2; public byte item3; public byte item4; [StructLayout(LayoutKind.Sequential,Pack=1)] private struct type3 public short item1; public int item2; public byte item3; public byte item4; 因みに System.Runtime.InteropServices.StructLayout 属性で指定する LayoutKind 列挙型は 下記の通りで有る 属性 Auto Explicit Sequential 解説ランタイムは アンマネージメモリ内のオブジェクトのメンバに対して適切なレイアウトを自動的に選択する 此の列挙体メンバで定義されたオブジェクトは マネージコードの外に公開出来ない 公開しようとすると 例外が生成される アンマネージメモリ内に有るオブジェクトの各メンバの正確な位置は 明示的に制御される 各メンバは FieldOffsetAttribute を使用して 其の型内のフィールドの位置を指定する必要が有る オブジェクトのメンバは アンマネージメモリにエクスポートする時に表示される順番に従ってレイアウトされる メンバは StructLayoutAttribute.Pack で指定したパッキングに従ってレイアウトされる メンバは非連続に出来る 上記の夫々の構造体に格納するバイト配列の各要素の値は 下記の通りで有る 25H 52H 9AH 44H 16H 42H 21H 2CH 上記に対して 下記のコードを実行した結果を検証して観る Dim B() As Byte = &H25, &H52, &H9A, &H44, &H16, &H42, &H21, &H2C Dim T1 As Type1 Dim T2 As Type2 Dim T3 As Type3 Dim P1 As System.IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(Type1))) Dim P2 As System.IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(Type2))) Dim P3 As System.IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(Type3))) Marshal.Copy(B, 0, P1, B.Length) Marshal.Copy(B, 0, P2, B.Length) Marshal.Copy(B, 0, P3, B.Length) T1 = DirectCast(Marshal.PtrToStructure(P1, GetType(Type1)), Type1) -2-
T2 = DirectCast(Marshal.PtrToStructure(P2, GetType(Type2)), Type2) T3 = DirectCast(Marshal.PtrToStructure(P3, GetType(Type3)), Type3) System.Runtime.InteropServices.Marshal.FreeHGlobal(P1) System.Runtime.InteropServices.Marshal.FreeHGlobal(P2) System.Runtime.InteropServices.Marshal.FreeHGlobal(P3) byte[] b = 0x25, 0x52, 0x9A, 0x44, 0x16, 0x42, 0x21, 0x2C ; type1 t1; type2 t2; type3 t3; IntPtr p1 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(type1))); IntPtr p2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(type2))); IntPtr p3 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(type3))); Marshal.Copy(b, 0, p1, b.length); Marshal.Copy(b, 0, p2, b.length); Marshal.Copy(b, 0, p3, b.length); t1 = (type1)marshal.ptrtostructure(p1, typeof(type1)); t2 = (type2)marshal.ptrtostructure(p2, typeof(type2)); t3 = (type3)marshal.ptrtostructure(p3, typeof(type3)); Marshal.FreeHGlobal(p1); Marshal.FreeHGlobal(p2); Marshal.FreeHGlobal(p3); 上記のコードを実行して DataGridView コントロールに各構造体の要素を表示したのが下図で有る Type1 構造体の様に各要素が同じデータ型で有れば正しく格納されるが Type2 構造体の様に各要素のデータ型が異なれば正しく格納されない 亦 Type3 構造体の様にアライメントを指定して構造体を定義すると 正しく格納される 猶 Pack:=1 の部分を Pack:=2 Pack:=4 Pack:=8 とすると 何う成るかも示して置く Pack:=1-3-
Pack:=2 Pack:=4 Pack:=8-4-
構造体 Byte 配列とコピーする方法 下記の構造体の値をバイト配列に格納した場合に付いて検証する 構造体は要素は総て最も問題と成る固定長の String 型にして有る 猶 構造体は System.Runtime.InteropServices.StructLayout 属性で構造体のメモリ内での配置をカスタマイズして居り 亦 固定長文字列を必要とする のファイル入出力関数 (FileGet や FilePut 等 ) を使う時の為に VBFixedStringAttribute 属性を使用して居る 其の為 では の参照設定を追加して置く必要が有る 猶 此の構造体には 構造体の要素をバイト配列に格納する為のユーザー定義関数 (Getbyte) が含まれて居る <StructLayout(LayoutKind.Sequential, Pack:=1)> Structure Buffer <VBFixedString(3), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _ Dim Item1 As String <VBFixedString(3), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _ Dim Item2 As String Function Getbyte(ByVal Data As Buffer) As Byte() Dim Num As Integer Dim Cnt As Integer Dim Buf() As Byte Dim Tmp() As Byte Num = Marshal.SizeOf(Data) ReDim Buf(Num - 1) Cnt = 0 Tmp = System.Text.Encoding.GetEncoding("SHIFT_JIS").GetBytes(Data.Item1) For I As Integer = 0 To (Tmp.Length - 1) If Cnt > Buf.Length - 1 Then Exit For Buf(Cnt) = Tmp(I) : Cnt += 1 Next Tmp = System.Text.Encoding.GetEncoding("SHIFT_JIS").GetBytes(Data.Item2) For I As Integer = 0 To (Tmp.Length - 1) If Cnt > Buf.Length - 1 Then Exit For Buf(Cnt) = Tmp(I) : Cnt += 1 Next Getbyte = Buf End Function End Structure [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct buffer [VBFixedString(3), MarshalAs(UnmanagedType.ByValTStr, SizeConst=3)] public string item1; [VBFixedString(3), MarshalAs(UnmanagedType.ByValTStr, SizeConst=3)] public string item2; public byte[] Getbyte(buffer data) int num; int cnt; byte[] buf; -5-
byte[] tmp; num = Marshal.SizeOf(data); buf = new byte[num]; cnt = 0; tmp = System.Text.Encoding.GetEncoding("SHIFT_JIS").GetBytes(data.item1); for (int i = 0; i < tmp.length; i++) if (cnt > buf.length - 1) break; buf[cnt] = tmp[i]; cnt++; tmp = System.Text.Encoding.GetEncoding("SHIFT_JIS").GetBytes(data.item2); for (int i = 0; i < tmp.length; i++) if (cnt > buf.length - 1) break; buf[cnt] = tmp[i]; cnt++; return buf; 因みに VBFixedStringAttribute 属性は 文字列の長さを文字数ではなくバイト数で指定する 猶 System.Runtime.InteropServices.StructLayout 属性に付いては前項を参照され度い 上記に対して 下記のコードを実行した結果を検証して観る Dim B() As Byte Dim T As Buffer With T.Item1 = txtitem1.text.item2 = txtitem2.text End With B = T.Getbyte(T) lstdisp.items.clear() For I As Integer = 0 To (B.Length - 1) lstdisp.items.add(b(i).tostring("x").padleft(2, "0"c) & " - " & Chr(B(I))) Next byte[] b; buffer t; t.item1 = txtitem1.text; t.item2 = txtitem2.text; -6-
b = t.getbyte(t); lstdisp.items.clear(); for (int i = 0; i < b.length; i++) lstdisp.items.add(b[i].tostring("x").padleft(2, '0') + " - " + System.Convert.ToChar(b[i]).ToString()); 上記のコードを実行して ListBox コントロールにバイト配列の要素を表示したのが下図で有る 構造体の夫々の要素が VBFixedStringAttribute 属性で指定したバイト数と等しい時は 正しく格納されるが 等しくない時は正しく格納されない 此れは VBFixedStringAttribute 属性は 此の属性に対応するメソッド ( 固定長文字列を必要とする の FileGet や FilePut 等のファイル入出力関数 ) 等に対する情報提供の為の属性で 可変長の文字列を固定長の文字列に変換する訳ではないからで有る 従って の FileSystem を使用してファイルに書き込めば VBFixedStringAttribute 属性で指定したバイト数と等しくない要素の値も正しく保存される 下記のコードでは 書き込んだファイルからバイト配列に読み込んで確認して居る 構造体のユーザー定義関数 (Getbyte) の中で 各要素のバイト数を Marshal.SizeOf メソッドで取得し 其のバイト数丈をバイト配列に格納する事が出来れば良いのだが String 型の要素の場合はサイズを取得する事が出来ない -7-
Dim B() As Byte Dim T As Buffer With T.Item1 = txtitem1.text.item2 = txtitem2.text End With Dim F As String = Application.StartupPath If Not F.EndsWith(" ") Then F &= " " F &= "temp.rnd" FileOpen(1, F, OpenMode.Random,,, 6) FilePut(1, T, 1) FileClose(1) B = Microsoft.VisualBasic.FileIO.FileSystem.ReadAllBytes(F) Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(F) lstdisp.items.clear() For I As Integer = 0 To (B.Length - 1) lstdisp.items.add(b(i).tostring("x").padleft(2, "0"c) & " - " & Chr(B(I))) Next byte[] b; buffer t; t.item1 = txtitem1.text; t.item2 = txtitem2.text; string f = Application.StartupPath; if (!f.endswith(@" ")) f += @" "; f += "temp.rnd"; FileSystem.FileOpen(1, f, OpenMode.Random, OpenAccess.Write, OpenShare.Default, 6); FileSystem.FilePut(1, t, 1); FileSystem.FileClose(1); b = Microsoft.VisualBasic.FileIO.FileSystem.ReadAllBytes(f); Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(f); lstdisp.items.clear(); for (int i = 0; i < b.length; i++) lstdisp.items.add(b[i].tostring("x").padleft(2, '0') + " - " + System.Convert.ToChar(b[i]).ToString()); -8-