I'm seeing some wierd behaviour when throwing exceptions and catching them in the Application.ThreadException event handler.
Basically whats happening in the sample below is that an exception is thrown in the DoWork event handler of a BackgroundWorker. The RunWorkerCompleted event handler rethrows a new exception with the original as the inner exception.
Why does the inner exception show up in the ThreadException event handler and not the acutal exception being thrown? If I do not provide an inner exception in the RunWorkerCompleted event handler, the correct exception will show up.
using System;
using System.Windows.Forms;
using System.ComponentModel;
namespace WierdExceptionApp
{
class WierdExceptionForm : Form
{
BackgroundWorker worker = new BackgroundWorker();
public WierdExceptionForm()
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
throw new Exception("worker_RunWorkerCompleted", e.Error);
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
throw new Exception("worker_DoWork");
}
[STAThread]
static void Main()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(new WierdExceptionForm());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message);
}
}
}
The RunWorkerCompleted event is marshaled from the BGW thread to the UI thread by the WF plumbing that makes Control.Invoke() work. Essentially, there's a queue with delegates that is emptied by the message loop. The code that does this, Control.InvokeMarshaledCallbacks(), you'll see it on the call stack, has a catch (Exception) clause to catch unhandled exceptions. That clause calls Application.OnThreadException, passing the value of Exception.GetBaseException().
Well, that explains why you only see the inner exception. Why it is done this way is a bit unclear. Possibly to slice off the stack frames of the code in the UI thread that are otherwise pretty confusing since the real exception came from the background thread.
if (e.Error != null)
{
throw new Exception("worker_RunWorkerCompleted", new Exception("Inner", new Exception("Inner inner")));
}
You get "inner inner" at the end. It seems that this is the behavior of Application_ThreadException method to look at the inner-most 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.
I have a WPF application that has a BackgroundWorker. I throw an exception in this BGW but it is not shown any where!, just the background worker fires its WorkerFinished event.
Where is it going?
Each thread has it's own call stack; exceptions can only move up their own call stack, there's no way for them to "bleed" over into another thread's call stack.
When your exception bubbles up to the BackgroundWorker's code that fires the DoWork event handler the exception will end up being explicitly caught and stored in the Error property rather than allowing it to reach the top of the call stack and crash the application.
If you want the program to end if your BGW throws an exception then you'll need to handle the completed event, check for an exception, and then re-throw it or throw a new exception.
Look here, there's a nice example. The exception in throwned in the RunWorkercompleted
Unhandled exceptions in BackgroundWorker
var worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
throw new InvalidOperationException("oh shiznit!");
};
worker.RunWorkerCompleted += (sender, e) =>
{
if(e.Error != null)
{
MessageBox.Show("There was an error! " + e.Error.ToString());
}
};
worker.RunWorkerAsync();
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 am handling thread exceptions but I want to get the name of the Thread that the exception occurred on. It appears that when the thread exception fires the event stays on the main thread although I think the exception could have occurred on a different thread.
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
ShowFaultDialog(e.Exception, "(Application) Thread Exception [" + System.Threading.Thread.CurrentThread.Name + "]");
}
In static void Main():
Thread.CurrentThread.Name = "Main Thread";
VS 2010 shows the main thread as having a 'Name' of "Main Thread" but actually the thread name is null.
If you mean handling of Application.ThreadException event: it fires only on exceptions, that was thrown from WinForms threads. Usually, there's one WinForms thread in application: the main thread.
UPDATE.
Here's sample that demonstrating Application.ThreadException and AppDomain.UnhandledException behavior difference:
1) Program class:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new Form1());
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.Name);
}
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.Name);
}
}
2) Main form (a form with two buttons) code-behind:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
throw new InvalidOperationException();
}
private void button2_Click(object sender, EventArgs e)
{
new Thread(() => { throw new InvalidOperationException(); })
{
Name = "Worker Thread"
}.Start();
}
}
When you are clicking on button1, you're throwing exception from WinForms thread. So, this exception will be handled at Application_ThreadException by default.
When you are clicking on button2, you're throwing exception from worker thread, which is not a WinForms thread. Application.ThreadException isn't fired in this case, instead AppDomain.UnhandledException event is fired (and CurrentDomain_UnhandledException is called, producing 'Worker Thread' line in output window).
Use an incrememnted numerical variable (such as byte) to give each thread it's own name eg
string threadname = "Thread" + threadnumber
And then use the catch statement to notify you like so:
ShowFaultDialog(e.exception, threadname)
That way you'll be able to tell which thread it is, in theory.
As I understand from MSDN the Application_ThreadException event allows Windows Forms applications to handle unhandled exceptions that occur in Windows Forms threads and when you reach this event you are in your main UI thread. So it will print always the same.
Have you checked the Exception.TargetSite property? This property gives you back the method name and signature where the exception occurred.
If I assign a ThreadExceptionEventHandler to Application.ThreadException, why when I invoke a delegate method using a control on the main application thread are any exceptions thrown by that delegate not triggering the event handler?
i.e.
static void Main()
{
...
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.Run(new Form1());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
Console.Error.Write("A thread exception occurred!");
}
...
private void Form1_Load(object sender, EventArgs e)
{
Thread syncThread = new Thread(new ThreadStart(this.ThrowException));
syncThread.Start();
}
private void ThrowException()
{
button1.Invoke(new MethodInvoker(delegate
{
// Not handled by ThreadExceptionEventHandler?
throw new Exception();
}));
}
The context on this is that I have a background thread started from a form which is throwing an unhandled exception which terminates the application. I know this thread is going to be unreliable since it is network connectivity reliant and so subject to being terminated at any point, but I'm just interested as to why this scenario doesn't play out as I expect?
Use AppDomain.CurrentDomain.UnhandledException instead , to catch exceptions that occur in threads not created and owned by Windows Forms
You should definetly see MSDN article to clarify this issue