9

DLL にコンパイルされた大量のネイティブ C++ コードがあります。

次に、C++ インターフェイスをラップする C++/CLI プロキシ コードを含む dll をいくつか用意します。

その上に、C++/CLI ラッパーを呼び出す C# コードがあります。

これまでのところ、標準的なもの。

しかし、ネイティブの C++ 例外が .Net の世界に伝播することが許可されているケースが多くあり、これらを System.Exception オブジェクトとしてラップする .Net の機能に依存しており、ほとんどの場合、これは正常に機能します。

ただし、スローの時点でスコープ内にあるオブジェクトのデストラクタが、例外が伝播したときに呼び出されていないことがわかっています。

いくつかの調査の結果、これはかなりよく知られている問題であることがわかりました。ただし、解決策/回避策は一貫性がないようです。ネイティブ コードを /EHsc ではなく /EHa でコンパイルすると、問題が解消されることがわかりました (少なくとも、テスト ケースでは解消されました)。ただし、SEH 例外を C++ 例外に変換するため、/EHsc を使用することを強くお勧めします。コンパイラに最適化の範囲を広げさせたいと考えています。

(C++/CLI レイヤーに加えて) (ネイティブ) try-catch-throw でネイティブ管理の境界を越えてすべての呼び出しをラップする以外に、この問題に対する他の回避策はありますか?

4

4 に答える 4

3

/E コンパイラ スイッチの MSDN ページには、この動作が記載されています。

http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx

関連する引用は次のとおりです。

/EHs を使用すると、catch 句は非同期例外をキャッチしません。また、Visual C++ 2005 では、非同期例外が処理されても、非同期例外が生成されたときにスコープ内のすべてのオブジェクトが破棄されません。

基本的に、/EHsc は楽観的な見方です。唯一の例外が真の C++ スタイルのものであると想定し、それに応じて最適化します。一方、/EHa は悲観的な見方をしており、どのコード行でも例外が生成される可能性があると想定しています。

アクセス違反、ページ内エラー、またはその他の SEH が発生しないことが保証できる場合は、/EHsc を使用します。ただし、サービスを作成している場合、および/または「ベスト エフォート」を提供したい場合は、/EHa が必要になります。

また、例外がモジュールの境界を越えることを許可しないという @JaredPar の意見にも同意します。CLR が例外を処理する方法について @nobugz が言っていることは正しいかもしれませんが、P/Invoke を使用してネイティブ コードを直接呼び出す .Net コードと、C++/CLI 相互運用 DLL を呼び出すことには違いがあると思います。前者の場合、CLR がユーザーに代わって状況を処理する必要がありますが、後者の場合はユーザーが制御し、それに応じて変換できます。

于 2010-03-23T21:51:11.080 に答える
3

残念ながら、良い回避策はないと思います。MS プラットフォームでの C++ 例外の実装は、SEH 例外 (IIRC) を使用して実装されます。CLR は SEH 処理にフックしてネイティブ例外をキャッチし、それらを CLR 例外に処理します。SEH レベルでそれらをキャッチするため、例外は C++ に対する SEH 例外のように見え、それに応じてデストラクタが実行されるか実行されません。

あなたが指摘したように、最良の2つのオプションは

  • /EHa でコンパイル
  • 関数の入口と出口に try/catch を追加します

理想的には、とにかく 2 番目のものを実行する必要があります。私の経験では、C++ の例外がコンポーネントの境界を越えることを許可するのは悪い習慣だと考えられています。

_set_seh_translator( Documentation )を使用して実現できるハックなソリューションもある可能性があります。ただし、CLR 例外処理をうっかり無効にして、多くの望ましくない問題を引き起こす可能性があるため、その関数を避けることを強くお勧めします。

于 2010-03-23T18:35:48.890 に答える
3

あなたはこれを正しくしていないと思います。_set_se_translator() を使用するには、すでに /EHa でコンパイルする必要があります。MSDN ライブラリ ページから:

You must use /EHa when using _set_se_translator.

さらに深刻なことに、使用するとマネージド コードが壊れてしまいます。CLR は、さまざまな事故を検出するために SEH 例外に依存しています。SetUnhandledExceptionFilter を使用してそれらをトラップします。特に NullReferenceException と OverflowException (x86) はこの方法で発生します。独自の __try ブロックを挿入すると、この例外が CLR に流れ込むのを防ぐことができます。それを「処理」しますが、例外の正確な理由の痕跡はありません。また、管理された try/catch ブロックはそれを検出できません。

/EHa 効率 (実際に問題がある場合) の解決策は、64 ビット コードです。その関数テーブル ベースのスタックの巻き戻しは、try ブロックのオーバーヘッドがゼロで非常に効率的です。

于 2010-03-23T19:38:17.020 に答える
1

CLR の 2.0 バージョンには、この問題を引き起こすバグがあります。マネージ実行可能ファイルを 4.0 CLR で実行すると、デストラクタを期待どおりに呼び出すことができます。

詳細については、例外がスローされた後に解放されない Boost 共有ミューテックスを参照してください。

于 2011-10-19T20:00:04.813 に答える