2009-07-09 113 views
3

首先讓我說我已閱讀this useful article徹底,並使用CodeProject中的SafeThread類。無論使用Thread還是SafeThread,我都會得到相同的結果。防止未處理的異常對話框出現

我已將我的問題簡化爲由兩種形式組成的應用程序,每種形式都有一個按鈕。主程序顯示一個表單。當你點擊那個按鈕時,一個新的線程開始顯示第二個表單。當你點擊第二個表單上的按鈕時,它在內部只是「拋出新的異常()」

當我在VS2008下運行這個時,我看到「DoRun()中的異常」。

當我運行VS2008的外面,我得到一個對話框「發生在應用程序中未處理的異常。如果您單擊繼續,應用程序......」

我試圖在應用程序設置legacyUnhandledExceptionPolicy。配置爲1和0.

在VS2008下運行時,我需要做什麼來捕獲以第二種形式拋出的異常?

這裏是我的Program.cs

static class Program 
    { 
     [STAThread] 
     static void Main() 
     { 
      Application.ThreadException += new ThreadExceptionEventHandler (Application_ThreadException); 
      Application.SetUnhandledExceptionMode (UnhandledExceptionMode.CatchException); 
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      try 
      { 
       Application.Run(new Form1()); 
      } 
      catch(Exception ex) 
      { 
       MessageBox.Show("Main exception"); 
      }     
     } 

     static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
     { 
      MessageBox.Show("CurrentDomain_UnhandledException"); 
     } 

     static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 
     { 
      MessageBox.Show("Application_ThreadException"); 
     } 
    } 

這裏的Form1中:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     SafeThread t = new SafeThread(new SimpleDelegate(ThreadMain)); 
     try 
     { 
      t.ShouldReportThreadAbort = true; 
      t.ThreadException += new ThreadThrewExceptionHandler(t_ThreadException); 
      t.ThreadCompleted += new ThreadCompletedHandler(t_ThreadCompleted); 
      t.Start(); 
     } 
     catch(Exception ex) 
     { 
      MessageBox.Show(string.Format("Caught externally! {0}", ex.Message)); 

     } 
    } 

    void t_ThreadCompleted(SafeThread thrd, bool hadException, Exception ex) 
    { 
     MessageBox.Show("t_ThreadCompleted"); 
    } 

    void t_ThreadException(SafeThread thrd, Exception ex) 
    { 
     MessageBox.Show(string.Format("Caught in safe thread! {0}", ex.Message)); 
    } 

    void ThreadMain() 
    { 
     try 
     { 
      DoRun(); 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(string.Format("Caught! {0}", ex.Message)); 
     } 
    } 

    private void DoRun() 
    { 
     try 
     { 
      Form2 f = new Form2(); 
      f.Show(); 
      while (!f.IsClosed) 
      { 
       Thread.Sleep(1); 
       Application.DoEvents(); 
      } 
     } 
     catch(Exception ex) 
     { 
      MessageBox.Show("Exception in DoRun()"); 
     } 
    } 
} 

這裏是窗體2:

public partial class Form2 : Form 
{ 
    public bool IsClosed { get; private set; } 

    public Form2() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     throw new Exception("INTERNAL EXCEPTION"); 
    } 

    protected override void OnClosed(EventArgs e) 
    { 
     IsClosed = true; 
    } 
} 
+0

你想要什麼樣的行爲? – scwagner 2009-07-09 18:08:40

+0

異常中的堆棧跟蹤是什麼? – 2009-07-09 18:10:58

+0

我想讓我的一個異常處理程序捕獲form2中button1_Click中引發的異常;這種情況發生在VS2008但不被外部 這裏的堆棧跟蹤的頂部 在Trapper.Form2.button1_Click(對象發件人,EventArgs e)如C:\ codefarm \捕手\ Form2.cs:線在System.Windows 17 .Forms.Control.OnClick(EventArgs e) at System.Windows.Forms.Button.OnClick(EventArgs e) – rc1 2009-07-09 18:21:12

回答

2

1.)我會推薦使用BackgroundWorker而不是像這樣的單獨線程。您的工作人員將捕獲異常並將其作爲參數傳遞給完整處理程序。

2.)當顯示第二個表單時,我將使用ShowDialog()而不是Show(),這將阻止該方法調用的DoRun(),並且異常應該被您周圍的try/catch(或BackgroundWorker如果你正在使用它)。

我認爲問題來了,因爲你打電話給Show(),你基本上是調用這個調用到Invoker,最終在UI線程中排隊。所以當發生異常時,callstack沒有什麼可以抓到它。我相信調用ShowDialog()會解決這個問題(也允許你放棄討厭的循環)。

事情是這樣的:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     // NOTE: I forget the event/method names, these are probably a little wrong. 
     BackgroundWorker worker = new BackgroundWorker(); 
     worker.DoWork += (o, e) => 
     { 
      Form2 f = new Form2(); 
      e.Result = f.ShowDialog(); 
     }; 
     worker.DoWorkComplete += (o, e) => 
     { 
      if(e.Error != null) 
       MessageBox.Show(string.Format("Caught Error: {0}", ex.Message)); 

      // else success! 
      // use e.Result to figure out the dialog closed result. 
     }; 

     worker.DoWorkAsync(); 
    } 
} 

其實,現在我想想,這有點怪異的是從後臺線程打開一個對話框,但我認爲這仍然可以工作。

0

您是否嘗試過把一個try/catch塊在主方法?

+0

是的,它沒有什麼區別(更新代碼清單來反映這一點) – rc1 2009-07-09 18:23:29

0

該問題似乎是由按鈕事件處理程序引起的。如果您在DoRun()中拋出異常 - 可能在Form.Show()之後 - 無論您是否在Visual Studio中運行,都會捕獲異常。

有趣的是,行爲取決於調試器是否附加到進程,或不。從Visual Studio外部啓動並稍後連接調試器會阻止發送反饋消息框,分離會再次發生。從Visual Studio中的相同 - 「無需調試就開始」會導致發送反饋消息框。

因此,在按鈕事件處理程序發生異常之後,我迅速瀏覽了框架源代碼,並對其進行了粗略的查看 - 消息泵,控件以及其他許多代碼可能會在那裏執行很多操作。因爲WinForms只是本地控件的封裝,所以我認爲出於某種原因,異常不會返回到同一個點或線程,具體取決於是否連接了調試器 - 當異常跨線程或進程邊界傳遞時可能會出錯類似的東西。

+0

謝謝Daniel。這確實是一個棘手的問題。 – rc1 2009-07-09 19:34:27

1

而是這行:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 

你需要這樣的:

#if DEBUG 
     Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 
#else 
     Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); 
#endif 

這樣,當你在調試模式下運行Visual Studio中的程序,Visual Studio將捕獲異常時,他們發生,所以你可以在它們發生的地方進行調試。當您在發佈模式下運行程序時,異常將由Application.ThreadException的處理程序或AppDomain的處理程序捕獲。

這在我的程序中完美地工作。我厭倦了通過「在您的應用程序中發生未處理的異常...」框來獲取電子郵件,因此我實現了一個帶有文本框的通用表單,該文本框允許我轉儲用於調試問題的特定信息。

2

如果你真的想你的第二個形式中打開一個單獨的UI線程(而不是作爲ShowDialog())捕獲異常,並傳送給你的Application_ThreadException方法,您需要確保第二個線程也被設置爲CatchException您還需要在該線程上訂閱Application.ThreadException。這兩個都是線程特定的(有點古怪)。

你可以通過調用設置默認「未處理的異常模式」:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException, false); 

此設置應用程序範圍內的模式CatchException爲您創建的任何UI線程。 (但是,在Visual Studio調試器和其他一些情況下運行時,此調用將失敗。)或者,您的新UI線程可以使用通常的調用設置自己的模式(與傳遞此超載相同)。

無論哪種方式,新的UI線程還需要訂閱Application.ThreadException事件本身,因爲訂閱者存儲在[ThreadStatic]變量中。

Application.ThreadException += Program.Application_ThreadException; 

或者它也可以使用一個單獨的處理器,而不是路由到同一個,如果這是有幫助的。

我不知道這是如何使用SafeThread來完成它相交,但我想如果這是第二UI線程使用SafeThread那就沒有必要做正確。這很像你會在你的主UI線程上執行它。

另請參閱我的回答this question瞭解更多關於這些東西的怪癖。

相關問題