I've applied these handlers in a test application:
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
I then create an exception on a nested Task/Async await on the form:
Despite the handler being fired - CurrentDomain.UnhandledException - the app still crashes.
Why would it crash and not show the dialog and stay running?
System.Exception was unhandled
Message: An unhandled exception of type 'System.Exception' occurred in mscorlib.dll
Additional information: dd
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("Main " + Thread.CurrentThread.ManagedThreadId);
try
{
await Hello();
}
catch (Exception ex)
{
Console.WriteLine("Exception on main " + Thread.CurrentThread.ManagedThreadId);
}
}
private async static Task Hello() //changed from void
{
await Task.Run(() => new IGetRun().Run1());
}
internal class IGetRun
{
public async Task Run1() //changed from void
{
Console.WriteLine("Run1 " + Thread.CurrentThread.ManagedThreadId);
await Task.Run(() => new IGetRun2().Run2());
}
}
internal class IGetRun2
{
public void Run2()
{
Console.WriteLine("Run2 " + Thread.CurrentThread.ManagedThreadId);
throw new Exception("dd");
}
}
EDIT:
Looks like I forgot to declare each async method with Task and not void thus exception handling works predictably now. The only thing I still do not know is why - if I stop handling the exception in the button event - when the exception is caught Application_ThreadException is fired and not TaskScheduler_UnobservedTaskException
Your original code does something that you should never do: calling async void methods. And problematic exception handling is one reason for that.
Let's look at the event handlers one by one:
Application.ThreadException only handles exceptions on the Winforms UI thread. The exception never got to the UI thread, so this event doesn't fire.
TaskScheduler.UnobservedTaskException only handles exceptions from Tasks that are unobserved (and even then, it does so when they are finalized, which might take a long time if you're not allocating much memory). But the unobserved exception is not tied to any Task, so this doesn't fire either.
AppDomain.CurrentDomain.UnhandledException handles all exceptions that are still considered unobserved after the previous two event handlers. But it can't be used to set the exception as observed, so the application still terminates. This is the only handler that actually fires.
Why exactly don't the first two handlers fire? In your code, you have Task.Run(() => new IGetRun().Run1()), where Run1() is an async void method that throws an exception.
When there is an unobserved exception in an async void method, the exception is rethrown on the current synchronization context. But since the method is running on a thread pool thread (because of the Task.Run()), there is no synchronization context. So the exception is thrown on a thread pool thread, which is not observable by the first two event handlers.
As you already discovered, the fix for this issue is to use async Task methods and await them properly.
In your updated code, Run1() is now an async Task method. That Task is unwrapped by Task.Run() and then awaited, so the exception is transferred to the Hello() Task. That in turn is awaited from the async void button1_Click handler on the UI thread.
This all means that there are no unobserved Task exceptions and that the exception is rethrown on the UI synchronization context. In Winforms, that means Application.ThreadException is raised, which is exactly what you're observing.
I'm not sure if you solved your problem ?
If the point was to catch your unhandeld exceptions, you could do this.
Add to program.cs before Application.Run
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
Create 2 voids to handle the exception data.
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message, "Unhandled Thread Exception");
// here you can log the exception ...
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show((e.ExceptionObject as Exception).Message, "Unhandled UI Exception");
// here you can log the exception ...
}
Related
From the Microsoft documentation the System.Timers.Timer elapsed method should swallow all exceptions.
The Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event.
https://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
However when subscribing using an async void method an exception is produced which crashes the application. See the below code:
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer(100);
//timer.Elapsed += On_ElapsedSync; //A
//timer.Elapsed += On_ElapsedAsync; //B
timer.Elapsed += On_ElapsedAsyncVoid; //C
timer.Start();
Console.WriteLine("Running...");
Console.ReadLine();
}
private static void On_ElapsedSync(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Throwing...");
throw new Exception("My Exception");
}
private static void On_ElapsedAsync(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Throwing...");
Task.Run(() => throw new Exception("Async Exception"));
}
private static async void On_ElapsedAsyncVoid(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Throwing...");
await Task.Run(() => throw new Exception("Async Exception"));
}
}
The lines commented A and B do not crash the application. The line commented C does.
Why is this the case?
The link you provided states:
The Timer component catches and suppresses all exceptions thrown by
event handlers for the Elapsed event. This behavior is subject to
change in future releases of the .NET Framework. Note, however, that
this is not true of event handlers that execute asynchronously and
include the await operator (in C#) or the Await operator (in Visual
Basic). Exceptions thrown in these event handlers are propagated back
to the calling thread, as the following example illustrates. For more
information on exceptions thrown in asynchronous methods, see
Exception Handling (Task Parallel Library).
Since you are using await then the latter part of the documentation applies:
Exceptions thrown in these event handlers are propagated back
to the calling thread, as the following example illustrates.
This question already has answers here:
Globally catch exceptions in a WPF application?
(7 answers)
Closed 6 years ago.
Sometimes, under not reproducible circumstances, my WPF application crashes without any message. The application simply close instantly.
Where is the best place to implement the global Try/Catch block. At least I have to implement a messagebox with: "Sorry for the inconvenience ..."
You can trap unhandled exceptions at different levels:
AppDomain.CurrentDomain.UnhandledException From all threads in the AppDomain.
Dispatcher.UnhandledException From a single specific UI dispatcher thread.
Application.Current.DispatcherUnhandledException From the main UI dispatcher thread in your WPF application.
TaskScheduler.UnobservedTaskException from within each AppDomain that uses a task scheduler for asynchronous operations.
You should consider what level you need to trap unhandled exceptions at.
Deciding between #2 and #3 depends upon whether you're using more than one WPF thread. This is quite an exotic situation and if you're unsure whether you are or not, then it's most likely that you're not.
You can handle the AppDomain.UnhandledException event
EDIT: actually, this event is probably more adequate: Application.DispatcherUnhandledException
A quick example of code for Application.Dispatcher.UnhandledException:
public App() {
this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}
void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {
string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
// OR whatever you want like logging etc. MessageBox it's just example
// for quick debugging etc.
e.Handled = true;
}
I added this code in App.xaml.cs
I use the following code in my WPF apps to show a "Sorry for the inconvenience" dialog box whenever an unhandled exception occurs. It shows the exception message, and asks user whether they want to close the app or ignore the exception and continue (the latter case is convenient when a non-fatal exceptions occur and user can still normally continue to use the app).
In App.xaml add the Startup event handler:
<Application .... Startup="Application_Startup">
In App.xaml.cs code add Startup event handler function that will register the global application event handler:
using System.Windows.Threading;
private void Application_Startup(object sender, StartupEventArgs e)
{
// Global exception handling
Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(AppDispatcherUnhandledException);
}
void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
\#if DEBUG // In debug mode do not custom-handle the exception, let Visual Studio handle it
e.Handled = false;
\#else
ShowUnhandledException(e);
\#endif
}
void ShowUnhandledException(DispatcherUnhandledExceptionEventArgs e)
{
e.Handled = true;
string errorMessage = string.Format("An application error occurred.\nPlease check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction in the application, and you better close it.\n\nError: {0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)",
e.Exception.Message + (e.Exception.InnerException != null ? "\n" +
e.Exception.InnerException.Message : null));
if (MessageBox.Show(errorMessage, "Application Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No) {
if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
Application.Current.Shutdown();
}
}
Best answer is probably https://stackoverflow.com/a/1472562/601990.
Here is some code that shows how to use it:
App.xaml.cs
public sealed partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
// setting up the Dependency Injection container
var resolver = ResolverFactory.Get();
// getting the ILogger or ILog interface
var logger = resolver.Resolve<ILogger>();
RegisterGlobalExceptionHandling(logger);
// Bootstrapping Dependency Injection
// injects ViewModel into MainWindow.xaml
// remember to remove the StartupUri attribute in App.xaml
var mainWindow = resolver.Resolve<Pages.MainWindow>();
mainWindow.Show();
}
private void RegisterGlobalExceptionHandling(ILogger log)
{
// this is the line you really want
AppDomain.CurrentDomain.UnhandledException +=
(sender, args) => CurrentDomainOnUnhandledException(args, log);
// optional: hooking up some more handlers
// remember that you need to hook up additional handlers when
// logging from other dispatchers, shedulers, or applications
Application.Dispatcher.UnhandledException +=
(sender, args) => DispatcherOnUnhandledException(args, log);
Application.Current.DispatcherUnhandledException +=
(sender, args) => CurrentOnDispatcherUnhandledException(args, log);
TaskScheduler.UnobservedTaskException +=
(sender, args) => TaskSchedulerOnUnobservedTaskException(args, log);
}
private static void TaskSchedulerOnUnobservedTaskException(UnobservedTaskExceptionEventArgs args, ILogger log)
{
log.Error(args.Exception, args.Exception.Message);
args.SetObserved();
}
private static void CurrentOnDispatcherUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
{
log.Error(args.Exception, args.Exception.Message);
// args.Handled = true;
}
private static void DispatcherOnUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
{
log.Error(args.Exception, args.Exception.Message);
// args.Handled = true;
}
private static void CurrentDomainOnUnhandledException(UnhandledExceptionEventArgs args, ILogger log)
{
var exception = args.ExceptionObject as Exception;
var terminatingMessage = args.IsTerminating ? " The application is terminating." : string.Empty;
var exceptionMessage = exception?.Message ?? "An unmanaged exception occured.";
var message = string.Concat(exceptionMessage, terminatingMessage);
log.Error(exception, message);
}
}
In addition to the posts above:
Application.Current.DispatcherUnhandledException
will not catch exceptions that are thrown from a thread other than the main thread. You have to catch those exceptions on the same thread they are thrown. But if you want to Handle them on your global exception handler you can pass it to the main thread:
System.Threading.Thread t = new System.Threading.Thread(() =>
{
try
{
...
//this exception will not be catched by
//Application.DispatcherUnhandledException
throw new Exception("huh..");
...
}
catch (Exception ex)
{
//But we can handle it in the throwing thread
//and pass it to the main thread wehre Application.
//DispatcherUnhandledException can handle it
System.Windows.Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action<Exception>((exc) =>
{
throw new Exception("Exception from another Thread", exc);
}), ex);
}
});
To supplement Thomas's answer, the Application class also has the DispatcherUnhandledException event that you can handle.
A complete solution is here
it's explained very nice with sample code. However, be careful that it does not close the application.Add the line
Application.Current.Shutdown();
to gracefully close the app.
As mentioned above
Application.Current.DispatcherUnhandledException will not catch
exceptions that are thrown from another thread then the main thread.
That actual depend on how the thread was created
One case that is not handled by Application.Current.DispatcherUnhandledException is System.Windows.Forms.Timer for which Application.ThreadException can be used to handle these
if you run Forms on other threads than the main thread you will need to set Application.ThreadException from each such thread
I have a WinForm async GUI app in which I've set up some "global" exception handling in program.cs. I also have a GUI thread that's doing an "await Task.WhenAll()" and catching its exception and throwing the awaited Task.Exception property, so that the AggregateException gets all the way to the exception handler in program.cs (I want to iterate over the inner exceptions and log them).
I can see that the exception being thrown out of my try/catch of the WhenAll() is indeed throwing an AggreateException, but when I debug the handler in program.cs, it's not an AggregateException anymore - it's just the first Exception of the AggregateException. I can't figure out what code is doing this "unwrapping" for me?
Program.cs:
static void Main() {
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
...
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
if (e.Exception is AggregateException) {
// expect to log the contents of (e.Exception as AggregateException).Flatten().InnerExceptions, but exception coming
// in is not AggregateException but instead is
// ApplicationException("message 1")
}
else {
// handling for non aggregate exceptions
}
In Form1.cs
private async void button1_Click(object sender, EventArgs e) {
Task overall = Task.WhenAll(
Task.Run(()=> { throw new ApplicationException("message 1"); }),
Task.Run(() => { throw new ApplicationException("message 2"); })
);
try {
await overall;
}
catch {
throw overall.Exception; // this is AggregateException
}
}
}
It's not just AggregateException - WinForms will always only send GetBaseException() to the handler. That is, only the innermost exception of any InnerException chain.
Apparently this is a longstanding WinForms bug, probably permanent at this point.
You'll have to work around it with your own type:
public class ExceptionWrapper : Exception
{
public new Exception InnerException { get; set; }
}
throw new ExceptionWrapper { InnerException = overall.Exception };
The best possible workaround for this issue is capturing the raised exception via the AppDomain.FirstChangeException event and then comparing this exceptions base exception reference against the exception raised by Application.ThreadException.
Something like this:
private Exception lastFirstChanceException;
AppDomain.CurrentDomain.FirstChanceException += (sender, e) =>
{
lastFirstChanceException = e.Exception;
};
Application.ThreadException += (sender, e) =>
{
if (lastFirstChanceException?.GetBaseException() == e.Exception)
{
var realException = lastFirstChanceException; // This is the "real" exception thrown by some code
}
};
I have a WPF window with a BackgroundWorker. I get an exception in Send() method here:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
smtpClient.Send(mail);
}
which in turn is invoked in a Click Event for some button like this:
private async void SendClickAsync(object sender, RoutedEventArgs e)
{
using (MessageServiceClient client = new MessageServiceClient())
{
try
{
[...]
worker.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show("Error! Check your sender data!", "!", MessageBoxButton.OK, MessageBoxImage.Error);
[...]
}
}
}
Why does this exception doesn't get handled? When I was doing it not asynchronously (everything was in SendClickAsync() method) the message box popped up nicely.
When you call worker.RunWorkerAsync() method, your main thread continue execution and exits try..catch block. To handle exception use RunWorkerCompleted event handler. RunWorkerCompletedEventArgs arguement has property Error which will contain exception object:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error", "!", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
The BackgroundWorker is using thread pool threads, so it doesn't want your code to be able to do something weird on the worker thread. That's why the BackgroundWorker swallows your exception that happened during DoWork and lets you know that it happened through RunWorkerCompletedEventArgs.Error.
The reason the exception is not handled is because it is thrown on a different thread.
The call to worker.RunWorkerAsync(); does not block, rather it starts the job on the threadpool and returns control immediately in the main thread - this is the whole point to doing it asynchronously.
But this also means that after the call to worker.RunWorkerAsync() the main thread exits your try-catch block, so the exception doesn't get handled (on your main thread).
I want to call a method once by a specified interval (e.g. 3 seconds), but I have problem catching exception within the calling method (e.g. Start) thrown by the called method (timer_Elapsed)
Update
public void Start()
{
Timer timer = new Timer(PingPeriod); //System.Timers
try
{
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
}
catch (Exception ex)
{
//I want to catch exception thrown by timer_Elapsed
}
//Logic here that keeps the method running
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
throw new exception("Catch this exception please");
}
Update:
Can anyone please confirm that the start() will NOT catch exception thrown from timer_Elapsed, or when trying to invoke timer_Elapsed if exception occurred.
Is there a better alternative, which can catch exception from a called method or get notified?
Any idea would be very much appreciated!
This just isn't possible, the Elapsed event handler runs later, well after your Start() method has exited. Furthermore, that event has the nasty habit of swallowing all exceptions without any diagnostic.
You'll need to handle exceptions within the event handler. Certainly not easy to do. Do consider using the System.Threading.Timer class instead. At least your program will terminate when the callback throws an exception.
This is not possible because the exception is thrown on a different execution context (thread) from the one that set up the timer.
You should handle any exceptions in your callback method. If you want your logic separated from your exception handling, simply create a separate method.
private void TimerElapsedCallback(object sender, ElapsedEventArgs e)
{
try
{
this.DoSomething();
}
catch (Exception ex)
{
// handle
}
}
private void DoSomething()
{
// logic goes here and can be agnostic of any exceptions it throws, if desired
}