如何使用 Control.Invoke() 抛出一个不会被忽略的异常

本文关键字:异常 一个 Control 何使用 Invoke | 更新日期: 2023-09-27 18:06:24

Motivation

我在Windows.Forms应用程序中使用Task,我想通过使用Task.ContinueWith()来处理Task引发的任何异常,Control.Invoke()在主 UI 线程上重新抛出任何异常。

但是,如果我使用Control.Invoke(),我无法注意到异常 - 但是如果我使用 Control.BeginInvoke() 它就可以工作。

有谁知道为什么它不适用于Control.Invoke(),以及如何让它工作?

解决方法

我目前正在使用Control.BeginInvoke()投掷而不是使用Control.Invoke()

重现步骤

环境:Windows 7 x64,Visual Studio 2012,为.Net 4编译(但.Net 4.5作为VS2012的一部分安装(。

(1( 使用名为 form1 的窗体创建默认的 Windows 窗体应用程序。

(2(在表单上放置一个名为button1的按钮,并为其添加一个名为button1_Click()的处理程序。

(3(实施如下button1_Click()

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew(() =>
    {
        Thread.Sleep(1000);
        this.BeginInvoke(new Action(() =>
        {
            throw new InvalidOperationException("TEST");
        }));
    });
}

(4( 运行程序并单击按钮。一秒钟后,将按预期显示异常对话框。

(5(现在将this.BeginInvoke更改为this.Invoke

(6( 再次运行程序并单击按钮。现在,异常被默默忽略!

Invoke()BeginInvoke() 都在指定Control的 UI 线程上执行,所以我不明白为什么在一种情况下忽略异常,而在另一种情况下不忽略......

我猜这一定与这样一个事实有关,即如果它抛出异常,Control.Invoke()永远不会回来,但我的大脑在试图弄清楚为什么这意味着异常(显然(被完全忽略时感到痛苦。

如何使用 Control.Invoke() 抛出一个不会被忽略的异常

这是

设计使然,Invoke(( 处理异常的方式与 BeginInvoke(( 不同。 它将封送异常并重新引发它,以便您知道调用的方法失败。 这不适用于 BeginInvoke((,因为线程已经移动,因此它在 UI 线程上引发。 下一个问题是 Task 类会吞噬异常,因此您永远不会看到它。

你正在以艰难的方式做到这一点。如果您喜欢默认的异常对话框,那么只需使用它来引发异常:

this.BeginInvoke(new Action(() => {
    using (var dlg = new ThreadExceptionDialog(new InvalidOperationException("TEST"))) {
        dlg.ShowDialog();
        Environment.Exit(1);
    }