如何在特定异常上终止 .NET 应用程序 - 可能无需展开堆栈

本文关键字:堆栈 应用程序 NET 异常 终止 | 更新日期: 2023-09-27 18:35:31

我有一个由C#和C++代码组成的.NET应用程序(服务)。

"崩溃"(即 C++ Code 中的System.AccessViolationException和其他损坏状态异常)将得到正确处理("非"),它们将直接导致我的AppDomain.CurrentDomain.UnhandledException处理程序(记录),然后应用程序将终止,如果配置如此,则写入 WER 转储文件(确实如此)。

对于此应用程序,我已经确定System.NullReferenceException始终是一个错误,特别是因为某些 C++/CLI 访问违规错误将报告此错误而不是 AV。

有没有办法让 .NET 不在异常边界上捕获NullReferenceException(在这种情况下是我的OnTimer回调),而是直接终止应用程序,而无需展开堆栈,基本上直接"跳"到AppDomain.CurrentDomain.UnhandledException

如何在特定异常上终止 .NET 应用程序 - 可能无需展开堆栈

你可以:

AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;

然后

static void CurrentDomain_FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
{
    if (e.Exception is NullReferenceException)
    {
        Environment.FailFast("FailFast", e.Exception);
    }
}

Environment.FailFast

将消息写入 Windows 应用程序事件日志后立即终止进程,然后将该消息包含在向Microsoft报告的错误中。

此方法在不运行任何活动的 try/finally 块或终结器的情况下终止进程。

显然,在CurrentDomain_FirstChanceException中,您可以复制UnhandledException中可能具有的日志记录代码(或者具有由两者调用的通用方法)

FirstChanceException实际上是一个"简单"的全局异常过滤器这一事实让我走上了正轨(它是否是"正确"的轨道还有待观察):

我们已经在 CLI 中有了异常筛选器

如果一个人有在 C# 6 中工作的奢侈,那么它就像:

        try
        {
            throw new NullReferenceException("No, Really");
        }
        catch(Exception ex) when (FilterExType(ex))
        {
            Console.WriteLine($"2: Caught 'any' exception: {ex}");
        }
    static bool FilterExType(Exception ex)
    {
        if (ex is NullReferenceException)
        {
            Environment.FailFast("BOOM from C#!", ex);
        }
        // always handle if we return
        return true;
    }

对于我们这些(像我一样)坚持使用早期版本的人,我们可以通过委托/lambda通过 VB.NET 路由过滤:

        try {
            VbFilterLib.FilteredRunner.RunFiltered(() =>
            {
                throw new NullReferenceException("Via VB.NET");
            });
        } 
        catch (Exception ex)
        {
            Console.WriteLine("1: Caught 'any' exception: {0}", ex");
        }

使用VB(请耐心等待,VB.NET 远非我精通的语言):

Public Class FilteredRunner
    Delegate Sub VoidCode()
    Private Shared Function FilterAction(x As Exception) As Boolean
        If TypeOf x Is NullReferenceException Then
            Environment.FailFast("Abort program! Investigate Bug via crash dump!", x)
        End If
        ' Never handle here:'
        Return False
    End Function
    Public Shared Sub RunFiltered(code As VoidCode)
        Try
            code.Invoke()
        Catch ex As Exception When FilterAction(ex)
            Throw New InvalidProgramException("Unreachable!", ex)
        End Try
    End Sub
End Class

显然,要使其工作,您需要更多配置绑定,但这似乎正是我想要的。