I am implementing a thread that needs the following features:
My initial implementation of the message pump used GetMessage like:
while not Terminated and GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
The problem I found with that, is that GetMessage will never return unless there is a message.
Meaning, if there is low message activity, it may be quite a while before it checks Terminated again.
My second implementation (inspired by this answer) used MsgWaitForMultipleObjects to wait until a message exists before checking (since it has a timeout)
while not Terminated do
begin
if MsgWaitForMultipleObjects(0, nil^, False, 1000, QS_ALLEVENTS) = WAIT_OBJECT_0 then
begin
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
end;
The problem I've found with this, is that MsgWaitForMultipleObjects blocks the thread while it waits. So, when a message is sent to the thread via SendMessageTimeout, it times out, where it doesn't when using GetMessage.
The solution that comes to mind is to go back to the GetMessage implementation, but add a timer to make sure a WM_TIMER message resets the loop every second.
Is this really the only way to do this? It seems there should be some better way to keep the thread responsive while waiting for messages.
My initial implementation of the message pump used GetMessage like:
while not Terminated and GetMessage(Msg, 0, 0, 0) do begin TranslateMessage(Msg); DispatchMessage(Msg); end;The problem I found with that, is that GetMessage will never return unless there is a message. Meaning, if there is low message activity, it may be quite a while before it checks
Terminatedagain.
You can override the thread's virtual TerminatedSet() method to post a message to the queue via PostMessage() or PostThreadMessage() to "wake up" GetMessage() if it is blocked.
Alternatively, have your thread constructor create a TEvent object, and free it in the thread's destructor. Then have TerminatedSet() signal that event. Your loop can then use MsgWaitForMultipleObjects() to wait on the message queue AND the event at the same time. The return value will tell you whether the wait was satisfied by a message or the event.
My second implementation (inspired by this answer) used
MsgWaitForMultipleObjectsto wait until a message exists before checking (since it has a timeout)while not Terminated do begin if MsgWaitForMultipleObjects(0, nil^, False, 1000, QS_ALLEVENTS) = WAIT_OBJECT_0 then begin while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin TranslateMessage(Msg); DispatchMessage(Msg); end; end; end;The problem I've found with this, is that
MsgWaitForMultipleObjectsblocks the thread while it waits. So, when a message is sent to the thread viaSendMessageTimeout, it times out, where it doesn't when usingGetMessage.
The SendMessage...() family of functions will deliver a message directly to the target window's message procedure, bypassing the message queue completely. So MsgWaitForMultipleObjects() and (Get|Peek)Message() will never report a sent message from SendMessage...(), only a posted message from PostMessage() or PostThreadMessage() (or a synthesized message, like WM_TIMER, WM_PAINT, etc). However, when sending a message across thread boundaries, the receiving thread still needs to perform message retrieval calls (is, (Get|Peek)Message()) in order for the sent message to actually be delivered to the window procedure.
The solution that comes to mind is to go back to the
GetMessageimplementation, but add a timer to make sure aWM_TIMERmessage resets the loop every second.
Inside a thread, it would be better to use a waitable timer instead of WM_TIMER, then you can use the timer with MsgWaitForMultipleObjects(). But really, there is very little difference between using GetMessage() with WM_TIMER vs MsgWaitForMultipleObjects() with a timeout, so there is no need to waste system resources creating the timer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With