Infosys Microsoft Alliance and Solutions blog

« Microsoft Blueprint Walkthrough – Creating My First Blueprint | Main | Silverlight Theming »

Handling Thread Exceptions

The other day someone asked "What is the best way to handle exceptions in a thread method and communicate back to the main thread?" The reason for the question is this - If you raise exceptions from a thread function (which is not the main thread) and if this is unhandled in the thread method itself, it causes an unhandled exception to be raised in the AppDomain and the application terminates.

You can handle the AppDomain.UnhandledException event to attempt to save some state/information as may be required and possible, but you can't stop the application from terminating. Note however that if a similar unhandled exception is raised from the main thread (of a winform application), the application may not terminate.

MSDN excerpt - "In applications that use Windows Forms, unhandled exceptions in the main application thread cause the Application..::.ThreadException event to be raised. If this event is handled, the default behavior is that the unhandled exception does not terminate the application, although the application is left in an unknown state."

To see this behavior, check the following code. In the Form1, I have added 2 buttons (button1 and button2). In the button1 code, I raise an exception, so this acts as an exception on the main thread itself. In button1 code, I start a new thread and then raise an exception in the thread function.

        private void button1_Click(object sender, EventArgs e)

        {

            throw new NotImplementedException();

        }

 

        private void button2_Click(object sender, EventArgs e)

        {

            Thread th = new Thread(new ThreadStart(func));

            th.Start();

        }

 

        void func()

        {

            throw new NotImplementedException();

        }

Finally, in the program.cs, I have added an event handler to handle the thread exception.

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)

        {

            MessageBox.Show("exception caught");

        }

To see the actual behavior, you should run the application and not debug it. In case of the button1 exception, you get a framework provided message box that allows you to handle the exception and continue executing the application. In case of the button1 exception, you will see the message box as per the code in the event handler that I have written in program.cs (as shown above), and after you click OK on the message box, the application terminates.

So back to the question we started with. Essentially the solution lies in not throwing the exception from the thread, but handling it in the thread method itself and instead firing an event that the main thread can handle and get details on the exception. The good news is that this behavior has already been provided in BackgroundWorker class. See the modified logic for button2 code to show how this works.

        private void button2_Click(object sender, EventArgs e)

        {

            BackgroundWorker worker = new BackgroundWorker();

            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

            worker.DoWork += new DoWorkEventHandler(worker_DoWork);

            worker.RunWorkerAsync();

        }

 

        void worker_DoWork(object sender, DoWorkEventArgs e)

        {

            throw new NotImplementedException();

        }

 

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

        {

            if (e.Error != null)

                MessageBox.Show(e.Error.Message);

            else

                MessageBox.Show("all fine");

        }

In case of exception the RunWorkerCompletedEventArgs.Error property is set and the details on the same can be obtained in the RunWorkerCompleted event handler. If you are interested, you can always debug into the framework code to see the details. The following code snippet shows how this is implemented in the BackgroundWorker.cs class

        private void WorkerThreadStart(object argument)

        {

            object workerResult = null;

            Exception error = null;

            bool cancelled = false;

 

          try

            {

                DoWorkEventArgs doWorkArgs = new DoWorkEventArgs(argument);

                OnDoWork(doWorkArgs);

                if (doWorkArgs.Cancel)

                {

                    cancelled = true;

                }

                else

                {

                    workerResult = doWorkArgs.Result;

                }

            }

            catch (Exception exception)

          {

             error = exception;

          }

 

            RunWorkerCompletedEventArgs e =

                new RunWorkerCompletedEventArgs(workerResult, error, cancelled);

 

            asyncOperation.PostOperationCompleted(operationCompleted, e);

        }

You can see the main execution logic on the BackgroundWorker is inside of a try catch block and the exception obtained is set to the Error property of the RunWorkerCompletedEventArgs class.

Hope this helps.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Please key in the two words you see in the box to validate your identity as an authentic user and reduce spam.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter