如何使用 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()
永远不会回来,但我的大脑在试图弄清楚为什么这意味着异常(显然(被完全忽略时感到痛苦。
设计使然,Invoke(( 处理异常的方式与 BeginInvoke(( 不同。 它将封送异常并重新引发它,以便您知道调用的方法失败。 这不适用于 BeginInvoke((,因为线程已经移动,因此它在 UI 线程上引发。 下一个问题是 Task 类会吞噬异常,因此您永远不会看到它。
你正在以艰难的方式做到这一点。如果您喜欢默认的异常对话框,那么只需使用它来引发异常:
this.BeginInvoke(new Action(() => {
using (var dlg = new ThreadExceptionDialog(new InvalidOperationException("TEST"))) {
dlg.ShowDialog();
Environment.Exit(1);
}