相互運用マーシャリング (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