Click here to Skip to main content
15,889,335 members
Articles / Desktop Programming / MFC

MessageBoxes and worker threads

Rate me:
Please Sign up or sign in to vote.
4.57/5 (6 votes)
25 Oct 2010Apache3 min read 44.5K   9   8
MessageBoxes and worker threads

This will hopefully become a more organized article with code samples, but I need to record my results for now. Here’s the practical problem I encountered in the application I am working on: several message boxes initiated by worker threads would pop up simultaneously confusing the user and wrecking havoc.

So, the task is: you want to display a notification (message box) from a worker thread. Sometimes it is fire-and-forget, sometimes you want to know the answer (“Do you want to continue? Yes|No”). I will assume the answer is important, since this case is more complicated.

Here are some facts that I discovered:

Fact #1: Dismissing a message box may enable the parent even if there are other pending message boxes. I am not sure what is the exact algorithm here, but if you have multiple message boxes on the screen, there is at least one of them that would re-enable the parent when dismissed. This will render all other message boxes effectively modeless. The user can continue to work on the parent and maybe even create more message boxes.

Fact #2: Calling MessageBox() from a worker thread effectively converts that thread to a UI thread with the message pump.

Fact #3: If the UI thread is blocked, MessageBox() will also block, even when called on a worker thread. The reason for that is that MessageBox() internally called DialogBox2() function in user32.dll, which calls NtUserCallHwndParamLock(). I am not sure what that function does, but it blocks if the UI thread does not respond.

Fact #4: If you use PostMessage (BeginInvoke) or SendMessage (Invoke) to render the message boxes on the UI thread, the message boxes can still appear in parallel. In fact, they become recursive. If you look at the call stack, it looks like one message box calls another.

This happens because the parent window is not dead while showing a message box: it is just disabled. The message box runs its own message pump, and when another thread sends a message instructing the parent window to show another message box, it will be happily processed, showing the second message box on top of the first.

Bottom line: “serializing” message box calls via PostMessage/SendMessage to the UI thread does not work.

Fact #5. If the UI thread is blocked while we send it “show that message box” messages, the message boxes will appear in sequence, starting with the latest. This puzzled me for a while, but after some research, I found the truth. The message boxes are still being called recursively, only they don't have a chance to show. Showing a window is a complex process that requires handling multiple WM_ messages. When MessageBox() function is called and starts running the inner message loop, there is no window yet. It will appear a little bit later. However, the first message that is processed in the new loop is a request for another message box, which effectively defers creation of the window until later. Only the last message box in the chain has a chance to display itself. Once it is dismissed, previous message box(es) take turn.

This article was originally posted at http://www.ikriv.com/blog?p=610

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Technical Lead Thomson Reuters
United States United States
Ivan is a hands-on software architect/technical lead working for Thomson Reuters in the New York City area. At present I am mostly building complex multi-threaded WPF application for the financial sector, but I am also interested in cloud computing, web development, mobile development, etc.

Please visit my web site: www.ikriv.com.

Comments and Discussions

 
GeneralBravo Ivan !! Pin
Tacitonitus3-Apr-15 15:14
Tacitonitus3-Apr-15 15:14 
GeneralMy vote of 1 Pin
.:floyd:.22-Feb-14 11:30
.:floyd:.22-Feb-14 11:30 
As 3soft stated earlier already, this blog entry leaves much to be desired. Facts, to be precise.

Fact #1: A message box (or any dialog, really) never has a parent. It can have an owner, or be unowned, but it will never be the child of a parent window. A window can own at most a single modal dialog. If you write code that allows a dialog to be owned by a window that already is the owner of a modal dialog, you're doing it wrong. There are no algorithms involved here: The outcome of violating the rules is unspecified.

Fact #2: Calling any user32.dll export from a non-GUI-thread propagates that thread to a GUI thread. A GUI thread is simply a thread that has a message queue. No message pump will be created. The developer is responsible for implementing a message loop to dispatch messages on a GUI thread.

Fact #3: Windows applications do not have a single dedicated GUI thread. There is no such thing as the UI thread. An application can have as many GUI threads as it requires. The GUI threads, like all threads, run in parallel. There is no secret synchronization between GUI threads.

Fact #4: Unless you violate Rule #1 for GUI programming: GUI's are single-threaded. If you create a cross-thread window relation, for example, by setting a message box' owner to a window owned by a different thread you instruct the system to call AttachThreadInput(). This causes the input queues of both threads to be merged into one, with strict FIFO-ordering. A wait for input on the owner window's thread without dispatching input messages on the message box thread will not ever be serviced.

Fact #5: PostMessage() is in fact the right tool to communicate from your worker thread back to your designated owner window's thread. It doesn't work for you, because of Fact #4: Your worker thread does not dispatch input messages, but owns a message box. When you attempt to have the owner window's thread render your message box you are trying to change the foreground window. Since your input queues are merged by now any input message to your message box will block message dispatching on the owner window's thread. WM_ACTIVATE is one of those messages that will Get You Into the Same Jail.

Fact #6: Sending messages does not involve the message queue - they are passed directly to the window procedure. It will work, even if you managed to block a thread's message dispatching.

Fact #7: These rules are ancient. They haven't changed in 20 years.

modified 22-Feb-14 18:36pm.

QuestionConvert UI thread back Pin
robiwano21-Feb-14 5:06
robiwano21-Feb-14 5:06 
AnswerRe: Convert UI thread back Pin
Ivan Krivyakov21-Feb-14 10:27
Ivan Krivyakov21-Feb-14 10:27 
QuestionLeaves a lot of questions open Pin
3soft22-May-13 22:25
3soft22-May-13 22:25 
AnswerRe: Leaves a lot of questions open Pin
Ivan Krivyakov23-May-13 8:06
Ivan Krivyakov23-May-13 8:06 
GeneralModal and not modal message boxes Pin
Stefan_Lang2-Nov-10 1:36
Stefan_Lang2-Nov-10 1:36 
GeneralGood stuff Pin
Manish K. Agarwal27-Oct-10 3:15
Manish K. Agarwal27-Oct-10 3:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.