Why isn't there a SendThreadMessage function?
Here's an interesting customer question:
Windows hasPostMessage
andSendMessage
. It also hasPostThreadMessage
but noSendThreadMessage
.
Why isn't there aSendThreadMessage
function? Am I forced to simulate it with an event?
What would this imaginary SendThreadMessage
function do?
Recall that SendMessage
delivers the message directly to the window procedure;
the message pump never sees it.
The imaginary SendThreadMessage
function would have to deliver the message directly to.... what?
There is no "thread window procedure" to deliver it to.
Okay, maybe you still intend to process the thread message in your message pump,
but you want the caller of the imaginary SendThreadMessage
function to wait until you've finished processing the message.
But how does it know when you're finished?
It can't wait for DispatchMessage
to return, since DispatchMessage
can't dispatch thread messages.
(Where would it dispatch them to?)
The processing of the thread message is completely under the control of the message pump.
The window manager gives it a thread message, and as far as the window manager is concerned, that's the end of the story.
You might say that the processing of the thread message is complete
when somebody next calls GetMessage
orPeekMessage
,
but there's no guarantee that the next call to a message-retrieval function will come from the message pump.
Handling the thread message may result in a call to MessageBox
, and as a modal function,
it will have its own message loop, which will call GetMessage
,
resulting in your imaginary SendThreadMessage
function deciding that message processing is complete when in fact it's still going on.
What should you do instead?
Just create a window and send it a message.
The scenarios where you would want to use the PostThreadMessage
function are very limited and specialized.
Under normal circumstances, you should just send a regular window message.
Thread messages (as generated by the PostThreadMessage
function) do not go anywhere
when passed to the DispatchMessage
function.
This is obvious if you think about it, because there is no window handle associated with a thread message.
DispatchMessage
has no idea what to do with a message with no associated window.
It has no choice but to throw the message away.
This has dire consequences for threads which enter modal loops,
which any thread with a window almost certainly will do at one time or another.
Recall that the traditional modal loop looks like this:
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
If a thread message is returned by the GetMessage
function,
it will just fall through the TranslateMessage
and DispatchMessage
without any action being taken.
Lost forever.
Thread messages are generally to be avoided on threads that create windows, for this very reason.
Of course, if you're going to create a window, why not use PostMessage
instead,
passing that window as the target of the posted message?
Since there is now a window handle, the DispatchMessage
function knows to give the message to your window procedure.
Result: Message not lost.
Why do messages posted by PostThreadMessage disappear?
The only thread message you can meaningfully post to a thread displaying UI is WM_NULL
,
and even then, it's only because you want to wake up the message loop for some reason.
A common problem I see is people who use PostThreadMessage
to talk to a thread that is displaying UI
and then wonder why the message never arrives.
Oh, the message arrived all right. It arrived and then was thrown away.
This is actually a repeat of an earlier entry with the title
Thread messages are eaten by modal loops,
but I'm repeating it with a better subject line to help search engines.
But since I'm here, I may as well augment the existing article.
Obvious places where you have modal loops on a UI thread are functions that are explicitly modal l
ike DialogBox
orMessageBox
or TrackPopupMenuEx(TPM_RETURNCMD)
or DoDragDrop
.
But there are less obvious modal loops, like the modal loop that runs when you click on the caption bar and hold the button
or the modal loop that runs when COM is waiting for a cross-thread call to complete.
And since you don't control those modal loops, when they call DispatchMessage
,
your thread message will simply be thrown away.
If you need to communicate reliably with a thread that also displays UI,
then create a hidden window and send or post messages to that window.
I think this bit from the Platform SDK is relevant to the article:
Message-Only Windows
A message-only window enables you to send and receive messages.
It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages.
The window simply dispatches messages.
To create a message-only window, specify the HWND_MESSAGE constant
or a handle to an existing message-only window in the hWndParent parameter of the CreateWindowEx function.
You can also change an existing window to a message-only window by specifying HWND_MESSAGE
in the hWndNewParent parameter of the SetParent function.
To find message-only windows, specify HWND_MESSAGE in the hwndParent parameter of the FindWindowEx function.
In addition, FindWindowEx searches message-only windows as well as top-level windows
if both the hwndParent and hwndChildAfter parameters are NULL.