开发者

Why is Method.Invoke generating unhandled exceptions? Unable to catch even with TargetInvocationException

开发者 https://www.devze.com 2023-04-04 05:51 出处:网络
I\'m trying to use Method.Invoke to call a windows form dialog, have a user perform some selections/in开发者_高级运维teraction and continue execution.This invoke call is happening in an asynchronous m

I'm trying to use Method.Invoke to call a windows form dialog, have a user perform some selections/in开发者_高级运维teraction 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.


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.


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).


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

精彩评论

暂无评论...
验证码 换一张
取 消