优雅地处理损坏的状态异常 [英] Gracefully handling corrupted state exceptions

查看:34
本文介绍了优雅地处理损坏的状态异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于这个问题,我想强制CLR让我的.NET 4.5.2应用程序捕获损坏状态例外,其唯一目的是记录它们然后终止应用程序.如果我在应用程序周围的多个位置有 catch (Exception ex),那么正确的方法是什么?

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?

因此,在我指定 属性后,如果我理解正确,所有 catch (Exception ex) 处理程序将捕获像 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 (Exception ex) 是一个坏主意™,但如果 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
    }
}

但是,它不适用于:

  • 未处理的应用域异常(即在非前台线程上抛出)
  • Windows 服务应用(没有实际的 Main 入口点)

所以看起来 是实现此功能的唯一方法,在这种情况下,我不知道在记录 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?

推荐答案

与其使用 不如使用 [HandleProcessCorruptedStateExceptions](和 [SecurityCritical]) 如下所述:

Instead of using <legacyCorruptedStateExceptionsPolicy> it would be better to use [HandleProcessCorruptedStateExceptions] (and [SecurityCritical]) as stated here:

https://msdn.microsoft.com/en-us/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.
    }
}

但请注意,这不会捕获更严重的异常,例如 StackOverflowExceptionExecutionEngineException.

But be aware that this doesn't catch the more serious exceptions like StackOverflowException and ExecutionEngineException.

还有 finally 涉及的 try 块不会被执行:

Also finally of involved try blocks will not be executed:

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

对于其他未处理的应用程序域异常,您可以使用:

For other unhandled appdomain exceptions you can use :

  • 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.

如果您从上面设置了所有适当的 ExceptionHandlers,您应该记录所有可以记录的异常.对于更严重的异常(如StackOverflowExceptionExecutionEngineException),你必须另辟蹊径,因为它们发生后整个过程都无法使用.一种可能的方法可能是另一个进程监视主进程并记录任何致命错误.

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.

其他提示:

  • In the AppDomain.CurrentDomain.UnhandledException you can safely cast the e.ExceptionObject to Exception without having to worry - at least if you don't have any IL code that throws other objects than Exception: 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆