コピーと固定
データをマーシャリングするときに、相互運用マーシャラーはマーシャリングされるデータをコピーまたは固定できます。 データをコピーすると、あるメモリ位置のデータのコピーが別のメモリ位置に配置されます。 マネージ メモリからアンマネージ メモリにコピーされた値型と、参照渡しされた型の違いを次の図に示します。
値渡しされる値型と参照渡しされる値型
値渡しされるメソッド引数は、スタック上の値としてアンマネージ コードにマーシャリングされます。 コピーのプロセスは直接的です。 参照渡しされる引数は、ポインターとしてスタック上に渡されます。 参照型も、値渡しまたは参照渡しできます。 値渡しされる参照型がコピーまたは固定されるようすを次の図に示します。
値渡しされる参照型と参照渡しされる参照型
一時的な固定は、現在のメモリ位置にデータをロックします。したがって、共通言語ランタイムのガベージ コレクターによって、そのデータが再配置されることを回避できます。 マーシャラーはデータを固定することでコピーによるオーバーヘッドを減少させ、パフォーマンスを向上させます。 マーシャリング プロセス中にデータがコピーされるのか固定されるのかは、そのデータの型によって決まります。 一時的な固定は、String などのオブジェクトのマーシャリング中に自動的に実行されますが、GCHandle クラスを使って手動でメモリを一時的に固定することもできます。
書式指定された Blittable クラス
書式指定された blittable クラスは、マネージ メモリ内でもアンマネージ メモリ内でも、固定レイアウト (書式指定されている) と共通のデータ表現を持ちます。 このような型をマーシャリングする必要がある場合は、ヒープ内のオブジェクトへのポインターが、呼び出し先に直接渡されます。 呼び出し先はポインターによって参照されるメモリ位置の内容を変更できます。
メモ |
---|
パラメーターに Out または In/Out のマークが付いている場合、呼び出し先はメモリの内容を変更できます。逆に、In としてマーシャリングするようにパラメーターが設定されている場合、呼び出し先は内容の変更を避ける必要があります。In は書式指定された blittable 型に対する既定の設定です。同一クラスをタイプ ライブラリにエクスポートし、アパートメント間呼び出しのために使用した場合に、In オブジェクトを修正すると問題が発生します。 |
書式指定された Blittable でないクラス
書式指定されたblittable でないクラスは、固定レイアウト (書式指定されている) を持ちますが、マネージ メモリとアンマネージ メモリではデータ表現が異なります。 次の状況では、データを変換する必要が生じる場合があります。
blittable でないクラスを値渡しでマーシャリングする場合、呼び出し先はデータ構造体のコピーへのポインターを受け取ります。
blittable でないクラスを参照渡しでマーシャリングする場合、呼び出し先はデータ構造体のコピーへのポインターを指すポインターを受け取ります。
InAttribute 属性が設定されている場合、このコピーは常にインスタンスの状態に初期化され、必要に応じてマーシャリングされます。
OutAttribute 属性が設定されている場合、制御が返されるときに常に状態がインスタンスにコピーされ、必要に応じてマーシャリングされます。
InAttribute と OutAttribute が両方設定されている場合は、両方のコピーが必要になります。 いずれか一方の属性が省略された場合、一方のコピーが省略されるため、処理が最適化されます。
参照型
参照型は値渡しまたは参照渡しできます。 値渡しする場合は、型へのポインターがスタック上に渡されます。 参照渡しする場合、型へのポインターを指すポインターがスタック上に渡されます。
参照型は、次のように条件付きで動作します。
参照型が値渡しされ、その参照型のメンバーのいずれかが非 blittable 型の場合、その型は 2 回変換されます。
1 回目は引数をアンマネージ側に渡すとき。
2 回目は呼び出しから制御が返されるとき。
不要なコピーと変換を避けるために、これらの型は In パラメーターとしてマーシャリングされます。 呼び出し先による変更内容を呼び出し元が確認する必要がある場合には、引数に対して明示的に InAttribute 属性と OutAttribute 属性を適用する必要があります。
参照型が値渡しされ、その参照型のメンバーがすべて blittable 型の場合は、マーシャリング中にその参照型を固定できます。また、呼び出し先による型のメンバーへの変更内容は呼び出し元にも反映されます。 このような動作が必要な場合は、InAttribute と OutAttribute を明示的に適用してください。 これらの方向属性を使用しない場合は、相互運用マーシャラーが方向情報をタイプ ライブラリにエクスポートしない (既定の In としてエクスポートします) ため、COM のアパートメント間マーシャリングで問題が生じることがあります。
参照型を参照渡しする場合は、既定によりその型は In/Out としてマーシャリングされます。
System.String と System.Text.StringBuilder
アンマネージ コードにデータを値渡しまたは参照しでマーシャリングする場合、一般に、マーシャラーはデータを 2 次バッファーにコピーし (可能な場合には、コピー中に文字セットを変換)、そのバッファーへの参照を呼び出し先に渡します。 この参照が SysAllocString によって割り当てられた BSTR である場合を除き、参照は常に CoTaskMemAlloc によって割り当てられます。
文字列型を値渡しでマーシャリングする (Unicode 文字列など) ときの最適化処理として、マーシャラーは新しいバッファーに文字列型をコピーする代わりに、呼び出し先に対して内部 Unicode バッファー内のマネージ文字列への直接ポインターを渡します。
注意 |
---|
文字列を値渡しする場合には、マーシャラーから渡された参照を呼び出し先で変更しないようにしてください。変更した場合はマネージ ヒープが破損することがあります。 |
System.String を参照渡しする場合、マーシャラーは呼び出しを実行する前にその文字列の内容を 2 次バッファーにコピーします。 次に呼び出しから制御が返されるときに、マーシャラーはバッファーの内容を新しい文字列の中にコピーします。 この技法により、変更不可のマネージ文字列が変更されないことが保証されます。
System.Text.StringBuilder を値渡しする場合、マーシャラーは StringBuilder の内部バッファーへの参照を直接に呼び出し元に渡します。 呼び出し元と呼び出し先で、バッファーのサイズが一致する必要があります。 呼び出し元は、適切な長さの StringBuilder を作成します。 呼び出し先は、バッファーのオーバーランが発生しないように必要な予防措置をとる必要があります。 StringBuilder は、値渡しされる参照型は既定により In パラメーターとして渡されるという規則の例外です。 StringBuilder は常に In/Out として渡されます。