Worker Threads

Worker threads & the GUI (2)

Dialogs and MessageBoxes

You must not try to launch any GUI window from a worker thread. This means a worker thread cannot call on MessageBox, DoModal, Create of a modeless dialog, and so on. The only way you can handle this is to post a user-defined message back to the main GUI loop to perform this service for you.

If you attempt to do this, you will get various odd failures. You will get ASSERT errors from MFC, dialogs will fail to come up, and essentially you will get all sorts of antisocial behavior from your application. If you really, really need to have GUI objects operating from a thread, you must not use a worker thread. You must use a user-interface thread, which has a message pump. I'm not an expert on these, although I've done a brief article on what I discovered about them while doing a SAPI application, and this may be of some assistance in using them.

If you must pause the thread until the dialog or MessageBox completes, you should probably use an Event for synchronization. What you would do is create, for example, an auto-reset Event, which is created in the Cleared state. Post a user-defined message (see my article on Message Management) to the main GUI thread, which will then launch the dialog. When the dialog completes, it calls ::SetEvent to set the event. Meanwhile, the thread, after posting the message, uses ::WaitForSingleObject to wait for the event to be set. The thread will block until the dialog completes and sets the event.

I really should check the VC++ 6.0 implementation of CEvent. The VC++ 4.2 implementation was so full of errors as to be totally unusable. You Have Been Warned.

AfxGetMainWnd

Well, I've shown how to post a message to the window for the class. But what if you want to post a message to the main window? This is obvious, right? Just do

AfxGetMainWnd()->PostMessage(...)

and you're done! Right? Of course not. It should be obvious by now that I wouldn't pose such a question if the answer was the obvious one. 

What will happen is either an ASSERT failure or an access fault. If you exercise a bit of debugging cleverness, you will find that the pointer to the main window is NULL! But how can this be! The application clearly has a main window...

Well, the answer is, yes, the application has a main window. But that's not what AfxGetMainWnd is defined as returning! Read the documentation carefully, and you will see that it says:

If AfxGetMainWnd is called from the application's primary thread, it returns the application's main window according to the above rules. If the function is called from a secondary thread in the application, the function returns the main window associated with the thread that made the call. [emphasis added]

A worker thread has no main window. Therefore, the call will return NULL. The workaround is to obtain a pointer to the application's main window by calling AfxGetMainWnd in the primary thread, and store it in a place (such as a member variable of the class) where the worker thread can find it.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Debugging is anticipated with distaste, performed with reluctance, and bragged about forever.” - Dan Kaminsky