私が知る限り、これはすべて 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 が、レジスタ自体としてではなく、レジスタが指すメモリ位置に渡されていることがわかります。