5

In a VCL Forms program, I have a Form that implements a method for handling windows messages and updating some controls on the Form, something like:

procedure OnMsgTest (var Msg: TMessage); message WM_CUSTOMTEST;

I use PostMessage with a custom message to this Form, using a code like this:

  h := FindWindow('TFrmTest', nil);    
  if IsWindow(h) then begin    
    PostMessage(h, WM_CUSTOMTEST, 0, 0);    
  end;

When the Form is instantiated several times, using the above code to send the message, only one Form instance updates the information on the screen. I would like all open and instantiated Forms to receive the message.

An important note: PostMessage can occur within the Form process itself, but also from another process. So, I believe a loop through the Forms would not work.

What would be the best approach to reach my goal?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Marcoscdoni
  • 955
  • 2
  • 11
  • 31
  • Use `EnumerateWindows` to find all top level windows. Check if they have a class name that matches. Hope that nobody else in the world uses the same form name as you do. – David Heffernan Jan 09 '18 at 14:00
  • 7
    A simpler and safer option would be to use `RegisterWindowMessage()` with `PostMessage(HWND_BROADCAST)`. Let the OS do the enumeration for you, and only interested windows will react to the message, other windows will ignore it. Then the class name doesn't matter. However, since `RegisterWindowMessage` is dynamic, you can't use a `message` handler, so have the Form override the virtual `WndProc` method instead. – Remy Lebeau Jan 09 '18 at 14:15
  • I second what Remy says – David Heffernan Jan 09 '18 at 14:51

1 Answers1

11

You would have to enumerate all running top-level windows, posting the message to each matching window individually. You could use EnumWindows() or a FindWindow/Ex() loop for that, but a simpler solution is to use PostMessage(HWND_BROADCAST) to broadcast a message that is registered with RegisterWindowMessage(). Only windows that handle the registered message will react to it, other windows will simply ignore it. For example:

type
  TMyForm = class(TForm)
  protected
    procedure WndProc(var Msg: TMessage); override;
  end;

...

var
  WM_CUSTOMTEST: UINT = 0;

procedure TMyForm.WndProc(var Msg: TMessage);
begin
  if (Msg.Msg = WM_CUSTOMTEST) and (WM_CUSTOMTEST <> 0) then
  begin
    ...
  end else
    inherited;
end;

initialization
  WM_CUSTOMTEST := RegisterWindowMessage('SomeUniqueNameHere');

Then you can do this when needed:

if WM_CUSTOMTEST <> 0 then
  PostMessage(HWND_BROADCAST, WM_CUSTOMTEST, 0, 0);    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770