【问题标题】:Unhandled exceptions in BackgroundWorkerBackgroundWorker 中未处理的异常
【发布时间】:2010-11-05 21:05:54
【问题描述】:

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

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

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

根据MSDN

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

我希望偶尔会抛出这些异常,并希望在 RunWorkerCompleted 事件中而不是在 DoWork 中处理它们。我的代码工作正常,错误在 RunWorkerCompleted 事件中得到了正确处理,但我终其一生都无法弄清楚如何停止抱怨“未处理异常”发生的 .NET 错误对话框。

BackgroundWorker 不应该自动捕获该错误吗?这不是 MSDN 文档所说的吗?我需要做什么来通知 .NET 这个错误正在被处理,同时仍然允许异常传播到 RunWorkerCompletedEventArgs 的 Error 属性中?

【问题讨论】:

    标签: c# exception backgroundworker unhandled-exception


    【解决方案1】:

    您所描述的不是 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 的情况下处理它。比如下面的代码就是bad,bad,bad,会在运行时抛出异常:

    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.
        }
    }
    

    瞧,您不会收到运行时异常。

    【讨论】:

    • +1 好点。我的示例指出了处理错误的细节,但如果 DoWork 方法中从未遇到异常,我的代码实际上会导致另一个异常。
    • 我不敢苟同....我也很难看到 BGW 类是如何消耗 exps 的...因为有时我的应用程序允许我访问 Win7 上的用户区域,有时却没有吨。当我进一步调查时,我发现访问被拒绝是因为该文件夹不存在。我发现有时会在我的应用程序中引发错误,有时不会。
    • @Ibrar,您是否总是在 RunWorkerCompleted 事件处理程序中检查 e.Error ?确保在做任何事情之前检查一下。所有可捕获的异常都将在此处报告。
    • 回复。我按照您的示例进行了修改……到目前为止一切都很好。没有摔倒我在 else 语句中添加了引发自定义事件并将错误记录到我的记录器类。允许异常处理而不是未处理。希望这是我最后一次看到这个。
    • @Judah:谢谢。你为我节省了很多时间。这闻起来很神奇,我真的希望 .NET 会抛出一个更直接的异常(“BackgroundWorker DoWork 抛出异常时可能无法访问结果属性”),而不是尝试做正确的事情并且只是令人困惑的事情。
    【解决方案2】:

    我会添加到MSDN text

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

    ...并且调试器会将异常报告为“~Exception is unhandled by user code”

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

    【讨论】:

    • 谢谢。我真正想知道的是为什么它会这样。使得无法调试RunWorkerCompleted 事件的异常处理。
    • 如果他们刚刚在文档中添加了关于调试器的那一点...那么真的。谢谢。
    • @yu_ominae:并非不可能。每当调试器因异常中断时,您实际上可以按 F5(继续)让 BackgroundWorker 捕获异常,然后执行将按预期在RunWorkerCompleted 中继续。
    • 马克 - 你真的应该考虑更新你的答案以纳入安德斯评论。
    【解决方案3】:

    [编辑]

    犹大有一个很好的观点。我的示例指出了处理错误的细节,但如果 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:繁荣在 BackgroundException.Form1.worker_DoWork(对象 发件人,DoWorkEventArgs e) 在 D:\Workspaces\Sandbox\BackgroundException\BackgroundException\Form1.cs:line 43 在 System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs 吃 System.ComponentModel.BackgroundWorker.WorkerThreadStart(对象 论据)

    一篇与您的问题相似的有趣文章。它有一个关于处理异常的部分。

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 我尝试在构造函数中抛出异常,RunWorkerCompleted 捕获了它。也许您尝试过使用控制台应用程序?根据this answer,控制台应用程序的行为不同。
      • 有可能。我遇到这个问题的具体异常给了我“xyz 的类型初始化程序引发了异常”错误,我不确定什么具体情况会产生这种情况等。
      • 老线程,但记住了这个故事的其余部分 - TypeInitializationException 是我想到的,如果在 static 构造函数中发生异常,则会抛出它。跨度>
      【解决方案5】:

      我遇到了同样的问题,并且在经过一番谷歌搜索后找到这个主题之前,我已经在应用 Judah 的答案了。

      嗯,犹大的答案是部分正确的。我找到了更好的答案here

      调试器运行良好,如果您在“真实条件”下运行应用程序,RunWorkerCompleted 会按预期处理异常,应用程序行为也是预期的。

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

      【讨论】:

      猜你喜欢
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多