C#(.NET Framework)では、構造体(Managed)やメンバに対して属性を指定することにより、C言語やC++(Unmanaged)間でデータの相互交換を行うことができます。
MarshalAs属性を用いた構造体の作成
C#では、Cと違って定義だけでは配列の長さがわかりません。そこで、C#(.NET Framework)では、構造体やメンバに対して属性を指定することにより、ManagedとUnmanagedで相互交換を行わせる事ができるようになっている。
例えば、以下のようなC++の構造体を、C#でアクセスします。
struct Info { int index; char name[128]; int statuses[50]; };
この場合、Managed構造体をメンバ順序もサイズも同じUnmanaged構造体を定義するには、属性を使用して以下のように定義します。
[StructLayout(LayoutKind.Sequential)] // メンバーは定義順に格納される public struct Info { [MarshalAs(UnmanagedType.I4)] // Signed int(4Byte)で格納(属性無くても大丈夫です) public int index; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)] // char[128]で格納 public string name; [MarshalAs(UnmanagedType.ByValArray,SizeConst=50)] // int[50]で格納 public int[] statuses; // ↑bool, byte, char, short, int, long, sbyte, ushort, // uint, ulong, float, double配列の場合は属性を付けずに以下のようにしても良い // この場合は配列のインスタンスを作成する必要は無い。→これはunsafeブロック中でしか使えないらしい・・・ // public fixed int statuses[50]; }
メンバー名 | 説明 |
---|---|
ByValArray | MarshalAsAttribute.Value プロパティを ByValArray に設定した場合、SizeConst フィールドは、配列の要素数を示すように設定する。 |
ByValTStr | 構造体内の C スタイルの固定サイズ文字列 (char s[5] など) と同様に機能する。 マネージ コードでのこの動作は、Microsoft Visual Basic 6.0 の動作 (終端が null でない。例 :MyString As String * 5) とは異なる。 |
Marshal.StructureToPtrによる構造体からポインタへの変換
Marshal.StructureToPtrを使います。ptr パラメーターが指す、事前に割り当てられたメモリ ブロックに structure の内容をコピーします。
static IntPtr ToPtr(Hoge obj) { IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Hoge))); Marshal.StructureToPtr(obj, ptr, false); return ptr; }
Marhsal.Copyを使用した構造体からバイト配列への変換
バイト配列に変換したい場合は、ポインタへ変換後、Marhsal.Copyします。アンマネージ メモリ ポインターのデータを 8 ビット符号なし整数のマネージ配列にコピーします。
static byte[] ToBytes(Hoge obj) { int size = Marshal.SizeOf(typeof(Hoge)); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(obj, ptr, false); byte[] bytes = new byte[size]; Marshal.Copy(ptr, bytes, 0, size); Marshal.FreeHGlobal(ptr); return bytes; }
Marshal.PtrToStructureを使用したポインタから構造体への変換
Marshal.PtrToStructureをすると、
。メモリの内容を元に構造体などを復元することができます。これでC言語などとデータをやりとりできます。構造体の配列に関し、MarshalAs属性を使用すれば扱えますが、この場合、コンパイル時にサイズを決定した固定長配列に限られます。
static Hoge ToStruct(IntPtr ptr) { return (Hoge)Marshal.PtrToStructure(ptr, typeof(Hoge)); }
GCHandleによるバイト配列から構造体への変換
先ほどと同様にMarshal.Copyを使うこともできますが、ここではもう一つの方法としてGCHandleでバイト配列のポインタを取得し、それを用いてMarshal.PtrToStructureを行います。GCHandleは、GCHandle::Alloc によってmanagedオブジェクトのハンドルを取得します。ハンドルはIntPtrに変換でき、またそのIntPtrからハンドルに戻せるので、unmanagedクラスではポインタを持っておけば、ポインタ経由で、managedなオブジェクトへの参照ができます。
static Hoge ToStruct(byte[] bytes) { GCHandle gch = GCHandle.Alloc(bytes, GCHandleType.Pinned); Hoge result = (Hoge)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(Hoge)); gch.Free(); return result; }