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