デリゲートに対する既定のマーシャリング
マネージ デリゲートは、呼び出し機構に基づいて、COM インターフェイスまたは関数ポインターとしてマーシャリングされます。
プラットフォーム呼び出しでは、デリゲートは既定によりアンマネージ関数ポインターとしてマーシャリングされます。
COM 相互運用機能では、デリゲートは既定により _Delegate 型の COM インターフェイスとしてマーシャリングされます。 _Delegate インターフェイスは、Mscorlib.tlb タイプ ライブラリで定義されており、Delegate.DynamicInvoke メソッドを含んでいます。このメソッドを使用すると、デリゲートが参照するメソッドを呼び出すことができます。
マネージ デリゲート データ型に対するマーシャリング オプションを次の表に示します。 MarshalAsAttribute 属性は、デリゲートをマーシャリングするための UnmanagedType 列挙値をいくつか提供します。
列挙型 |
アンマネージ表現の説明 |
---|---|
UnmanagedType.FunctionPtr |
アンマネージ関数ポインター |
UnmanagedType.Interface |
Mscorlib.tlb で定義されている、_Delegate 型のインターフェイス |
DelegateTestInterface のメソッドを COM タイプ ライブラリにエクスポートする次のコード例について検討します。 キーワード ref (または ByRef) でマークされたデリゲートだけが In/Out パラメーターとして渡されます。
using System;
using System.Runtime.InteropServices;
public interface DelegateTest {
void m1(Delegate d);
void m2([MarshalAs(UnmanagedType.Interface)] Delegate d);
void m3([MarshalAs(UnmanagedType.Interface)] ref Delegate d);
void m4([MarshalAs(UnmanagedType.FunctionPtr)] Delegate d);
void m5([MarshalAs(UnmanagedType.FunctionPtr)] ref Delegate d);
}
タイプ ライブラリ表現
importlib("mscorlib.tlb");
interface DelegateTest : IDispatch {
[id(…)] HRESULT m1([in] _Delegate* d);
[id(…)] HRESULT m2([in] _Delegate* d);
[id(…)] HRESULT m3([in, out] _Delegate** d);
[id()] HRESULT m4([in] int d);
[id()] HRESULT m5([in, out] int *d);
};
他の任意のアンマネージ関数ポインターを逆参照できるのと同じように、関数ポインターも逆参照できます。
メモ |
---|
アンマネージ コードによって保持されるマネージ デリゲートへの関数ポインターを参照する場合、共通言語ランタイムによって該当のマネージ オブジェクトに対してガベージ コレクションが実行されるのは回避できません。 |
たとえば、次のコードは、SetChangeHandler メソッドに渡される cb オブジェクトへの参照が、Test メソッドの有効期間を超えて cb を有効な状態に維持できるわけではないため、正しくありません。 cb オブジェクトがガベージ コレクションによって収集されると、SetChangeHandler に渡された関数ポインターは無効になります。
public class ExternalAPI {
[DllImport("External.dll")]
public static extern void SetChangeHandler(
[MarshalAs(UnmanagedType.FunctionPtr)]ChangeDelegate d);
}
public delegate bool ChangeDelegate([MarshalAs(UnmanagedType.LPWStr) string S);
public class CallBackClass {
public bool OnChange(string S){ return true;}
}
internal class DelegateTest {
public static void Test() {
CallBackClass cb = new CallBackClass();
// Caution: The following reference on the cb object does not keep the
// object from being garbage collected after the Main method
// executes.
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
}
予測不可能なガベージ コレクションに対処するために、呼び出し元は、アンマネージ関数ポインターが使用されている間は cb オブジェクトを有効な状態に維持する必要があります。 オプションとして、次の例に示すように、関数ポインターが不要になったことをアンマネージ コードからマネージ コードに通知することもできます。
internal class DelegateTest {
CallBackClass cb;
// Called before ever using the callback function.
public static void SetChangeHandler() {
cb = new CallBackClass();
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
// Called after using the callback function for the last time.
public static void RemoveChangeHandler() {
// The cb object can be collected now. The unmanaged code is
// finished with the callback function.
cb = null;
}
}