Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you catch exception from inside Application.DoEvents()?

I've encountered a strange difference between a program running in VS2005 and running the executable directly. Essentially, when an exception is thrown in a method inside an Application.DoEvents() call, the exception can be caught when running inside Visual Studio. When running the compiled executable, the exception is not caught and the program crashes.

Here is some simple code to demonstrate the problem. Assume standard winforms boilerplate and two buttons and a label.

To run this, click the start button to start the 10 second count. Before 10 seconds elapses, press the abort button. and an exception will be thrown inside the DoEvents(). The exception should be caught. This only happens when running inside Visual Studio.

    private void StartButton_Click(object sender, EventArgs e) {
        DateTime start = DateTime.Now;

        try {
            while (DateTime.Now - start < new TimeSpan(0, 0, 10)) {
                this.StatusLabel.Text = DateTime.Now.ToLongTimeString();
                Application.DoEvents();
            }

            MessageBox.Show("Completed with no interuption.");
        } catch (Exception) {
            MessageBox.Show("User aborted.");                
        }
    }

    private void ButtonAbort_Click(object sender, EventArgs e) {
        throw new Exception("aborted");
    }

I want to be able to catch these exceptions. Is there any way to make it work?

Update:

I'm willing to consider approaches other than the re-entrant-headache-inducing DoEvents(). But I haven't found one that seems to work better. My scenario is that I have a long running loop which is controlling some scientific instruments, and frequently has to wait for a temperature to stabilize or something. I want to give my users the ability to abort the process, so I have an abort button that simply throws a custom exception, that I intend to catch at the site where the process is originally kicked off. It seemed to be a perfect solution. Except for the fact that it doesn't work for some reason.

If it's not possible to get this to work, is there a better approach?

Update 2:

When I add this as the first line of Main(), that makes it work as an executable, but not in VS, so the situation is reversed. The crazy thing is that it appears to be a no-op. I can understand how this does anything.

Application.ThreadException += delegate(
        object sender, 
        System.Threading.ThreadExceptionEventArgs e
    ) 
    { throw e.Exception; };

This is insane.

like image 655
recursive Avatar asked Mar 22 '26 08:03

recursive


2 Answers

Do you really have to use DoEvents? It leads to re-entrancy which can be very hard to debug.

I suspect that if you remove the re-entrancy from your application, you'll be able to work out where to catch the exception more easily.

EDIT: Yes, there's definitely a better way of doing this. Do your long-running task on a different thread. The UI thread should only be doing UI operations. Make your "abort" button set a flag which the long-running task checks regularly. See my WinForms threading page for an example of WinForms threading, and the volatility page for an example of one thread setting a flag to be watched by another thread.

EDIT: I've just remembered that BackgroundWorker (which my article doesn't cover - it was written pre-.NET 2.0) has a CancelAsync method - basically this (used with the CancellationPending and WorkerSupportsCancellation which basically deals with the "have a flag to set to cancel" stuff for you. Just check CancellationPending from the worker thread, and call CancelAsync from your abort button click handler.

like image 89
Jon Skeet Avatar answered Mar 23 '26 20:03

Jon Skeet


Because DoEvents causes the underlying message loop to pump, it can lead to re-entrancy (as stated in MSDN) which will make the behaviour of your application unpredictable.

I would suggest that you move your work (waiting for a temperature to stabilize) into a thread and use a signal of some kind to tell your user interface when the wait is over. This will allow your user interface to remain responsive without having to use Application.DoEvents.

like image 38
Jeff Yates Avatar answered Mar 23 '26 22:03

Jeff Yates