Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MessageLoop within Thread

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

like image 211
Santos Oliveira Avatar asked Dec 18 '12 11:12

Santos Oliveira


2 Answers

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;
like image 126
David Heffernan Avatar answered Sep 18 '22 14:09

David Heffernan


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.

like image 44
GolezTrol Avatar answered Sep 19 '22 14:09

GolezTrol