Global Error Handling in Task.Run for realtime User ntofication - wpf

I have a wpf c# app.
I normally use a global error handler to catch all errors:
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
try
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Xceed.Wpf.Toolkit.MessageBox.Show(e.Exception.ToString(), "Error",
MessageBoxButton.OK, MessageBoxImage.Error)));
e.Handled = true;
InformedWorkerDataService.Common.Shared.RecordMessage(e.Exception.ToString(), true);
}
finally { }
}
However, if start off a task.run 'bit of code' and it throws an error then i have observed that error is not caught:
Task.Run(() =>
{
throw and error here
});
so I have to put a 'Try-Catch' thing in to capture it:
Task.Run(() =>
{
try
{
throw an error here
}
catch (Exception ex)
{
do something with error
}
});
~ which defeats the object of having a Global Error handler
But, if i use this approach:
TaskScheduler.UnobservedTaskException += (s, e) => {
e.Exception //The Exception that went unobserved.
e.SetObserved(); //Marks the Exception as "observed," thus preventing it from triggering exception escalation policy which, by default, terminates the process.
};
... it will do my global exception handling but if I want to notify the user of the error real-time it does not do so very well because IT IS on a separate thread.
What would be a good compromise?

Unfortunately the TaskScheduler.UnobservedTaskException is not guarenteed to fire in real time with the exception being thrown. This means that using this handler for user notification can be quite confusing as the user action and error notification will not happen synchronously. For user driven handling of 'unexpected' task exceptions, you could create helper methods as below and use TaskEx.Run instead of Task.Run:
public static class TaskEx
{
public static Task Run(Action function)
{
return Task.Run(() =>
{
try
{
function();
}
catch (Exception ex)
{
TraceEx.TraceException(ex);
//Dispatch your MessageBox etc.
}
});
}
}
Obviously this is not as simple as adding a global handler (which should still be done for tracing purposes) but is simple enough to implement in UI driven code.

Related

async Task Main() in WinForms application having async initialization

We're having a winforms application that uses an async initialization process. Simplified you can say that the application will run the following steps:
Init - this runs async
Show MainForm
Application.Run()
The currently existing and working code looks like this:
[STAThread]
private static void Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
var task = StartUp();
HandleException(task);
Application.Run();
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
private static async void HandleException(Task task)
{
try
{
await Task.Yield();
await task;
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
The background how this is working is described very detailed by Mark Sowul here.
Since C# 7.1 we're able to use async Task in main method. We tried it in a straight forward way:
[STAThread]
private static async Task Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
try
{
await StartUp();
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
But that doesn't work. The reason is clear. All the code after the first await will be forwarded to the message loop. But the message loop hasn't startet yet because the code that starts it (Application.Run()) is located after the first await.
Removing the synchronization context will fix the problem but causes to run the code after await in a different thread.
Reordering the code to call Application.Run() before the first await will not work because it is a blocking call.
We try to use the new feature of having an async Task Main() that allows us to remove the HandleException-solution that is hard to understand. But we don't know how.
Do you have any suggestions?
You don't need async Main. Here is how it can possibly be done:
[STAThread]
static void Main()
{
void threadExceptionHandler(object s, System.Threading.ThreadExceptionEventArgs e)
{
Console.WriteLine(e);
Application.ExitThread();
}
async void startupHandler(object s, EventArgs e)
{
// WindowsFormsSynchronizationContext is already set here
Application.Idle -= startupHandler;
try
{
await StartUp();
}
catch (Exception)
{
// handle if desired, otherwise threadExceptionHandler will handle it
throw;
}
};
Application.ThreadException += threadExceptionHandler;
Application.Idle += startupHandler;
try
{
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
Application.Idle -= startupHandler;
Application.ThreadException -= threadExceptionHandler;
}
}
Note, if you don't register threadExceptionHandler event handler and StartUp throws (or anything else on the message loop throws, for the matter), it will still work. The exception will be caught inside the try/catch which wraps Application.Run. It will just be a TargetInvocationException exception with the original exception available via its InnerException property.
Updated to address the comments:
But for me it looks very strange to register an EventHandler to the
idle event so startup the whole application. It's totally clear how
that works but still strange. In that case I prefer the
HandleException solution that I already have.
I guess it's a matter of taste. I don't know why WinForms API designers didn't provide something like WPF's Application.Startup. However, in the lack of a dedicated event for this on WinForm's Application class, deferring specific initialization code upon the first Idle event is IMO an elegant solution, and it's widely used here on SO.
I particularly don't like the explicit manual provisioning of WindowsFormsSynchronizationContext before Application.Run has started, but if you want an alternative solution, here you go:
[STAThread]
static void Main()
{
async void startupHandler(object s)
{
try
{
await StartUp();
}
catch (Exception ex)
{
// handle here if desired,
// otherwise it be asynchronously propogated to
// the try/catch wrapping Application.Run
throw;
}
};
// don't dispatch exceptions to Application.ThreadException
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
using (var ctx = new WindowsFormsSynchronizationContext())
{
System.Threading.SynchronizationContext.SetSynchronizationContext(ctx);
try
{
ctx.Post(startupHandler, null);
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
System.Threading.SynchronizationContext.SetSynchronizationContext(null);
}
}
}
IMO, either approach is more clean than the one used in your question. On a side note, you should be using ApplicationContext to handle the form closure. You can pass an instance of ApplicationContext to Application.Run.
The only point that I'm
missing is your hint that the synchronization context is already set.
Yes it is - but why?
It is indeed set as a part of Application.Run, if not already present on the current thread. If you like to learn more details, you could investigate it in .NET Reference Source.

Application dispatcher invoke freezes the application

I am having problem with the application freezing. Let me explain my scenario, I have a service which does an async call to a database to get a list of items, It is run by a task. Inside this task I have a try catch block, so it looks like this
public Task<List<T>> ComboListAsync(int? id = null, EnumDTO dto = EnumDTO.Default)
{
return Task.Run(() =>
{
using (var context = new ContextService())
{
try
{
return GetComboList(id, dto, context);
}
catch (Exception e)
{
Handler.DatabaseConnectionException();
throw;
}
}
});
}
Then it throws an exception as GetComboList its just this (for the moment)
protected virtual List<T> GetComboList(int? id, EnumDTO dto, ContextService context)
{
throw new NotImplementedException();
}
So the call catches the exception and goes inside here
public void Show(string message)
{
Message = message;
Application.Current.Dispatcher.Invoke(() =>
{
dialogView = new DialogView() {DataContext = this, Owner = Application.Current.MainWindow};
dialogView.ShowDialog();
});
}
Now the Dispatcher freezes the app, I tried to change it to use begin invoke, it does the same. Without the dispatcher I get an error message that the calling thread is not a STA. I simply want to display my message in a dialog window, that there was a problem connecting to a database. Can anyone help?
I looked online and there is many threads about dispatcher, but none actually show a solution that will fix my issue.
Thank you
EDIT
Code which calls the ComboListAsync
protected override void RetrieveRelatedActiveLists()
{
MyCollection = service.ComboListAsync().Result;
}
Its a deadlock because of the calling code is using the .Result.
Using service.ComboListAsync().Result makes the UI thread await for this method to return, when you call Application.Current.Dispatcher.Invoke from within it you are sending a message to the UI thread that is awaiting the return of method itself.
You must await the method service.ComboListAsync() like this:
protected override async void RetrieveRelatedActiveLists()
{
MyCollection = await service.ComboListAsync();
}

WPF Dispatcher.BeginInvoke Throw Exception

I am using Threadpool.QueueUserWorkItem like following
public void TestMain()
{
try
{
ThreadPool.QueueUserWorkItem(x =>
{
this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod()));
}
);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void BackGroundMethod()
{
try
{
int a = 0;
int b = 100;
var error = b / a;
}
catch(Exception)
{
throw;
}
}
By this way, TestMain() can not catch exception.
Program will shut down...
How can i catch this error?
Thx.
The reason is this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod)); executes asynchonously, so it will finished execute all code inside TestMain before BackGroundMethod is executed.
Use Dispatcher.UnhandledException event to catch the exception in the TestMain() method like this:
Dispatcher.UnhandledException += (sender, args) => MessageBox.Show(args.Exception.Message);
ThreadPool.QueueUserWorkItem(ignore => Dispatcher.Invoke(new Action(BackGroundMethod)));
Edit: Remember to set the Handled property to prevent the internal exception handler from being called:
Dispatcher.UnhandledException += (sender, args) =>
{
MessageBox.Show(args.Exception.Message);
args.Handled = true;
}
To prevent the crash, add a try/catch around the operation which might throw:
ThreadPool.QueueUserWorkItem(x =>
{
try {
this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod()));
}
catch (DivideByZeroException ex) {
// handle the error somehow
}
});
However, think about what you are doing here: you are pushing some action to the thread pool, and in turn it pushes the same action to the dispatcher thread. Why not do this yourself directly without the QueueUserWorkItem call?
You can catch all exceptions in all threads within the application by handling AppDomain.UnhandledException
When you use the Dispatcher to create a new thread, that thread has it's own stack, and so the exceptions wont bubble to the try...catch in TestMain, but instead they will originate in BackgroundMethod method. As you are throwing the exception in BackgroundMethod your try catch is useless, and so if you were to not throw the exception in BackgroundMethod your program wouldn't shut down.
private void BackGroundMethod()
{
try
{
int a = 0;
int b = 100;
var error = b / a;
}
catch(Exception)
{
MessageBox.Show(ex.Message);
}
}
You can do this by catching the exception in the BeginInvoke function and saving it in a temporary variable. After the await you can then rethrow it on the correct thread.
Exception thrown = null;
await Application.Current.Dispatcher.BeginInvoke(new Action<Action>(x =>
{
try
{
BackGroundMethod();
}
catch (Exception ex)
{
//exceptions on this thread MUST be caught or the program will exit
//we will save it, and then when we are back on the main thread we will rethrow it
thrown = ex;
}
}));
if (thrown != null)
throw thrown; //<--- I'm rethrowing it right here on the correct thread

WPF throw exception from worker thread but continue

I have a scenario where a worker thread performs a task that looks like
try
{
for()
{
functionA();
functionB();
}
}
catch(Exception ex)
{
throw new Exception("Message");
}
I'm basically re-throwing the exception so that when the worker completes, the UI thread will receive the exception.
Now, if an exception occurs in functionA I'd like to throw the exception but continue the loop and not stop. How can I achieve this?
edit: If it isn't clear, this is so that the UI can be notified that an exception has occurred but not stop the worker
Something like this:
List<Exception> errorsLog = new List<Exception>();
for()
{
try
{
functionA();
}
catch(Exception e) {
errorsLog.Add(e);
}
try
{
functionB();
}
catch(Exception e) {
errorsLog.Add(e);
}
}
if (errorsLog.Count > 0) {
throw new AggregateException(errorsLog);
}
Where AggregateException is your custom exception that contains a list of other exceptions.

How to handle exceptions thrown during the instantiation of a WPF Application?

I'm using the following code to display unhandled exceptions in a WPF application:
public MyApplication() {
this.DispatcherUnhandledException += (o, e) => {
var exceptionMessage = new ExceptionWindow();
exceptionMessage.ExceptionMessage.Text = e.Exception.Message;
exceptionMessage.ExceptionCallStack.Text = e.Exception.StackTrace;
exceptionMessage.ExceptionInnerException.Text = e.Exception.InnerException.Message;
exceptionMessage.WindowStartupLocation = WindowStartupLocation.CenterScreen;
exceptionMessage.WindowStyle = WindowStyle.ToolWindow;
exceptionMessage.ShowDialog();
e.Handled = true;
Shell.Close();
};
}
Turns out that I have an exception during the instantiation of the application, so the app constructor is never executed.
A simple way to reproduce it (with a different exception) is by introducing an extra "<" before some tag in your app's configuration file and run it.
A useless error message like that appears before the application constructor get called.
alt text http://srtsolutions.com/cfs-filesystemfile.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/mikewoelmer/ExceptionWPF1_5F00_1C1F39AA.jpg
Does anyone know how to catch such kind of exceptions?
Remark: I'm using Caliburn and my application extends CaliburnApplication.
Okay. I solved the problem by doing the following:
Change the Build Action of the App.xaml file from ApplicationDefinition to Page.
Create a new class like following:
public class AppStartup {
[STAThread]
static public void Main(string[] args) {
try {
App app = new App();
app.InitializeComponent();
app.Run();
}
catch (Exception e) {
MessageBox.Show(e.Message + "\r\r" + e.StackTrace, "Application Exception", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
It replaces the generated App.g.cs Main method by this one, so we have a chance to catch the exceptions.

Resources