How can I implement a message loop within a thread using OTL? Application.ProcessMessages; is what i used so far but it isn\'t very safe to use it.
Thanks
This is how I pull messages off a thread's queue:
while GetMessage(Msg, 0, 0, 0) and not Terminated do begin
Try
TranslateMessage(Msg);
DispatchMessage(Msg);
Except
Application.HandleException(Self);
End;
end;
Using Application.ProcessMessages
will pull messages of the queue of the calling thread. But it's not appropriate to use in a message loop because it won't block. That's why you use GetMessage
. It blocks if the queue is empty. And Application.ProcessMessages
also calls other TApplication
methods that are not designed to be thread safe. So there are plenty of reasons not to call it from a thread other than the main thread.
When you need to terminate the thread, then I do this:
Terminate;
PostThreadMessage(ThreadID, WM_NULL, 0, 0);
//wake the thread so that it can notice that it has terminated
None of this is OTL specific. This code all is intended to live in a TThread
descendent. However, the ideas are transferable.
In the comments you indicate that you want to run a busy, non-blocking message loop. You would use PeekMessage
for that.
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
Try
TranslateMessage(Msg);
DispatchMessage(Msg);
Except
Application.HandleException(Self);
End;
end;
You can build a loop that is similar to the one in Application.Run.
You can call PeekMessage
, which checks if a message is available. PeekMessage checks the message queue of the current thread, so if you use it in your thread, it checks the message queue of the thread (to which you can post messages using PostThreadMessage).
Instead of PeekMessage, you can also use GetMessage
, which waits until a message is received. GetMessage
returns 0 returns false
. *)
when it gets a WM_QUIT
message, which is also the signal to terminate the message loop. It is risky to call GetMessage again after that, since it may not receive another message and it may prevent your application from closing down properly, since it is blocking.
Application.ProcessMessages indeed isn't very safe, because it does a lot of extras that is specific to the main thread. For one, it triggers Application.OnIdle as soon as the message queue is empty, which would mean that it calls that (problem 1) when the thread message queue is empty and (problem 2) in the context of the thread, not allowing any VCL interaction in the event.
*) About the return value of GetMessage: I noticed Delphi implements GetMessage as returning a LongBool. However the actual return value is an integer. It returns 0 in case of WM_QUIT, -1 in case of an error and non-zero in other cases. Microsoft states:
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
Unfortunately, you would have to import GetMessage under a different alias to use it correctly.