【问题标题】:Using MessageBox to show exception information in multithreaded application使用 MessageBox 在多线程应用程序中显示异常信息
【发布时间】:2023-03-27 20:11:01
【问题描述】:

您好,我正在使用 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(); }
    }
}

【问题讨论】:

  • 你是否在调试模式下单步调试过你的代码?

标签: c# multithreading exception show messagebox


【解决方案1】:

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

最好让 UI 线程(创建并拥有 Form1 的线程)执行 MessageBox-ing。你要么想要创建事件,要么在你的工作类中有一个错误回调委托。

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

【讨论】:

  • 异常情况下可以进行事件吗?
  • @JeffreyGoines 为什么不呢?您的 MessageBox 代码在异常情况下正常工作。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    正如 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* 的过程。这就是你的 MessageBox 代码应该去的地方。

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

    【讨论】:

      猜你喜欢
      • 2011-07-30
      • 1970-01-01
      • 2012-09-25
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      • 2011-10-06
      • 2022-01-06
      相关资源
      最近更新 更多