マーシャリングの変更

更新 : 2007 年 11 月

以下の各セクションでは、相互運用機能アセンブリに対して加えることのできる変更のうち、インポート プロセスの出力に関する問題に対処するために変更できるアイテムを示します。

  • 均整のとれた C スタイルの配列

  • C スタイルの In/Out 配列

  • C スタイルの多次元配列

  • 下限がゼロ以外の SAFEARRAY

  • シグネチャの維持

  • 値型への参照の代わりに null を渡す

これらのセクションで説明する以外の場合でも、相互運用機能アセンブリを編集することはあります。たとえば、相互運用機能アセンブリをさらに使いやすくするために編集することもできます。どのようなカスタマイズが必要なのかを判断するための唯一の方法は、実際に相互運用機能アセンブリを使用したコードを作成してみることです。相互運用機能アセンブリの編集方法については、「方法 : 相互運用機能アセンブリを編集する」を参照してください。

クライアントとサーバーが互換性のないアパートメント内にある場合、マーシャリングに影響します。以下に示す例では、マーシャリングされるパラメータのほとんどはオートメーションとの互換性を持たないため、次のアクションのいずれかを実行する必要があります。

  • クライアントとサーバーが互換性を持つアパートメント内に存在する (したがって、COM のマーシャリングが行われない) ことを確認する。

  • インターフェイス定義言語 (IDL: Interface Definition Language) から生成されたプロキシとスタブを登録する。このような場合にはタイプ ライブラリの登録は役に立ちません。それは、マーシャリングに必要な情報の多くは、IDL からタイプ ライブラリには反映されないためです。

均整のとれた C スタイルの配列

次の IDL 宣言は、C スタイルの配列を示しています。

HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int 
aConf[]);

この型にはオートメーションとの互換性がないため、1 番目と 2 番目のパラメータ間のリンクなど、配列のサイズに関する情報はタイプ ライブラリの中では表現できません。タイプ ライブラリ インポータ (Tlbimp.exe) は、2 番目のパラメータをマネージ配列としてではなく整数への参照としてインポートします。このパラメータを、MSIL を編集することで調整できます。

編集対象の MSIL コード

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall

[置換後の文字列]

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32[] marshal([]) aConf) runtime managed internalcall

マネージ コードから呼び出すには

int[] param1 = { 11, 22, 33 };
tstArrays.ConformantArray( 3, param1 );

C スタイルの In/Out 配列

次の IDL 宣言は、C スタイルの In/Out 配列を示しています。

HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(,*pcElems)] 
int** ppInOut);

この場合、この配列のサイズを変更して新しいサイズを返すことができます。この型にはオートメーションとの互換性がないため、1 番目と 2 番目のパラメータ間のリンクなど、配列のサイズに関する情報はタイプ ライブラリの中では表現できません。Tlbimp.exe は、2 番目のパラメータを IntPtr としてインポートします。引き続きこのメソッドをマネージ コードから呼び出すことはできますが、配列のサイズを変更するには、MSIL を編集し、Marshal クラスに属するメソッドを使用して、手動でメモリの割り当てと解放を行う必要があります。

編集対象の MSIL コード

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall

[置換後の文字列]

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall

マネージ コードから呼び出すには

int[] inArray = { 11, 22, 33 };
int arraySize = inArray.Length;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof( int )) * inArray.Length );
Marshal.Copy( inArray, 0, buffer, inArray.Length );
tstArrays.InOutArray( ref arraySize, ref buffer );
if( arraySize > 0 )
{
int[] arrayRes = new int[ arraySize ];
Marshal.Copy( buffer, arrayRes, 0, arraySize );
Marshal.FreeCoTaskMem( buffer );
}

C スタイルの多次元配列

次の IDL 宣言は、2 次元の C スタイルの配列を示しています。

HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);

この型にはオートメーションとの互換性がないため、1 番目と 2 番目のパラメータの間のリンクなど、配列のサイズおよび次元数に関する情報はタイプ ライブラリの中では表現できません。Tlbimp.exe は、2 番目のパラメータをマネージ多次元配列としてではなく IntPtr 型としてインポートします。このパラメータを、MSIL を編集することで調整できます。

編集対象の MSIL コード

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall

[置換後の文字列]

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([]) aMatrix) runtime managed internalcall

マネージ コードから呼び出すには

int[,] param = {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }};
tstArrays.TwoDimArray( 3, param );

下限がゼロ以外の SAFEARRAY

次の IDL 宣言は、SAFEARRAY パラメータを示しています。

HRESULT InSArray([in] SAFEARRAY(int) *ppsa);

この SAFEARRAY は、下限がゼロではありません。マネージ コードでは、このような配列は System.Array 型によって表現されます。しかし、既定では、インポータはすべての SAFEARRAY パラメータをマネージ配列への参照に変換します。この既定の動作を変更する方法は 2 つあります。

  • /sysarray スイッチを指定して Tlbimp.exe を使用し、タイプ ライブラリに含まれるすべての配列を System.Array としてインポートする。

  • MSIL を手動で編集することでいくつかのパラメータを System.Array 型としてインポートする。次に例を示します。

    編集対象の MSIL コード

    .method public hidebysig newslot virtual 
    instance void  InSArray([in] int32[]&  marshal( safearray int) ppsa) runtime managed internalcall
    

    [置換後の文字列]

    .method public hidebysig newslot virtual 
    instance void  InSArray(class [mscorlib]System.Array& marshal( safearray) ppsa) runtime managed internalcall
    

    マネージ コードから呼び出すには

    int[] lengthsArray = new int[1] { 3 };   
    int[] boundsArray = new int[1] { -1 };
    Array param2 = Array.CreateInstance( typeof(int), lengthsArray, boundsArray );
    for( int i = param2.GetLowerBound( 0 ); i <= param2.GetUpperBound( 0 ); i++ )
    param2.SetValue( i * 10, i ); 
    sum = tstArrays.InSArray( ref param2 );
    

シグネチャの維持

次の IDL 宣言は、COM メソッド シグネチャを示しています。

HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);

Tlbimp.exe は COM メソッドのシグネチャを変更します。IDL の中で [out, retval] でマークされたパラメータは、マネージ メソッドの戻り値になります。エラーを示すすべての HRESULT 値は、マネージ例外へと変換されます。メソッドが正常な HRESULT 以外の値を返す場合などには、元の COM メソッド シグネチャを維持する必要が生じることがあります。次のマネージ表現は、修正できるシグネチャの例を示しています。

MSIL におけるマネージ表現

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall
{

[置換後の文字列]

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam, [out] int32& outParam) runtime managed internalcall preservesig

返された HRESULT を参照するには

int hr = tst.TestPreserveSig2( -3, out retValue );
Console.WriteLine( "Return value is {0}", retValue );
if( hr == 0 )
Console.WriteLine( "HRESULT = S_OK" );
else if ( hr == 1 )
Console.WriteLine( "HRESULT = S_FALSE" );
else
Console.WriteLine( "HRESULT = {0}", hr );

値型への参照の代わりに null を渡す

次の IDL 宣言は、構造体への IDL ポインタを示しています。

HRESULT TestPassingNull([in, unique] Point* refParam);

Tlbimp.exe は、このパラメータを値型 Point への参照としてインポートします。C# および Visual Basic 2005 では、値型への参照が必要とされる場合に null 参照 (Visual Basic では Nothing) をパラメータとして渡すことはできません。COM 関数で null (Nothing) パラメータが必要な場合には、MSIL を編集すると、シグネチャを変更できます。

編集対象の MSIL コード

.method public hidebysig newslot virtual 
instance void  TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam) 
runtime managed internalcall

[置換後の文字列]

.method public hidebysig newslot virtual 
instance void  TestPassingNull([in] native int) runtime managed internalcall

変更済みのシグネチャを使用すると、null 値を渡すことができます。しかし、なんらかの実際の値を渡す必要がある場合には、次の例で示すように Marshal クラスのメソッドを使用する必要があります。

tagPoint p = new tagPoint();
p.x = 3;
p.y = 9;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));
Marshal.StructureToPtr( p, buffer, false );
tst.TestPassingNull( buffer );
Marshal.FreeCoTaskMem( buffer );
tst.TestPassingNull( IntPtr.Zero );

参照

処理手順

方法 : 相互運用機能アセンブリを編集する

方法 : ラッパを手動で作成する

概念

ランタイム呼び出し可能ラッパーのカスタマイズ

COM のデータ型

COM 呼び出し可能ラッパーのカスタマイズ

参照

タイプ ライブラリ インポータ (Tlbimp.exe)