[英]Exception handling with multiple forms
我正在看到不同的行為,在我調試時遇到或未捕獲異常,而不是在運行已編譯的.exe時。 我有兩種形式(Form1和Form2)。 Form1上有一個按鈕,它在Form2上實例化並調用ShowDialog。 Form2上有一個按鈕,故意產生除零誤差。 當我調試時,Form1中的catch塊被命中。 當我運行編譯的.exe時,它沒有被命中,而是我得到一個消息框,指出“你的應用程序中發生了未處理的異常。如果你點擊繼續,應用程序將忽略此錯誤並嘗試繼續。如果你單擊退出,應用程序將立即關閉...嘗試除以零“。 我的問題是為什么在調試時和運行.exe時會出現不同的行為? 如果這是預期的行為,那么是否有必要在每個事件處理程序中放置try / catch塊? 這似乎有點瘋狂,不是嗎?
這是Form1的代碼。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
Form2 f2 = new Form2();
f2.ShowDialog();
}
catch(Exception eX)
{
MessageBox.Show( eX.ToString()); //This line hit when debugging only
}
}
}
這是Form2的代碼:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int x = 0;
int y = 7 / x;
}
}
是的,這是設計使然與Windows窗體的工作方式密切相關。 在Winforms應用程序中,代碼運行以響應Windows發布到活動窗口的消息。 每個本機Windows應用程序都包含一個消息循環來檢測這些消息。 Winforms管道確保您的一個事件處理程序運行響應; button1_在你的示例代碼中單擊。
大多數Winforms控件都實現了自己的事件處理程序。 例如,PictureBox有一個Paint事件處理程序,可以確保將Image映射到屏幕。 這一切都是自動完成的,您不必自己編寫任何代碼來完成這項工作。
但是,當此代碼拋出異常時會出現問題,因為沒有涉及到您自己的代碼,所以無法捕獲這樣的異常。 換句話說,沒有地方可以注入自己的try塊。 您自己的程序代碼的最后一點是啟動消息循環的代碼。 Application.Run()方法調用,通常在Program.cs中。 或者,如果顯示對話框,則調用Form.ShowDialog()。 這些方法中的任何一個都會啟動消息循環。 在Application.Run()調用周圍放置一個try塊是沒用的,應用程序將在捕獲異常后終止。
為解決此問題,Winforms消息循環代碼包含調度事件的代碼周圍的try塊。 它的catch子句顯示您提到的對話框,它由ThreadExceptionDialog類實現。
深入探討您的問題:這個catch子句確實妨礙了在調試時解決代碼問題。 當沒有處理異常的catch塊時,調試器將僅在異常時停止。 但是當你的代碼拋出異常時,你會想要在調試時知道它。 前面提到的消息循環中的代碼知道是否附加了調試器。 如果是,則調度沒有try / catch塊的事件。 現在,當您的代碼拋出異常時,沒有catch子句來處理它,調試器將停止程序,讓您有機會找出問題所在。
也許您現在可以看到為什么您的程序的行為方式。 在調試時,消息循環中的catch子句被禁用,使Form1代碼中的catch子句有機會捕獲異常。 如果不這樣做,消息循環catch子句處理異常(通過顯示對話框)並防止異常展開到Form1代碼。
您可以通過調用Application.SetUnhandledExceptionMode()方法來阻止消息循環catch子句的使用,並傳遞UnhandledExceptionMode.ThrowException。 在Application.Run()調用之前,在Main()方法中執行此操作。 現在你的程序將以相同的方式運行。
這通常不是一個壞主意,讓用戶在異常對話框中選擇繼續是一個值得懷疑的功能。 在這種情況下,請為AppDomain.UnhandledException事件實現事件處理程序,以便至少對用戶進行一些診斷。
我得到了和你一樣的行為。 我不知道為什么會發生這種情況,但是假設從表單中的事件生成的異常將出現在ShowDialog()調用的堆棧上似乎是一個壞主意。 做這兩件事會更好:
更新:這是堆棧跟蹤。 調試版本:
System.DivideByZeroException: Attempted to divide by zero.
at WindowsFormsApplication1.Form2.button1_Click(Object sender, EventArgs e) in ...\WindowsFormsApplication1\Form2.cs:line 27
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.RunDialog(Form form)
at System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
at System.Windows.Forms.Form.ShowDialog()
at WindowsFormsApplication1.Form1.button1_Click(Object sender, EventArgs e) in ...\WindowsFormsApplication1\Form1.cs:line 45
發布:
System.DivideByZeroException: Attempted to divide by zero.
at WindowsFormsApplication1.Form2.button1_Click(Object sender, EventArgs e) in ...\WindowsFormsApplication1\Form2.cs:line 27
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
請注意, System.Windows.Forms.Form.ShowDialog()
不在發布模式下的堆棧跟蹤中,這就是為什么try {} catch {}
什么也不做。 另外值得注意的是,在調試的情況下,它使用的是NativeWindow.DebuggableCallback
,它可能是為了通過不破壞堆棧來幫助調試,而在Release模式下則使用NativeWindow.Callback
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.