2010-06-09 183 views
9

我使用Application.ThreadException事件來處理和記錄我的winforms應用程序中的意外的異常。非常奇怪的Application.ThreadException行爲

現在,在我的應用程序的某個地方,我有以下代碼(或等價的東西相當,但這種虛擬的代碼足以重現我的問題):

  try 
      { 
       throw new NullReferenceException("test"); 
      } 
      catch (Exception ex) 
      { 
       throw new Exception("test2", ex); 
      } 

我清楚地期待我Application_ThreadException處理程序通過「test2」異常,但情況並非總是如此。通常,如果另一個線程將我的代碼編組到UI中,我的處理程序會收到「測試」異常,就好像我沒有抓到「測試」一樣。

這裏是一個簡短的例子,再現這種行爲。我省略了設計者的代碼。

 static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); 
     //Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); // has no impact in this scenario, can be commented. 

     AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     Application.Run(new Form1()); 
    } 

     static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 
    { 
     //this handler is never called 
    } 

    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) 
    { 
     Console.WriteLine(e.Exception.Message); 
    } 
} 

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     button1.Click+=new EventHandler(button1_Click); 
    } 

    protected override void OnLoad(EventArgs e) { 
    System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx)); 
    t.Start(); 
    } 


    private void button1_Click(object sender, EventArgs e) 
    { 
     try 
     { 
      throw new NullReferenceException("test"); 
     } 
     catch (Exception ex) 
     { 
      throw new Exception("test2", ex); 
     } 
    } 

    void ThrowEx() 
    { 
     this.BeginInvoke(new EventHandler(button1_Click)); 
    } 
} 

我的計算機上運行此程序的輸出是:

test 
... here I click button1 
test2 

我轉載此.NET的2.0,3.5和4.0。有人有合理的解釋嗎?

回答

1

例外#1:InvokeBeginInvoke在創建窗口句柄之前無法在控件上調用。

所以,不要試圖從構造函數調用。做到在OnLoad()

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     this.Load += new EventHandler(Form1_Load); 
     button1.Click += new EventHandler(button1_Click); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx)); 
     t.Start(); 
    } 

    ... 
} 
+0

更改了答案。 – 2010-06-09 15:35:01

+0

你的建議很有道理,但這並不能改變這種奇怪的行爲。 – Brann 2010-06-09 15:37:36

+0

@Brann,你是對的。奇怪的行爲依然存在。但是,我已經通過在按鈕點擊處理程序中不執行catch(Exception ex)來糾正它,而只是「catch」。很有意思。不會拋出Exception和Exception的異常。 – 2010-06-09 15:40:43

0

你必須調用

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

首先在您的Main()方法中。

+0

我試過了,沒有成功。 – Brann 2010-06-09 15:41:57

7

在代碼中存在一個錯誤,使得很難調試發生了什麼:在表單的Handle創建之前啓動線程。這將使BeginInvoke失敗。修復:

protected override void OnLoad(EventArgs e) { 
     System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx)); 
     t.Start(); 
    } 

Anyhoo,這是設計的行爲。在運行的BeginInvoke目標Windows窗體的代碼如下所示:

try 
    { 
     this.InvokeMarshaledCallback(tme); 
    } 
    catch (Exception exception) 
    { 
     tme.exception = exception.GetBaseException(); 
    } 
    ... 
     if ((!NativeWindow.WndProcShouldBeDebuggable && (tme.exception != null)) && !tme.synchronous) 
     { 
      Application.OnThreadException(tme.exception); 
     } 

它是exception.GetBaseException()調用搞砸了你的異常信息。爲什麼Windows Forms設計師選擇這樣做對我來說不是很清楚,對Reference Source中的代碼沒有評論。我只能猜測,如果沒有它,異常將更加難以調試,以防由Windows窗體管道代碼而不是應用程序代碼引發。不是一個很好的解釋。

他們已經表示,他們won't fix it,也許你可以添加你的投票。不要期待你的希望。

解決方法是不設置InnerException。當然不是一個好選擇。

+0

正是我在找的答案;謝謝。 – Brann 2010-06-09 15:54:27

+1

'GetBaseException'調用旨在從'Invoke'解包'TargetInvocationException'。 – SLaks 2010-06-09 15:59:36

+3

不是這樣。 Control.BeginInvoke()與Delegate.BeginInvoke()完全不同。例如,它不會將異常封送回調用方。 – 2010-06-09 16:04:29