wamoz
asked on
HWND_BROADCAST and custom messages
I'm having problems sending a broadcast notification message between some apps I've written.
I can get HWND_BROADCAST to work with system messages. For example, the following code causes a Notify button to pop up a message window. Clicking any of the window titlebar buttons will also pop the message because these buttons also generate this message. The call to inherited is merely to allow normal processing so you can close the application.
const
BBCC_NEWMESSAGE = WM_SYSCOMMAND;
...
public
procedure NewMessage(var msg: TMessage); message BBCC_NEWMESSAGE;
...
procedure TformInjector.cmdNotifyCli ck(Sender: TObject);
begin
Win32Check(SendNotifyMessa ge(HWND_BR OADCAST,BB CC_NEWMESS AGE,0,0));
end;
procedure TformInjector.NewMessage(v ar msg: TMessage);
begin
showmessage('BBCCWM_NEWMES SAGE');
inherited
end;
But if we change the constant like this
const
BBCC_NEWMESSAGE = WM_APP + 1;
then it stops working.
Can anyone tell me why WM_APP + x doesn't seem to be received, and what to do about it?
For the record, I've also tried WM_USER + 1
I can get HWND_BROADCAST to work with system messages. For example, the following code causes a Notify button to pop up a message window. Clicking any of the window titlebar buttons will also pop the message because these buttons also generate this message. The call to inherited is merely to allow normal processing so you can close the application.
const
BBCC_NEWMESSAGE = WM_SYSCOMMAND;
...
public
procedure NewMessage(var msg: TMessage); message BBCC_NEWMESSAGE;
...
procedure TformInjector.cmdNotifyCli
begin
Win32Check(SendNotifyMessa
end;
procedure TformInjector.NewMessage(v
begin
showmessage('BBCCWM_NEWMES
inherited
end;
But if we change the constant like this
const
BBCC_NEWMESSAGE = WM_APP + 1;
then it stops working.
Can anyone tell me why WM_APP + x doesn't seem to be received, and what to do about it?
For the record, I've also tried WM_USER + 1
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
BTW I have a MSDN installed. I've already perused the material you quote plus quite a lot more.
I suspect that registering the message merely prevents crosstalk, but I'm willing to try it if you have some clues on how to bind the dynamically allocated messageid.
I suspect that registering the message merely prevents crosstalk, but I'm willing to try it if you have some clues on how to bind the dynamically allocated messageid.
ASKER
I've just tried handling the message from Application.onMessage, which also didn't get it, and then for the sake of argument (since I already had an OnMessage handler set up) I tried registering the message.
You were right, suddenly the message is arriving.
Which leads us back to the question of how to bind a method to a message id at runtime.
The reason that Delphi insists on a constant is that it gets used by a case statement in various WndProcs. I suppose I could play dirty and get the address of the constant and fiddle the data. What do you think?
You were right, suddenly the message is arriving.
Which leads us back to the question of how to bind a method to a message id at runtime.
The reason that Delphi insists on a constant is that it gets used by a case statement in various WndProcs. I suppose I could play dirty and get the address of the constant and fiddle the data. What do you think?
ASKER
I've just tried handling the message from Application.onMessage, which also didn't get it, and then for the sake of argument (since I already had an OnMessage handler set up) I tried registering the message.
You were right, suddenly the message is arriving.
Which leads us back to the question of how to bind a method to a message id at runtime.
The reason that Delphi insists on a constant is that it gets used by a case statement in various WndProcs. I suppose I could play dirty and get the address of the constant and fiddle the data. What do you think?
You were right, suddenly the message is arriving.
Which leads us back to the question of how to bind a method to a message id at runtime.
The reason that Delphi insists on a constant is that it gets used by a case statement in various WndProcs. I suppose I could play dirty and get the address of the constant and fiddle the data. What do you think?
is there any reason why you can't pass the Cardinal value of the message? There's no real reason to know what the constant is... I think that n=makes sense, but it is getting a little late and my mind may be fooling me.
Good luck!!
Good luck!!
interested
what about BroadcastSystemMessage() ,i have an example (actually from a book) if its any use to you.
the recieving apps change there windowproc to listen for your messages.
Declares your BBCC_NEWMESSAGE as a UINT and then calls
BBCC_NEWMESSAGE := RegisterWindowMessage('Tes t Message');
the recieving apps change there windowproc to listen for your messages.
Declares your BBCC_NEWMESSAGE as a UINT and then calls
BBCC_NEWMESSAGE := RegisterWindowMessage('Tes
Hi,
Just to clarify the reason why (WM_APP + xxx) can't be sent to other app:
- < WM_USER - reserved for Windows;
-WM_USER (0x0400) through 0x7FFF - Window specific messages;
-WM_APP (0x8000) through 0xBFFF - Application specific messages;
-0xC000 through 0xFFFF - System wide messages (use RegisterWindowMessage API to obtain such a message number);
Regards, Geo
Just to clarify the reason why (WM_APP + xxx) can't be sent to other app:
- < WM_USER - reserved for Windows;
-WM_USER (0x0400) through 0x7FFF - Window specific messages;
-WM_APP (0x8000) through 0xBFFF - Application specific messages;
-0xC000 through 0xFFFF - System wide messages (use RegisterWindowMessage API to obtain such a message number);
Regards, Geo
> Which leads us back to the question of how to bind a
> method to a message id at runtime.
> The reason that Delphi insists on a constant is that it
> gets used by a case statement in various WndProcs.
> I suppose I could play dirty and get the address of the
> constant and fiddle the data. What do you think?
You can do this:
type
TFormInjector = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
function AppMessage(var Message: TMessage): Boolean;
end;
procedure TFormInjector.FormCreate(S ender: TObject);
begin
Application.HookMainWindow (AppMessag e);
end;
procedure TFormInjector.FormDestroy( Sender: TObject);
begin
Application.UnhookMainWind ow(AppMess age);
end;
function TFormInjector.AppMessage(v ar Message: TMessage): Boolean;
begin
case Message.Msg of
BBCC_NEWMESSAGE:
begin
// do your stuff here
Result := True;
end;
else
Result := False;
end;
end;
HTH
TOndrej
> method to a message id at runtime.
> The reason that Delphi insists on a constant is that it
> gets used by a case statement in various WndProcs.
> I suppose I could play dirty and get the address of the
> constant and fiddle the data. What do you think?
You can do this:
type
TFormInjector = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
function AppMessage(var Message: TMessage): Boolean;
end;
procedure TFormInjector.FormCreate(S
begin
Application.HookMainWindow
end;
procedure TFormInjector.FormDestroy(
begin
Application.UnhookMainWind
end;
function TFormInjector.AppMessage(v
begin
case Message.Msg of
BBCC_NEWMESSAGE:
begin
// do your stuff here
Result := True;
end;
else
Result := False;
end;
end;
HTH
TOndrej
ASKER
Geobul explains WHY it is necessary to register the message -
> 0xC000 through 0xFFFF - System wide messages (use RegisterWindowMessage API to obtain such a message
number);
I actually did read this in the course of my researches but it didn't sink in.
Constants are compiled into the code segment rather than the data segment, so it is flat-out impossible to modify them. Even if you figure out where they are, attempting to write to this memory will quite correctly cause an access violation. So much for that idea.
TOndrej is on the right track; to paraphrase him, "dispatch it yourself".
However, HookMainWindow() is going a bit far. The dispatching can be done in the Application.OnMessage handler. Moreover, I suspect that most of us are going to make my mistake and use classic Delphi method binding before discovering that WM_APP + x won't broadcast. So I propose this adaptation of TOndrej's approach:
1. Remove "message MY_MESSAGEn;" from the method declarations of those messages that need runtime binding.
2. Declare the message names as public variables of the unit.
3. Register the message names in the initalisation section of the unit, assigning the message id's to these variables.
4. Put a TApplicationEvents widget on the main form and implement its Message event.
5. In the message event, perform the dispatching.
if (tagMsg.Message = MY_MESSAGE1) then
MY_MESSAGE1_Handler(TMyMes sage(tagMe ssage))
else if (tagMsg.Message = MY_MESSAGE2) then
MY_MESSAGE2_Handler(TMyMes sage(tagMe ssage))
...
You would NOT deregister the message names anywhere as this would cause problems for other applications using the message.
I have tried the approach I describe and it works perfectly.
As a related aside, I had a window that set CanClose to false and minimised to the systray instead. This prevents system shutdown. The solution is to bind a method to WM_QUERYENDSESSION and change a form member FShuttingDown from FALSE to TRUE. In the form's QueryClose event, instead of setting CanClose to false, you set it to FShuttingDown. This allows the form to close in the event of logoff or shutdown.
> 0xC000 through 0xFFFF - System wide messages (use RegisterWindowMessage API to obtain such a message
number);
I actually did read this in the course of my researches but it didn't sink in.
Constants are compiled into the code segment rather than the data segment, so it is flat-out impossible to modify them. Even if you figure out where they are, attempting to write to this memory will quite correctly cause an access violation. So much for that idea.
TOndrej is on the right track; to paraphrase him, "dispatch it yourself".
However, HookMainWindow() is going a bit far. The dispatching can be done in the Application.OnMessage handler. Moreover, I suspect that most of us are going to make my mistake and use classic Delphi method binding before discovering that WM_APP + x won't broadcast. So I propose this adaptation of TOndrej's approach:
1. Remove "message MY_MESSAGEn;" from the method declarations of those messages that need runtime binding.
2. Declare the message names as public variables of the unit.
3. Register the message names in the initalisation section of the unit, assigning the message id's to these variables.
4. Put a TApplicationEvents widget on the main form and implement its Message event.
5. In the message event, perform the dispatching.
if (tagMsg.Message = MY_MESSAGE1) then
MY_MESSAGE1_Handler(TMyMes
else if (tagMsg.Message = MY_MESSAGE2) then
MY_MESSAGE2_Handler(TMyMes
...
You would NOT deregister the message names anywhere as this would cause problems for other applications using the message.
I have tried the approach I describe and it works perfectly.
As a related aside, I had a window that set CanClose to false and minimised to the systray instead. This prevents system shutdown. The solution is to bind a method to WM_QUERYENDSESSION and change a form member FShuttingDown from FALSE to TRUE. In the form's QueryClose event, instead of setting CanClose to false, you set it to FShuttingDown. This allows the form to close in the event of logoff or shutdown.
ASKER
You can't pass a cardinal - or anything else.
procedure ...; message MESSAGE_CONSTANT;
is resolved at compile time and the compiler won't countenance anything but a true constant.
procedure ...; message MESSAGE_CONSTANT;
is resolved at compile time and the compiler won't countenance anything but a true constant.
ASKER
const
BBCC_NEWMESSAGE:integer = WM_APP;
...
initialization
BBCC_NEWMESSAGE := RegisterWindowMessage('BBC
which I have already tried.
Is there a way to dynamically bind message id's that doesn't involve Application.OnMessage?