我无法捕获从续体任务抛出的未处理异常。
为了说明这个问题,让我展示一些可以工作的代码。这段代码来自一个基本的Windows Forms应用程序。
首先是program.cs:
这将订阅所有可用的异常处理程序。(实际上只会引发
现在这里有一个表单,可以导致正确显示未处理异常的消息:
当你运行这个程序时,一秒后会出现一个消息框,显示异常信息。
正如你所看到的,我正在编写一个“请稍候”样式的窗体,它会在后台完成一些工作,然后自动关闭。
因此,我在
为了说明这个问题,让我展示一些可以工作的代码。这段代码来自一个基本的Windows Forms应用程序。
首先是program.cs:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += (sender, args) =>
{
MessageBox.Show(args.Exception.Message, "ThreadException");
};
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
MessageBox.Show(args.ExceptionObject.ToString(), "UnhandledException");
};
try
{
Application.Run(new Form1());
}
catch (Exception exception)
{
MessageBox.Show(exception.Message, "Application.Run() exception");
}
}
}
}
这将订阅所有可用的异常处理程序。(实际上只会引发
Application.ThreadException
,但我想确保消除了所有其他可能性。)现在这里有一个表单,可以导致正确显示未处理异常的消息:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
doWork();
this.Close();
}
void doWork()
{
Thread.Sleep(1000); // Simulate work.
throw new InvalidOperationException("TEST");
}
}
}
当你运行这个程序时,一秒后会出现一个消息框,显示异常信息。
正如你所看到的,我正在编写一个“请稍候”样式的窗体,它会在后台完成一些工作,然后自动关闭。
因此,我在
OnShown()
中添加了一个后台任务,代码如下:protected override void OnShown(EventArgs e)
{
base.OnShown(e);
Task.Factory.StartNew(doWork).ContinueWith
(
antecedent =>
{
if (antecedent.Exception != null)
throw antecedent.Exception;
this.Close();
},
TaskScheduler.FromCurrentSynchronizationContext()
);
}
我原本认为继续运行会在表单的上下文中进行,并且异常会被我在program.cs
中订阅的一个未处理异常事件所捕获。
不幸的是,什么都没有被捕获,表单保持打开状态,没有任何表示出现了问题。
有没有人知道我应该如何做才能使工作任务中抛出的异常(如果未明确地捕获和处理)被外部未处理的异常事件所捕获?
[编辑]
Niyoko Yuliawan建议使用await
。不幸的是,我不能使用它,因为这个项目被困在古老的.NET 4.0上。然而,我可以确认,如果我能使用它,使用await
就会解决这个问题!
为了完整起见,这里是我可以使用的更简单、更易读的解决方案(如果我使用.NET 4.5或更高版本的话):
protected override async void OnShown(EventArgs e)
{
base.OnShown(e);
await Task.Factory.StartNew(doWork);
this.Close();
}
[编辑2]
raidensan也提供了一个看起来有用的答案,但不幸的是它也不起作用。我认为它只是稍微改变了问题的位置。下面的代码也无法导致异常消息被显示-即使在调试器下运行并在antecedent => {throw antecedent.Exception; }
这一行上设置断点,也可以看到该行正在被执行。
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
var task = Task.Factory.StartNew(doWork);
task.ContinueWith
(
antecedent => { this.Close(); },
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext()
);
task.ContinueWith
(
antecedent => { throw antecedent.Exception; },
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.FromCurrentSynchronizationContext()
);
}
InvalidOperationException
,它符合CLS。请注意,当不是从后台任务抛出时,异常会被正确捕获。 - Matthew WatsonOnShown()
中等待,因此我使用了一个 continuation 来等待doWork()
完成(这相当于等待任务完成)。 - Matthew Watson