配列の既定のマーシャリング

全体がマネージド コードで構成されるアプリケーションでは、共通言語ランタイムは、配列型を In/Out パラメーターとして渡します。 これに対し、相互運用マーシャラーは、既定で In パラメーターとして配列を渡します。

ピン留め最適化を使用すると、同じアパートメント内のオブジェクトと対話するときに、blittable 配列を In/Out パラメーターとして操作しているように見せることができます。 ただし、後でコードをコンピューター間のプロキシを生成するために使用されるタイプ ライブラリにエクスポートし、そのライブラリがアパートメント間で呼び出しをマーシャリングするために使用される場合は、呼び出しで In パラメーターの動作を true に戻すことができます。

配列は本質的に複雑で、マネージド配列とアンマネージド配列間の違いが、他の非 blittable 型より多くの情報を保証します。

マネージド配列

マネージド配列型は異なっても、System.Array クラスはすべての配列型の基底クラスです。 System.Array クラスには、ランク、長さ、および配列の下限と上限を決定するためのプロパティに加え、配列のアクセス、並べ替え、検索、コピー、および作成するためのメソッドがあります。

これらの配列型は動的で、基底クラス ライブラリで定義されている対応する静的型はありません。 要素型とランクのそれぞれの組み合わせを配列の別個の型として考えると便利です。 このため、整数の 1 次元配列の型は double 型の 1 次元配列の型とは異なります。 同様に、整数の 2 次元配列は整数の 1 次元配列とは異なります。 型を比較するときに、配列の境界は考慮されません。

次の表に示すように、マネージド配列の任意のインスタンスは、特定の要素の型、ランク、および下限があります。

マネージド配列型 要素タイプ 順位 下限 シグネチャの記法
ELEMENT_TYPE_ARRAY 種類別に指定 ランクで指定 必要に応じて境界で指定する type[n,m]
ELEMENT_TYPE_CLASS Unknown Unknown Unknown System.Array
ELEMENT_TYPE_SZARRAY 種類別に指定 1 0 タイプ[n]

非管理配列

アンマネージ配列は、COM スタイルのセーフ配列または固定長または可変長の C スタイルの配列です。 セーフ配列は、関連付けられた配列データの型、ランク、および境界を格納する自己記述型の配列です。 C スタイル配列は下限が 0 に固定された 1 次元型の配列です。 マーシャリング サービスには、両方の配列型の制限されたサポートがあります。

.NET コードへの配列パラメーターの引き渡し

C スタイル配列とセーフ配列は、どちらもセーフ配列または C スタイル配列としてアンマネージ コードから .NET コードに渡すことができます。 次の表では、アンマネージド型の値とインポートされた型を表示します。

アンマネージ型 インポートされたデータ型
SafeArray(Type) ELEMENT_TYPE_SZARRAY<変換型>

ランク = 1、下限 = 0。 サイズはマネージド シグネチャで指定された場合にのみ判明します。 ランク = 1 または下限 = 0 ではないセーフ配列は、SZARRAY としてマーシャリングできません。
Type[] ELEMENT_TYPE_SZARRAY<変換型>

ランク = 1、下限 = 0。 サイズはマネージド シグネチャで指定された場合にのみ判明します。

セーフ配列

セーフ配列がタイプ ライブラリから .NET アセンブリにインポートされるときに、配列は既知の型 (int など) の 1 次元配列に変換されます。 パラメーターに適用される同じ型変換規則は、配列要素にも適用されます。 たとえば、 BSTR 型のセーフ配列は文字列のマネージド配列になり、バリアントのセーフ配列はオブジェクトのマネージド配列になります。 SAFEARRAY要素の型はタイプ ライブラリからキャプチャされ、SAFEARRAY列挙型のUnmanagedType値に保存されます。

セーフ配列のランクと境界はタイプ ライブラリからは判断できないため、ランクは 1 に等しく下限は 0 に等しいと見なされます。 ランクと境界は、タイプ ライブラリ インポーター (Tlbimp.exe) によって生成されるマネージド シグネチャで定義する必要があります。 実行時にメソッドに渡されるランクが異なる場合は、 SafeArrayRankMismatchException がスローされます。 実行時に渡される配列の型が異なる場合は、 SafeArrayTypeMismatchException がスローされます。 次の例は、マネージド コードとアンマネージド コードでのセーフ配列を示しています。

非管理シグネチャ

HRESULT New1([in] SAFEARRAY( int ) ar);
HRESULT New2([in] SAFEARRAY( DATE ) ar);
HRESULT New3([in, out] SAFEARRAY( BSTR ) *ar);

マネージド シグネチャ

Sub New1(<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VT_I4)> _
   ar() As Integer)
Sub New2(<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VT_DATE)> _
   ar() As DateTime)
Sub New3(ByRef <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VT_BSTR)> _
   ar() As String)
void New1([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_I4)] int[] ar) ;
void New2([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_DATE)]
   DateTime[] ar);
void New3([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_BSTR)]
   ref String[] ar);

Tlbimp.exe によって生成されたメソッドシグネチャが、ELEMENT_TYPE_SZARRAYの代わりに ELEMENT_TYPE_ARRAY の要素型を示すように変更された場合、多次元 (0 以外のバインドされたセーフ配列) をマネージド コードにマーシャリングできます。 または、Tlbimp.exe で /sysarray スイッチを使用してすべての配列を System.Array オブジェクトとしてインポートできます。 渡される配列が多次元配列だとわかっている場合は、Tlbimp.exe で生成された共通中間言語 (CIL) コードを編集してから再コンパイルすることができます。 CIL コードの変更方法の詳細については、「ランタイム呼び出し可能ラッパーのカスタマイズ」を参照してください。

C スタイル配列

C スタイル配列がタイプ ライブラリから .NET アセンブリにインポートされると、その配列は ELEMENT_TYPE_SZARRAY に変換されます。

配列要素型は、タイプ ライブラリから決定され、インポート中は保持されます。 パラメーターに適用される同じ変換規則は、配列の要素にも適用されます。 たとえば、 LPStr 型の配列は、 String 型の配列になります。 Tlbimp.exe は配列要素型をキャプチャし、MarshalAsAttribute 属性をパラメーターに適用します。

配列ランクは 1 と等しいと見なされます。 ランクが 1 より大きい場合、配列は 1 次元配列として列優先順でマーシャリングされます。 下限は常に = 0 です。

タイプ ライブラリには、固定長または可変長の配列を含めることができます。 Tlbimp.exe は、タイプ ライブラリから固定長配列のみをインポートできます。これは、タイプ ライブラリに可変長配列をマーシャリングするために必要な情報が不足しているためです。 固定長配列では、サイズはタイプ ライブラリからインポートされ、パラメーターに適用される MarshalAsAttribute でキャプチャされます。

次の例に示すように、可変長配列を含むタイプ ライブラリを手動で定義する必要があります。

非管理シグネチャ

HRESULT New1(int ar[10]);
HRESULT New2(double ar[10][20]);
HRESULT New3(LPWStr ar[10]);

マネージド シグネチャ

Sub New1(<MarshalAs(UnmanagedType.LPArray, SizeConst=10)> _
   ar() As Integer)
Sub New2(<MarshalAs(UnmanagedType.LPArray, SizeConst=200)> _
   ar() As Double)
Sub New2(<MarshalAs(UnmanagedType.LPArray, _
   ArraySubType=UnmanagedType.LPWStr, SizeConst=10)> _
   ar() As String)
void New1([MarshalAs(UnmanagedType.LPArray, SizeConst=10)] int[] ar);
void New2([MarshalAs(UnmanagedType.LPArray, SizeConst=200)] double[] ar);
void New2([MarshalAs(UnmanagedType.LPArray,
   ArraySubType=UnmanagedType.LPWStr, SizeConst=10)] String[] ar);

インターフェイス定義言語 (IDL) ソースの配列に size_is 属性または length_is 属性を適用してサイズをクライアントに伝えることができますが、Microsoft インターフェイス定義言語 (MIDL) コンパイラでは、その情報はタイプ ライブラリに伝達されません。 サイズがわからないと、相互運用マーシャリング サービスが配列要素をマーシャリングできません。 その結果、可変長配列は参照引数としてインポートされます。 例えば次が挙げられます。

非管理シグネチャ

HRESULT New1(int ar[]);
HRESULT New2(int ArSize, [size_is(ArSize)] double ar[]);
HRESULT New3(int ElemCnt, [length_is(ElemCnt)] LPStr ar[]);

マネージド シグネチャ

Sub New1(ByRef ar As Integer)
Sub New2(ByRef ar As Double)
Sub New3(ByRef ar As String)
void New1(ref int ar);
void New2(ref double ar);
void New3(ref String ar);

Tlbimp.exe によって生成された共通中間言語 (CIL) コードを編集して、マーシャラーに配列サイズを提供してから再コンパイルすることができます。 CIL コードの変更方法の詳細については、「ランタイム呼び出し可能ラッパーのカスタマイズ」を参照してください。 配列内の要素の数を示すには、次の方法のいずれかの方法で、MarshalAsAttribute 型をマネージド メソッド定義の配列パラメーターに適用します。

  • 配列内の要素数を含む別のパラメーターを特定します。 パラメーターは位置によって識別され、最初のパラメーターは番号 0 から始まります。

    Sub [New](ElemCnt As Integer, _
       <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
       ar() As Integer)
    
    void New(
       int ElemCnt,
       [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] int[] ar );
    
  • 配列のサイズを定数として定義します。 例えば次が挙げられます。

    Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeConst:=128)> _
       ar() As Integer)
    
    void New(
       [MarshalAs(UnmanagedType.LPArray, SizeConst=128)] int[] ar );
    

アンマネージド コードからマネージド コードに配列をマーシャリングする場合、マーシャラーはパラメーターに関連付けられている MarshalAsAttribute をチェックして配列のサイズを決定します。 配列サイズが指定されていない場合は、1 つの要素のみがマーシャリングされます。

Note

MarshalAsAttributeは、マネージド配列をアンマネージ コードにマーシャリングすることには影響しません。 その方向では、配列サイズは検査で決定されます。 マネージド配列のサブセットをマーシャリングする方法はありません。

相互運用マーシャラーは、Windows 上の CoTaskMemAlloc メソッドと CoTaskMemFree メソッド、または他のオペレーティング システムの malloc メソッドと free メソッドを使用してメモリを割り当て、取得します。 アンマネージ コードによって実行されるメモリの割り当てでは、これらのメソッドも使用する必要があります。

COM への配列の引き渡し

すべてのマネージド配列型は、マネージド コードからアンマネージド コードに渡すことができます。 次の表に示すように、マネージド型とそれに適用される属性に応じて、セーフ配列または C スタイル配列として配列にアクセスできます。

マネージド配列型 次のようにエクスポートされました
ELEMENT_TYPE_SZARRAY<種類> UnmanagedType .SafeArray()

UnmanagedType.LPArray

型はシグネチャで提供されます。 ランクは常に 1 で、下限は常に 0 です。 サイズは実行時に常に認識されます。
ELEMENT_TYPE_ARRAY<タイプ><ランク>[<境界>] UnmanagedType.SafeArray(type)

UnmanagedType.LPArray

型、ランク、境界はシグネチャで提供されます。 サイズは実行時に常に認識されます。
ELEMENT_TYPE_CLASS<System.Array> UT_Interface

UnmanagedType.SafeArray(type)

型、ランク、境界、およびサイズは、実行時に常に認識されます。

LPSTR または LPWSTR を含む構造体の配列に関連する OLE オートメーションの制限があります。 したがって、 String フィールドは UnmanagedType.BSTR としてマーシャリングする必要があります。 この操作を行わない場合、例外がスローされます。

ELEMENT_TYPE_SZARRAY

ELEMENT_TYPE_SZARRAY パラメーター (1 次元配列) を含むメソッドを .NET アセンブリからタイプ ライブラリにエクスポートすると、配列パラメーターは特定の型のSAFEARRAYに変換されます。 同じ変換規則が配列要素型に適用されます。 マネージド配列の内容はマネージド メモリから SAFEARRAY に自動的にコピーされます。 例えば次が挙げられます。

マネージド シグネチャ

Sub [New](ar() As Long)
Sub [New](ar() As String)
void New(long[] ar );
void New(String[] ar );

非管理署名

HRESULT New([in] SAFEARRAY( long ) ar);
HRESULT New([in] SAFEARRAY( BSTR ) ar);

セーフ配列のランクは常に 1 で、下限は常に 0 です。 サイズは、渡されるマネージド配列のサイズによって実行時に決定されます。

MarshalAsAttribute 属性を使用することで、配列を C スタイル配列としてマーシャリングすることもできます。 例えば次が挙げられます。

マネージド シグネチャ

Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
   ar() As Long, size as Integer)
Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
   ar() As String, size as Integer)
Sub [New](<MarshalAs(UnmanagedType.LPArray, _
   ArraySubType= UnmanagedType.LPStr, SizeParamIndex:=1)> _
   ar() As String, size as Integer)
void New([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
   long [] ar, int size );
void New([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
   String [] ar, int size );
void New([MarshalAs(UnmanagedType.LPArray, ArraySubType=
   UnmanagedType.LPStr, SizeParamIndex=1)]
   String [] ar, int size );

非管理署名

HRESULT New(long ar[]);
HRESULT New(BSTR ar[]);
HRESULT New(LPStr ar[]);

マーシャラーには配列をマーシャリングするために必要な長さ情報がありますが、配列の長さは通常、呼び出し先に長さを伝えるために個別の引数として渡されます。

ELEMENT_TYPE_ARRAY

ELEMENT_TYPE_ARRAY パラメーターを含むメソッドが .NET アセンブリからタイプ ライブラリにエクスポートされると、配列パラメーターは特定の型のSAFEARRAYに変換されます。 マネージド配列の内容はマネージド メモリから SAFEARRAY に自動的にコピーされます。 例えば次が挙げられます。

マネージド シグネチャ

Sub [New](ar(,) As Long)
Sub [New](ar(,) As String)
void New( long [,] ar );
void New( String [,] ar );

非管理署名

HRESULT New([in] SAFEARRAY( long ) ar);
HRESULT New([in] SAFEARRAY( BSTR ) ar);

セーフ配列のランク、サイズ、および境界は、実行時にマネージド配列の特性によって決定されます。

MarshalAsAttribute 属性を適用することで、配列を C スタイル配列としてマーシャリングすることもできます。 例えば次が挙げられます。

マネージド シグネチャ

Sub [New](<MarshalAs(UnmanagedType.LPARRAY, SizeParamIndex:=1)> _
   ar(,) As Long, size As Integer)
Sub [New](<MarshalAs(UnmanagedType.LPARRAY, _
   ArraySubType:=UnmanagedType.LPStr, SizeParamIndex:=1)> _
   ar(,) As String, size As Integer)
void New([MarshalAs(UnmanagedType.LPARRAY, SizeParamIndex=1)]
   long [,] ar, int size );
void New([MarshalAs(UnmanagedType.LPARRAY,
   ArraySubType= UnmanagedType.LPStr, SizeParamIndex=1)]
   String [,] ar, int size );

非管理署名

HRESULT New(long ar[]);
HRESULT New(LPStr ar[]);

入れ子にされた配列をマーシャリングすることはできません。 たとえば、次のシグネチャをタイプ ライブラリ エクスポーター (Tlbexp.exe) を使用してエクスポートすると、エラーが発生します。

マネージド シグネチャ

Sub [New](ar()()() As Long)
void New(long [][][] ar );

ELEMENT_TYPE_CLASS <System.Array>

System.Array パラメーターを含むメソッドが .NET アセンブリからタイプ ライブラリにエクスポートされると、配列パラメーターは_Array インターフェイスに変換されます。 マネージド配列の内容には、 _Array インターフェイスのメソッドとプロパティを介してのみアクセスできます。 System.Array は、SAFEARRAY属性を使用してMarshalAsAttributeとしてマーシャリングすることもできます。 セーフ配列としてマーシャリングすると、配列要素はバリアントとしてマーシャリングされます。 例えば次が挙げられます。

マネージド シグネチャ

Sub New1( ar As System.Array )
Sub New2( <MarshalAs(UnmanagedType.SafeArray)> ar As System.Array )
void New1( System.Array ar );
void New2( [MarshalAs(UnmanagedType.SafeArray)] System.Array ar );

非管理署名

HRESULT New([in] _Array *ar);
HRESULT New([in] SAFEARRAY(VARIANT) ar);

構造体内の配列

アンマネージ構造体には、埋め込まれた配列を含めることができます。 既定では、これらの埋め込まれた配列フィールドは SAFEARRAY としてマーシャリングされます。 次の例では、s1 が構造体そのものに直接割り当てられている埋め込まれた配列です。

管理されていない表現

struct MyStruct {
    short s1[128];
}

UnmanagedType としてマーシャリングできる配列で、MarshalAsAttribute フィールドを設定する必要があります。 サイズは定数としてのみ設定できます。 次のコードは、MyStruct の対応するマネージド定義を示しています。

Public Structure <StructLayout(LayoutKind.Sequential)> MyStruct
   Public <MarshalAs(UnmanagedType.ByValArray, SizeConst := 128)> _
     s1() As Short
End Structure
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1;
}

こちらも参照ください