Unhandled exceptions in BackgroundWorker

52,423

Solution 1

What you're describing is not the defined behavior of BackgroundWorker. You're doing something wrong, I suspect.

Here's a little sample that proves BackgroundWorker eats exceptions in DoWork, and makes them available to you in RunWorkerCompleted:

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();

My psychic debugging skills are revealing your problem to me: You are accessing e.Result in your RunWorkerCompleted handler -- if there's an e.Error, you must handle it without accessing e.Result. For example, the following code is bad, bad, bad, and will throw an exception at runtime:

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) => 
    { 
        throw new InvalidOperationException("oh shiznit!"); 
    };
worker.RunWorkerCompleted += (sender, e) =>
    {
        // OH NOOOOOOOES! Runtime exception, you can't access e.Result if there's an
        // error. You can check for errors using e.Error.
        var result = e.Result; 
    };
worker.RunWorkerAsync();

Here's a proper implementation of the RunWorkerCompleted event handler:

private void RunWorkerCompletedHandler(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
       DoSomethingWith(e.Result); // Access e.Result only if no error occurred.
    }
}

VOILA, you won't receive runtime exceptions.

Solution 2

I would add to the MSDN text:

If the operation raises an exception that your code does not handle, the BackgroundWorker catches the exception and passes it into the RunWorkerCompleted event handler, where it is exposed as the Error property of System.ComponentModel..::.RunWorkerCompletedEventArgs. If you are running under the Visual Studio debugger, the debugger will break at the point in the DoWork event handler where the unhandled exception was raised.

... AND the debugger will report the exception as "~Exception was unhandled by user code"

Solution: Don't run under the debugger and it works as expected: Exception caught in e.Error.

Solution 3

[Edit]

Judah has a great point. My example pointed out the specifics of handling the error but my code would actually cause another exception if an exception was never hit in the DoWork method. This example is OK due to the fact that we are specifically showing the error handling capabilities of the BackgroundWorker. However if you are not checking the error parameter against null then this could be your issue.

[/Edit]

I don't see the same results. Can you post a little code? Here is my code.

private void Form1_Load(object sender, EventArgs e)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.RunWorkerAsync();
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Will cause another exception if an exception didn't occur.
    // We should be checking to see if e.Error is not "null".
    textBox1.Text = "Error? " + e.Error;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < 10; i++)
    {
        if (i < 5)
        {
            Thread.Sleep(100);
        }
        else
        {
            throw new Exception("BOOM");
        }   
    }
}

Program Output:

Error? System.Exception: BOOM at BackgroundException.Form1.worker_DoWork(Object sender, DoWorkEventArgs e) in D:\Workspaces\Sandbox\BackgroundException\BackgroundException\Form1.cs:line 43 at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e) at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)

An interesting article that looks similar to your question. It has a section on handling exceptions.

http://www.developerdotstar.com/community/node/671

Solution 4

This is an old question, but I found it while Googling the same symptoms. Posting this in case someone else finds it for the same reason.

Judah's answer is right, but it isn't the only reason the "unhandled exception in user code" dialog can appear. If an exception is thrown from inside a constructor on the background thread then that exception will cause the dialog immediately, and won't be passed to the RunWorkerCompleted event. If you move the offending code outside of any constructors (to any other method) it works as expected.

Share:
52,423
Andy
Author by

Andy

Updated on July 24, 2022

Comments

  • Andy
    Andy over 1 year

    I have a small WinForms app that utilizes a BackgroundWorker object to perform a long-running operation.

    The background operation throws occasional exceptions, typically when somebody has a file open that is being recreated.

    Regardless of whether the code is run from the IDE or not .NET pops up an error dialog informing the user that an Unhandled exception has occurred. Compiling the code using the Release configuration doesn't change this either.

    According to MSDN:

    If the operation raises an exception that your code does not handle, the BackgroundWorker catches the exception and passes it into the RunWorkerCompleted event handler, where it is exposed as the Error property of System.ComponentModel..::.RunWorkerCompletedEventArgs. If you are running under the Visual Studio debugger, the debugger will break at the point in the DoWork event handler where the unhandled exception was raised.

    I expect these exceptions to be thrown on occasion and would like to handle them in the RunWorkerCompleted event rather than in DoWork. My code works properly and the error is handled correctly within the RunWorkerCompleted event but I can't for the life of me figure out how to stop the .NET error dialog complaining about the "Unhandled exception" from occurring.

    Isn't the BackgroundWorker supposed to catch that error automagically? Isn't that what the MSDN documentation states? What do I need to do to inform .NET that this error is being handled while still allowing the exception to propage into the Error property of RunWorkerCompletedEventArgs?

  • Bobby Cannon
    Bobby Cannon almost 15 years
    +1 Good point. My example pointed out the specifics of handling the error but my code would actually cause another exception if an exception was never hit in the DoWork method.
  • IbrarMumtaz
    IbrarMumtaz over 14 years
    I beg to differ .... I am also struggling to see how the BGW class eats up exps ... as sometimes my app grants me access to my user area on Win7 and sometimes it doesn't. When I investigate further, i found that access being denied is because the folder doesn't exist. I find that sometimes the error is thrown in my app and sometimes it doesn't.
  • Judah Gabriel Himango
    Judah Gabriel Himango over 14 years
    @Ibrar, are you always checking for e.Error inside your RunWorkerCompleted event handler? Make sure you check that before you do anything. All catchable exceptions will be reported there.
  • IbrarMumtaz
    IbrarMumtaz over 14 years
    ty for replying. I followed your example and amended it ... so far so good. Hasn't fallen over i added in else statement that raises a custom event and logs the error to my logger class. Allow the exception go handled and not unhandled. Hopefuly this is the last i see of this.
  • Michael Petrotta
    Michael Petrotta over 13 years
    @Judah: thanks for this. You saved me much time. This smells of magic, and I really wish .NET would throw a more on-the-nose exception ("Result property may not be accessed when an exception was thrown by BackgroundWorker DoWork"), rather than trying to do the right thing and just confusing matters.
  • yu_ominae
    yu_ominae over 10 years
    Thanks for that. What I really would like to know is why on earth it behaves like this. Makes it impossible to debug the RunWorkerCompleted event's exception handling.
  • andersop
    andersop almost 10 years
    If they had just put that bit about the debugger in the documentation... So true. Thanks.
  • ispiro
    ispiro over 9 years
    I tried throwing an exception in a constructor and the RunWorkerCompleted caught it. Perhaps you tried it with a Console application? According to this answer, Console apps behave differently.
  • Rich
    Rich over 9 years
    It's possible. The specific exceptions I was having this problem with were giving me "The type initializer for xyz threw an exception" errors, I'm not sure what specific circumstances would produce that etc.
  • Rich
    Rich over 9 years
    Old thread but remembered the rest of this story - the TypeInitializationException is the one I was thinking of, and it is thrown if an exception occurs inside a static constructor.
  • Anders Carstensen
    Anders Carstensen over 9 years
    @yu_ominae: Not impossible. Whenever the debugger breaks on the exception, you can actually press F5 (Continue) to let the BackgroundWorker catch the exception and the execution will continue as expected in RunWorkerCompleted.
  • user20358
    user20358 about 9 years
    Is there a way to pass the exception occurring in the background thread back into the main calling thread?
  • DavidRR
    DavidRR about 8 years
    Mark - you really should consider updating your answer to incorporate Anders comment.
  • DavidRR
    DavidRR about 8 years
    This earlier answer addresses what happens in the Visual Studio debugger.
  • Neal Davis
    Neal Davis over 6 years
    I have a question about this: should you even use a try - catch statements in a backgroundworker DoWork Handler routine? Shouldn't you always just "catch" any error in RunWorkerCompleted Handler?
  • Judah Gabriel Himango
    Judah Gabriel Himango over 6 years
    If it makes sense for your model, sure. There may be times when you want to try/catch in DoWork handler (e.g. retrying some action), but in most cases, you won't have any try/catch in the DoWork handler, and instead you'll deal with errors in RunWorkerCompleted.