Catch-all Exception Handler for non-UI Threads in WPF - wpf

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
This part works
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
This part doesn't work
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?
What I ended up doing
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

TaskScheduler.UnobservedTaskException Event is what you want to subscribe from App start.
https://msdn.microsoft.com/en-us/library/vstudio/system.threading.tasks.taskscheduler.unobservedtaskexception(v=vs.100).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
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.

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

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.

Related

Application.Current.DispatcherUnhandledException

I would like to catch an unhandled exception that causes my application to silently close.
I have read that there is
Application.Current.DispatcherUnhandledException
My application however uses a form to run and the application framework, not a sub main.
The example on MSDN (http://msdn.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx) seems to rely on a sub main, as it seems to me.
Could somebody tell me how to install the exception handler for a project that uses the application framework?
I have tried the following:
I have changed my application to use a Sub Main instead and used the following code:
Public Sub Main()
' Set the unhandled exception mode to force all Windows Forms errors to go through'
' our handler. '
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException)
' Add the event handler for handling UI thread exceptions to the event. '
AddHandler Application.ThreadException, AddressOf frmMain.UIThreadException
' Add the event handler for handling non-UI thread exceptions to the event. '
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf frmMain.CurrentDomain_UnhandledException
' Runs the application. '
Application.Run(New frmMain())
End Sub
However, the error I am getting is:
"The thread exception mode can no longer be changed as soon as controls were created in this thread".
You could use a Try Catch to avoid an application from closing by exception. You could try something like
Try
Console.WriteLine("Hello, world!")
Dim A As Integer = 0
Dim I As Integer
For I = 1 to 500000000000
A*=I
Next
Catch ex As Exception
Console.WriteLine("Error occurred")
End Try
This is a little example in a Console Application program, but you will be able to use something like in your project.
The generic Exception catches every kind of exception you could have, so also Application.Current.DispatcherUnhandledException.
Your program shouldn't crash anymore with a try statement.
Hope it helps.
It is good practice to also register an event handler for the case that some exception does not get caught at runtime.
It allows you to log the exception cause and to exit application in a controlled manner.
But as already said, you should primarily use Try Catch statements to catch exceptions in first instance.

VB.net GetProcess Options

I currently have the following code that runs an external Virtual Application(created with Cameyo), when the Run Button is clicked. I also have a Timer that checks ever second to see if the Process(virtual exe program) is still open. In theory the GetProcessByName should find the program listed in the Task Manager right? However it doesn't! I even tried using GetProcessByName to Kill the process (one another button clicked), but the processs is not killed.
Could it be because I virtualized the program that I want GetProcessByName to recognize? Therefore the name of the Task in the Task Manager is not correct?.
Example
The program started: SmartDefrag.virtual.exe
It runs
The Task Manager shows it as SmartDefrag.exe
Use GetProcessByName("SmartDefrag.exe") to Disable Run Button if process SmartDefrag.exe running.
Does not Disable Run Button.
Could I use TITLE OF PROCESS? Or will the PID be the same everytime the process opens? Any other options?
Code:
Private Sub SMDFRunAppMainButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles SMDFRunAppMainButton.Click
' LoadingSMDFMainButton.Visibility = Windows.Visibility.Visible
Dim downloadlocation As String = (currentpath & "\1stAidApps\SMDF\SmartDefrag.virtual.exe")
My.Settings.FileLoad = downloadlocation
Try 'Errors on Cancel
dp1Timer = New DispatcherTimer
dp1Timer.Interval = TimeSpan.FromMilliseconds(1000)
AddHandler dp1Timer.Tick, AddressOf TickMe1
dp1Timer.Start()
fileload = My.Settings.FileLoad
Process.Start(fileload)
Catch ex As Exception
MessageBox.Show("Failed to launch. Please try again.", "Launch Failed")
End Try
End Sub
Private Sub TickMe1()
Dim p() As Process
p = Process.GetProcessesByName("SmartDefrag.exe")
If p.Count > 0 Then
LoadingSMDFMainButton.Visibility = Windows.Visibility.Hidden
SMDFRunAppMainButton.IsEnabled = False
Else
SMDFRunAppMainButton.IsEnabled = True
End If
End Sub
GetProcessByName doesn't take the full path, but rather the "name" of the process. This will likely need to be GetProcessByName("SmartDefrag").
From the documentation for GetProcessByName:
The process name is a friendly name for the process, such as Outlook, that does not include the .exe extension or the path.

Closing Windows SplashScreen when lost focus throws Win32Exception

In our application we open a splashscreen for the intial loading of the login screen, this logonscreen is shown async.
when we close the splashscreen sometimes we get following error:
System.ComponentModel.Win32Exception was unhandled. Message="The operation completed successfully"
I figured out this has something to do with loosing focus and it's a known bug in .net 3.5.
However I did not find a solution to fix this in my code. And we are unable to upgrade the application to 4.0.
I allready tried: Application.Current.MainWindow.Focus() but this doens't work since i'm not on the correct thread due to the async call
I hope somebody had this problem before and can provide me a working fix for this bug.
This is the code and the splashscreen comes from system.windows.dll:
Public Sub New(ByVal splashResourceName As String)
logonSplash = New SplashScreen(splashResourceName)
logonSplash.Show(False)
InitializeComponent()
GetAllInfo()
DataContext = context
End Sub
Private Sub LoginDialog_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
'Focus()
If logonSplash IsNot Nothing Then
logonSplash.Close(Nothing)
logonSplash = Nothing
End If
txtPassword.Focus()
End Sub

I don't know where to catch this error: Access to the port is denied

I'm using a serial port with a Serial Object and sometimes I get this error.
UnauthorizedAccessException: Access to the port is denied.
with a stack trace of:
at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
at System.IO.Ports.InternalResources.WinIOError()
at System.IO.Ports.SerialStream.Dispose(Boolean disposing)
at System.IO.Ports.SerialStream.Finalize()
It doesn't occur at any line of code (within my code at least) so I'm not sure how to trap it. I figure what is happening is the serial (via USB) port is being physically unplugged/disconnected for a split second and is throwing everything into whack.
I can click Continue on the error which I'm debugging and everything is fine. Communication with the serial device is flawless otherwise. But when the program is actually published, deployed and running it gives me several error messages and is all ugly for the user.
How can I trap this error/what can I do to prevent it in the first place?
Thanks
I encounter the same exception and stack trace in my WinForms application when used with a USB-to-serial converter. I can consistently recreate it by
creating an instance of SerialPort
calling SerialPort.Open,
removing the USB-to-serial converter,
closing the app (which calls SerialPort.Dispose)
My suspicion is that the exception is being thrown in SerialPort's finalizer.
Others have experienced the same symptoms - see here.
To work around I followed the recommendation of Kuno and KyferEz (from link above) to implement my own ExtSerialPort. This inherits SerialPort, overrides the Dispose method and (using reflection) disposes the SerialPort's internalSerialStream.
Imports System.IO.Ports
Public Class ExtSerialPort
Inherits SerialPort
Public Sub New()
MyBase.new()
End Sub
Public Sub New(ByVal portName As String)
MyBase.New(portName)
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Dim mytype As Type = GetType(SerialPort)
Dim field As Reflection.FieldInfo = mytype.GetField("internalSerialStream", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
Dim stream As Object = field.GetValue(Me)
If stream IsNot Nothing Then
Try
stream.Dispose()
Catch ex As Exception
End Try
End If
MyBase.Dispose(disposing)
End Sub
End Class

Global Exception Handling for winforms control

When working on ASP.NET 1.1 projects I always used the Global.asax to catch all errors. I'm looking for a similar way to catch all exceptions in a Windows Forms user control, which ends up being a hosted IE control. What is the proper way to go about doing something like this?
You need to handle the System.Windows.Forms.Application.ThreadException event for Windows Forms. This article really helped me: http://bytes.com/forum/thread236199.html.
Currently in my winforms app I have handlers for Application.ThreadException, as above, but also AppDomain.CurrentDomain.UnhandledException
Most exceptions arrive via the ThreadException handler, but the AppDomain has also caught a few in my experience
If you're using VB.NET, you can tap into the very convenient ApplicationEvents.vb. This file comes for free with a VB.NET WinForms project and contains a method for handling unhandled exceptions.
To get to this nifty file, it's "Project Properties >> Application >> Application Events"
If you're not using VB.NET, then yeah, it's handling Application.ThreadException.
To Handle Exceptions Globally...
Windows Application
System.Windows.Forms.Application.ThreadException event
Generally Used in Main Method. Refer MSDN Thread Exception
Asp.Net
System.Web.HttpApplication.Error event
Normally Used in Global.asax file. Refer MSDN Global.asax Global Handlers
Console Application
System.AppDomain.UnhandledException event
Generally used in Main Method. Refer MSDN UnhandledException
Code from MSDN: http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2
Sub Main()
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf MyHandler
Try
Throw New Exception("1")
Catch e As Exception
Console.WriteLine("Catch clause caught : " + e.Message)
Console.WriteLine()
End Try
Throw New Exception("2")
End Sub
Sub MyHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim e As Exception = DirectCast(args.ExceptionObject, Exception)
Console.WriteLine("MyHandler caught : " + e.Message)
Console.WriteLine("Runtime terminating: {0}", args.IsTerminating)
End Sub

Resources