32

この質問に関連して、.NET 4.5.2 アプリが破損状態の例外をキャッチできるように CLR を強制したいと思います。これは、それらをログに記録してからアプリケーションを終了するという唯一の目的のためです。catch (Exception ex)アプリの周りにいくつかの場所がある場合、これを行う正しい方法は何ですか?

したがって、<legacyCorruptedStateExceptionsPolicy>属性を指定した後、正しく理解していれば、すべてのcatch (Exception ex)ハンドラーが例外をキャッチAccessViolationExceptionし、喜んで続行します。

ええ、それcatch (Exception ex)が悪い考えであることは承知していますが、CLR が少なくとも正しいスタック トレースをイベント ログに記録してくれるなら、喜んで顧客に説明したいと思います。彼のサーバー アプリは午前 1 時に急速に失敗し、しばらくの間オフラインになっています。夜は良いことです。しかし残念なことに、CLR は無関係な例外をイベント ログに記録してからプロセスを終了するため、実際に何が起こったのかを知ることができません。

問題は、これを実現する方法、プロセス全体です。

if the exception thrown is a Corrupted State Exception:
    - write the message to the log file
    - end the process 

(アップデート)

言い換えれば、これはおそらく単純なアプリのほとんどの例外で機能します。

[HandleProcessCorruptedStateExceptions] 
[SecurityCritical]
static void Main() // main entry point
{
    try 
    {

    }
    catch (Exception ex)
    {
        // this will catch CSEs
    }
}

ただし、次の場合は機能しません。

  • 未処理のアプリ ドメイン例外 (つまり、非フォアグラウンド スレッドでスローされる)
  • MainWindows サービス アプリ (実際のエントリ ポイントを持たない)

この作業を行う唯一の方法のように思え<legacyCorruptedStateExceptionsPolicy>ますが、その場合、CSE をログに記録した後に失敗する方法がわかりませんか?

4

3 に答える 3

33

それを使用する代わりに、ここに記載されているように(and )<legacyCorruptedStateExceptionsPolicy>を使用することをお勧めします。[HandleProcessCorruptedStateExceptions][SecurityCritical]

https://msdn.microsoft.com/en-us/magazine/dd419661.aspx

その後、Mainメソッドは次のようになります。

[HandleProcessCorruptedStateExceptions, SecurityCritical]
static void Main(string[] args)
{
    try
    {
        ...
    }
    catch (Exception ex)
    {
        // Log the CSE.
    }
}

StackOverflowExceptionただし、これはや などのより深刻な例外をキャッチしないことに注意してExecutionEngineExceptionください。

finally関連するブロックもtry実行されません:

https://csharp.2000things.com/2013/08/30/920-a-finally-block-is-not-executed-when-a-corrupted-state-exception-occurs/

その他の未処理の appdomain 例外については、次を使用できます。

  • AppDomain.CurrentDomain.UnhandledException
  • Application.Current.DispatcherUnhandledException
  • TaskScheduler.UnobservedTaskException

(特定のハンドラーが状況に適している場合は、詳細を検索してください。TaskScheduler.UnobservedTaskExceptionたとえば、少しトリッキーです。)

メソッドにアクセスできない場合はMain、AppDomain 例外ハンドラーをマークして CSE をキャッチすることもできます。

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

...

[HandleProcessCorruptedStateExceptions, SecurityCritical]
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    // AccessViolationExceptions will get caught here but you cannot stop
    // the termination of the process if e.IsTerminating is true.
}

防御の最後の行は、次のような管理されていない UnhandledExceptionFilter である可能性があります。

[DllImport("kernel32"), SuppressUnmanagedCodeSecurity]
private static extern int SetUnhandledExceptionFilter(Callback cb);
// This has to be an own non generic delegate because generic delegates cannot be marshalled to unmanaged code.
private delegate uint Callback(IntPtr ptrToExceptionInfo);

そして、プロセスの最初のどこかで:

SetUnhandledExceptionFilter(ptrToExceptionInfo =>
{
    var errorCode = "0x" + Marshal.GetExceptionCode().ToString("x2");
    ...
    return 1;
});

考えられるリターン コードの詳細については、次を参照してください。

https://msdn.microsoft.com/en-us/library/ms680634(VS.85).aspx

の「特殊性」はUnhandledExceptionFilter、デバッガーがアタッチされている場合は呼び出されないことです。(少なくとも、WPF アプリを使用している私の場合はそうではありません。)そのことに注意してください。

上記の適切な ExceptionHandlers をすべて設定すると、ログに記録できるすべての例外がログに記録されます。より深刻な例外 (StackOverflowExceptionや などExecutionEngineException) については、発生後にプロセス全体が使用できなくなるため、別の方法を見つける必要があります。考えられる方法は、メイン プロセスを監視し、致命的なエラーをログに記録する別のプロセスである可能性があります。

追加のヒント:

于 2016-10-10T11:10:45.460 に答える
13

[HandleProcessCorruptedStateExceptions]ハンドラー メソッドを1属性でデコレートすることもできることを指摘してくれた @haindl に感謝します。そのため、想定どおりに機能するかどうかを確認するためだけに、小さなテスト アプリを作成しました。

1 注:ほとんどの回答では、属性も含める必要があると述べられてい[SecurityCritical]ますが、以下のテストではそれを省略しても動作は変わりませんでした ([HandleProcessCorruptedStateExceptions]単独でも問題なく動作するように見えました)。ただし、これらの人々はすべて彼らが言っていることを知っていると推測しているので、両方の属性を以下に残しておきます. これは、「StackOverflow からコピー」パターンが実際に使用されている学校の例です。

アイデアは、明らかに、設定を から削除することです。つまり、最も外側の (エントリーレベルの) ハンドラーのみが例外をキャッチし、ログに記録してから失敗することを許可します。設定を追加すると、内部ハンドラーで例外をキャッチした場合にアプリを続行できますが、これはあなたが望むものではありません: アイデアは、正確な例外情報を取得して惨めに死ぬことです.<legacyCorruptedStateExceptionsPolicy>app.config

次のメソッドを使用して例外をスローしました。

static void DoSomeAccessViolation()
{
    // if you have any questions about why this throws,
    // the answer is "42", of course

    var ptr = new IntPtr(42);
    Marshal.StructureToPtr(42, ptr, true);
}

1. からの例外のキャッチMain:

[SecurityCritical]
[HandleProcessCorruptedStateExceptions]
static void Main(string[] args)
{
    try
    {
        DoSomeAccessViolation();
    }
    catch (Exception ex)
    {
        // this will catch all CSEs in the main thread
        Log(ex);
    }
}

2. バックグラウンド スレッド/タスクを含むすべての例外をキャッチします。

// no need to add attributes here
static void Main(string[] args)
{
    AppDomain.CurrentDomain.UnhandledException += UnhandledException;

    // throw on a background thread
    var t = new Task(DoSomeAccessViolation);
    t.Start();
    t.Wait();
}

// but it's important that this method is marked
[SecurityCritical]
[HandleProcessCorruptedStateExceptions]
private static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    // this will catch all unhandled exceptions, including CSEs
    Log(e.ExceptionObject as Exception);
}

後者のアプローチのみを使用し、他のすべての場所[HandleProcessCorruptedStateExceptions]からを削除して、例外が間違った場所でキャッチされないようにすることをお勧めします。つまり、どこかにブロックがあり、がスローされた場合、CLR がブロックをスキップして、アプリを終了する前に伝播する必要があります。try/catchAccessViolationExceptioncatchUnhandledException

于 2016-10-10T11:49:55.077 に答える