Reputation: 708
Specifically, I'm using WPF with MVVM. I have a MainWindow, which is a WPF Window where all of the action happens. It uses a corresponding View Model class for its properties, commands, etc.
I have set up main UI thread and non-UI thread exception handlers in Application.xaml.vb StartUp like this:
Private Sub Application_DispatcherUnhandledException(sender As Object, e As Windows.Threading.DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
' catches main UI thread exceptions only
ShowDebugOutput(e.Exception)
e.Handled = True
End Sub
Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
' catches background exceptions
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
AddHandler System.Threading.Tasks.TaskScheduler.UnobservedTaskException, AddressOf BackgroundTaskExceptionHandler
End Sub
Sub UnhandledExceptionHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim ex As Exception = DirectCast(args.ExceptionObject, Exception)
ShowDebugOutput(ex)
End Sub
Sub BackgroundTaskExceptionHandler(sender As Object, args As System.Threading.Tasks.UnobservedTaskExceptionEventArgs)
Dim ex As Exception = DirectCast(args.Exception, Exception)
ShowDebugOutput(ex)
End Sub
When I try to test this out, by deliberately throwing an exception, it works. It is actually in the View Model in the Sub that handles the Select All button click.
The button:
<Button Content="Select All" Height="23" Width="110" Command="{Binding SelectAllCommand}" />
The Command where I'm throwing the exception that is successfully caught:
Private Sub SelectAll()
Throw (New Exception("UI Thread exception"))
SetAllApplyFlags(True)
End Sub
There's another button in the same MainWindow similarly bound to a command. However, it uses a Task to perform its work in the background, and an exception thrown in there does NOT get caught by my catch-all handlers.
Private Sub GeneratePreview()
' run in background
System.Threading.Tasks.Task.Factory.StartNew(
Sub()
' ... stuff snipped out, issue is the same with or without the rest of the code here ...
Throw (New Exception("Throwing a background thread exception"))
End Sub)
End Sub
There are several similar questions, but I haven't been able to actually figure out my answer from them. The AppDomain UnhandledException seems to be the answer in most cases, but it isn't for mine. What exactly do I have to add to be able to catch an exception that might be thrown in a non-UI thread this way?
I could not get the TaskScheduler.UnobservedTaskException event to call my event handler when I was handling it in Application.xaml.vb. But I took hints from the other answer, and I'll mark it as the answer because it ultimately helped.
However, it is not at the application level, so if this was a larger application, I'd have to duplicate this in every instance where I used a Task. This wasn't really what I was looking for, but not willing to spend more time on it now.
I ended up putting a try-catch inside the Task. In the catch, I was able to use Dispatcher.Invoke to still display a user-friendly dialog with the exception info.
Private Sub GeneratePreview()
' run in background
System.Threading.Tasks.Task.Factory.StartNew(
Sub()
Try
' ... stuff snipped out, issue is the same with or without the rest of the code here ...
Throw (New Exception("Throwing a background thread exception"))
Catch ex As Exception
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, DirectCast(
Sub()
HRNetToADImport.Application.ShowDebugOutput(ex)
End Sub, Action))
End Try
End Sub)
End Sub
Upvotes: 2
Views: 1195
Reputation: 4913
Sandra,
I read cscmh99 proposition, took your code,and try to run, and it works !
I mean you can subscribe to TaskScheduler.UnobservedTaskException
.
Then you will catch UnobservedException
But you won't catch observed exceptions
Observed exceptions are those from Tasks waited with .Wait()
or .Result
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
' Here follows an Unobserved Exception
System.Threading.Tasks.Task.Factory.StartNew(
Sub()
Throw (New Exception("Throwing a background thread exception"))
End Sub)
' Here follows an ObservedException
' ObservedException because there is call to .Wait() (or .Result)
' You can't use UnobservedException for that one
Dim t As Task = System.Threading.Tasks.Task.Factory.StartNew(
Sub()
Throw (New Exception("Throwing a background thread exception"))
End Sub)
t.Wait()
End Sub
Here is code to working solution : http://1drv.ms/1XOvTbK
Regards
Upvotes: 1
Reputation: 2781
TaskScheduler.UnobservedTaskException Event is what you want to subscribe from App start.
Occurs when a faulted Task's unobserved exception is about to trigger exception escalation policy, which, by default, would terminate the process.
This AppDomain-wide event provides a mechanism to prevent exception escalation policy (which, by default, terminates the process) from triggering.
NOTE: The event might not be fired right away (possible a few second delay). You could imagine there's some operations of call stack bubbling and context switching of normal exception operation before ended up reaching the UnobservedTaskException event.
One thing I want to point out is that, it's a must to wrap your whole application with generic exception handler to prevent application being terminate. But, please do remember that it's also a must to implement proper exception handling to all paths that might throw exception.
Upvotes: 1
Reputation: 4913
Sandra,
There is a way to catch the exception from inside your background Task.
I admit my solution is not global, but at least it catches and prevents crash !
Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim t As Task = Task.Factory.StartNew(
Sub()
' ... stuff snipped out, issue is the same with or without the rest of the code here ...
Throw (New Exception("Throwing a background thread exception"))
End Sub)
Try
Await t
Catch ex1 As Exception
Debug.WriteLine("Exception from inside task " & ex1.Message)
End Try
End Sub
Think it could help, may be you, may be others.
Upvotes: 0