优雅地处理损坏的状态异常 [英] Gracefully handling corrupted state exceptions
问题描述
与此问题有关,我想强制CLR让我的.NET 4.5.2应用程序捕获损坏的状态异常,其唯一目的是记录它们,然后终止应用程序。如果我在应用程序周围的多个地方都有 catch(异常例外)
,执行此操作的正确方法是什么?
Related to this question, I would like to force CLR to let my .NET 4.5.2 app catch Corrupted State Exceptions, for the sole purpose of logging them and then terminating the application. What's the correct way to do this, if I have catch (Exception ex)
at several places around the app?
因此,在我指定了< legacyCorruptedStateExceptionsPolicy>
属性后,如果我理解正确的话,所有 catch(异常除外)
处理程序将捕获 AccessViolationException
之类的异常并愉快地继续。
So, after I specify the <legacyCorruptedStateExceptionsPolicy>
attribute, if I understood correctly, all the catch (Exception ex)
handlers will catch exceptions like AccessViolationException
and happily continue.
是的,我知道 catch (例外)
是一个坏主意,但是如果CLR至少将正确的堆栈跟踪放入事件日志中,我很乐意向客户解释他的服务器应用程序快速失败凌晨1点,整夜不在线是一件好事。但是不幸的是,CLR将无关的异常记录到事件日志中,然后关闭该过程,以使我无法发现实际发生的情况
Yeah, I know catch (Exception ex)
is a Bad Idea™, but if CLR would at least put the correct stack trace into the Event Log, I would be more than happy to explain to the customer that his server app failing fast at 1AM and being offline for the night is a good thing. But unfortunately, CLR logs an unrelated exception into the Event Log and then closes the process so that I cannot find out what actually happened.
问题是,如何实现这一目标,需要在整个过程中进行:
The question is, how to make this happen, process wide:
if the exception thrown is a Corrupted State Exception:
- write the message to the log file
- end the process
(更新)
换句话说,这可能适用于简单的应用程序:
In other words, this would probably work for most exceptions in a simple app:
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main() // main entry point
{
try
{
}
catch (Exception ex)
{
// this will catch CSEs
}
}
但是,它不适用于:
- 未处理的应用程序域异常(即t
- Windows Service应用程序(没有实际的
Main
入口点)
- Unhandled app domain exceptions (i.e. thrown on non-foreground threads)
- Windows Service apps (which don't have an actual
Main
entry point)
因此,看来< legacyCorruptedStateExceptionsPolicy>
是使这项工作唯一的方法,在登录CSE后我不知道该怎么办?
So it seems like <legacyCorruptedStateExceptionsPolicy>
is the only way to make this work, in which case I don't know how to fail after logging the CSE?
推荐答案
而不是使用< ; legacyCorruptedStateExceptionsPolicy>
,最好使用 [HandleProcessCorruptedStateExceptions]
(和 [SecurityCritical]
),如此处所述:
Instead of using <legacyCorruptedStateExceptionsPolicy>
it would be better to use [HandleProcessCorruptedStateExceptions]
(and [SecurityCritical]
) as stated here:
https://msdn.microsoft.com/zh-cn/magazine/dd419661.aspx
在此之后,您的 Main
方法应如下所示:
Following that, your Main
method should look something like this:
[HandleProcessCorruptedStateExceptions, SecurityCritical]
static void Main(string[] args)
{
try
{
...
}
catch (Exception ex)
{
// Log the CSE.
}
}
但请注意,这并不能抓住更多严重的异常,例如 StackOverflowException
和 ExecutionEngineException
。
But be aware that this doesn't catch the more serious exceptions like StackOverflowException
and ExecutionEngineException
.
也最终个涉及的
try
块将不会执行:
Also finally
of involved try
blocks will not be executed:
对于其他未处理的应用程序域例外,可以使用:
For other unhandled appdomain exceptions you can use :
-
AppDomain.CurrentDomain.UnhandledException
-
Application.Current.DispatcherUnhandledException
-
TaskScheduler.UnobservedTaskException
AppDomain.CurrentDomain.UnhandledException
Application.Current.DispatcherUnhandledException
TaskScheduler.UnobservedTaskException
(请进行搜索特定处理程序适合您的详细信息情况。例如, TaskScheduler.UnobservedTaskException
有点棘手。)
(Please do a search for the details when a specific handler is appropriate for your situation. TaskScheduler.UnobservedTaskException
for example is a bit tricky.)
如果您无权访问 Main
方法,您还可以标记AppDomain异常处理程序以捕获CSE:
If you don't have access to the Main
method, you can also mark your AppDomain exception handler to catch the 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,如下所示:
The last line of defense could be an unmanaged UnhandledExceptionFilter like this:
[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);
然后在流程开始的某个地方:
And then somewhere at the beginning of your process:
SetUnhandledExceptionFilter(ptrToExceptionInfo =>
{
var errorCode = "0x" + Marshal.GetExceptionCode().ToString("x2");
...
return 1;
});
您可以在此处找到有关可能的返回码的更多信息:
You can find more information about the possible return codes here:
https://msdn.microsoft。 com / en-us / library / ms680634(VS.85).aspx
UnhandledExceptionFilter的特色
是如果连接了调试器则不会调用它。 (至少在我拥有WPF应用程序的情况下没有。)请注意这一点。
A "specialty" of the UnhandledExceptionFilter
is that it isn't called if a debugger is attached. (At least not in my case of having a WPF app.) So be aware of that.
如果从上面设置了所有适当的ExceptionHandler,则应该记录所有可以记录的异常。对于更严重的异常(例如 StackOverflowException
和 ExecutionEngineException
),您必须找到另一种方法,因为在此之后整个过程将无法使用他们发生了。一种可能的方式可能是监视主进程并记录所有致命错误的另一个进程。
If you set all the appropriate ExceptionHandlers from above, you should be logging all exceptions that can be logged. For the more serious exceptions (like StackOverflowException
and ExecutionEngineException
) you have to find another way because the whole process is unusable after they happened. A possible way could perhaps be another process that watches the main process and logs any fatal errors.
其他提示:
- 在
AppDomain.CurrentDomain.UnhandledException
中,您可以安全地投射e.ExceptionObject
到Exception
不必担心-至少如果您没有任何IL代码会抛出除Exception
以外的其他对象:为什么UnhandledExceptionEventEventsArgs.ExceptionObject是对象而不是Exception ? - 如果要隐藏Windows错误报告对话框,可以在此处查看:当程序崩溃时如何终止程序? (应该只是在单元测试中失败,而不是永远卡住)
- 如果您有一个带有多个调度程序的WPF应用程序,则还可以使用
Dispatcher .UnhandledException
(其他调度程序)。
- In the
AppDomain.CurrentDomain.UnhandledException
you can safely cast thee.ExceptionObject
toException
without having to worry - at least if you don't have any IL code that throws other objects thanException
: Why is UnhandledExceptionEventArgs.ExceptionObject an object and not an Exception? - If you want to suppress the Windows Error Reporting dialog, you can take a look here: How to terminate a program when it crashes? (which should just fail a unit test instead of getting stuck forever)
- If you have a WPF application with multiple dispatchers you can also use a
Dispatcher.UnhandledException
for the other dispatchers.
这篇关于优雅地处理损坏的状态异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!