Using MessageBox to display exception information in a multi-threaded application

Hi, I am working with winform and trying to use MessageBox to handle exceptions. The strange thing is, MessageBox appears only after closing the main form ("Form1" in the code below).

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

What could be the best way to use MessageBox to handle exceptions?


... So I added some codes for MessageBox-ing to the user interface stream, but the problem remains.

 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) } 

Actually there was a different form and a different worker. When Worker (Worker1) has established a connection to the server, Form1 hides (.Hide ()), Form2 shows (.Show ()), and Worker2 starts working with the created Work.conf connection.

 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(); } } } 
+1
source share
3 answers

In fact, I bet that the MessageBox appears behind the main form, and you just don't see it until you close it.

You will be much better off allowing UI thread (the one that created and owns Form1 ) MessageBox-ing. You either want to do events, or otherwise delegate the callback in your working class.

However, BackgroundWorker might be worth checking out here rather than trying to roll on its own. Assuming this is a fatal exception, you can save and retrieve the error state, and you get an event that is automatically triggered when the stream ends.

+2
source

You really have to block the doWork method so that multiple threads cannot access it at the same time, they must queue.

Take a look at Stream Attachment. Imagine if you get two exceptions at the same time. Your application will crash. Locking the code area that will be duplicated again will create a queue for your threads to access the code area that handles the exception.

+1
source

As indicated in lc, it is likely that your message box appears behind the main form, and therefore you only see it when you close the main form.

The model I use to handle unhandled exceptions in a Windows Forms application is as follows:

 // 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). 

The first line says that you want to catch any unhandled exception and handle it yourself, rather than allowing the WinForms framework. Note that starting with .NET 4 this option will not work for an exception that distorts the state of the process (for example, OutOfMemory).

If you have an unhandled exception in the user interface thread, the second line will call the procedure you create with the name * App_UiThreadException *. This is where your MesssageBox code should be.

If you have an unhandled exception in a thread other than the UI, the last line will call the procedure you create, called * App_NonUiThreadException *. This is where your MesssageBox code should be.

+1
source

Source: https://habr.com/ru/post/953901/


All Articles