5

启用//1下面的行将导致程序崩溃而不会打印“proc exit”,但如果是 line //2,将打印“proc exit”。在这两种情况下都会打印“未处理”btw。

为什么有区别,一般有什么规则?显然,使用例如任务管理器杀死应用程序将阻止打印“proc exit”,但除此之外,它没有被打印的情况是什么?

static void Main(string[] args)
{
    Thread.GetDomain().UnhandledException +=
        (sender, eventArgs) => Console.WriteLine("unhandled");
    AppDomain.CurrentDomain.ProcessExit +=
        (sender, eventArgs) => Console.WriteLine("proc exit");
    //1 new Thread(_ => { throw new Exception(); }).Start();
    //2 ThreadPool.QueueUserWorkItem(_ => { throw new Exception(); });
    Thread.Sleep(1000);
    return;
4

2 回答 2

5

请看这篇博文:AppDomain.ProcessExit is notGuaranteed to be called。从帖子中引用:

不保证调用 AppDomain.ProcessExit。它非常有弹性,可以处理低信任 IL 可能导致的常见问题(异常、内存不足等),但有些事情(如粗鲁的进程关闭),进程内事件永远无法抵御.

(...)

回调是从进程内部调用的。如果进程粗鲁地退出,回调将不会被调用。粗鲁退出的常见来源包括:

  1. 通过 TaskManager 或 kernel32!TerminateProcess 从外部杀死。
  2. Stackoverflow 处理程序消耗超过保护页面。

ProcessExit 事件就像告诉某人“请给我打电话说你快死了”。如果死亡来得足够快,他们可能无法传达信息。

事实上,我已经尝试过您的代码,并且在取消注释第 1 行甚至第 2 行时它不会打印“proc exit” !(我尝试在 Visual Studio 2013 中针对所有 .NET 版本进行编译)。当然,当没有抛出异常并且进程正常退出时,它会打印它。(编辑:如果我在发布模式下编译代码,我会看到“proc exit”消息,但在调试模式下编译时不会看到)

作为旁注,这里是您的代码的建议重构(未完全测试并且可能不完整,但您明白了):

static void Main(string[] args)
{
    Thread.GetDomain().UnhandledException +=
        (sender, eventArgs) => Exiting((Exception)eventArgs.ExceptionObject);
    AppDomain.CurrentDomain.ProcessExit +=
        (sender, eventArgs) => Exiting(null);
    //1 new Thread(_ => { throw new Exception(); }).Start();
    //2 ThreadPool.QueueUserWorkItem(_ => { throw new Exception(); });
    Thread.Sleep(1000);
    return;
}

static void Exiting(Exception exception)
{
    //Put common cleanup code here (or at the end of the method)

    if(exception == null)
    {
        Console.WriteLine("normal proc exit");
    }
    else
    {
        Console.WriteLine("unhandled exception: " + exception.GetType().Name);
    }
}
于 2013-12-19T07:47:36.847 回答
4

这两种情况没有区别,很难解释为什么你会看到一个。

使用编写的代码,永远不应引发 AppDomain.ProcessExit 事件。正常情况应该会发生,CLR 将异常传递给操作系统,然后弹出 Windows 错误报告崩溃对话框。如果异常是程序崩溃的已知原因(不会),它会与 Microsoft 服务器检查一段时间,然后在用户关闭对话框时终止程序。所以没有 ProcessExit 事件。

您当然可以编写代码,以便引发ProcessExit。它要求您不要让操作系统来处理它。您必须自己关闭该程序。您可以通过使用 AppDomain.UnhandledException 事件处理程序自己显式终止程序来执行此操作。像这样:

    Thread.GetDomain().UnhandledException += (s, e) => {
        var ex = (Exception)e.ExceptionObject;
        Console.WriteLine(ex.ToString());
        Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
    };

您可以选择如何终止程序。我展示了 Environment.Exit() 但您也可以考虑 Environment.FailFast() 要求立即中止而不运行任何更多代码。这更安全,但当然你也不会得到 ProcessExit 。

于 2013-12-19T10:47:16.217 回答