Results 1 to 6 of 6

Thread: SendMessage PostMessage

  1. #1

    SendMessage PostMessage

    Om verschillende delen van mijn software te laten samen werken maak ik gebruik van windows messages

    Wanneer ik gebruik maak van SendMessage wordt deze nooit ontvangen terwijl met een PostMessage deze wel aankomt.
    Code:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    SendMessage(application.Handle,WM_User+3,0,0);   //GetMessage code wordt niet uitgevoerd
    PostMessage(application.Handle,WM_User+3,0,0);   //GetMessage code wordt wel uitgevoerd
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    application.OnMessage:=GetMessage;
    end;
    
    procedure TForm1.GetMessage(var Msg: TMsg; var Handled: Boolean);
    begin
     if (Msg.message = WM_User+3) then
     begin
       showmessage('hebbes');
       Handled:=true;
     end;
    end;
    Wat is naast het feit dat SendMessage wel gesynchroniseerd is nog meer het verschil tussen Send / Postmessage

  2. #2
    Loopt de code wel door of blijft het hangen op die SendMessage?

    Ik kan me voorstellen dat het niet goed werkt omdat je event (Button1Click) zelf al optreedt in reactie op een message. Ik kan me voorstellen dat de application loop dus niet al begint met verwerken van de volgende message zolang die click nog niet volledig afgehandeld is.

    Een veelgebruikte workaround daarvoor is Application.ProcessMessages, maar dat zal dat probleem niet oplossen als de SendMessage hangt. Daarbij is die oplossing wel veelgebruikt, maar niet per se de beste.

    Alternatieven op een rijtje, niet per se op volgorde van wenselijkheid.

    • PostMessage. Als dat werkt, waarom niet. Mocht je pointers naar objecten of record meegeven, hou dan wel heel goed rekening met de lifetime daarvan en de verantwoordelijkheid voor het opruimen. De aanroepende partij mag het niet, of niet te vroeg, vrijgegeven.
    • Application.Perform roept rechtstreeks de message handler aan. Lijkt heel erg op SendMessage, maar je slaat heel de Windows message queue over.
    • Application.ProcessMessages dus, maar dat kan soms aparte neveneffecten hebben, omdat je de volgende message al afhandelt terwijl het afhandelen van de vorige nog bezig is. Sommige code gaat er misschien juist van uit dat dit niet gebeurt. Daarbij zal het niet helpen als SendMessage hangt, omdat je het aan zou moeten roepen na SendMessage.
    • SendMessage in aparte thread. Asynchroon aanroepen, in een TThread of TTask. Vooral handig als je het antwoord op de message wilt gebruiken om nog iets te doen. Met PostMessage krijg je tenslotte niets terug.
    • Event/Callback. Van messages af stappen voor dit doel en direct een procedure aanroepen. Dat kan ook een callback zijn, zoals de event handlers in Delphi. TForm weet bijvoorbeeld niets van jouw FormCreate procedure. Het enige dat 'ie weet is dat er een procedure aan z'n OnCreate event gekoppeld zit, en dat die dus aangeroepen moet worden. Op die manier kun je ook je eigen callbacks/events genereren.
    Last edited by GolezTrol; 30-Jan-23 at 16:04.
    1+1=b

  3. #3
    Thanks voor update

    De code loopt wel gewoon door / hij blijft dus niet hangen bij de SendMessage
    Normaal gesproken maak ik ook het liefst gebruik van Events maar in dit geval is dat vanwege de structuur van de software niet to slecht mogelijk.

    Ook met een application.ProcessMessages werkt de SendMessage niet
    Was idd wat aan het experimenteren met het doorgeven van pointers naar objecten. Ik laat hierbij de ontvangende kant het object weer opruimen

    Code:
    procedure TForm1.Button1Click(Sender: TObject);
    var temp:TMessageClass;
    begin
    
    temp:=TMessageClass.Create;
    
    PostMessage(application.Handle,WM_User+3,integer(temp),0);   //GetMessage code wordt wel uitgevoerd
    
    end;
    
    
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    application.OnMessage:=GetMessage;
    end;
    
    procedure TForm1.GetMessage(var Msg: TMsg; var Handled: Boolean);
    var temp2:TMessageClass;
    begin
     if (Msg.message = WM_User+3) then
     begin
       temp2:=TMessageClass(Msg.wParam);
       showmessage(temp2.name);
       temp2.Free;
     end;
    
    end;
    
    
    { TMessageClass }
    
    constructor TMessageClass.Create;
    begin
      inherited;
      Name:='Christian';
      Adress:='Hierzo';
      Nummer:=10;
    end;
    
    destructor TMessageClass.Destroy;
    begin
      //
      inherited;
    end;

  4. #4
    Quote Originally Posted by cpri View Post
    Om verschillende delen van mijn software te laten samen werken maak ik gebruik van windows messages
    Maar... je stuurt daar messages binnen je programma zelf.
    Dan zal een SendMessage niet werken want dan wacht je programma op terugkeer.
    Maar je programma kan dus ook niet de GetMessage() uitvoeren want je programma is nog aan het wachten.
    Application.ProcessMessage kun je nergens toevoegen want die SendMessage blokkeert gewoon de boel.

    Enfin... geen SendMessages naar jezelf doen.

    (Of werkt de Application.Messages met een thread intern?)

    Als je overigens alleen binnen je eigen applicatie messages hoeft rond te sturen, dan zou ik eerder kiezen voor de WM_USER + X binnen je form unit zelf en dan direct met message een class procedure declareren. Dan is het niet de Application.OnMessage die het binnenkrijgt maar direct je class. (Moet je wel de handle van het object weten)

    Delphi Code:
    1. const
    2.   DOC_ZET_REFRESH = WM_USER + 2009;
    3. //...
    4.     procedure ZetRefresh(var Msg: TMessage); message DOC_ZET_REFRESH;

  5. #5
    Ik had ook verwacht dat het zou hangen, maar dat is niet het geval. In plaats daarvan komt SendMessage gewoon terug. Wellicht zit er ergens een bescherming om dit te voorkomen, maar het werkt inderdaad niet.

    Vanuit een thread zou het inderdaad wel werken, alleen als je een thread of task maakt, en daar direct op wacht na het starten, dan zal je code alsnog hangen. Dan gebeurt er eigenlijk wat Rik en ik voorspelden. Eventueel resultaat van de SendMessage zal je dus ook binnen de thread of task moeten verwerken, en eventueel synchronizen als dat nodig is. Hierbij 4 button clicks, waarbij m.i. de laatste de enige juiste is (van deze, er zijn vast nog andere manieren).

    Delphi Code:
    1. procedure TForm3.Button1Click(Sender: TObject);
    2. begin
    3.   // ShowMessage wordt bereikt, maar de hele message naar application lijkt verdwenen
    4.   SendMessage(Application.Handle, WM_DESTROY, 0, 0);
    5.   Showmessage('done!' + (WM_USER+1).ToString);
    6. end;
    7.  
    8. procedure TForm3.Button2Click(Sender: TObject);
    9. var
    10.   t: ITask;
    11. begin
    12.   t := TTask.Create(
    13.     procedure
    14.     begin
    15.       // Message wordt verstuurd, doordat hij nu uit een andere thread verstuurd wordt.
    16.       SendMessage(Application.Handle, WM_DESTROY, 0, 0);
    17.     end);
    18.  
    19.   t.Start.Wait; // Dit blokkeert, omdat de applicatie nooit de message verwerkt, en de thread dus nooit afgerond wordt. De applicatie hangt.
    20.   Showmessage('done!' + (WM_USER+1).ToString);
    21. end;
    22.  
    23. procedure TForm3.Button3Click(Sender: TObject);
    24. var
    25.   t: ITask;
    26. begin
    27.   t := TTask.Create(
    28.     procedure
    29.     begin
    30.       // Message wordt verstuurd, doordat hij nu uit een andere thread verstuurd wordt.
    31.       SendMessage(Application.Handle, WM_DESTROY, 0, 0);
    32.       // Message wordt getoond vanuit de thread. Dit lijkt te werken, maar is volgens mij
    33.       // een risico, omdat dit een VCL form gebruikt, wat niet thread safe is. Als jouw code daar niet gevoelig voor is, is dat natuurlijk geen probleem.
    34.       Showmessage('done!' + (WM_USER+1).ToString);
    35.     end);
    36.  
    37.   t.Start;
    38. end;
    39.  
    40. procedure TForm3.Button4Click(Sender: TObject);
    41. var
    42.   t: ITask;
    43. begin
    44.   t := TTask.Create(
    45.     procedure
    46.     begin
    47.       // Message wordt verstuurd, doordat hij nu uit een andere thread verstuurd wordt.
    48.       SendMessage(Application.Handle, WM_DESTROY, 0, 0);
    49.       // Het tonen van de message (of eventueel andere niet-threadsafe opvolging)
    50.       // wordt gesynct met de main thread.
    51.       TThread.Synchronize(TThread.Current,
    52.         procedure
    53.         begin
    54.           Showmessage('done!' + (WM_USER+1).ToString);
    55.         end);
    56.     end);
    57.  
    58.   t.Start;
    59. end;
    E.e.a in een testproject dat de ontvangen message ook laat zien.
    MsgDemo.zip
    1+1=b

  6. #6
    Silly member NGLN's Avatar
    Join Date
    Aug 2004
    Location
    Werkendam
    Posts
    5,133
    Quote Originally Posted by cpri View Post
    Wanneer ik gebruik maak van SendMessage wordt deze nooit ontvangen terwijl met een PostMessage deze wel aankomt.
    Dat klopt.

    Wat is ... nog meer het verschil tussen Send / PostMessage
    Met PostMessage stuur je iets zonder te wachten op een resultaat. Je informeert, fire and forget.
    Met SendMessage stuur je iets dat eerst afgehandeld moet worden en waarbij je een resultaat kan verwachten. Je verstuurt een aanvraag.
    Messages verstuurd met SendMessage komen niet in een queue, maar worden direct beantwoord.

    Uit de Online Help van TApplication.OnMessage:
    Note: OnMessage only receives messages that are posted to the message queue, not those sent directly with the Windows API SendMessage function.
    Je kan zelf reageren op SendMessage-aanvragen door WndProc te overriden van bijvoorbeeld een form. TApplication biedt diezelfde mogelijkheid met HookMainWindow, bijvoorbeeld op de volgende manier:
    Delphi Code:
    1. const
    2.   UM_CPRI = WM_USER + 3;
    3.  
    4. procedure TForm1.Button1Click(Sender: TObject);
    5. begin
    6.   SendMessage(Application.Handle, UM_CPRI, 0, 0);
    7. end;
    8.  
    9. procedure TForm1.FormCreate(Sender: TObject);
    10. begin
    11.   Application.HookMainWindow(AppWndHook);
    12. end;
    13.  
    14. procedure TForm1.FormDestroy(Sender: TObject);
    15. begin
    16.   Application.UnhookMainWindow(AppWndHook);
    17. end;
    18.  
    19. function TForm1.AppWndHook(var Message: TMessage): Boolean;
    20. begin
    21.   if Message.Msg = UM_CPRI then
    22.   begin
    23.     ShowMessage('Hebbes');
    24.     Message.Result := 1;
    25.   end;
    26.   Result := Message.Result <> 0;
    27. end;
    Merk trouwens op dat beide type messages ook met een compleet andere messagehandler en message-type worden afgehandeld:
    • PostMessage: TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object;
      TMsg heeft geen result parameter en TMessageEvent heeft alleen een boolean parameter om aan te geven of de message al is afgehandeld.
    • SendMessage: TWindowHook = function (var Message: TMessage): Boolean of object;
      TMessage daarentegen heeft wel een Result parameter waarin je als gebruiker het resultaat van de aanvraag kunt retourneren.
    (Sender as TNLDUser).Signature := 'Groeten van Albert';

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •