For robustness I want to add a handler for TaskScheduler.UnobservedTaskException.
The handler does log Exception(s) inform the user and shutdown the app.
Does the following implementation make sense?
Is it OK in WPF to show a MessageBox or a Windows in this handler, or is this a bad idea because we are running in the finalizers of unobserved Tasks?
Private Sub TaskSheduler_UnobservedTaskException(sender As Object, e As UnobservedTaskExceptionEventArgs)
Static _wasUserInformed As Boolean = False
e.SetObserved()
'Trace all Exceptions
e.Exception.Flatten.Handle(Function(ex As Exception)
'TODO trace and log Exception
Debug.Print("UnobservedTaskException: {0}", ex.Message)
Return True
End Function)
If Not _wasUserInformed Then
'Show root Exception
_wasUserInformed = True
Application.Current.Dispatcher.BeginInvoke(Sub()
'MessageBox.Show(e.Exception.GetBaseException.Message)
Dim win As New UnexpectedExceptionWindow
win.UnexpectedException = e.Exception.GetBaseException
win.ShowDialog()
Application.Current.Dispatcher.BeginInvoke(Sub() Application.Current.Shutdown())
End Sub)
End If
End Sub
[EDIT]
As a result of our discussion I came up with the following solution.
Private Sub TaskScheduler_UnobservedTaskException(sender As Object, e As UnobservedTaskExceptionEventArgs
) Handles TaskScheduler.UnobservedTaskException
Static _wasUserInformed As Boolean = False
'Free the finalizer thread and execute on the UI thread to be able to inform user
Dispatcher.BeginInvoke(Sub() LogException(e.Exception))
e.SetObserved()
If Not _wasUserInformed Then
_wasUserInformed = True
'Show first error
Dispatcher.BeginInvoke(Sub()
NotifyUser(e.Exception)
Application.Current.Shutdown()
End Sub)
End If
End Sub
This is a very good question. As this event is running on the finalizer thread (I have confirmed this with Reflector) we cannot afford to block for an extend period of time. This would stop finalizer from being processed.
So the answer is: This is not a good design because the duration of the UI operation is unbounded.
A better solution would be to queue the processing to a new
Taskso that it runs on the thread-pool.This answer depends on an implementation detail (finalizers being executed on a single thread and the event firing on the finalizer thread). But at least for apps supporting .NET 4.0 this answer is valid.