相互運用マーシャリング (Interop Marshaling) によって、マネージド型とアンマネージド型との間でメソッドの引数と戻り値を変換できます。
Marshalクラスからは、アンマネージド コードを扱うときに利用できるメソッドが数多く提供されます。
[System.Security.SecurityCritical]
public static IntPtr AllocHGlobal (
int cb // メモリ内の必要バイト数
);
AllocHGlobal(Int32) - Marshal.AllocHGlobal メソッド (System.Runtime.InteropServices) | Microsoft Learn
cbバイト分のメモリを確保し、そのメモリへのポインタを取得できます。確保したメモリは、Marshal.FreeHGlobal()で必ず解放します。
[System.Security.SecurityCritical] public static void FreeHGlobal ( IntPtr hglobal );Marshal.FreeHGlobal(IntPtr) メソッド (System.Runtime.InteropServices) | Microsoft Learn
[SecurityCriticalAttribute] public static void Copy( int[] source, // コピー元の配列 int startIndex, // コピー元の配列の開始位置 IntPtr destination, // コピー先のポインタ int length // コピーする要素数 )Copy(Int32[], Int32, IntPtr, Int32) - Marshal.Copy Method (System.Runtime.InteropServices) | Microsoft Learn
[System.Security.SecurityCritical] public static void Copy ( IntPtr source, // コピー元のポインタ int[] destination, // コピー先の配列 int startIndex, // コピー先の配列の開始位置 int length // コピーする要素数 );Copy(IntPtr, Int32[], Int32, Int32) - Marshal.Copy Method (System.Runtime.InteropServices) | Microsoft Learn
int[] arary1 = { 1, 2, 3 }; int size = Marshal.SizeOf(arary1[0]) * arary1.Length; IntPtr ptr = Marshal.AllocHGlobal(size); // 配列を格納できる大きさのメモリを割り当てる try { // マネージド配列を、アンマネージド配列へコピーする Marshal.Copy(arary1, 0, ptr, arary1.Length); unsafe { // アンマネージド配列に格納された値を確認する int p0 = *(int*)ptr; // 1 int p1 = *((int*)ptr + 1); // 2 int p2 = *((int*)ptr + 2); // 3 } // アンマネージド配列を、マネージド配列へコピーする int[] arary2 = new int[arary1.Length]; Marshal.Copy(ptr, arary2, 0, arary1.Length); } finally { // アンマネージド配列のためのメモリを解放する Marshal.FreeHGlobal(ptr); }使用例 - Marshal.Copy メソッド (Int32[], Int32, IntPtr, Int32) (System.Runtime.InteropServices) | MSDN
これはマーシャリングとは無関係ですが、Buffer.MemoryCopy()やCopyMemory()で、アンマネージド間でコピーできます。c# - Copy data from from IntPtr to IntPtr - Stack Overflow
[SecurityCriticalAttribute] public static IntPtr StringToHGlobalAuto( string s // コピーする文字列 )[SecurityCriticalAttribute] public static IntPtr StringToHGlobalAuto( string s ) | MSDN
マネージド文字列の内容を、アンマネージド メモリーへコピーできます。このメソッドは必要なときにANSIに変換しますが、これを強制したいならばStringToHGlobalAnsi()を用います。
メソッド | ANSIへの変換 |
---|---|
StringToHGlobalAuto() | 必要ならば変換する |
StringToHGlobalAnsi() | 変換する |
StringToHGlobalUni() | 変換しない |
String managed = "ABC"; IntPtr ptr = Marshal.StringToHGlobalAuto(managed); unsafe { char* str = (char*)(ptr.ToPointer()); // str[0] … 65 'A' char // str[1] … 66 'B' char // str[2] … 67 'C' char // str[3] … 0 '\0' char } Marshal.FreeHGlobal(ptr);
同様の処理は、C++/CLIでは次のように記述します。
String^ managed = gcnew String("ABC"); IntPtr ptr = Marshal::StringToHGlobalAuto(managed); System::Char* str = static_cast<System::Char*>(ptr.ToPointer()); // str[0] … 65 'A' wchar_t // str[1] … 66 'B' wchar_t // str[2] … 67 'C' wchar_t // str[3] … 0 '\0' wchar_t Marshal::FreeHGlobal(ptr);
これは、次のように記述しても同じです。
System::Char* str = static_cast<System::Char*>(Marshal::StringToHGlobalAuto(managed).ToPointer()); // ... Marshal::FreeHGlobal(IntPtr(str)); // ポインタを取得してから渡す
C++/CLIのcharは8ビットであり、C#の16ビットであるそれとは異なるため、charにキャストすると、
// str[0] … 65 'A' char // str[1] … 0 '\0' char // str[2] … 66 'B' char // str[3] … 0 '\0' char // str[4] … 67 'C' char // str[5] … 0 '\0' char
のように変換されます。これに対処するにはStringToHGlobalAnsi()で、ANSI形式に変換した上でコピーします。
char* str = static_cast<char*>(Marshal::StringToHGlobalAnsi(managed).ToPointer());
[System.Security.SecurityCritical] [System.Runtime.InteropServices.ComVisible(true)] public static void StructureToPtr ( object structure, // コピー元のオブジェクト IntPtr ptr, // コピー先のポインタ bool fDeleteOld );StructureToPtr(Object, IntPtr, Boolean) - Marshal.StructureToPtr メソッド (System.Runtime.InteropServices) | Microsoft Learn
マネージド オブジェクトのデータを、アンマネージド メモリーへコピーできます。
fDeleteOldをtrueとすると、データをコピーする前にDestroyStructure()が呼ばれptrの領域が開放されます。そこに有効なデータが含まれているときにfalseとすると、メモリリークを引き起こす恐れがあります。
メソッド | |
---|---|
GetComInterfaceForObject(Object, Type) | |
GetIUnknownForObject(Object) | |
GetIDispatchForObject(Object) | |
指定のインターフェイスの、参照カウントを減少できます。
[System.Security.SecurityCritical] public static int Release (IntPtr pUnk);Marshal.Release(IntPtr) Method (System.Runtime.InteropServices) | Microsoft Learn
インターフェイスを指定するpUnkは、GetComInterfaceForObject()などから取得できます。
戻り値は、参照カウントの新しい値です。
共通言語ランタイム (CLR) がCOMオブジェクトの参照カウントを管理するため、通常はこのメソッドを直接使う必要はありません。カスタム マーシャラ (custom marshaler) を試すときに、オブジェクトの有効期間 (lifetime) を手動で管理するような場合に使用します。Remarks - Marshal.Release(IntPtr) Method (System.Runtime.InteropServices) | Microsoft Learn
指定のオブジェクトが使用中の場合、制御が戻らないことがあります。
指定のCOMオブジェクトに関連付けられているRCW (Runtime Callable Wrapper / ランタイム呼び出し可能ラッパー) の、参照カウントを減少できます。
[System.Security.SecurityCritical] public static int ReleaseComObject (object o);Marshal.ReleaseComObject(Object) Method (System.Runtime.InteropServices) | Microsoft Learn
戻り値は、RCWの参照カウントの新しい値です。RCWはラップされたCOMオブジェクトへの参照を1つだけ保持するため、この値は通常0です。
指定のCOMオブジェクトが使用中の場合、制御が戻らないことがあります。
このメソッドの呼び出しは、戻り値が0になるまでReleaseComObject()をくり返し呼ぶのと同じです。Remarks - Marshal.FinalReleaseComObject(Object) Method (System.Runtime.InteropServices) | Microsoft Learn
C++のクラスや構造体のラッパーを作成するときには、そのデータメンバのメモリ内での配置を明示するためにStructLayout属性を指定する必要があります。
特別な処理なくマーシャリング可能なデータならば、refで参照渡しすることで暗黙的にマーシャリングされます。c# - .NET Interop IntPtr vs. ref - Stack Overflow