Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

HWND_BROADCAST and custom messages

Posted on 2002-04-18
12
Medium Priority
?
1,580 Views
Last Modified: 2010-08-05
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.cmdNotifyClick(Sender: TObject);
begin
Win32Check(SendNotifyMessage(HWND_BROADCAST,BBCC_NEWMESSAGE,0,0));
end;

procedure TformInjector.NewMessage(var msg: TMessage);
begin
showmessage('BBCCWM_NEWMESSAGE');
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
0
Comment
Question by:wamoz
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
12 Comments
 
LVL 6

Accepted Solution

by:
DrDelphi earned 200 total points
ID: 6952540
From the Windows 32 API help file:


"...If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows. "


and

"Applications that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function to obtain a unique message for inter-application communication."


So... on one hand we see that that HWND_BROADCAST is sending that message to any number of top levels windows, meaning that it could simply be taking a while for you to see the desired result in your application. On the other hand, we see that the message itself (albeit WM_APP+1, WM_USER+1, etc) needs to be registered unique before passing it out HWND_BROADCAST... which actually makes sense, considering there is a finite number of available messages that can be used.


Hope this helps.


Good luck!!
 
0
 
LVL 1

Author Comment

by:wamoz
ID: 6952554
Something I'm rather vague on is how to use RegisterWindowMessage with Delphi, given that the syntax for binding a method to a messageid requires a constant and won't even tolerate

const
  BBCC_NEWMESSAGE:integer = WM_APP;
...
initialization
  BBCC_NEWMESSAGE := RegisterWindowMessage('BBCC_NEWMESSAGE');

which I have already tried.

Is there a way to dynamically bind message id's that doesn't involve Application.OnMessage?
0
 
LVL 1

Author Comment

by:wamoz
ID: 6952562
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.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 1

Author Comment

by:wamoz
ID: 6952582
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?
0
 
LVL 1

Author Comment

by:wamoz
ID: 6952589
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?
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6952606
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!!
0
 
LVL 9

Expert Comment

by:ginsonic
ID: 6952678
interested
0
 
LVL 17

Expert Comment

by:inthe
ID: 6952744
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('Test Message');
0
 
LVL 17

Expert Comment

by:geobul
ID: 6952796
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
0
 
LVL 8

Expert Comment

by:TOndrej
ID: 6953012
> 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(Sender: TObject);
begin
  Application.HookMainWindow(AppMessage);
end;

procedure TFormInjector.FormDestroy(Sender: TObject);
begin
  Application.UnhookMainWindow(AppMessage);
end;

function TFormInjector.AppMessage(var 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
0
 
LVL 1

Author Comment

by:wamoz
ID: 6957227
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(TMyMessage(tagMessage))
      else if (tagMsg.Message = MY_MESSAGE2) then
          MY_MESSAGE2_Handler(TMyMessage(tagMessage))
      ...

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.
0
 
LVL 1

Author Comment

by:wamoz
ID: 6961712
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.
0

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Want to learn how to record your desktop screen without having to use an outside camera. Click on this video and learn how to use the cool google extension called "Screencastify"! Step 1: Open a new google tab Step 2: Go to the left hand upper corn…
Suggested Courses

715 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question