この記事では、この 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.SetLastError が true に設定された 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") の名前空間とクラスで構成されます。
.NET