簡體   English   中英

使用多個表單的異常處理

[英]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()調用的堆棧上似乎是一個壞主意。 做這兩件事會更好:

  • 在Form2中的事件處理程序中捕獲並處理異常,這樣做是有意義的,並且當您可以使用異常執行有意義的操作時。
  • 為整個應用程序添加一個未處理的異常處理程序(`Application_ThreadException`)以捕獲任何未處理的異常。

更新:這是堆棧跟蹤。 調試版本:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM