18

問題:

アンマネージ コードからCLRに入るスレッドの未処理の例外は、「通常の」未処理の例外 CLR 処理をトリガーしません。

以下のコードではCSSimpleObject.GetstringLength()、C++ から

  • "1" は、呼び出し元のスレッド (CLR で作成されていないスレッド) で例外をスローします。
  • "2" は、new Thread() (CLR で作成されたスレッド) で例外をスローします。

「1」の場合

  • CurrentDomain_UnhandledException() が呼び出されることはありません。
  • アプリケーション ドメインとプロセスはロードされたまま実行され、FAILED のみが表示されます。

「2」の場合(想定される動作)

  • CurrentDomain_UnhandledException() が呼び出されます。
  • プロセスは強制終了されます。

質問:

「通常の」動作を得るために何をしなければなりませんか?

サンプルコード:

以下のコードは、「すべての相互運用と融合のサンプル」の Visual Studio 2010 の「 CppHostCLR」コード サンプルに基づいています。

ランタイムホスト (C++):

PCWSTR pszStaticMethodName = L"GetStringLength";
PCWSTR pszStringArg = L"1";
//PCWSTR pszStringArg = L"2";

hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath,
    pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet);
if (FAILED(hr))
{
    wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr);
    goto Cleanup;
}

マネージド コード (C#):

public class CSSimpleObject
{
    public CSSimpleObject()
    {
    }
    //------8<----------
    public static int GetStringLength(string str)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        switch (str)
        {
            case "1":
                throw new Exception("exception in non-CLR-created thread");
            case "2":
                Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
                thread.Start();
                break;
        }
        return str.Length;

    }
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine("CurrentDomain_UnhandledException:" + e.ToString());
        Console.ReadKey();
    }
    public static void WorkThreadFunction()
    {
        throw new Exception(""exception in CLR-created thread"");
    }

これまでの研究:

MSDN は当初、非 CLR で作成されたスレッドで未処理の例外が多かれ少なかれ「自然に」動作する必要があることを暗示しています。「マネージ スレッドの例外」を参照してください。

共通言語ランタイムでは、スレッド内の未処理の例外のほとんどが自然に処理されます。ほとんどの場合、これは未処理の例外が原因でアプリケーションが終了することを意味します。」

「ほとんど」とは、CLR で作成されたスレッドの内部、スレッドの中止、およびアプリケーション ドメインのアンロード例外が異なる方法で処理されることを意味します。非 CLR スレッド内

「彼らは正常に進行し、アプリケーションは終了します。」

さらに調査した結果、「CLR で処理されない例外処理」にたどり着き、次のことがわかりました。

「例外が処理されなかった場合...マネージメソッドでは、例外はCLRを終了しますが、ネイティブSEH例外としてスタックを伝播し続けます(マネージ例外はネイティブSEH例外として表されます)... OS未処理例外フィルター (UEF) メカニズムによって、CLR の未処理の例外処理が常にトリガーされるとは限りません。通常の状況では、これは期待どおりに機能し、CLR の未処理の例外処理がトリガーされます。ただし、特定のインスタンスでは、これが発生しない場合があります。」

上記のコードの何が問題なのですか、または CLR の未処理の例外処理がトリガーされるように変更するにはどうすればよいですか?

更新 (2011-05-31):

私は古いバグ レポートを見つけまし

この問題を報告していただきありがとうございます。この動作は実際には、CLR 実行エンジンと CRT が UnhandledExceptionFilter をめぐって競合することによって引き起こされるバグです。CLR のアーキテクチャは、このシナリオをサポートするバージョン 4.0 で改訂されました。

更新 (2011-06-06):

これを正しく理解することがなぜ重要なのでしょうか?

  • ホスティング環境を作成している場合、開発者は例外処理で一貫した動作を期待しています
  • ネイティブ スレッドで「通常の CLR 例外処理」をトリガーする方法がない限り、常にマネージド スレッドに実行を転送する必要があることを意味します (たとえば、スレッド プールでのエンキュー)。
  • 実行をネイティブ スレッドからマネージド スレッドに転送する小さなコードがまだあります。すべての例外をキャッチし、何らかの方法でその状況を別の方法で処理する必要があります。

注: CLR の動作を変更するSetActionOnFailure()と、元の例外がマスクされてしまうという意味で、問題が悪化します (つまり、メモリ不足の代わりに、元のエラーがどこから来たのか手がかりがなくて、threadAborts が表示されます)。

4

1 に答える 1

2

この更新は、おそらくここに解決策があることを意味します。ただし、その解決策はすべての場合に機能するとは限らないため、ここにいくつかの詳細情報があります.

CLR Unhandled Exception の動作を好む場合は、それを最も外側のプログラムにして、特定の機能を実行するためだけにネイティブ コードを呼び出します。これにより、CLR が未処理の例外フィルターを確実に制御できるようになります。

現在の構造を保持したいが、C++ コードが小さい場合は、CRT の使用をまったく停止できます (これにより、静的コンストラクターや C++ 例外処理などの多くの便利なものが使用できなくなります)。これにより、CLR が Unhandled Exception Filter を確実に取得できるようになります。

もちろん、自分で SetUnhandledExceptionFilter を呼び出すだけで、必要な動作を取得できます。

ただし、この状況での最善の推奨事項は、例外が発生したときに何かを実行したい任意のスレッドのコール スタックに、catch ブロックを含む実際の関数を配置し、UEF メカニズムに依存しないことだと思います。コンポーネントシステムの場合、複数のユーザーが競合するため、常に脆弱です。

マーティン

于 2011-06-04T18:21:29.943 に答える