1

您好我正在使用 winform 并尝试使用 MessageBox 进行异常处理。这里奇怪的是,MessageBox 仅在主窗体(下面代码中的“Form1”)关闭后出现。

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 进行异常处理的更好方法是什么?


...所以我在 UI 线程中添加了一些 MessageBox-ing 代码,但问题仍然存在。

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(); }
    }
}
4

3 回答 3

2

实际上,我敢打赌 MessageBox 出现在主窗体的后面,而您在关闭它之前是看不到它的。

Form1让 UI 线程(创建和拥有的那个)执行 MessageBox-ing会好得多。您要么想要制作事件,要么在您的工作人员类中有一个错误回调委托。

但是,BackgroundWorker可能值得在这里查看而不是尝试自己动手。假设它是一个致命异常,您可以保存和检索错误状态,并且当线程完成时您会自动调用一个事件。

于 2012-09-10T08:13:51.907 回答
1

您确实应该锁定 doWork 方法,以便多个线程无法同时访问它,它们需要排队。

查看“加入线程”。想象一下,如果您同时遇到两个异常。你的应用程序会崩溃。锁定将重复复制的代码区域将为您的线程形成一个队列,以访问处理异常的代码区域。

于 2012-09-10T08:23:38.673 回答
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).

第一表示您希望捕获任何未处理的异常并自己处理它,而不是让 WinForms 基础结构处理它。请注意,从 .NET 4 开始,此设置不适用于破坏进程状态的异常(例如 OutOfMemory)。

如果您在 UI 线程上有未处理的异常,则第二行将触发您创建的名为 *App_UiThreadException* 的过程。这就是你的 MessageBox 代码应该去的地方。

如果您在非 UI 线程上有未处理的异常,最后一行将触发您创建的名为 *App_NonUiThreadException* 的过程。这就是你的 MessageBox 代码应该去的地方。

于 2012-09-10T11:21:26.203 回答