Date: | June 8, 2004 / year-entry #225 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040608-00/?p=38983 |
Comments: | 16 |
Summary: | Everybody who has messed with window messaging knows that GetMessage and PeekMessage retrieve queued messages, which are dispatched to windows via DispatchMessage. Most people also know that GetMessage and PeekMessage will also dispatch nonqueued messages. (All pending nonqueued messages are dispatched, then the first queued message is returned.) But apparently not many people realize that... |
Everybody who has messed with window messaging knows that GetMessage and PeekMessage retrieve queued messages, which are dispatched to windows via DispatchMessage. Most people also know that GetMessage and PeekMessage will also dispatch nonqueued messages. (All pending nonqueued messages are dispatched, then the first queued message is returned.) But apparently not many people realize that SendMessage will also dispatch messages! If one thread T1 send a message to a window that belongs to another thread T2, the sending thread T1 is put to sleep until the receiving thread replies to the message. But if somebody else sends a message to thread T1, thread T1 is woken to process the message, then is returned to sleep. Why is that? Well, when two threads T1 and T2 are working together, it's common that thread T1 may send a message to thread T2, and while handling the message, thread T2 will send messages back to thread T1 before it returns to T1. Therefore, thread T1 must be ready to accept incoming sent messages. For example, thread T1 may send a message saying, "Tell me about all the X's that you know." Thread T2 will then send one message back to thread T1 saying, "Here's an X", and then another message to say "Here's another X", and so on, until it has finished telling thread T1 about all the X's, at which point it returns. Thread T1 now knows, when the original message returns, that it has received the entire list of X's from thread 2. This back-and-forth is how DDE service discovery works. Another case is that thread T1 sends a message to thread T2, and thread T2 needs to ask thread T1 for help before it can finish the operation. This isn't as strange as it sounds. You do something similar all the time without realizing it when you respond to a WM_NOTIFY message by sending messages back to the control that sent the notification. (For example, you may respond to a LVN_ITEMACTIVATE by sending back a LVM_GETITEM to get information about the item that was activated.) So remember: Any time you send a message, there is a potential for re-entrancy. |
Comments (16)
Comments are closed. |
Here’s a dumb question I just thought about. What happens when you thread 1 sends a message to thread 2, and thread 2, in the handler, decides to terminate itself? Or same with sending a process that terminates itself?
What’s the return value of SendMessage to the caller?
Just one of the reasons why GUI code and multi-threaded applications is hard.
It is amusing when you break in the debugger and see your code in the call stack 3 or 4 times, with Win32 code interspersed between them. Can make for some real entertaining bugs…
This is also one of the reasons why MsgWaitForMultipleObjects() is not particularly useful. You really cannot control what happens on your thread. Dialogs are one of the places that you can’t get into the Message Queue processing.
Is there a way to get control of the Sleep that SendMessage puts the thread into? At least then you could process other events while you are waiting on the other thread….
LRESULT BlockingSendMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
LRESULT ret;
SendMessageTimeout(hwnd, msg, wp, lp, SMTO_BLOCK, INFINITE, &ret);
return ret;
}
Doug: you can poll SendMessageTimeout in a PeekMessage style message loop to do what you want.
Doug:
1. MsgWaitForMultipleObjects isn’t really for GUI programming. It’s great if you have worker threads that you want to drive using a message pump though.
2. Controlling the Sleep? Sure! You have three options that I can think of:
a. Use PostMessage instead of SendMessage, then you won’t sleep at all.
b. Use SendMessageTimeout so that you can expire if you’re waiting too long.
c. Use SendReply from the thread you’re responding to the SendMessage call in, so you can wake up your other thread as soon as possible, and still continue running if you need to.
Simon, do you mean ReplyMessage in item c?
Seth: Yep, thanks for catching that one. :) That’s what I get for replying before I get my latte :)
Centaur:
Sure, but if you don’t want to sleep at all, that means that you want to be completely asynchronous anyway. So you’d need something like that.
SendMessageCallback.
Raymond,
Is this similar to the reason why, when you call MessageBox(), the program continues to run (i.e. other messages, such as WM_PAINT and WM_TIMER, are handled while the user is still reading the dialog box)?
When I first starting Windows programming, this caught me off guard – since, even though I knew Windows was a multitasking / multithreading environment, my sample application only had one thread, and the code that appears immediately after the call to MessageBox() is not run until it returns – yet other parts of my program were running. Then, they were causing this same code to be run, calling MessageBox() multiple times, displaying hundreds of dialogs, until the application finally crashed.
No that is another issue entirely – the modal message loop. Clearly messages are dispatched while the dialog box is up – you rely on it every time you yourself call DialogBox!
(Tearing apart dialog boxes is a month-long subject that I hope to cover in little pieces. It took me three months just to write the first week’s worth!)
Commenting on this entry has been closed.
TrackBack From:http://www.cppblog.com/flyingxu/archive/2006/04/22/6088.html
TrackBack From:http://www.cppblog.com/flyingxu/archive/0001/01/01/6088.html