Enabling line //1
below will make the program crash without "proc exit" being printed,
but in case of line //2
, "proc exit" will be printed. "unhandled" btw gets printed in both cases.
Why the difference, and what are the rules in general? Obviously killing an app using e.g. the task manager will prevent "proc exit" from being printed, but other than that, what are the cases it doesn't get printed?
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;
There is no difference between the two cases, hard to explain why you'd see one.
With the code as written, the AppDomain.ProcessExit event should never be raised. The normal thing should happen, the CLR passes the exception to the operating system and the Windows Error Reporting crash dialog pops up. It trundles for a while to check with Microsoft servers if the exception is a known reason for programs to crash (it won't be), then terminates the program when the user dismisses the dialog. So no ProcessExit event.
You can certainly write your code so that ProcessExit will be raised. It requires you not leaving it up to the operating system to take care of it. You must shut down the program yourself. Which you do by explicitly terminating the program yourself with your AppDomain.UnhandledException event handler. Like this:
Thread.GetDomain().UnhandledException += (s, e) => {
var ex = (Exception)e.ExceptionObject;
Console.WriteLine(ex.ToString());
Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
};
You do have a choice on how you terminate the program. I showed Environment.Exit() but you could also consider Environment.FailFast() to ask for an instant abort without running any more code. That's safer but of course you won't get ProcessExit either.
Please take a look at this blog post: AppDomain.ProcessExit is not guaranteed to be called. Quoted from the post:
The AppDomain.ProcessExit is not guaranteed to be called. It's pretty
resilient and will deal with common things you may cause from
low-trust IL (exceptions, out-of-memory, etc), but there are some
things (like rude process shutdown), that an inprocess event can never
be resilient against.
(...)
The callback is invoked from within the process. If the process rudely
exits, the callback would not be invoked. Common sources for rudely
exiting include:
- Killed externally via TaskManager or kernel32!TerminateProcess.
- Stackoverflow handler consumes past the guard page.
The ProcessExit event is like telling somebody "please telephone me
that your about to die". If death comes quickly enough, they may not
get the message out.
In fact, I have tried out your code and it does never print "proc exit" when uncommenting line 1 or even line 2! (I have tried compiling against all .NET versions, in Visual Studio 2013). Of course it does print it when no exceptions are thrown and the process exits normally. (EDIT: I see the "proc exit" message if I compile the code in Release mode, but not when I compile in Debug mode)
As a side note, here is a suggested refactor for your code (not fully tested and probably incomplete, but you get the idea):
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);
}
}