Console apps and AppDomain.CurrentDomain.UnhandledException

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.

Hi Jeff,
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” :slight_smile:

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…

Hermann,

The syntax highlighter is covered here:

http://www.codinghorror.com/blog/archives/000172.html

(not mine, but it is really cool JavaScript!)

David/Drazen

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.

http://lab.msdn.microsoft.com/productfeedback/default.aspx

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:

http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=d752a8c9-262d-4e0c-be44-84f0880a60d5

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:

http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackId=FDBK21092

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.

Hi all,
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.

Jonathan Keljo
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!

Again, thanks!
Eric

Test if I can post? If so I have a possible solution…

Ok…
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.

Sub Main()
’ 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.
TestPopup()

Windows.Forms.Application.Run(New Form1)

’ Now on Form 1 raise an error.
’ It will be trapped by Global Handler.

End Sub

Public Sub TestPopup()
Dim oMCDelegate As MulticastDelegate
Dim oThreadStatCol As Collection = New Collection

    oThreadStatCol.Add(New LaunchPopup)

    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))
End Sub

Public Sub ThreadFinished(ByVal State As Object)

End Sub

Private Class LaunchPopup
    Public Sub launch(ByVal State As Object)
        Dim frm As New Form2
        frm.ShowDialog()
    End Sub

End Class

PM for CLR Exceptions. Nice title :slight_smile: