【发布时间】:2017-03-11 04:29:21
【问题描述】:
我无法捕获从延续任务引发的未处理异常。
为了演示这个问题,让我向您展示一些有效的代码。此代码来自一个基本的 Windows 窗体应用程序。
首先,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()
);
}
【问题讨论】:
-
我不知道抛出的异常是否是非 CLS 投诉,但如果是,您应该只使用 catch {} 捕获它。 msdn.microsoft.com/en-us/bb264489.aspx
-
@mybirthname 不,这是符合 CLS 的基本
InvalidOperationException。请注意,当未从后台任务中抛出异常时,该异常会被正确捕获。 -
@NiyokoYuliawan 这可能有效,但不幸的是(根据问题上的
.net-4.0标签)我无法使用它。 -
是的,抱歉,我没有看到你的标签。
-
@NiyokoYuliawan 这是一个好主意,事实上,如果你可以使用它,它确实有效(可惜我不能!) - 我会在这个问题中添加一些内容!跨度>
标签: c# winforms exception .net-4.0 task