2

I'm trying to use Method.Invoke to call a windows form dialog, have a user perform some selections/interaction and continue execution. This invoke call is happening in an asynchronous method.

While everything works fine, should an error occur on the windows form, an unhandled exception is thrown, even when trying to catch TargetInvocationException or just Exception.

Both forms are in the same winforms project. I realize where are other ways to perform an async call but this is just to illustrate the issue.

The form dialog is as follows:

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

    private void btnOK_Click(object sender, EventArgs e)
    {
        throw new Exception("oh noes!");

        this.DialogResult = DialogResult.OK;
        this.Close();
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        this.DialogResult = DialogResult.Cancel;
        this.Close();
    }

    public new DialogResult ShowDialog()
    {
        base.ShowDialog();
        return this.DialogResult;
    }
}

And here is the calling code. None if the catch blocks are being executed, even when not debugging (my problem is not debugging exceptions in the IDE as mentioned here. The following results in an unhandled exception).

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

    private void Form1_Load(object sender, EventArgs e)
    {

        MethodInvoker simpleDelegate = new MethodInvoker(InvokeForm);
        IAsyncResult tag = simpleDelegate.BeginInvoke(null, null);
        simpleDelegate.EndInvoke(tag);
        MessageBox.Show("All done");
    }

    private void InvokeForm()
    {
        try
        {
            Type t = typeof(FakeDialog);
            MethodInfo showDialogMethod = t.GetMethod("ShowDialog", new Type[] { });
            object dialog = Activator.CreateInstance(t);
            System.Windows.Forms.DialogResult result = (System.Windows.Forms.DialogResult)showDialogMethod.Invoke(dialog, new object[] { });
            MessageBox.Show(result.ToString());

        }            
        catch (TargetInvocationException tie)
        {
            MessageBox.Show("Tie exception");
        }
        catch (Exception ex)
        {
            MessageBox.Show("general exception");
        }
    }
}

UPDATE:

Strangely, I'm able to catch the exception when running with debugging (I'm sure the IDE is helping here). Running without debugging causes the unhandled exception.

Also, invoking via an async call doesnt seem to make a difference. If i just call InvokeForm() (ignore all the methodInvoker stuff), I can achieve the same result.

Operating on .NET 2.0 using Visual Studio 2008.

Community
  • 1
  • 1
MoSlo
  • 2,780
  • 4
  • 33
  • 37
  • Which framework? I can't repro on 4.0; also... why are you doing an async begin/end? that achieves nothing here (but makes life hard) – Marc Gravell Sep 13 '11 at 07:01
  • On framework 2.0. The async begin/end is just to have an async process and reproduce the issue outside of a giant application (where the dialog is being invoked on an async call). – MoSlo Sep 13 '11 at 07:37
  • 1
    I've reproduced the error. .net 2 and vs2010 the unhandled exception is only thrown if you run the exe outside of vs – fluf Sep 13 '11 at 09:16
  • 1
    You can remove the error by adding the Jit debugging to the application config file I have no idea why this solves the issue and if it truly does but it can help. – fluf Sep 13 '11 at 13:26

3 Answers3

2

Ok, figured it out.

The result from the code was unhandled exceptions. And while using Method.Invoke for a simple method in another class would behave correctly, the environment changes with the source of the exception occurring in a form. A form event. And I eventually found on Microsoft Support that unhandled exceptions in Windows Form events are not propagated up call stack. There's some pretty interesting reasons for this ("Windows Forms applications have a top-level exception handler that allows the program to continue to run if it can recover").

It also gives credence to what Marc mentioned over putting things in the Load event. Sigh. So the reason for all this is pretty obvious now.

As for the code running fine while debugging (as opposed to without), I think I can thank the JIT debugger for that. @fluf pointed to me that specifically enabling the JIT debugger gave the same result as running with debugging. @Marc Gravell, depending on VS settings this might explain only I and fluf could reproduce. There's some more info on it here but it's not a production fix.

So the real solution is either handling the exceptions in the event handlers themselves or use the solution as mentioned in the Microsoft Support article above, which solves my issues.

MoSlo
  • 2,780
  • 4
  • 33
  • 37
1

I can't actually repro from your code, but: the Load event is.... different, and some odd things can happen if you get an exception inside the Load event. Simply my advice would be: move this code out of the Load event. It also doesn't help that attaching a debugger here changes the behaviour (a Heisenbug).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Have you tried to repo in .NET 2.0? Same result even if I place the code in a btn_Click. The only reason I placed the code in the form_load was for the test app. The real application invokes this from a thread (reason for the async calls I put in place but see now its not needed to reproduce). – MoSlo Sep 13 '11 at 08:05
  • @MoSlo my test project is targetting 2.0, indeed. – Marc Gravell Sep 13 '11 at 08:07
0

Without seeing MethodInvoker's declaration i can only guess, but it is possible that InvokeForm() method is executed on non-UI thread.

MethodInvoker simpleDelegate = new MethodInvoker(InvokeForm); 
IAsyncResult tag = simpleDelegate.BeginInvoke(null, null); 

To show a dialog you may consider to rewrite this as follows:

Action simpleDelegate = new Action(InvokeForm); 
this.BeginInvoke(simpleDelegate);
alexm
  • 6,854
  • 20
  • 24