TargetInvocationException from BackgroundWorker_RunWorkerCompleted
Asked Answered
S

1

3

Suppose the following situation. A form has a button that on click starts a background worker. In RunWorkerCompleted event handler there is a piece of code that throws unhandeled exception. The form is started from Application.Run method.

public partial class FormMain : Form
{
    public FormMain()
    {
        InitializeComponent();
    }

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        throw new Exception();
    }

    private void button_Click(object sender, EventArgs e)
    {
        backgroundWorker.RunWorkerAsync();
    }
}

The problem is that Visual Studio breaks at Application.Run call instead at "throw new Exception()" in FormMain.backgroundWorker_RunWorkerCompleted method. On top of that real exception is wrapped with TargetInvocationException and call stack is reduced to Program.Main method and code that caused exception can't be inspected because of that.

How to prevent that wrapping? Am I doing something intrinsically wrong?

Judging from the call stack supplied with TargetInvocationException, there are a lot of invocation methods stacked up, too much for my basic understanding of message loops and not-so-basic understanding of threading.

EDIT: I know there is InnerException property in TargetInvocationException and that the error can be traced by looking there but that is not the question. The question is how to make Visal Studio stop before wrapping real exception with TargetInvocationException so I can use all those nice debugging features that VS IDE provides.

Smallscale answered 28/5, 2012 at 14:4 Comment(2)
In case of TargetInvocationException The InnerException property holds the underlying exception.Kinghood
@Kinghood I know and that is not what I'm asking. I've edited the question to prevent this kind of answers.Smallscale
S
3

Yes, this is an unfortunate side effect from the magic that makes the RunWorkerCompleted event run on the UI thread. There is no code that you wrote that made it run so the debugger cannot show anything relevant but the last statement that is in your program that was still involved, the Application.Run() call that started the message loop.

You have to debug this by forcing the debugger to stop when the exception is thrown. Debug + Exceptions, tick the Thrown checkbox for CLR exceptions. Also note the behavior when you run this without a debugger, you'll get the Wheel Of Fortune dialog. Fix that with Application.SetUnhandledExceptionMode().

Shinleaf answered 28/5, 2012 at 15:4 Comment(4)
Catching all thrown exceptions has a nasty side-effect of stopping the IDE on every exception, including handled ones. I suppose it is still possible to send a windows message and handle it somewhere in the main thread.Smallscale
Use exceptions only for exceptional conditions. You already have a "somewhere" and you already are on the main thread. No need to send a message, handle it in the RunWorkerCompleted event handler.Shinleaf
Thing is, I'm working on the project where a non-trivial logic has to be done after background worker completes. It's really annoying to dig through inner exception stack trace when something as trivial as putting duplicate key in dictionary throws exception. Don't worry I don't practice "exception driven design"Smallscale
I did some experiments with PostMessage, pinvoke and message filtering. It gets the job done without reflection. Tradeoff is that pinvoke introduces some low level stuff that should stay below C#'s level, such as pointers as window handles.Smallscale

© 2022 - 2024 — McMap. All rights reserved.