この質問 に関連して、それらをログに記録してからアプリケーションを終了することのみを目的として、CLRが.NET 4.5.2アプリに破損状態の例外をキャッチするように強制したいと思います。アプリのいくつかの場所にcatch (Exception ex)
がある場合、これを行う正しい方法は何ですか?
したがって、_<legacyCorruptedStateExceptionsPolicy>
_属性を指定した後、正しく理解すると、すべてのcatch (Exception ex)
ハンドラーがAccessViolationException
のような例外をキャッチし、問題なく続行します。
ええ、私はcatch (Exception ex)
がBad Idea™であることを知っていますが、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
}
}
_
ただし、次の場合は機能しません。
Main
エントリポイントがない)したがって、_<legacyCorruptedStateExceptionsPolicy>
_がこれを機能させる唯一の方法であるように思われます。その場合、CSEのログ記録後に失敗する方法がわかりませんか?
_<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
は実行されません。
他の未処理の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
など)の場合、プロセス全体が発生すると使用できなくなるため、別の方法を見つける必要があります。考えられる方法は、メインプロセスを監視して致命的なエラーをログに記録する別のプロセスである可能性があります。
追加のヒント:
AppDomain.CurrentDomain.UnhandledException
_では、心配することなく_e.ExceptionObject
_をException
に安全にキャストできます。少なくともException
以外のオブジェクトをスローするILコードがない場合 なぜUnhandledExceptionEventArgsなのか。 ExceptionObject例外ではなくオブジェクト?Dispatcher.UnhandledException
_を使用することもできます。ハンドラメソッドを[HandleProcessCorruptedStateExceptions]
で装飾することもできることを指摘してくれた@haindlに感謝1 属性なので、実際に想定どおりに機能するかどうかを確認するためだけに、小さなテストアプリを作成しました。
1 注:ほとんどの回答では、[SecurityCritical]
属性も含める必要があると記載されていますが、以下のテストでは省略しても動作は変わりませんでした( [HandleProcessCorruptedStateExceptions]
だけでも問題なく動作するように見えました)。しかし、私はこれらの人々すべてが彼らが言っていることを知っていたと推定しているので、両方の属性を以下に残します。これは、「StackOverflowからコピーした」パターンの実際の学校の例です。
アイデアは、明らかに、remove<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]
を他のすべての場所から削除して、例外がキャッチされないようにすることをお勧めします間違った場所。つまりtry/catch
ブロックがどこかにあり、AccessViolationException
がスローされた場合、アプリを終了する前に、CLRでcatch
ブロックをスキップしてUnhandledException
に伝達する必要があります。