我有一个小型WinForms应用程序,它利用BackgroundWorker对象执行长时间运行。
后台操作会抛出偶然的异常,通常是有人打开了正在重新创建的文件。
无论代码是否从IDE运行,.NET都会弹出一个错误对话框,通知用户发生了未处理的异常。使用Release配置编译代码也不会改变它。
根据MSDN:
如果操作引发了代码无法处理的异常,则BackgroundWorker会捕获异常并将其传递给RunWorkerCompleted事件处理程序,并将其作为System.ComponentModel .. ::。RunWorkerCompletedEventArgs的Error属性公开。如果您在Visual Studio调试器下运行,则调试器将在DoWork事件处理程序中出现未处理的异常的位置中断。
我希望有时会抛出这些异常,并希望在RunWorkerCompleted事件中而不是在DoWork中处理它们。我的代码正常工作,并且在RunWorkerCompleted事件中正确处理错误,但我不能在我的生活中弄清楚如何停止.NET错误对话框抱怨发生“未处理的异常”。
BackgroundWorker不应该自动捕获该错误吗?这不是MSDN文档中的内容吗?我需要做什么来告知.NET这个错误是正在处理,同时仍然允许异常进入RunWorkerCompletedEventArgs的Error属性?
答案 0 :(得分:118)
您所描述的不是BackgroundWorker的已定义行为。你怀疑,你做错了什么。
以下是一个小样本,证明BackgroundWorker在 DoWork 中吃了异常,并在 RunWorkerCompleted 中为您提供了这些:
var worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
throw new InvalidOperationException("oh shiznit!");
};
worker.RunWorkerCompleted += (sender, e) =>
{
if(e.Error != null)
{
MessageBox.Show("There was an error! " + e.Error.ToString());
}
};
worker.RunWorkerAsync();
我的通灵调试技巧向我揭示了你的问题:你正在RunWorkerCompleted处理程序中访问e.Result - 如果有e.Error,你必须处理它而不访问e.Result。例如,以下代码是坏的,坏的,坏的,并且会在运行时抛出异常:
var worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
throw new InvalidOperationException("oh shiznit!");
};
worker.RunWorkerCompleted += (sender, e) =>
{
// OH NOOOOOOOES! Runtime exception, you can't access e.Result if there's an
// error. You can check for errors using e.Error.
var result = e.Result;
};
worker.RunWorkerAsync();
这是RunWorkerCompleted事件处理程序的正确实现:
private void RunWorkerCompletedHandler(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
DoSomethingWith(e.Result); // Access e.Result only if no error occurred.
}
}
VOILA,您不会收到运行时异常。
答案 1 :(得分:36)
我会添加到MSDN text:
如果操作引发了代码无法处理的异常,则BackgroundWorker会捕获异常并将其传递给RunWorkerCompleted事件处理程序,并将其作为System.ComponentModel .. ::。RunWorkerCompletedEventArgs的Error属性公开。 如果您在Visual Studio调试器下运行,调试器将在DoWork事件处理程序中出现未处理的异常的位置中断。
...并且调试器会将异常报告为“〜用户代码未处理异常”
解决方案:不要在调试器下运行并且它按预期工作:e.Error中捕获异常。
答案 2 :(得分:2)
这是一个老问题,但我在谷歌搜索时发现了相同的症状。发布此信息以防其他人因同样的原因找到它。
犹大的回答是正确的,但这并不是出现“用户代码中未处理的异常”对话框的唯一原因。如果在后台线程上从构造函数中抛出异常,则该异常将立即导致该对话,并且不会传递给RunWorkerCompleted事件。如果您将违规代码移到任何构造函数之外(对任何其他方法),它将按预期工作。
答案 3 :(得分:1)
[编辑]
犹大有一个很好的观点。我的例子指出了处理错误的细节,但是如果在DoWork方法中没有遇到异常,我的代码实际上会引起另一个异常。这个例子没问题,因为我们专门展示了BackgroundWorker的错误处理功能。但是,如果您没有针对null检查error参数,则可能是您的问题。
[/编辑]
我没有看到相同的结果。你能发一点代码吗?这是我的代码。
private void Form1_Load(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Will cause another exception if an exception didn't occur.
// We should be checking to see if e.Error is not "null".
textBox1.Text = "Error? " + e.Error;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10; i++)
{
if (i < 5)
{
Thread.Sleep(100);
}
else
{
throw new Exception("BOOM");
}
}
}
节目输出:
错误? System.Exception:BOOM at BackgroundException.Form1.worker_DoWork(对象 发件人,DoWorkEventArgs e)in d:\工作区\沙盒\ BackgroundException \ BackgroundException \ Form1.cs中:行 43点 System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs 吃 System.ComponentModel.BackgroundWorker.WorkerThreadStart(对象 参数)
一篇与您的问题类似的有趣文章。它有一个关于处理例外的部分。
答案 4 :(得分:0)
我遇到了同样的问题,在我用谷歌谷歌搜索之后发现这个话题之前我已经应用了犹大的答案。
嗯,犹大的回答是部分正确的。我找到了更好的答案here
调试器正在顺利完成工作,如果您在&#34;真实条件&#34;中运行应用程序,RunWorkerCompleted将按预期处理异常,并且应用程序行为也是预期的。
我希望这个答案有所帮助。