I have a background worker which can be cancelled.
The normal flows interrupt itself when the CancelPending variable turns to true (responding to user interaction on UI which call worker.CancelAsynch() ), exceptions are thrown because if that (since normal flow is interrupted, lots of null ref exception are thrown)
So when the worker returns, I want to be able to distinguish exception that have been thrown when the worker
was canceled (to silently ignore them) from exceptions thrown when worker was not canceled (to report them to UI).
My code is as follow (sorry for the c#/vb mix ...) :
The worker class:
Public Class ClassBaseGetObjectsWorker
Inherits System.ComponentModel.BackgroundWorker
Protected Overrides Sub OnDoWork(ByVal e As System.ComponentModel.DoWorkEventArgs)
Try
Dim cpt As Int16 = 0
While cpt < 5
System.Threading.Thread.Sleep(1000)
cpt = cpt + 1
If CheckForCancellation() Then
'Simulating exception due to cancel
Throw New Exception("Exception du to cancel !")
End If
End While
Catch exc As Exception
e.Cancel = Me.CancellationPending
Throw exc
End Try
End Sub
End Class
The call back:
void setObjSetCollWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (e.Cancelled) {
resultLabel.Text += "Canceled";
//e.Error is selently ignored
}
else {
if (e.Error != null) {
//Reporting errors arising during normal (uncanceled) flow
throw e.Error.InnerException;
}
else {
//Worker process finished without error or being canceled.
updateUIAfterBackgroundJob();
}
}
}
Then, when I'm doing worker.CancelAsynch(), e.Cancelled is set to false (which is not what I expected) in the Completed call back. If I comment out "Trow exc" in the worker, if I test again, e.Cancelled is correctly set to true.
What is the cleanest way to get the information I want, that is: I want to know if the exception popping out in the completed handler was thrown when the worker was in the cancellationPending state or not?
If the best way to structure the code in your OnDoWork()-implementation is to throw an exception when you detect cancellation, do the following:
Create a CancelException:
public class CancelException: Exception {}
Throw this CancelException when you detect that cancellation is pending:
if(CheckForCancellation()) throw new CancelException();
Add a try-catch around the code in your OnDoWork()-method:
protected override void OnDoWork(DoWorkEventArgs e){
try{
//...
}
catch(CancelException){
// silently return
return;
}
}
That way your code will obey the BackgroundWorker-contract (which is to return from OnDoWork() when you detect cancellation pending, rather than to throw an exception), and the Cancelled property should now be as you expect
Related
I have a background worker that will throw an exception when there is a problem. I'm trying to catch the exception in the workcompleted event.
My code is roughly as follows:
void workerProcessReports_DoWork(object sender, DoWorkEventArgs e)
{
try
{
//Work is here. The code here throws an exception
}
catch (Exception ex)
{
throw ex;
}
}
void workerProcessReports_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("There was a problem");
}
else
{
MessageBox.Show("No Problems");
}
}
The problem is that visual studio breaks on the throw ex in the dowork method with the Message
InvalidOperationException was unhandled by user code
What's going on?
Don't catch and rethrow your same exception, that's a waste and causes your stack trace to get messed up.
If you want the exact same exception to be thrown after catching it from a parent exception type, just write throw; in your catch block. If you plan on just throwing everything anyway, don't bother catching it in the first place.
If you do allow your exception to occur without handling it in the doWork method, then you can throw on the parent thread in your RunWorkerCompleted method.
Within your RunWorkerCompleted method you can throw it as such:
if(e.Error != null)
{
throw e.Error;
}
However, at this point if it's something you can handle you may just want to recover and continue, instead of throwing since you are already aware that an exception occured. Especially, if this parent thread is your UI thread, then this is the perfect opportunity to display an error message instead of throwing and potentially crashing your application for a non-fatal error.
I am handling all of my exceptions in Dispatcher.UnhandledException so I tried to direct exceptions coming from Tasks into this event. |I tried to send exception in the main thread.
task.ContinueWith((t) =>
{
....
if (t.Exception != null)
throw t.Exception;
}, TaskScheduler.FromCurrentSynchronizationContext());
but I can not take this exception in UnhandledException event , what is wrong with my solution?
More Info , the task and continuation is happening in a RealyCommand in WPF app using MVVM Light:
here is my test view model, ExecuteTestCommand is running in UI thread
class MainViewModel : ViewModelBase
{
public MainViewModel()
{
App.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
}
void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show("got it");
e.Handled = true;
}
#region TestCommand
private RelayCommand _testCommand;
public RelayCommand TestCommand
{
get
{
if (_testCommand == null)
_testCommand = new RelayCommand(ExecuteTestCommand, CanExecuteTestCommand);
return _testCommand;
}
}
private bool CanExecuteTestCommand()
{
return true;
}
private void ExecuteTestCommand()
{
// throw new Exception();
Task.Factory.StartNew(() =>
{
}).ContinueWith(t =>
{
App.Current.MainWindow.Title = "It is the main UI thread" + (App.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread).ToString();
throw new NotImplementedException();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
#endregion
}
The implementation of the Task Parallel Library includes an exception handler which ensures (in most cases) that unhandled exceptions within a task do not result in the process terminating. This handler means your UnhandledException event will never be called.
When you are not using async/await, handling exceptions within a task is generally handled by the following process:
If your task is actually a sub-task of an "enclosing" task, then the "enclosing" task either propagates the exception or handles it directly (by the steps below).
If the code needs to handle exceptions, it generally registers a continuation which checks for the antecedent task entering the Faulted status, and performs an appropriate action (such as directly reporting the error to a handler of some form).
I developed the Rackspace Threading Library specifically for simplifying these scenarios. The Select and Then methods include complete handling for canceled and faulted antecedent tasks so you don't have to check the status of the antecedent in every continuation. The Select and Then methods which include a supportsErrors parameter can be used to handle a faulted task (and not propagate the error to the parent). The Finally methods can be used to handle a canceled or faulted task while still propagating the error to the parent.
If you can attach a handler to TaskScheduler.UnobservedTaskException you'll catch these exceptions too. However, note that this doesn't necessarily run on your main thread.
If you need to handle the exception on your main thread you could add code in the handler to marshal to the main thread (and then even re-throw).
I could not find any explanation why this happens?!!! but to solve my problem I used Dispatcher.Invoke
private void ExecuteTestCommand()
{
// throw new Exception();
Task.Factory.StartNew(() =>
{
}).ContinueWith(t =>
{
if (t.Exception != null)
{
App.Current.Dispatcher.Invoke(new Action(() =>
{
throw t.Exception;
}));
return;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
I'm using a BackgroundWorker since I need to give a feedback in my UI. I also need to use ThreadPool in my class for it to run asynchronously but it seems that my background worker cannot catch the exception it throws.
It's intended to give a feedback on what task it's doing.
I made a simple application which reproduces the problem :
// MAIN UI CLASS
BackgroundWorker _bgWorker = new BackgroundWorker();
void _bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// Returns custom error
MessageBox.Show(e.Error.Message);
}
}
void _bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
try {
// Initialize test error class
var testClass = new TestClass();
}
catch (Exception ex)
{
MessageBox.Show("im from trycatch");
}
}
private void Window_ContentRendered(object sender, EventArgs e)
{
_bgWorker.DoWork += _bgWorker_DoWork;
_bgWorker.RunWorkerCompleted += _bgWorker_RunWorkerCompleted;
}
/////////////////////////////////////////////////////////////////////////
// TEST ERROR CLASS
public TestClass()
{
throw new Exception("im a custom error");
}
This works all right. With the try-catch, it catches the error as handled by the try-catch. Whereas, without the try-catch, it handles it in RunWorkerCompleted.
However, I really need to do these methods asynchronously (which is why I use ThreadPool) but the debugger always points at the exception. (it only crashes the program when I try it without the debugger)
// TEST ERROR CLASS
public TestClass()
{
ThreadPool.QueueUserWorkItem(delegate
{
throw new Exception("im a custom error");
}
}
I tried to rethrow the error using a try-catch statement (hoping it would pass the exception to the BackgroundWorker) but it didn't work.
Hope you can help me.
Thanks in advance.
You do not need to put code on the ThreadPool from the DoWork method. The BackgroundWorker will raise your _bgWorker_DoWork method on a ThreadPool thread automatically when you call RunWorkerAsync().
If you are trying to start SEVERAL MORE ThreadPool tasks from your background worker, then I suppose that could be a consideration, but in that case I would suggest using the TPL in .NET4 if at all possible...
http://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
If you can not use TPL, then you must wrap your ThreadPool code in a TryCatch and put the Exception some place that can be accessed outside of that method.
For example add a internal static List<Exception> backgroundExceptions; member to your UI class, the background worker add's any caught exceptions to this list, and then you must investigate this list after your background work has completed to see if there were any Exceptions present.
This is essentially what the TPL is doing for you ... giving you a place to 'observe' exceptions that happened on the background thread, but it is still your responsibility to look for those Exceptions, either in the Task.Exception property, or by calling a method that will re-throw the Exception for you.
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
}
I have the following code:
public Mainform()
{
...
// scheduler
scheduler.DoWork += new System.ComponentModel.DoWorkEventHandler(scheduler_DoWork);
scheduler.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(scheduler_RunWorkerCompleted);
scheduler.WorkerReportsProgress = false;
scheduler.WorkerSupportsCancellation = true;
...
...
scheduler_DoWork(this, null);
scheduler.RunWorkerAsync(1000);
...
}
void scheduler_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
scheduler_Enabled = false;
CustomExceptionHandler eh = new CustomExceptionHandler();
eh.HandleUnhandledException(e.Error, "scheduler");
}
if(scheduler_Enabled)
{
scheduler.RunWorkerAsync(1000);
}
}
void scheduler_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
try
{
...do some stuff
}
catch(MyException ex)
{
ThreadSafeShowError();
}
finally
{}
...do more stuff
}
finally
{
if (e != null && e.Argument != null)
{
Thread.Sleep((int)e.Argument);
}
}
}
The backgroundworker thread died unexpectedly without any exception being thrown. I did not encounter this problem during development and it seems to be hard to reproduce. I suspected that maybe a cross thread exception was occurring when I am doing work in the scheduler_DoWork. I have tried to explicitly update the UI without checking if InvokeRequired and the thread continues to run without problems in a release build. How can this be? (Cross thread exception should occur) How can I determine what causes the thread to die? Any suggestions on how to figure out what is going wrong or how to debug this issue will be appreciated?
The RunWorkerCompleted event might not be fired on the UI Thread. If it is not, then the thread will end and your scheduler object will be garbage collected, which will make it seem like it just quit with no error. See this post for more details. Here and here are SO posts about this.
Your sample doesn't show enough code to determine what's going on but:
Maybe an exception is being thrown from ThreadSafeShowError? Why are you trying to show an error from the worker thread anyway - the conventional thing to do is to show e.Error if not null in the RunWorkerCompleted event handler.
To debug the issue try putting the following around all the code in your DoWork handler:
try
{
// do work
// log a trace statement here
}
catch(Exception ex)
{
// log exception, e.g. with System.Diagnostics.Debug.Write
throw;
}
finally
{
// log a trace statement here
}
You can do several things to increase the possibility of catching the exception:
Enable Managed Debugging Assistants for all exceptions in VS. To do that, go to Debug menu -> Exceptions..., and put a check mark next to "Managed Debugging Assistants" to enable all exceptions to be caught using debugger. Also, depending on the code you are executing, expand the CLR Exceptions node and select nodes of interest ("System" node, for example, will catch all exceptions from the System namespace in the debugger).
Obviously, put a try/catch block around your code, with some logging. You can also do something like this, if you are in real trouble:
try
{
// do stuff
}
catch (Exception ex)
{
#if DEBUG
// break only in DEBUG builds
System.Diagnostics.Debugger.Break();
#endif
// log the exception and throw
throw;
}
Put a try/catch with logging around your Application.Run(...) code in the Main() method. Exceptions do propagate up there sometimes, and this can catch them (even if not coming from this specific part of your code).
Add an Application.ThreadException event handler in your Main() method, before calling Application.Run, like this:
Application.ThreadException +=
new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
In Mainform, you never call scheduler.RunWorkerAsync, so your BackgroundWorker does not start at all.