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

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

このインターフェイスには ICustomMarshaler 、メソッド呼び出しを処理するためのカスタム ラッパーが用意されています。

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

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

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

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

Note

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

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

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

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

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には、変換のサポート、クリーンup のサポート、およびマーシャリングするデータに関する情報を提供するメソッドが含まれています。

操作の種類 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

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

Note

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

GetInstance メソッドを実装する

インターフェイスのICustomMarshaler実装に加えて、カスタム マーシャラーは、パラメーターとして a Stringstatic受け取り、戻り値のICustomMarshaler型を持つ呼び出しGetInstanceメソッドを実装する必要があります。 この 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 (必須): カスタム マーシャラーのアセンブリ修飾名。 名前には、カスタム マーシャラーの名前空間とクラスを含める必要があります。 使用されているアセンブリでカスタム マーシャラーが定義されていない場合は、定義されているアセンブリの名前を指定する必要があります。

    Note

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

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

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

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

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

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

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

次の例では、インターフェイスはIUserDataカスタム マーシャラーをNewOldMarshaler使用して、アンマネージ クライアント アプリケーションがメソッドにインターフェイスDoSomeStuffIOld渡せるようにします。 メソッドのマネージド記述は、前の 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 ではなく、この例に示すアンマネージ定義を生成します。 メソッドのINewマネージド定義の引数に適用される属性はMarshalAsAttribute、次のDoSomeStuff例に示すように、引数がカスタム マーシャラーを使用することを示します。

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.CustomMarshalerですUnmanagedType.CustomMarshaler

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