デリゲートに対する既定のマーシャリング

更新 : 2007 年 11 月

マネージ デリゲートは、呼び出し機構に基づいて、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;
   }
}

参照

概念

Blittable 型と非 Blittable 型

方向属性

コピーと固定

その他の技術情報

既定のマーシャリングの動作