c# - 优雅地处理损坏的状态异常

标签 c# .net access-violation fail-fast corrupted-state-exception

this question相关,我想强制 CLR 让我的 .NET 4.5.2 应用程序捕获损坏的状态异常,其唯一目的是记录它们然后终止应用程序。如果我有catch (Exception ex),正确的方法是什么?在应用周围的几个地方?

所以,在我指定 <legacyCorruptedStateExceptionsPolicy> 之后属性,如果我没理解错的话,所有的catch (Exception ex)处理程序将捕获类似 AccessViolationException 的异常并愉快地继续。

是的,我知道catch (Exception ex)是一个坏主意™,但如果 CLR 至少将正确的堆栈跟踪放入事件日志中,我会非常乐意向客户解释他的服务器应用程序在凌晨 1 点快速失败并在晚上离线是一个好消息事物。但不幸的是,CLR 记录了一个 unrelated exception进入事件日志,然后关闭进程,这样我就无法找出实际发生的事情。

问题是,如何在流程范围内实现这一点:

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
    }
}

但是,它不适用于:

  • 未处理的应用域异常(即在非前台线程上抛出)
  • Windows 服务应用(没有实际的 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.
    }
}

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

还有 finally涉及 try block 将不会被执行:

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

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

  • 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,您应该记录所有可以记录的异常。对于更严重的异常(如 StackOverflowExceptionExecutionEngineException )你必须找到另一种方法,因为它们发生后整个过程将无法使用。一种可能的方法可能是监视主进程并记录任何 fatal error 的另一个进程。

其他提示:

关于c# - 优雅地处理损坏的状态异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39956163/

相关文章:

c++ - 为什么 CoUninitialize 在退出时会导致错误?

c++ - 添加 std::string 定义会导致访问冲突

c# - 更改 LiveCharts 中轴刻度标签的格式

c# - 如何在泛型方法中调用 Action<T> 参数?

c# - 一个可执行文件中的 C# 中的自删除应用程序

c# - 从 Azure 表存储中删除批处理时如何避免 404

delphi - 为什么以下使用 IOmniThreadPool 的代码会导致访问冲突?

c# - 单元测试静态实用程序类

c# - 为什么java和c#有终结器?

c# - 如何将文件中的 "0x1B"替换为空,即删除