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?
You would have to enumerate all running top-level windows, posting the message to each matching window individually. You can use EnumWindows()
and GetClassName()
for that purpose, for example:
const
WM_CUSTOMTEST = ...;
type
TMyForm = class(TForm)
private
procedure BroadcastTest;
procedure OnMsgTest(var Msg: TMessage); message WM_CUSTOMTEST;
end;
...
function BroadcastProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
ClsName: array[0..10] of Char;
begin
GetClassName(hwnd, ClsName, Length(ClsName));
if StrComp(ClsName, 'TFrmTest') = 0 then
PostMessage(hwnd, WM_CUSTOMTEST, 0, 0);
Result := TRUE;
end;
procedure TMyForm.BroadcastTest;
begin
EnumWindows(@BroadcastProc, 0);
end;
procedure TMyForm.OnMsgTest(var Msg: TMessage);
begin
...
end;
That being said, a more reliable and safer option would be to use RegisterWindowMessage()
with PostMessage(HWND_BROADCAST)
. Let the OS do the enumeration for you. Only windows that are interested in the message will react to it, while other windows will ignore it. This way, the class name doesn't matter at all. However, since RegisterWindowMessage()
is dynamic, you can't use a message
handler, but you can have your Form override the virtual WndProc()
method instead. For example:
type
TMyForm = class(TForm)
protected
procedure WndProc(var Msg: TMessage); override;
private
procedure BroadcastTest;
end;
...
var
WM_CUSTOMTEST: UINT = 0;
procedure TMyForm.BroadcastTest;
begin
if WM_CUSTOMTEST <> 0 then
PostMessage(HWND_BROADCAST, WM_CUSTOMTEST, 0, 0);
end;
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');