A simple search for DoEvents brings up lots of results that lead, basically, to:
DoEventsis evil. Don't use it. Use threading instead.
The reasons generally cited are:
- Re-entrancy issues
- Poor performance
- Usability issues (e.g. drag/drop over a disabled window)
But some notable Win32 functions such as TrackPopupMenu and DoDragDrop perform their own message processing to keep the UI responsive, just like DoEvents does.
And yet, none of these seem to come across these issues (performance, re-entrancy, etc.).
How do they do it? How do they avoid the problems cited with DoEvents? (Or do they?)
DoEvents() is dangerous. But I bet you do lots of dangerous things every day. Just yesterday I set off a few explosive devices (future readers: note the original post date). So, with care, we can sometimes account for the dangers. Of course, that means knowing and understanding what the dangers are:
Re-entry issues. There are actually two dangers here:
- Part of the problem here has to do with the call stack. If you're calling .DoEvents() in a loop that itself handles messages that use DoEvents(), and so on, you're getting a pretty deep call stack. It's easy to over-use DoEvents() and accidentally fill up your call stack, resulting in a StackOverflow exception. If you're only using .DoEvents() in one or two places, you're probably okay. But if it's the first tool you reach for whenever you have a long-running process, you can easily find yourself in trouble here. Even one use in the wrong place can make it possible for a user to force a stackoverflow exception (sometimes just by holding down the enter key), and that can be a security issue.
- It is sometimes possible to find your same method on the call stack twice. If you didn't build the method with this in mind (hint: you probably didn't) then bad things can happen. If everything passed in to the method is a value type, and there is no dependance on things outside of the method, you might be fine. But otherwise, you need to think carefully about what happens if your entire method were to run again before control is returned to you at the point where .DoEvents() is called. What parameters or resources outside of your method might be modified that you did not expect?
Performance Issues. DoEvents() can give the illusion of multi-threading, but it's not real mutlithreading. This also has two real dangers:
- When you call DoEvents(), you are giving control on your existing thread to the message pump. The message pump might in turn give control to something else that takes a while. As a result, your operation might take much longer to finish than if it were in it's own real thread, definitely longer than it needs.
- The other issue here is the extreme version of the first: a potential to deadlock. If something else in your program depends on your process finishing, and will block until it does, and that thing is called by the message pump from DoEvents(), your app will get stuck and become unresponsive. This may sound far-fetched, but it's easy to do accidentally. This is at the root of some of the hung app situations you may have experienced on your own computer.
- Usability Issues. These are side-effects that result from not properly accounting for the other dangers. There's nothing new here, as long as you looked in other places appropriately.
If you can be sure you accounted for all these things, then go right ahead. But most of the time, at least in the .Net world, a BackgroundWorker control is just plain easier, at least once you've done it once or twice.