2010-10-22 171 views
2

RunWorkerCompleted事件中重新拋出異常時,我遇到了BackgroundWorker或.NET框架的奇怪行爲。RunWorkerCompleted未在主UI線程中執行?

發生在後臺線程中的異常傳遞給RunWorkerCompleted。在那裏我做了一個object temp = e.Result重新拋出異常。因爲這應該發生在主UI線程中,所以我期望異常被傳播到Application.Run(...),我用try-catch塊包圍它。

在Visual Studio中運行應用程序時,此工作正常。但是,當我在Visual Studio之外執行exe文件時,異常不會被處理並使應用程序崩潰。

難道RunWorkerCompleted事件是而不是在主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中運行它。在應用程序退出之前,您會收到一個友好的消息框。
  2. 在Visual Studio外運行編譯的可執行文件。你會得到一個.NET框架「未處理的異常」消息。

有趣的部分是,如果我在2中點擊「繼續」,應用程序仍然存在,並顯示窗體並接受用戶輸入。這意味着不是主UI線程,後臺線程崩潰了。主線程仍然活着。 (爲什麼我想要做所有這些事情?我顯示一個啓動畫面,並且一些應用程序啓動工作在後臺完成時,啓動畫面通過標籤和進度條顯示進度。必須創建並顯示啓動畫面,然後獲取主UI線程以啓動BackgroundWorker。在原始代碼中,它將報告進度返回到更新啓動窗體的主線程。如果在啓動期間發生異常,我想要捕獲它。在某些情況下,它們是「業務例外」,具有特定含義,例如「您無權使用此應用程序」。在這些情況下,我會在應用程序死前顯示一個友好的消息框。此外,我有一個finally塊清理資源我不確定.NET框架「未處理的異常」對話框是否執行finally b在殺死應用程序之前鎖定)。

+0

你能制定一個問題嗎? – 2010-10-22 14:49:49

+0

@Pieter:我做了,它是粗體。 – chiccodoro 2010-10-22 14:50:32

回答

4

Application.Run()中的消息循環代碼周圍有一個try/catch塊,用於捕獲由事件處理程序引發的未處理的異常。 catch子句引發了Application.ThreadException事件。該事件有一個默認處理程序,它顯示一個ThreadExceptionDialog。給予用戶忽略錯誤或中止程序的選項。

當您使用調試器運行程序時,此catch子句被禁用。這使您可以輕鬆調試異常。在禁用它的情況下,CLR將在您的Main()方法中找到catch子句。要禁用此行爲,這行代碼添加到您的Main()方法的頂部:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); 

現在的行爲同樣的方式在調試器中。這裏更好的捕鼠器是爲AppDomain.CurrentDomain.UnhandledException實現事件處理程序。這會捕獲所有未處理的異常,包括在工作線程中引發的異常。並且讓你調試未處理的異常,調試器停止在throw語句中。

+0

謝謝你這個富有洞察力的答案!我是否知道這是一種通常處理我的代碼中的任何事件處理程序中發生的所有異常的方法?特別是處理按鈕點擊事件,菜單點擊事件等的異常?到目前爲止,我用try-catch塊包圍了每一個事件處理程序。這會不必要? – chiccodoro 2010-10-22 15:11:40

+0

'SetUnhandledExceptionMode'做到了。對於事件訂閱方法,我將不得不多閱讀一些內容才能理解如何使用它... – chiccodoro 2010-10-22 15:18:41

相關問題