什么时候AppDomain.ProcessExit不会被调用?

时间:2013-12-19 06:42:13

标签: .net exception process-exit

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

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

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;

2 个答案:

答案 0 :(得分:3)

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

使用编写的代码,永远不应引发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。

答案 1 :(得分:2)

请查看此博文:AppDomain.ProcessExit is not guaranteed to be called。引自帖子:

  

不保证会调用AppDomain.ProcessExit。这很美   有弹性,会处理你可能造成的常见事情   低信任IL(异常,内存不足等),但也有一些   事情(如粗鲁的进程关闭),inprocess事件永远不会   能够抵抗。

     

(...)

     

从进程内调用回调。如果这个过程粗鲁   退出,不会调用回调。粗鲁的常见来源   退出包括:

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

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