【问题标题】:How can I use Control.Invoke() to throw an exception that won't be ignored?如何使用 Control.Invoke() 抛出不会被忽略的异常?
【发布时间】:2013-03-20 05:47:14
【问题描述】:

动机

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

但是,如果我使用 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() 都在 UI 线程上为指定的 Control 执行,所以我不明白为什么在一种情况下会忽略异常,而在另一种情况下不会忽略异常...

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

【问题讨论】:

标签: c# multithreading winforms exception


【解决方案1】:

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

你这样做很困难。如果您喜欢默认的异常对话框,那么只需使用它并引发异常即可:

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

【讨论】:

  • 谢谢,这是有道理的。我不想使用默认的异常对话框。我的实际处理是在类库中的用户控件中,所以我不知道如何处理异常的上下文;所以我只需要重新抛出它。
  • 它不会被处理,没有运行在 UI 线程上的代码会捕获它。超出显示 ThreadExceptionDialog 的默认异常处理程序。从无处注入异常的库不是可用的库。至少引发一个事件,以便客户端代码可以处理它。
  • 别担心 - 它正在处理中,并且已经在工作。主程序使用自定义异常对话框处理所有异常,它订阅Application.ThreadException、AppDomain.CurrentDomain.UnhandledException,当然还有一个围绕Application.Run() 调用的try/catch。 Task 代码已从以前的 Threads 转换而来,这就是异常处理的差异所在。异常本身被传递给从外部程序一路传入的委托,因此它不是外部异常。
  • 嗯,这只是表示它正在被报告。处理异常需要做更多的工作。不要忘记 Environment.Exit() 调用。
  • 是的,刚刚被举报。如果遇到意外异常,我们将关闭程序。但是我确实省略了异常不是从类库中抛出的重要信息,而是使用委托调用最外层应用程序中的方法(带有异常类型的参数),它是 那 实际上会重新抛出异常-因此它由知道会发生什么的上下文处理。 (我之前不应该说“实际处理在用户控件中”;这完全是误导——我的意思是我可以依赖外部处理)
猜你喜欢
  • 2013-10-02
  • 1970-01-01
  • 1970-01-01
  • 2022-06-29
  • 1970-01-01
  • 2011-05-28
  • 1970-01-01
  • 2011-09-12
  • 1970-01-01
相关资源
最近更新 更多