Unhandled exception was handled??

L

Lucvdv

Can someone explain why this code pops up a messagebox saying the
ThreadAbortException wasn't handled?

The first exception is reported only in the debug pane, as expected.
The second (caused by thread.Abort()) is reported twice: once in the debug
window, and once through the message box.

Is it because the thread was sleeping when the exception occurred?


Imports System.Threading

[...]

Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load

Dim currentDomain As AppDomain = AppDomain.CurrentDomain

AddHandler currentDomain.UnhandledException, _
AddressOf UnhandledExceptionHandler

Try
Throw New Exception("This is a test")
Catch ex As Exception
Debug.WriteLine(ex.ToString)
End Try

Dim t As New Thread(AddressOf ThreadProc)
t.Start()
Thread.Sleep(1000)
t.Abort()
End Sub

Private Sub UnhandledExceptionHandler(ByVal sender As Object, _
ByVal args As UnhandledExceptionEventArgs)

MsgBox("Unhandled " & args.ExceptionObject.GetType.ToString())
End Sub

Private Sub ThreadProc()
Try
Do
Thread.Sleep(100)
Loop
Catch ex As ThreadAbortException
Debug.WriteLine(ex.ToString)
End Try
End Sub
 
J

Jay B. Harlow [MVP - Outlook]

Lucvdv,
Windows Forms itself is displaying the Message Box, as the Load Event had an
unhandled exception.

Generally you need to handle both the AppDomain.UnhandledException & the
Application.ThreadException.

The Application.ThreadException will take care of unhandled exception in
your Windows Forms, while the AppDomain.UnhandledException will take care of
unhandled exceptions in your other threads. I would add handlers for both
exceptions first thing in my Sub Main, not the Load event, as the exceptions
could occur before the Load event occured.

Depending on the type of application you are creating, .NET has three
different global exception handlers.

For ASP.NET look at:
System.Web.HttpApplication.Error event
Normally placed in your Global.asax file.

For console applications look at:
System.AppDomain.UnhandledException event
Use AddHandler in your Sub Main.

For Windows Forms look at:
System.Windows.Forms.Application.ThreadException event
Use AddHandler in your Sub Main.

It can be beneficial to combine the above global handlers in your app, as
well as wrap your Sub Main in a try catch itself.

There is an article in the June 2004 MSDN Magazine that shows how to
implement the global exception handling in .NET that explains why & when you
use multiple of the above handlers...

http://msdn.microsoft.com/msdnmag/issues/04/06/NET/default.aspx

For example: In my Windows Forms apps I would have a handler attached to the
Application.ThreadException event, plus a Try/Catch in my Main. The
Try/Catch in Main only catches exceptions if the constructor of the MainForm
raises an exception, the Application.ThreadException handler will catch all

uncaught exceptions from any form/control event handlers.

Note David has some excellent comments on argument validation to your class
libraries. Especially if those class libraries are going to be used outside
of your current solution.

Hope this helps
Jay


Lucvdv said:
Can someone explain why this code pops up a messagebox saying the
ThreadAbortException wasn't handled?

The first exception is reported only in the debug pane, as expected.
The second (caused by thread.Abort()) is reported twice: once in the debug
window, and once through the message box.

Is it because the thread was sleeping when the exception occurred?


Imports System.Threading

[...]

Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load

Dim currentDomain As AppDomain = AppDomain.CurrentDomain

AddHandler currentDomain.UnhandledException, _
AddressOf UnhandledExceptionHandler

Try
Throw New Exception("This is a test")
Catch ex As Exception
Debug.WriteLine(ex.ToString)
End Try

Dim t As New Thread(AddressOf ThreadProc)
t.Start()
Thread.Sleep(1000)
t.Abort()
End Sub

Private Sub UnhandledExceptionHandler(ByVal sender As Object, _
ByVal args As UnhandledExceptionEventArgs)

MsgBox("Unhandled " & args.ExceptionObject.GetType.ToString())
End Sub

Private Sub ThreadProc()
Try
Do
Thread.Sleep(100)
Loop
Catch ex As ThreadAbortException
Debug.WriteLine(ex.ToString)
End Try
End Sub
 
L

Lucvdv

Lucvdv,
Windows Forms itself is displaying the Message Box, as the Load Event had an
unhandled exception.

Thanks for the information, but it still isn't clear to me.

Thread.Abort() causes a ThreadAbortException in the thread it's called on,
but not in the thread where it is called from -- so there was no unhandled
exception in the Load event.
The debug output at the bottom of this message confirms this.


In the mean time I solved my problem by just adding an IF that throws out
all ThreadAbortExceptions, but I'd still like to know why it's happening.


I was expecting that only those exceptions the debugger would normally
catch or that would make an error dialog pop up in the compiled program,
would be reported through the UnhandledException event.

It seems completely illogical that the same exception would ever be handled
twice, but that's exactly what is happening here.


In my example code, when there's no unhandled exception handler, nothing is
reported by the debugger (just comment out the AddHandler line, and the
program runs without error).

If there is such a handler, the ThreadAbortException is *first* caught by
the Catch clause in the thread, and *then* still reported as unhandled (see
output below).


I created a new console application and slightly modified the previous code
so I could see where the exception is handled first, and where it's
reported to have occurred:

'-----------------
Imports System.Threading

Module Module1

Dim thr As Thread

Sub Main()
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, _
AddressOf UnhandledExceptionHandler

thr = New Thread(AddressOf ThreadProc)
thr.Start()
Thread.Sleep(1000)
thr.Abort()
End Sub

Private Sub UnhandledExceptionHandler(ByVal sender As Object, _
ByVal args As UnhandledExceptionEventArgs)
Dim ex As ThreadAbortException = CType(args.ExceptionObject,
ThreadAbortException)
Debug.WriteLine("Unhandled " & ex.ToString())
System.Console.WriteLine("Unhandled " & ex.ToString())
End Sub

Private Sub ThreadProc()
Try
Do
Thread.Sleep(100)
Loop
Catch ex As ThreadAbortException
Debug.WriteLine("Handled " & ex.ToString)
System.Console.WriteLine("Handled " & ex.ToString())
End Try
End Sub

End Module
'-----------------


The output, either in the debug pane or at the console in the compiled
program, shows that the exception is handled twice:

Handled System.Threading.ThreadAbortException: Thread was being aborted.
at System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
at ThreadAbortExceptionTest2.Module1.ThreadProc()
in D:\My Programs\Test\ThreadAbortExceptionTest2\Module1.vb:line 28
Unhandled System.Threading.ThreadAbortException: Thread was being aborted.
at ThreadAbortExceptionTest2.Module1.ThreadProc()
in D:\My Programs\Test\ThreadAbortExceptionTest2\Module1.vb:line 32
The thread '<No Name>' (0xb08) has exited with code 0 (0x0).
The program '[2952] ThreadAbortExceptionTest2.exe' has exited with code 0
(0x0).

If you comment out line 32 (System.Console.Writeline), the 'unhandled'
message changes to line 31 (Debug.Writeline). Comment that out too, and it
changes to line 30 (Catch).


Comment out the AddHandler, and it becomes:

Handled System.Threading.ThreadAbortException: Thread was being aborted.
at System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
at ThreadAbortExceptionTest2.Module1.ThreadProc()
in D:\My Programs\Test\ThreadAbortExceptionTest2\Module1.vb:line 28
The thread '<No Name>' (0xb08) has exited with code 0 (0x0).
The program '[2952] ThreadAbortExceptionTest2.exe' has exited with code 0
(0x0).

without any unhandled exception.
 
M

[MSFT]

ThreadAbortException is a special exception that can be caught, but it will
automatically be raised again at the end of the catch block, and re-thrown
it. This is propagated all the way up, so that everyone in the chain gets a
chance to do any cleanup that may be necessary, so that the thread
terminates gracefully.

Luke
 
J

Jay B. Harlow [MVP - Outlook]

Lucvdv,
Doh! I read you sample code too quickly rather then try it.


As you pointed out Thread.Abort raises ThreadAbortException, as Luke pointed
out ThreadAbortException is automatically raised again.


To prevent ThreadAbortException from being automatically raised again you
need to use Thread.ResetAbort.


Hope this helps
Jay

Lucvdv said:
Lucvdv,
Windows Forms itself is displaying the Message Box, as the Load Event had an
unhandled exception.

Thanks for the information, but it still isn't clear to me.

Thread.Abort() causes a ThreadAbortException in the thread it's called on,
but not in the thread where it is called from -- so there was no unhandled
exception in the Load event.
The debug output at the bottom of this message confirms this.


In the mean time I solved my problem by just adding an IF that throws out
all ThreadAbortExceptions, but I'd still like to know why it's happening.


I was expecting that only those exceptions the debugger would normally
catch or that would make an error dialog pop up in the compiled program,
would be reported through the UnhandledException event.

It seems completely illogical that the same exception would ever be handled
twice, but that's exactly what is happening here.


In my example code, when there's no unhandled exception handler, nothing is
reported by the debugger (just comment out the AddHandler line, and the
program runs without error).

If there is such a handler, the ThreadAbortException is *first* caught by
the Catch clause in the thread, and *then* still reported as unhandled (see
output below).


I created a new console application and slightly modified the previous code
so I could see where the exception is handled first, and where it's
reported to have occurred:

'-----------------
Imports System.Threading

Module Module1

Dim thr As Thread

Sub Main()
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, _
AddressOf UnhandledExceptionHandler

thr = New Thread(AddressOf ThreadProc)
thr.Start()
Thread.Sleep(1000)
thr.Abort()
End Sub

Private Sub UnhandledExceptionHandler(ByVal sender As Object, _
ByVal args As UnhandledExceptionEventArgs)
Dim ex As ThreadAbortException = CType(args.ExceptionObject,
ThreadAbortException)
Debug.WriteLine("Unhandled " & ex.ToString())
System.Console.WriteLine("Unhandled " & ex.ToString())
End Sub

Private Sub ThreadProc()
Try
Do
Thread.Sleep(100)
Loop
Catch ex As ThreadAbortException
Debug.WriteLine("Handled " & ex.ToString)
System.Console.WriteLine("Handled " & ex.ToString())
End Try
End Sub

End Module
'-----------------


The output, either in the debug pane or at the console in the compiled
program, shows that the exception is handled twice:

Handled System.Threading.ThreadAbortException: Thread was being aborted.
at System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
at ThreadAbortExceptionTest2.Module1.ThreadProc()
in D:\My Programs\Test\ThreadAbortExceptionTest2\Module1.vb:line 28
Unhandled System.Threading.ThreadAbortException: Thread was being aborted.
at ThreadAbortExceptionTest2.Module1.ThreadProc()
in D:\My Programs\Test\ThreadAbortExceptionTest2\Module1.vb:line 32
The thread '<No Name>' (0xb08) has exited with code 0 (0x0).
The program '[2952] ThreadAbortExceptionTest2.exe' has exited with code 0
(0x0).

If you comment out line 32 (System.Console.Writeline), the 'unhandled'
message changes to line 31 (Debug.Writeline). Comment that out too, and it
changes to line 30 (Catch).


Comment out the AddHandler, and it becomes:

Handled System.Threading.ThreadAbortException: Thread was being aborted.
at System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
at ThreadAbortExceptionTest2.Module1.ThreadProc()
in D:\My Programs\Test\ThreadAbortExceptionTest2\Module1.vb:line 28
The thread '<No Name>' (0xb08) has exited with code 0 (0x0).
The program '[2952] ThreadAbortExceptionTest2.exe' has exited with code 0
(0x0).

without any unhandled exception.
 
L

Lucvdv

ThreadAbortException is a special exception that can be caught, but it will
automatically be raised again at the end of the catch block, and re-thrown
it. This is propagated all the way up, so that everyone in the chain gets a
chance to do any cleanup that may be necessary, so that the thread
terminates gracefully.

Thanks (and to Jay too).

I'll sleep a bit better now that I know it's nothing abnormal ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top