2

.NET 値型 (マネージド C++ 構造体) はスタックに格納されるため、ポインターをアンマネージド関数に渡すためにそれらを pin_ptr する必要があるのはなぜですか?

例えば。BYTE b[100];

最初に固定せずにアンマネージ関数に &b を渡すと、スタックが破損する可能性がありますか?

CLR スタックは、GC ヒープと同じように変更される可能性がありますか? CLR スタックはプロセッサ レジスタを使用するなどの異常な最適化を使用しているため、アンマネージ関数へのバッファとして使用するのに適していないと私は信じています。スタックに値の型を固定することに関する規則は不明確なようです。

この方法でバッファ配列をカーネル NTDLL 関数 NtfsControlFile に送信すると、破損しているように見えることに気付きました。値の型を固定すると、問題が解決します。ただし、API 呼び出しには使用しないでください。

したがって、最初に固定せずに、スタック上の任意の値型へのポインターをアンマネージ関数に渡すことは、基本的に安全ではありませんか?

4

4 に答える 4

2

ネイティブ スタックで作成されているため、マネージド ヒープの移動などの影響を受けないことは正しいですがBYTE b[100];、問題は単純な C++ の間違いだと思います。

あなたは言う、

最初に固定せずにアンマネージ関数に &b を渡すと、スタックが破損する可能性がありますか?

配列名自体がすでに配列の開始アドレスになっているため、配列名 (b) にアドレス演算子 (&) を使用しないでください。を使用して&bも機能せず、結果の動作は複数の要因 (コンパイラとその設定など) に依存します。

簡単に言えば、関数を呼び出すときに、配列名 (&b) でアドレス演算子を使用する代わりに、配列名 (b) を渡すだけでよいということです。

ちなみに、スタック上の管理された値の型を最初に固定せずにネイティブ関数に渡すことができるかどうかを尋ねることで、問題を混同していると思います.マネージド値型とは関係ありません。

于 2009-12-13T12:58:28.983 に答える
1

はい、メモリ管理はアドレスを切り替えることができ、それらへの独自の内部参照を更新するだけです。管理されたレイヤーの下に潜るとすぐに、操作しているポインターが別の場所に移動しないように安全を確保する必要があります。pin_ptr を使用すると、メモリ マネージャはこのメモリをそのままにしておくように指示されます。

于 2009-07-09T16:34:25.147 に答える
0

したがって、最初に固定せずに、スタック上の任意の値型へのポインターをアンマネージ関数に渡すことは、基本的に安全ではありませんか?

はい。

これは、GC がそれらをメソッドに対して非同期に削除/移動するためです。

CLR GC の仕組みについては、移動 GCを参照してください。

于 2009-07-09T16:39:48.177 に答える
0

私が知る限り、これはすべて GC ヒープ上のオブジェクトを参照しています。

ポイントは、具体的にはスタックメモリについて言及していることです。

スタック メモリをバッファとして渡すAPI 呼び出し中にスタック メモリが破損していると思われる例を投稿しました。 90b8-4a6a-9b14-f48d709cb27c

スタックメモリを固定する必要がある場合、これは「それはうまくいく」という考えを破るようです。アンマネージ C++ では、スタック バッファーを宣言し、それへのポインターを API 関数に渡すことができます。ただし、マネージ コードに移行するためにそれを固定する必要がある場合は、"It Just Works" が根本的に損なわれるように思われます。

pin_ptr の MSDN ドキュメントが、オブジェクトの移動を防ぐためだけであると言っているように見えるのは紛らわしいですが、スタック上にあるように見え、とにかく移動してはならない値の型を固定することもできます。

特に、マネージド コードとアンマネージド コードのどちらでスタック メモリが同じように扱われるかという問題を提起します。MSIL のデバッグ中に、スタックを表示できないことがわかりました。そのためのスタック ビューアー ツールはありません。MSIL には "実際の" スタックがなく、代わりに仮想マシン CLR が自由に最適化できると聞いたことがありますが、実際のメモリの代わりに空きプロセッサ レジスタを使用するなどです。これが正しいかどうか、また、パラメーターの受け渡しのようにスタックに適用されるのか、ローカル変数メモリのようにスタックに適用されるのかは不明です。

上記のサンプル プロジェクトの奇妙な効果は、破損しているオブジェクトの pin_ptr が問題を解決しているように見えることです。ただし、オブジェクトは STACK にあり、固定する必要はありません。/CLR が pin_ptr を「このオブジェクトを移動しない」だけでなく、「この領域を真のメモリとして残し、その上でレジスタの最適化を試みない」と解釈する可能性があります。 ?

/CLR が、API の呼び出し中にメソッド内スタック メモリの最適化を回避するのに十分なほど賢いかどうかを具体的に知りたいのですが、上記の例では、 NTDLL と、関数が typedef として宣言される方法。

関数 typedef にマーシャリング属性を追加することを検討しましたが、そうできないようです。WinAPI定義にはMarshallAs属性がないことに注意してください。

NTDLL 呼び出しの直前に __debugbreak() を使用して上記のプロジェクトに侵入することができましたが、これはネイティブ コードにステップインできないように見えるマネージ デバッグ モードのみを提供します。「asm int 3」は x64 でサポートされていないため記述できません。ただし、誤った値 NumberOfPairs が、レジスタ自体としてではなく、レジスタが指すメモリ位置に渡されていることがわかります。

于 2009-07-13T12:22:42.243 に答える