Search code examples
delphifiremonkeydelphi-xe6

Firemonkey message handling using TMessageManager and TThread.Queue


Like many other Firemonkey developers, I need a general multi-platform solution to send messages from a thread to the main thread (to replace PostMessage). I need it to also work on iOS.

There is a solution by François Piette that is implemented for Android and Windows, but not for iOS: TMessagingSystem.

However, I think it can be done much more simple by using the "new" TMessageManager in combination with TThread.Queue(). But no one have published code, using this aproach, that actually works (e.g. this one is not complete).

Do you have a tested implementation you would like to share with the community (or maybe just suggestions how to implement it right)?


Solution

  • Ok, here is my implementation. I did not use TMessagingSystem as it seems to just add complexity (for my situation at least). It works so far, but if anyone have suggestions for improvements, I will be happy to improve it.

    I looked at the solution by Uwe Raabe but I wanted to make it more straightforward and easy to implement in the large codebase that I am converting to FMX. With the solution below I can simply replace all PostMessage() with gMessageHandler.PostMessage (removing the win handle argument), and add the message functions in the form to tMainForm.MessageCallBack.

    I created a small unit that I can include everywhere I need the PostMessage function. Those places does not need to know about the form:

    unit MessageHandler
    interface
    tAllOSMessage = procedure(aMessageID, aData1, aData2: integer) of object;
    tAllOSMessageHandler = class
    private
      fOnMessage : tAllOSMessage;
    public
      constructor Create(aMessageCallBack: tAllOSMessage);
      procedure PostMessage(aMessageID, aData1, aData2: integer; aSourceThread: TThread = nil);
    end;
    
    var
      gMessageHandler: tAllOSMessageHandler;
    
    implementation
    
    constructor tAllOSMessageHandler.Create(aMessageCallBack: tAllOSMessage);
    begin
      fOnMessage := aMessageCallBack;
    end;
    
    procedure tAllOSMessageHandler.PostMessage(aMessageID, aData1, aData2: integer; aSourceThread: TThread);
    begin
      if aSourceThread=nil then
        aSourceThread := TThread.CurrentThread;
      aSourceThread.Queue(nil, procedure
                               begin
                                 if Assigned(fOnMessage) then
                                   fOnMessage(aMessageID, aData1, aData2);
                               end  );
    end;
    end.
    

    Then I add these lines to the main form unit:

    //Added to main form:
    tMainForm = class(TForm)
    ...
    procedure MessageCallBack(aMessageID, aData1, aData2: integer);
    
    //Added to MainFormCreate
    gMessageHandler := tAllOSMessageHandler.Create(MessageCallBack);
    
    //Added to MainFormDestroy
    FreeAndNil(gMessageHandler)
    
    procedure tMainForm.MessageCallBack(aMessageID, aData1, aData2: integer);
    begin
      case aMessageID of
        MyMessage1 :  MyFunction1(aData1,aData2);
        ...
      end;
    end;