オブジェクトに対する既定のマーシャリング
更新 : 2007 年 11 月
System.Object 型として指定されているパラメータおよびフィールドを、次のいずれかの型としてアンマネージ コードに公開できます。
そのオブジェクトがパラメータの場合にはバリアント。
そのオブジェクトが構造体フィールドの場合にはインターフェイス。
COM 相互運用機能だけがオブジェクト型のマーシャリングをサポートします。既定の動作では、オブジェクトは COM バリアントへとマーシャリングされます。これらの規則は Object 型だけに適用され、Object クラスから派生した、厳密に型指定されたオブジェクトには適用されません。
ここでは、オブジェクト型のマーシャリングに関する追加情報を示します。
マーシャリング オプション
インターフェイスへのオブジェクトのマーシャリング
バリアントへのオブジェクトのマーシャリング
オブジェクトへのバリアントのマーシャリング
ByRef バリアントのマーシャリング
マーシャリング オプション
Object データ型に対するマーシャリング オプションを次の表に示します。MarshalAsAttribute 属性は、オブジェクトをマーシャリングするための UnmanagedType 列挙値をいくつか提供します。
列挙型 |
アンマネージ表現の説明 |
---|---|
UnmanagedType.Struct (パラメータの既定値) |
COM スタイルのバリアント。 |
UnmanagedType.Interface |
可能な場合は IDispatch インターフェイス。それ以外の場合は IUnknown インターフェイス。 |
UnmanagedType.IUnknown (フィールドの既定値) |
IUnknown インターフェイス。 |
UnmanagedType.IDispatch |
IDispatch インターフェイス。 |
MarshalObject のマネージ インターフェイス定義を次の例に示します。
Interface MarshalObject
Sub SetVariant(o As Object)
Sub SetVariantRef(ByRef o As Object)
Function GetVariant() As Object
Sub SetIDispatch( <MarshalAs(UnmanagedType.IDispatch)> o As Object)
Sub SetIDispatchRef(ByRef <MarshalAs(UnmanagedType.IDispatch)> o _
As Object)
Function GetIDispatch() As <MarshalAs(UnmanagedType.IDispatch)> Object
Sub SetIUnknown( <MarshalAs(UnmanagedType.IUnknown)> o As Object)
Sub SetIUnknownRef(ByRef <MarshalAs(UnmanagedType.IUnknown)> o _
As Object)
Function GetIUnknown() As <MarshalAs(UnmanagedType.IUnknown)> Object
End Interface
interface MarshalObject {
void SetVariant(Object o);
void SetVariantRef(ref Object o);
Object GetVariant();
void SetIDispatch ([MarshalAs(UnmanagedType.IDispatch)]Object o);
void SetIDispatchRef([MarshalAs(UnmanagedType.IDispatch)]ref Object o);
[MarshalAs(UnmanagedType.IDispatch)] Object GetIDispatch();
void SetIUnknown ([MarshalAs(UnmanagedType.IUnknown)]Object o);
void SetIUnknownRef([MarshalAs(UnmanagedType.IUnknown)]ref Object o);
[MarshalAs(UnmanagedType.IUnknown)] Object GetIUnknown();
}
MarshalObject インターフェイスをタイプ ライブラリにエクスポートするコード例を次に示します。
interface MarshalObject {
HRESULT SetVariant([in] VARIANT o);
HRESULT SetVariantRef([in,out] VARIANT *o);
HRESULT GetVariant([out,retval] VARIANT *o)
HRESULT SetIDispatch([in] IDispatch *o);
HRESULT SetIDispatchRef([in,out] IDispatch **o);
HRESULT GetIDispatch([out,retval] IDispatch **o)
HRESULT SetIUnknown([in] IUnknown *o);
HRESULT SetIUnknownRef([in,out] IUnknown **o);
HRESULT GetIUnknown([out,retval] IUnknown **o)
}
メモ : |
---|
相互運用マーシャラは、バリアント内に割り当てられたオブジェクトがある場合は、呼び出しの後で自動的にそのオブジェクトを解放します。 |
書式指定された値型を次の例に示します。
Public Structure ObjectHolder
Dim o1 As Object
<MarshalAs(UnmanagedType.IDispatch)> Public o2 As Object
End Structure
public struct ObjectHolder {
Object o1;
[MarshalAs(UnmanagedType.IDispatch)]public Object o2;
}
書式指定された型をタイプ ライブラリにエクスポートするコード例を次に示します。
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
インターフェイスへのオブジェクトのマーシャリング
オブジェクトをインターフェイスとして COM に公開する場合、そのインターフェイスはマネージ型 Object 用のクラス インターフェイス (_Object インターフェイス) です。このインターフェイスは、結果のタイプ ライブラリでは、IDispatch (UnmanagedType.IDispatch) 型または IUnknown (UnmanagedType.IUnknown) 型として指定されます。COM クライアントは、マネージ クラスのメンバ、または派生クラスによって実装されるメンバを _Object インターフェイス経由で動的に呼び出すことができます。クライアントは QueryInterface を呼び出して、マネージ型によって明示的に実装された他の任意のインターフェイスも取得できます。
バリアントへのオブジェクトのマーシャリング
オブジェクトをバリアントへとマーシャリングする場合、内部バリアント型は次の規則に従って実行時に決定されます。
オブジェクト参照が null (Visual Basic では Nothing) の場合、オブジェクトは VT_EMPTY 型のバリアントへとマーシャリングされます。
オブジェクトが、次の表に列挙されるいずれかの型のインスタンスである場合、結果として生成されるバリアント型は、表内に示されている、マーシャラに組み込みの規則によって決定されます。
マーシャリングの動作を明示的に制御する必要があるその他のオブジェクトは、IConvertible インターフェイスを実装できます。その場合、バリアントの型は IConvertible.GetTypeCode メソッドから返される型コードによって決定されます。それ以外の場合、オブジェクトは VT_UNKNOWN 型のバリアントとしてマーシャリングされます。
バリアントへのシステム型のマーシャリング
マネージ オブジェクト型、および対応する COM バリアント型を次の表に示します。これらの型は、呼び出されるメソッドのシグネチャが System.Object 型の場合に限って変換されます。
オブジェクト型 |
COM バリアント型 |
---|---|
null オブジェクト参照 (Visual Basic では Nothing)。 |
VT_EMPTY |
VT_NULL |
|
VT_ERROR |
|
VT_ERROR と E_PARAMNOTFOUND |
|
VT_DISPATCH |
|
VT_UNKNOWN |
|
VT_CY |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_I4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY |
上の例で定義した MarshalObject インターフェイスを使用して、次のコード例では各種の型のバリアントを COM サーバーに渡す方法を示します。
Dim mo As New MarshalObject()
mo.SetVariant(Nothing) ' Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value) ' Marshal as variant of type VT_NULL.
mo.SetVariant(CInt(27)) ' Marshal as variant of type VT_I2.
mo.SetVariant(CLng(27)) ' Marshal as variant of type VT_I4.
mo.SetVariant(CSng(27.0)) ' Marshal as variant of type VT_R4.
mo.SetVariant(CDbl(27.0)) ' Marshal as variant of type VT_R8.
MarshalObject mo = new MarshalObject();
mo.SetVariant(null); // Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value); // Marshal as variant of type VT_NULL.
mo.SetVariant((int)27); // Marshal as variant of type VT_I2.
mo.SetVariant((long)27); // Marshal as variant of type VT_I4.
mo.SetVariant((single)27.0); // Marshal as variant of type VT_R4.
mo.SetVariant((double)27.0); // Marshal as variant of type VT_R8.
ErrorWrapper、DispatchWrapper、UnknownWrapper、CurrencyWrapper などのラッパー クラスを使用すると、対応するマネージ型を持たない COM 型をマーシャリングできます。これらのラッパーを使用して各種の型のバリアントを COM サーバーに渡す方法を次のコード例に示します。
Imports System.Runtime.InteropServices
' Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(New UnknownWrapper(inew))
' Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(New DispatchWrapper(inew))
' Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(New ErrorWrapper(&H80054002))
' Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(New CurrencyWrapper(New Decimal(5.25)))
using System.Runtime.InteropServices;
// Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(new UnknownWrapper(inew));
// Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(new DispatchWrapper(inew));
// Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(new ErrorWrapper(0x80054002));
// Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(new CurrencyWrapper(new Decimal(5.25)));
ラッパー クラスは、System.Runtime.InteropServices 名前空間で定義されます。
バリアントへの IConvertible インターフェイスのマーシャリング
上のセクションで列挙した以外の型は、IConvertible インターフェイスを実装することにより、型のマーシャリング方法を制御できます。オブジェクトが IConvertible インターフェイスを実装する場合、その COM バリアント型は IConvertible.GetTypeCode メソッドから返された TypeCode 列挙体の値によって実行時に決定されます。
TypeCode 列挙体に対して有効な値、およびそれぞれの値に対応する COM バリアント型を次の表に示します。
TypeCode |
COM バリアント型 |
---|---|
TypeCode.Empty |
VT_EMPTY |
TypeCode.Object |
VT_UNKNOWN |
TypeCode.DBNull |
VT_NULL |
TypeCode.Boolean |
VT_BOOL |
TypeCode.Char |
VT_UI2 |
TypeCode.Sbyte |
VT_I1 |
TypeCode.Byte |
VT_UI1 |
TypeCode.Int16 |
VT_I2 |
TypeCode.UInt16 |
VT_UI2 |
TypeCode.Int32 |
VT_I4 |
TypeCode.UInt32 |
VT_UI4 |
TypeCode.Int64 |
VT_I8 |
TypeCode.UInt64 |
VT_UI8 |
TypeCode.Single |
VT_R4 |
TypeCode.Double |
VT_R8 |
TypeCode.Decimal |
VT_DECIMAL |
TypeCode.DateTime |
VT_DATE |
TypeCode.String |
VT_BSTR |
サポートなし。 |
VT_INT |
サポートなし。 |
VT_UINT |
サポートなし。 |
VT_ARRAY |
サポートなし。 |
VT_RECORD |
サポートなし。 |
VT_CY |
サポートされていません。 |
VT_VARIANT |
COM バリアントの値は、IConvertible.ToType インターフェイスを呼び出すことで判断されます。ToType は、IConvertible.GetTypeCode から返された型に対応する変換ルーチンです。たとえば、IConvertible.GetTypeCode から TypeCode.Double を返すオブジェクトは、VT_R8 型の COM バリアントとしてマーシャリングされます。バリアントの値は、IConvertible インターフェイスにキャストし、ToDouble メソッドを呼び出すことで取得でき、その COM バリアントの dblVal フィールド内に格納されます。
オブジェクトへのバリアントのマーシャリング
バリアントをオブジェクトへとマーシャリングする場合、マーシャリングされるバリアントの型、および場合によっては値が、生成されるオブジェクトの型を決定します。各バリアント型、および対応するオブジェクト型を次の表に示します。オブジェクト型はバリアントが COM から .NET Framework に渡されるときにマーシャラによって作成されます。
COM バリアント型 |
オブジェクト型 |
---|---|
VT_EMPTY |
null オブジェクト参照 (Visual Basic では Nothing)。 |
VT_NULL |
|
VT_DISPATCH |
System.__ComObject または (pdispVal == null) の場合は null。 |
VT_UNKNOWN |
System.__ComObject または (punkVal == null) の場合は null。 |
VT_ERROR |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_I4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY | VT_* |
|
VT_CY |
|
VT_RECORD |
対応するボックス化された値型。 |
VT_VARIANT |
サポートされていません。 |
COM からマネージ コードに渡された後で COM に返されるバリアント型が、呼び出し中に同じバリアント型を維持しないことがあります。VT_DISPATCH 型のバリアントが COM から .NET Framework に渡されるときに、何が起こるのかを検討してみます。マーシャリング時に、バリアントは System.Object に変換されます。次に Object が COM に返される場合には、VT_UNKNOWN 型のバリアントにマーシャリングされます。オブジェクトをマネージ コードから COM へとマーシャリングするときに、生成されるバリアントの型が、最初にオブジェクトを生成するときに使用したバリアントの型と同じになる保証はありません。
ByRef バリアントのマーシャリング
バリアント自体を値渡しまたは参照渡しすることはできますが、VT_BYREF フラグを任意のバリアント型と併用した場合は、そのバリアントの内容を値渡しではなく参照渡しすることを指定できます。バリアントの参照渡しによるマーシャリングと、VT_BYREF フラグの設定によるバリアントのマーシャリングとの違いは微妙です。その違いを次の図で明確にします。
値渡しされるバリアントと参照渡しされるバリアント
オブジェクトとバリアントを値渡しによってマーシャリングする場合の既定の動作
オブジェクトをマネージ コードから COM に渡す場合、そのオブジェクトの内容は、「バリアントへのオブジェクトのマーシャリング」で定義される規則に従ってマーシャラによって作成される新しいバリアントにコピーされます。アンマネージ側でバリアントに対して行われた変更の内容は、呼び出しから制御が返されるときに、元のオブジェクトには反映されません。
バリアントを COM からマネージ コードに渡す場合、そのバリアントの内容は、「オブジェクトへのバリアントのマーシャリング」で定義される規則に従って新規作成されるオブジェクトにコピーされます。マネージ側でオブジェクトに対して行われた変更の内容は、呼び出しから制御が返されるときに、元のバリアントには反映されません。
オブジェクトとバリアントを参照渡しによってマーシャリングする場合の既定の動作
変更内容を呼び出し元にも反映させるためには、パラメータを参照渡しする必要があります。たとえば、C# でキーワード ref (Visual Basic マネージ コードの場合は ByRef) を使用すると、パラメータを参照渡しできます。COM の場合は、参照パラメータを渡すには、variant * などのポインタを使用します。
オブジェクトを COM に参照渡しする場合、マーシャラは呼び出しを実行する前に新しいバリアントを作成し、オブジェクト参照の内容をそのバリアントにコピーします。このバリアントはアンマネージ関数に渡されます。ここで、バリアントの内容を自由に変更できます。呼び出しから制御が返されるときに、アンマネージ側でバリアントが変更されている場合には、その内容が元のオブジェクトに反映されます。バリアントの型が、呼び出しに渡されたバリアントの型と異なる場合、変更内容は別の型を持つオブジェクトに反映されます。つまり、呼び出しに渡したオブジェクトの型が、呼び出しから返されるオブジェクトの型と異なることがあります。
バリアントをマネージ コードに参照渡しする場合、マーシャラは呼び出しを実行する前に、新しいオブジェクトを作成し、バリアントの内容をそのオブジェクトにコピーします。オブジェクトへの参照がマネージ関数に渡されます。ここで、オブジェクトを自由に変更できます。呼び出しから制御が返されるときに、参照先オブジェクトが変更されている場合には、その内容が元のバリアントに反映されます。オブジェクトの型が、呼び出しに渡されたオブジェクトの型と異なる場合、元のバリアントの型が変更され、値がそのバリアントに反映されます。ここでも、呼び出しに渡されたバリアントの型が、呼び出しから返されるバリアントの型と異なることがあります。
VT_BYREF フラグの設定によるバリアントのマーシャリングの既定の動作
マネージ コードに参照渡しするバリアントには、VT_BYREF フラグを設定することによって、そのバリアントが値ではなく参照を含むことを指定できます。この場合でも、バリアントは値渡しされるため、バリアントは依然としてオブジェクトへとマーシャリングされます。呼び出しを実行する前に、マーシャラは自動的にバリアントの内容を逆参照し、その内容を新しく作成されるオブジェクトへとコピーします。次に、そのオブジェクトがマネージ関数に渡されます。ただし、呼び出しから制御が返されるときに、このオブジェクトの内容は元のバリアントには反映されません。マネージ オブジェクトに対する変更内容は失われます。
注意 : バリアントに VT_BYREF フラグが設定されていても、そのバリアントが値渡しされた場合、その値を変更する方法はありません。
マネージ コードに参照渡しするバリアントについても、VT_BYREF フラグを設定することで、バリアントが別の参照を含むことを指定できます。このようにすると、バリアントは参照渡しされるため、ref オブジェクトにマーシャリングされます。呼び出しを実行する前に、マーシャラは自動的にバリアントの内容を逆参照し、その内容を新しく作成されるオブジェクトへとコピーします。呼び出しから制御が返されるときに、オブジェクトの値が元のバリアントに含まれる参照に反映されるのは、そのオブジェクトの型が呼び出しに渡されたオブジェクトの型と同じ場合に限られます。つまり、反映によって VT_BYREF フラグが設定されたバリアントの型が変更されることはありません。呼び出しの間にオブジェクトの型が変更された場合、呼び出しから制御が返されるときに InvalidCastException が発生します。
バリアントとオブジェクトに関する反映規則を次の表にまとめます。
マーシャリング前 |
マーシャリング後 |
変更内容の反映 |
---|---|---|
Variantv |
Object o |
反映しない |
Object o |
Variantv |
反映しない |
Variant*pv |
Ref Objecto |
常に反映 |
Ref Objecto |
Variant*pv |
常に反映 |
Variantv(VT_BYREF|VT_*) |
Object o |
反映しない |
Variantv(VT_BYREF|VT_) |
Ref Objecto |
型が変更されていない場合にだけ反映 |