代码之家  ›  专栏  ›  技术社区  ›  chiccodoro

RunWorkerCompleted未在主UI线程中执行?

  •  2
  • chiccodoro  · 技术社区  · 14 年前

    我经历了一个奇怪的行为 BackgroundWorker RunWorkerCompleted

    在后台线程中发生的异常被传递给 RunWorker已完成 . 我做了一个 object temp = e.Result Application.Run(...) ,我用一个拦网拦住了它。

    在Visual Studio中运行应用程序时,此操作很好。但是,当我在Visual Studio外部执行exe文件时,不会处理异常并使应用程序崩溃。

    RunWorker已完成 事件是 在主UI线程中执行?

    这是一个极其简化的代码示例,它再现了错误:创建一个WinForms项目并添加:

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
    
        try
        {
            Form form = new Form();
    
            form.Load += delegate
            {
                BackgroundWorker bw = new BackgroundWorker();
                bw.DoWork += (sender, e) => 
                {
                    // do nothing
                };
                bw.RunWorkerCompleted += (sender, e) =>
                {
                    throw new Exception("Booo!");
                };
                bw.RunWorkerAsync();
            };
    
            Application.Run(form);
        }
        catch (Exception ex)
        {
            while (ex is TargetInvocationException && ex.InnerException != null)
                ex = ex.InnerException;
    
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    

    然后尝试:

    1. 在Visual Studio外部运行编译的可执行文件。您将收到一条.NET framework“未处理的异常”消息。

    有趣的是,如果我在2中单击“continue”,应用程序将继续运行,并显示表单并接受用户输入。这一定意味着一个后台线程崩溃了,而不是主UI线程。主线仍然是活的。

    后台工作人员 . 在原始代码中,它将进度报告回更新splash表单的主线程。如果在启动期间发生异常,我想捕获它。在某些情况下,它们是具有特定含义的“业务例外”,例如“您无权使用此应用程序”。在这些情况下,我会在让应用程序死掉之前显示一个友好的消息框。我还有一个 finally 阻止以清理资源。我不确定.NET框架的“未处理异常”对话框是否在终止应用程序之前执行finally块。)

    1 回复  |  直到 8 年前
        1
  •  4
  •   Hans Passant    14 年前

    Application.Run()中的消息循环代码周围有一个try/catch块,用于捕获事件处理程序引发的未经处理的异常。catch子句引发Application.ThreadException事件。该事件有一个默认处理程序,它显示一个ThreadExceptionDialog。为用户提供忽略错误或中止程序的选项。

    使用调试器运行程序时,此catch子句被禁用。这允许您轻松调试异常。禁用它后,CLR将在Main()方法中找到catch子句。要禁用此行为,请将这行代码添加到Main()方法的顶部:

      Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
    

    它现在的行为与调试器中的相同。这里更好的捕鼠器是为AppDomain.CurrentDomain.UnhandledException实现一个事件处理程序。它捕获所有未处理的异常,包括在工作线程中引发的异常。并且允许您调试未处理的异常,调试器在throw语句处停止。

    推荐文章