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.
Consider the simple example:
using System; class Foo
static void Main(string args)
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
static void CurrentDomain_ProcessExit(object sender, EventArgs e)
Console.WriteLine("Process is exiting!");
Normally, this would print:
<whatever text you enter at the readline>
Process is exiting!
However, if while the process is stopped at ReadLine, we use task manager to rudely kill it, then the ProcessExit event will not run. Try for yourself!
(Update[12/1/06]: kudos to Antosha for pointing out that if the ProcessExit event is hooked after the ReadLine, it proves nothing. I've updated the example and reverified that it indeed doesn't run).
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:
1. Killed externally via TaskManager or kernel32!TerminateProcess.
2. 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.
Don't have a security model that requires that the ProcessExit event be run on shutdown.
Compare to Debugger's Exit Process event:
In contrast, the Debugger's exit process event (via ICorDebugManagedCallback::ExitProcess) is reliable because it's in a separate process and directly wired to waiting on the process handle of the debuggee. (Process handles are signaled when the process exits). This means a robust debugger can always expect the Exit Process event (as discussed on the forum here), but should not rely on any other event coming.