BackgroundWorker中未处理的异常


70

我有一个小型的WinForms应用程序,该应用程序使用BackgroundWorker对象执行长时间运行的操作。

后台操作偶尔会引发异常,通常是当有人打开了正在重新创建的文件时。

不管代码是否从IDE运行,.NET都会弹出一个错误对话框,通知用户发生了未处理的异常。使用Release配置编译代码也不会对此进行更改。

根据MSDN

如果该操作引发您的代码无法处理的异常,则BackgroundWorker将捕获该异常并将其传递到RunWorkerCompleted事件处理程序,在该事件处理程序中将其公开为System.ComponentModel .. ::。RunWorkerCompletedEventArgs的Error属性。如果在Visual Studio调试器下运行,则调试器将在DoWork事件处理程序中引发未处理异常的点处中断。

我希望有时会抛出这些异常,并且希望在RunWorkerCompleted事件中而不是在DoWork中处理它们。我的代码正常运行,并且在RunWorkerCompleted事件中正确处理了错误,但是我终生无法弄清楚如何停止抱怨“未处理的异常”的.NET错误对话框。

BackgroundWorker是否应该自动捕获该错误?这不是MSDN文档说明的吗?我需要做什么做的通知.NET,这个错误被处理,同时仍允许例外propage到RunWorkerCompletedEventArgs的错误属性?

Answers:


133

您所描述的不是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好点。我的示例指出了处理错误的细节,但是如果从未在DoWork方法中命中异常,则我的代码实际上会导致另一个异常。
鲍比·坎农

我希望有所不同...。我也正在努力查看BGW类如何吞噬exp ... ...有时我的应用程序授予我访问Win7上用户区的权限,有时却没有。当我进一步调查时,我发现访问被拒绝是因为该文件夹不存在。我发现有时在我的应用程序中会引发错误,有时却不会。
IbrarMumtaz

@Ibrar,您是否始终在RunWorkerCompleted事件处理程序中检查e.Error?在执行任何操作之前,请确保已检查。所有可捕获的异常都将在此报告。
Judah Gabriel Himango 09年

ty进行回复。我遵循了您的示例并对其进行了修改...到目前为止一切顺利。还没结束,我在else语句中添加了一个引发自定义事件并将错误记录到我的logger类的语句。允许处理异常而不是未处理异常。希望这是我最后看到的。
IbrarMumtaz

4
@犹大:谢谢你。你节省了我很多时间。这闻起来很神奇,我真的希望.NET会抛出一个更复杂的异常(“当抛出异常时,可能无法访问结果属性BackgroundWorker DoWork”),而不是试图做正确的事情并使事情变得混乱。
Michael Petrotta 2010年

40

我将添加到MSDN文本中

如果该操作引发了代码无法处理的异常,则BackgroundWorker将捕获该异常并将其传递到RunWorkerCompleted事件处理程序,在该事件处理程序中将其公开为System.ComponentModel .. ::。RunWorkerCompletedEventArgs的Error属性。如果您在Visual Studio调试器下运行,则调试器将在DoWork事件处理程序中引发未处理异常的点处中断。

...并且调试器将报告异常,因为“ ~~异常未由用户代码处理”

解决方案:不要在调试器下运行,它会按预期运行:e.Error中捕获了异常。


5
感谢那。我真正想知道的是为什么它在地球上的行为如此。使调试RunWorkerCompleted事件的异常处理成为不可能。
yu_ominae 2013年

如果他们只是在文档中提到了调试器的话……那么是的。谢谢。
andersop 2014年

1
@yu_ominae:并非没有。每当调试器中断异常时,您实际上可以按F5(继续)让BackgroundWorker捕获异常,并且执行将按预期继续RunWorkerCompleted
2014年

马克-您确实应该考虑更新您的答案以纳入Anders评论。
DavidRR

2

这是一个古老的问题,但我在谷歌搜索时发现了同样的症状。发布此邮件以防其他人出于相同的原因找到它。

犹大的回答是正确的,但这并不是出现“用户代码中未处理的异常”对话框的唯一原因。如果后台线程的构造函数内部引发了异常,则该异常将立即导致对话框,并且不会传递给RunWorkerCompleted事件。如果将有问题的代码移到任何构造函数之外(移至任何其他方法),它将按预期工作。


我试图在构造函数中引发异常并RunWorkerCompleted捕获它。也许您是通过控制台应用程序尝试过的?根据此答案,控制台应用程序的行为有所不同。
ispiro 2014年

这是可能的。具体例外我是有这个问题,是给我“的类型初始为XYZ抛出异常”的错误,我不知道具体是什么情况下会产生等
丰富的

是旧线程,但记住了本文的其余部分-TypeInitializationException是我一直在想的那种,如果在静态构造函数中发生异常,则抛出该异常。
Rich

1

[编辑]

犹大很有意思。我的示例指出了处理错误的细节,但是如果从未在DoWork方法中命中异常,则我的代码实际上会导致另一个异常。由于我们专门展示了BackgroundWorker的错误处理功能,因此该示例是可以的。但是,如果您不检查错误参数是否为null,则可能是您的问题。

[/编辑]

我看不到相同的结果。您可以张贴一些代码吗?这是我的代码。

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位于BackgroundException.Form1.worker_DoWork(对象发送者,DoWorkEventArgs e)位于D:\ Workspaces \ Sandbox \ BackgroundException \ BackgroundException \ Form1.cs:System 43处System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e) .ComponentModel.BackgroundWorker.WorkerThreadStart(Object自变量)

一篇与您的问题相似的有趣文章。它有一节处理异常。

http://www.developerdotstar.com/community/node/671


0

我遇到了同样的问题,在经过谷歌搜索之后发现这个话题之前,我已经在应用犹大答案。

好吧,imo犹大的答案部分正确。我在这里找到了更好的答案

调试器可以很好地完成工作,如果您在“真实条件”下运行应用程序,则RunWorkerCompleted会按预期处理异常,并且应用程序行为也将符合预期。

我希望这个答案会有所帮助。


此较早的答案解决了Visual Studio调试器中发生的情况。
DavidRR
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.