2012-09-10 53 views
1

嗨我正在使用winform並嘗試使用MessageBox進行異常處理。 這裏奇怪的是,MessageBox只有在主窗體(下面的代碼中的「Form1」)被關閉後纔會出現。使用MessageBox在多線程應用程序中顯示異常信息

public class Worker { 
    /* edited (see below) 
    public void doWork() { 
     try { 
      // do something 
      client.Connect(serverAddress); 
      stream = client.GetStream(); 
     } 
     catch(Exception e) { 
      MessageBox.Show(e.ToString(), 
       "This will not show up until Form1 is closed"); 
     } 
    } 
    */ 
} 

public class Form1 { 
    /* edited (see below) 
    * public void threadProc() { 
    * Worker worker = new Worker(); 
    * worker.doWork(); 
    * } 
    */ 
    void button_Click(object sender, EventArgs e) { 
     // create a thread that will end up throwing an exception 
     Thread thread = new Thread(threadProc); 
     thread.Start(); 
    } 
} 

什麼可能是一個更好的方式來使用MessageBox進行異常處理?


...所以我加了一些代碼的MessageBox-ING在UI線程,但問題仍然存在。

public class WorkExceptionArgs : EventArgs { 
    public Exception e; 
    public WorkExceptionArgs (Exception e) { this.e = e; } 
} 
public partial class Worker1 { // renamed (Worker->Worker1) 
    /* (edited) Now Worker1 doesn't trigger any event (see below) 
     public event EventHandler<WorkExceptionArgs> workException; 
    */ 
    public void doWork() { 
     try { 
      // do something 
      client.Connect(serverAddress); 
      stream = client.GetStream(); 
     } 
     catch(Exception e) { 
      /* (edited) suppose Worker1 never throws any exception (see below) 
      * // trigger event that will cause MessageBox-ing by UI thread 
      * workException(this, new WorkExceptionArgs(e)); 
      */ 
     } 
    } 
} 
public partial class Form1 { 
    public void threadProc() { 
     Worker1 worker1 = new Worker(); 
     /* (edited) Now Worker1 never throws any exception 
     * worker.workException += new EventHandler<WorkException>(worker_WorkException); 
     */ 
     worker1.doWork(); 
     // (added) After doWork() is done, Form1 creates Worker2 
     Worker2 w2 = new Worker2(this, this.form2); 
     w2.workException += new EventHandlerArgs<WorkExceptionArgs>(form2.worker2_WorkException); 
     w2.doSomeOtherWork(); 
    } 
    /* public void worker_WorkException(object sender, WorkExceptionArgs eArg) { 
    * MessageBox.Show(eArg.e.ToString(), "Still not showing"); 
    * } */ 
    Form2 form2 = new Form2(); // (added) At first form2 is hidden (see below) 
} 

其實已經出現了另一種形式和另一名工人。一旦Worker(Worker1)連接到服務器,Form1隱藏(.Hide()),Form2顯示(.Show()),並且Worker2開始使用連接Worker1創建。

public class Worker2 { 
    Worker2(Worker1 w1, Form2 frm2) { this.w1=w1; this.frm2=frm2; } 
    public Worker1 w1; 
    public Form2 frm2; 
    public event EventHandler<WorkExceptionArgs> workException; 
    public void doSomeOtherWork() { // do some other, using data in Worker 1. 
     try { // This will throw an exception 
      BinaryFormatter formatter = new BinaryFormatter(); 
      MyObj mo = (MyObj)formatter.Deserialize(w1.getStream()); 
     } 
     catch(Exception e) { 
      workException(this, new WorkExceptionArgs(e)); 
     } 
    }    
} 

public class Form2 { 
    public Form2(Form1 frm1) { // to switch from frm1 to frm2 
     InitializeComponent(); 
     this.frm1 = frm1; 
    } 
    public Frm1 frm1 {get;set;} 
    public void worker2_WorkException(object sender, WorkExceptionArgs ea) { 
     MessageBox.Show(this, ea.e.ToString(), "SHOWS ONLY IF FORM2 IS CLOSED"); 
    } 

}  

public partial class Form1 { 
    delegate void switchWindow_Callback(); 
    public void switchWindow() { this.Hide(); form2.Show(); } 
    public void switchWindowCb(object sender, EventArgs e) { 
     if(this.InvokeRequired) { 
      SwitchWindow_Callback hcb = new SwitchWindow_Callback(switchWindow); 
      this.Invoke(hcb, new object[] {}); 
     } 
     else { this.switchWindow(); } 
    } 
} 
+0

你是否在調試模式下逐步完成了代碼? – Derek

回答

2

其實我敢打賭,在MessageBox正在出現背後的主要形式,而你只是沒有看到它,直到你關閉它。

讓UI線程(創建並擁有Form1的那個線程)執行MessageBox-ing會好得多。您可能想要創建事件,或者您的工作者類中有錯誤的回調委託。

但是,BackgroundWorker可能值得在這裏查看,而不是試圖推出自己的。假設這是一個致命的異常,您可以保存並檢索錯誤狀態,並在線程完成時自動調用一個事件。

+0

事件是否可以例外? –

+0

@JeffreyGoines爲什麼不呢?您的MessageBox代碼在異常時正常工作。 –

1

你應該真的鎖定doWork方法,以便多個線程不能同時訪問它,他們需要排隊。

看着「加入線程」。想象一下,如果你同時得到兩個異常。你的應用會崩潰。鎖定將被重複複製的代碼區域將形成一個隊列,讓您的線程訪問處理異常的代碼區域。

1

正如lc所述,很可能您的消息框出現在主窗體後面,因此只有在主窗體關閉時才能看到它。

我用它來對付在Windows未處理異常模型窗體應用程序是這樣的:

// Switch-off the Windows Forms default handler for unhandled exceptions. 
// NB From .NET 4 upwards, this won't work if the process state is corrupted. 
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 

// Setup event handler to intercept an unhandled exception on a UI thread . 
// NB The exception will still terminate the application. 
// But you can show a MessageBox in the event handler and log the exception. 
Application.ThreadException += 
    new ThreadExceptionEventHandler(App_UiThreadException); 

// Setup event handler to intercept an unhandled exception on a non-UI thread. 
AppDomain.CurrentDomain.UnhandledException += new 
    UnhandledExceptionEventHandler(App_NonUiThreadException); 

// Run the application (open main form etc). 

first line說,你想捕捉任何未處理的異常,並用它自己處理,而不是讓的WinForms基礎設施處理它。請注意,從.NET 4開始,此設置對於破壞進程狀態的異常(例如OutOfMemory)不起作用。

如果您在UI線程上有未處理的異常,則第二行將觸發您創建的名爲* App_UiThreadException *的過程。這就是你的MesssageBox代碼應該去的地方。

如果您在非UI線程上有未處理的異常,最後一行將觸發您創建的名爲* App_NonUiThreadException *的過程。這就是你的MesssageBox代碼應該去的地方。

相關問題