次の方法で共有


System.Runtime.InteropServices.ICustomMarshaler インターフェイス

この記事では、この API のリファレンス ドキュメントに補足的な解説を提供します。

ICustomMarshaler インターフェイスは、メソッド呼び出しを処理するためのカスタム ラッパーを提供します。

マーシャラーは、古いインターフェイスと新しいインターフェイスの機能の間のブリッジを提供します。 カスタム マーシャリングには、次の利点があります。

  • これにより、古いインターフェイスで動作するように設計されたクライアント アプリケーションは、新しいインターフェイスを実装するサーバーでも動作できます。
  • これにより、新しいインターフェイスで動作するように構築されたクライアント アプリケーションは、古いインターフェイスを実装するサーバーと連携できます。

異なるマーシャリング動作を導入するインターフェイス、またはコンポーネント オブジェクト モデル (COM) に別の方法で公開されるインターフェイスがある場合は、相互運用マーシャラーを使用する代わりにカスタム マーシャラーを設計できます。 カスタム マーシャラーを使用すると、新しい .NET Framework コンポーネントと既存の COM コンポーネントの区別を最小限に抑えることができます。

たとえば、 INewというマネージド インターフェイスを開発しているとします。 このインターフェイスが標準の COM 呼び出し可能ラッパー (CCW) を介して COM に公開されている場合は、マネージド インターフェイスと同じメソッドを持ち、相互運用マーシャラーに組み込まれているマーシャリング規則を使用します。 ここで、 IOld というよく知られた COM インターフェイスが、 INew インターフェイスと同じ機能を既に提供しているとします。 カスタム マーシャラーを設計することで、INew インターフェイスのマネージド実装への呼び出しを単に委任するIOldのアンマネージ実装を提供できます。 そのため、カスタム マーシャラーは、マネージド インターフェイスとアンマネージド インターフェイスの間のブリッジとして機能します。

ディスパッチ専用インターフェイスでマネージド コードからアンマネージ コードに呼び出すとき、カスタム マーシャラーは呼び出されません。

マーシャリングの種類を定義する

カスタム マーシャラーを構築する前に、マーシャリングされるマネージド インターフェイスとアンマネージド インターフェイスを定義する必要があります。 これらのインターフェイスは一般的に同じ関数を実行しますが、マネージド オブジェクトとアンマネージド オブジェクトには異なる方法で公開されます。

マネージド コンパイラはメタデータからマネージド インターフェイスを生成し、結果として得られるインターフェイスは他のマネージド インターフェイスと同様です。 次の例は、一般的なインターフェイスを示しています。

public interface INew
{
    void NewMethod();
}
Public Interface INew
    Sub NewMethod()
End Interface

アンマネージ型は、インターフェイス定義言語 (IDL) で定義し、Microsoft インターフェイス定義言語 (MIDL) コンパイラを使用してコンパイルします。 次の例に示すように、ライブラリ ステートメント内でインターフェイスを定義し、ユニバーサル一意識別子 (UUID) 属性を持つインターフェイス ID を割り当てます。

 [uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
     [uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
     interface IOld : IUnknown
         HRESULT OldMethod();
}

MIDL コンパイラは、複数の出力ファイルを生成します。 インターフェイスが Old.idl で定義されている場合、次の例に示すように、出力ファイル Old_i.c はインターフェイスのインターフェイス識別子 (IID) を持つ const 変数を定義します。

const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};

Old.h ファイルも MIDL によって生成されます。 これには、C++ ソース コードに含めることができるインターフェイスの C++ 定義が含まれています。

ICustomMarshaler インターフェイスを実装する

カスタム マーシャラーは、ランタイムに適切なラッパーを提供するために、 ICustomMarshaler インターフェイスを実装する必要があります。

次の C# コードは、すべてのカスタム マーシャラーによって実装される必要がある基本インターフェイスを表示します。

public interface ICustomMarshaler
{
    Object MarshalNativeToManaged(IntPtr pNativeData);
    IntPtr MarshalManagedToNative(Object ManagedObj);
    void CleanUpNativeData(IntPtr pNativeData);
    void CleanUpManagedData(Object ManagedObj);
    int GetNativeDataSize();
}
Public Interface ICustomMarshaler
     Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
     Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
     Sub CleanUpNativeData( pNativeData As IntPtr )
     Sub CleanUpManagedData( ManagedObj As Object )
     Function GetNativeDataSize() As Integer
End Interface

ICustomMarshaler インターフェイスには、変換のサポート、クリーンアップのサポート、マーシャリングするデータに関する情報を提供するメソッドが含まれています。

操作の種類 ICustomMarshaler メソッド 説明
変換 (ネイティブからマネージド コードへ) MarshalNativeToManaged ネイティブ データへのポインターをマネージド オブジェクトにマーシャリングします。 このメソッドは、引数として渡されるアンマネージ インターフェイスをマーシャリングできるカスタム ランタイム呼び出し可能ラッパー (RCW) を返します。 マーシャラーは、その型のカスタム RCW のインスタンスを返す必要があります。
変換 (マネージド コードからネイティブ コードへ) MarshalManagedToNative マネージド オブジェクトをネイティブ データへのポインターにマーシャリングします。 このメソッドは、引数として渡されるマネージド インターフェイスをマーシャリングできるカスタム COM 呼び出し可能ラッパー (CCW) を返します。 マーシャラーは、その型のカスタム CCW のインスタンスを返す必要があります。
クリーンアップ (ネイティブ コードの場合) CleanUpNativeData マーシャラーが、 MarshalManagedToNative メソッドによって返されるネイティブ データ (CCW) をクリーンアップできるようにします。
クリーンアップ (マネージド コードの) CleanUpManagedData マーシャラーが、 MarshalNativeToManaged メソッドによって返されるマネージド データ (RCW) をクリーンアップできるようにします。
情報 (ネイティブ コードについて) GetNativeDataSize マーシャリングするアンマネージ データのサイズを返します。

変換

ICustomMarshaler.MarshalNativeToManaged

ネイティブ データへのポインターをマネージド オブジェクトにマーシャリングします。 このメソッドは、引数として渡されるアンマネージ インターフェイスをマーシャリングできるカスタム ランタイム呼び出し可能ラッパー (RCW) を返します。 マーシャラーは、その型のカスタム RCW のインスタンスを返す必要があります。

ICustomMarshaler.MarshalManagedToNative

マネージド オブジェクトをネイティブ データへのポインターにマーシャリングします。 このメソッドは、引数として渡されるマネージド インターフェイスをマーシャリングできるカスタム COM 呼び出し可能ラッパー (CCW) を返します。 マーシャラーは、その型のカスタム CCW のインスタンスを返す必要があります。

クリーンアップ

ICustomMarshaler.CleanUpNativeData

マーシャラーが、 MarshalManagedToNative メソッドによって返されるネイティブ データ (CCW) をクリーンアップできるようにします。

ICustomMarshaler.CleanUpManagedData

マーシャラーが、 MarshalNativeToManaged メソッドによって返されるマネージド データ (RCW) をクリーンアップできるようにします。

サイズ情報

ICustomMarshaler.GetNativeDataSize

マーシャリングするアンマネージ データのサイズを返します。

カスタム マーシャラーが、ネイティブからマネージドへのマーシャリング時またはクリーンアップ時に最後の P/Invoke エラーを設定するメソッドを呼び出す場合、 Marshal.GetLastWin32Error() および Marshal.GetLastPInvokeError() によって返される値は、マーシャリング呼び出しまたはクリーンアップ呼び出しの呼び出しを表します。 これにより、 DllImportAttribute.SetLastErrortrue に設定された P/Invoke でカスタム マーシャラーを使用すると、エラーが見逃される可能性があります。 最後の P/Invoke エラーを保持するには、ICustomMarshaler実装で Marshal.GetLastPInvokeError() メソッドと Marshal.SetLastPInvokeError(Int32) メソッドを使用します。

GetInstance メソッドを実装する

カスタム マーシャラーは、ICustomMarshaler インターフェイスの実装に加えて、パラメーターとしてStringを受け取り、戻り値の型がICustomMarshalerを持つGetInstanceと呼ばれるstatic メソッドを実装する必要があります。 この static メソッドは、カスタム マーシャラーのインスタンスをインスタンス化するために、共通言語ランタイムの COM 相互運用レイヤーによって呼び出されます。 GetInstanceに渡される文字列は、返されたカスタム マーシャラーをカスタマイズするためにメソッドが使用できる Cookie です。 次の例は、最小限の完全な ICustomMarshaler 実装を示しています。

public class NewOldMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string pstrCookie)
        => new NewOldMarshaler();

    public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
    public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
    public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
    public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
    public int GetNativeDataSize() => throw new NotImplementedException();
}

MarshalAsAttribute を適用する

カスタム マーシャラーを使用するには、マーシャリングされるパラメーターまたはフィールドに MarshalAsAttribute 属性を適用する必要があります。

また、 UnmanagedType.CustomMarshaler 列挙値を MarshalAsAttribute コンストラクターに渡す必要があります。 さらに、次のいずれかの名前付きパラメーターを使用して MarshalType フィールドを指定する必要があります。

  • MarshalType (必須): カスタム マーシャラーのアセンブリ修飾名。 名前には、カスタム マーシャラーの名前空間とクラスを含める必要があります。 使用されているアセンブリでカスタム マーシャラーが定義されていない場合は、定義されているアセンブリの名前を指定する必要があります。

    MarshalType フィールドではなく、MarshalTypeRef フィールドを使用できます。 MarshalTypeRef は、指定が容易な型を受け取ります。

  • MarshalCookie (省略可能): カスタム マーシャラーに渡される Cookie。 Cookie を使用して、マーシャラーに追加情報を提供できます。 たとえば、同じマーシャラーを使用して多数のラッパーを提供する場合、Cookie は特定のラッパーを識別します。 クッキーは、マーシャラーの GetInstance メソッドに渡されます。

MarshalAsAttribute属性は、適切なラッパーをアクティブ化できるように、カスタム マーシャラーを識別します。 次に、共通言語ランタイムの相互運用サービスは属性を調べ、引数 (パラメーターまたはフィールド) を初めてマーシャリングする必要がある場合にカスタム マーシャラーを作成します。

その後、ランタイムはカスタム マーシャラーの MarshalNativeToManaged メソッドと MarshalManagedToNative メソッドを呼び出して、呼び出しを処理する適切なラッパーをアクティブにします。

カスタム マーシャラーを使用する

カスタム マーシャラーが完了したら、特定の型のカスタム ラッパーとして使用できます。 次の例は、 IUserData マネージド インターフェイスの定義を示しています。

interface IUserData
{
    void DoSomeStuff(INew pINew);
}
Public Interface IUserData
    Sub DoSomeStuff(pINew As INew)
End Interface

次の例では、IUserData インターフェイスは、NewOldMarshaler カスタム マーシャラーを使用して、アンマネージド クライアント アプリケーションが DoSomeStuff メソッドにIOld インターフェイスを渡せるようにします。 DoSomeStuff メソッドのマネージド記述は前の例に示すようにINew インターフェイスを受け取りますが、アンマネージ バージョンのDoSomeStuffは、次の例に示すようにIOld インターフェイス ポインターを受け取ります。

[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
     [uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
     interface IUserData : IUnknown
         HRESULT DoSomeStuff(IUnknown* pIOld);
}

IUserDataのマネージド定義をエクスポートすることによって生成されるタイプ ライブラリは、標準定義ではなく、この例に示すアンマネージ定義を生成します。 DoSomeStuff メソッドのマネージド定義のINew引数に適用されるMarshalAsAttribute属性は、次の例に示すように、引数がカスタム マーシャラーを使用することを示します。

using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
    void DoSomeStuff(
        [MarshalAs(UnmanagedType.CustomMarshaler,
         MarshalType="NewOldMarshaler")]
    INew pINew
    );
}
Public Interface IUserData
    Sub DoSomeStuff( _
        <MarshalAs(UnmanagedType.CustomMarshaler, _
        MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface

前の例では、MarshalAsAttribute属性に指定された最初のパラメーターは、UnmanagedType.CustomMarshalerUnmanagedType.CustomMarshaler列挙値です。

2 番目のパラメーターは、カスタム マーシャラーのアセンブリ修飾名を提供する MarshalType フィールドです。 この名前は、カスタム マーシャラー (MarshalType="MyCompany.NewOldMarshaler") の名前空間とクラスで構成されます。