为什么Method.Invoke会生成未处理的异常?甚至无法捕获TargetInvocationException

时间:2011-09-13 06:50:07

标签: c# winforms reflection asynchronous

我正在尝试使用Method.Invoke调用Windows窗体对话框,让用户执行一些选择/交互并继续执行。这个调用调用是在异步方法中进行的。

虽然一切正常但如果Windows窗体上发生错误,即使在尝试捕获 TargetInvocationException 或只是异常时,也会抛出未处理的异常。

两种形式都在同一个winforms项目中。我意识到执行异步调用的其他方法在哪里,但这只是为了说明问题。

表单对话框如下:

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;
    }
}

这是调用代码。如果正在执行catch块,则无,即使没有调试(我的问题不是在IDE中调试异常here。以下结果是未处理的异常)。

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");
        }
    }
}

更新:

奇怪的是,我可以在运行调试时捕获异常(我确信IDE在这里提供帮助)。不经调试运行会导致未处理的异常。

此外,通过异步调用调用似乎没有什么区别。如果我只是调用 InvokeForm()(忽略所有methodInvoker的东西),我可以达到相同的结果。

使用Visual Studio 2008在.NET 2.0上运行。

3 个答案:

答案 0 :(得分:2)

好的,想通了。

代码的结果是未处理的异常。虽然将Method.Invoke用于另一个类中的简单方法会表现正常,但环境会随着表单中发生的异常来源而变化。表单事件。我最终在Microsoft支持上发现unhandled exceptions in Windows Form events are not propagated up call stack.这有一些非常有趣的原因(“Windows Forms应用程序有一个顶级异常处理程序,允许程序继续如果它可以恢复“)。

它也证实了Marc在Load事件中提到的内容。叹。所以这一切的原因现在非常明显。

至于在调试时运行良好的代码(相对于没有),我想我可以感谢JIT调试器。 @fluf指出我,专门启用JIT调试器给出了与调试运行相同的结果。 @Marc Gravell,根据VS设置,这可能只解释我和fluf可以重现。关于它的更多信息here但它不是生产修复。

因此,真正的解决方案是在事件处理程序本身处理异常,或者使用上面的Microsoft支持文章中提到的解决方案来解决我的问题。

答案 1 :(得分:1)

我实际上无法重复您的代码,但是:Load事件是...... 不同的,如果您在{{em>}中获得异常,可能会发生一些奇怪的事情{1}}事件。我的建议就是:将此代码移出Load事件。在此处附加调试器会改变行为(Heisenbug)也无济于事。

答案 2 :(得分:0)

没有看到MethodInvoker的声明我只能猜测,但InvokeForm()方法可能在非UI线程上执行。

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

要显示对话框,您可以考虑按如下方式重写:

Action simpleDelegate = new Action(InvokeForm); 
this.BeginInvoke(simpleDelegate);