This one has me stumped. I'd swear this behaved differently prior to .NET 1.1 service pack 1 (and/or XP SP2), but I can't prove it. As reported by a CodeProject reader, you'll get the standard .NET crash dialog in a console app, even if you've registered an unhandled exception handler for your AppDomain. What gives? Why doesn't AppDomain.CurrentDomain.UnhandledException capture exceptions on the main thread of a .NET console application?
This is a companion discussion topic for the original blog entry at: http://www.codinghorror.com/blog/2005/02/console-apps-and-appdomain-currentdomain-unhandledexception.html
Just to add another test environment, I tried running this in Mono and it worked fine.
You gotta be kidding me! I submitted a feedback from that MSDN page indicating that the sample… uh… doesn’t work.
I just noticed your code formatter (I rarely leave bloglines, where it can’t be seen for some reason) with the options like copying to the clipboard. That’s really sweet. Where can I get that or is it your personal creation?
I just tried it, and you’re right. This is one of the most obvious omissions on MS side, unless it’s “by design”
I just tried it, not just a 1.1 SP1 problem, I just tried in on 1.0, 1.1 and 2.0 and all ran into the same problem.
I’ve never noticed it, as I typically place a try…catch around everything in the main anyway…
The syntax highlighter is covered here:
Thanks for testing this. So it’s not just me. The weird thing is, I would swear, SWEAR it wasn’t like this before.
But it looks like David is right-- the only “solution” is to wrap main in a Try…Catch. And that of course works.
Which begs the question: why does the MSDN code sample expect to work as printed? It’s totally broken! It’s certainly what I would expect to happen, eg, unhandled exceptions go to the unhandled exception handler… and it’s what I remember happening before. Hmm.
Maybe you should add either a suggestion or bug (depending on whether this is by design) to the Microsoft Product Support Center.
I don’t think that this was only applicable to console applications, it was also applicable to Windows Forms applications, if the exception occurred on the main thread.
You can still post issues for 1.0/1.1, they just don’t have it in the dropdown.
Actually, just created an suggestion, saying that they add the current versions:
OK I did that. I hope it works this way in 2.0 (as posted by David Kean) or else I’m gonna look stupid. I couldn’t find a way to open a bug for just 1.1, only for the beta 2.0 stuff.
Anyway here it is:
The unhandled exception handler works properly for winforms apps. Slightly different handler, though: Application.ThreadException. You only have problems on new threads you spawn, and to get those to work, all you need to do is add the same handler per-thread.
I generally add handlers for both, and I don’t think I’ve ever seen any exception condition where both fire. It’s usually one or the other.
Sorry for the confusion. This behavior is actually the design, though the design can be a little convoluted at times.
The first thing to understand is that the UnhandledException event is not an unhandled exception "handler". Registering for the event, contrary to what the documentation says :-(, does not cause unhandled exceptions to be handled. (Since then they wouldn't be unhandled, but I'll stop with the circular reasoning already...) The UnhandledException event simply notifies you that an exception has gone unhandled, in case you want to try to save state before your thread or application dies. FWIW, I have filed a bug to get the docs fixed.
Just to complicate things, in v1.0 and 1.1, an unhandled exception did not always mean that your application would die. If the unhandled exception occurred on anything other than the main thread or a thread that began its life in unmanaged code, the CLR ate the exception and allowed your app to keep going. This was generally evil, because what would often happen was, for example, that ThreadPool threads would silently die off, one by one, until your application wasn't actually doing any work. Figuring out the cause of this kind of failure was nearly impossible. This may be why Jeff thought it worked before...he just always saw crashes on non-main threads.
In v2.0, an unhandled exception on any thread will take down the application. We've found that it's tremendously easier to debug crashes than it is to debug hangs or the silent-stoppage-of-work problem described above.
BTW, on my 1.1 machine the example from MSDN does have the expected output; it's just that the second line doesn't show up until after you've attached a debugger (or not). In v2 we've flipped things around so that the UnhandledException event fires before the debugger attaches, which seems to be what most people expect.
CLR Exceptions PM
Jeff, I love you! (metaphorically speaking, of course)
Your comment on handling Application.ThreadException has saved me about one hundred hours of rewriting an application in VB6. There is an issue with the C# I’m writing in that the COM that is calling it is buggy. The error doesn’t matter, technically, so I am okay with ignoring it. The trick is, I don’t want the user to need to click Continue in a JIT window.
I am seriously indebted to you. I randomly found this entry via Google but since earlier this summer I’ve been reading your blog. Too bad I didn’t start reading your blog back in February of this year instead!
Test if I can post? If so I have a possible solution…
You want global error handling to work.
You want multiple threads.
You can probably clean this code up more.
But… It works.
I needed similar:
Before main form displays, a warning form that user is connected to test (if they are) is displayed. This is done on a background thread.
We still want the global error handling to work after creating the thread.
The following looks dubious but accomplishes this.
’ This will trap errors on main form even after
’ launching other threads.
AddHandler Application.ThreadException, AddressOf Application_ThreadException
’ Create another thread to display a popup.
’ In my case I check and display warning if user
’ Is connected to test.
’ Now on Form 1 raise an error.
’ It will be trapped by Global Handler.
Public Sub TestPopup()
Dim oMCDelegate As MulticastDelegate
Dim oThreadStatCol As Collection = New Collection
oMCDelegate = CType(oMCDelegate.Combine(New WaitCallback(AddressOf CType( _
oThreadStatCol.Item(1), LaunchPopup).launch), _
New WaitCallback(AddressOf ThreadFinished)), MulticastDelegate)
ThreadPool.UnsafeQueueUserWorkItem(CType(oMCDelegate, WaitCallback), oThreadStatCol.Item(1))
Public Sub ThreadFinished(ByVal State As Object)
Private Class LaunchPopup
Public Sub launch(ByVal State As Object)
Dim frm As New Form2
PM for CLR Exceptions. Nice title