5
votes

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?

1
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
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
I second what Remy saysDavid Heffernan

1 Answers

11
votes

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);