Remember what happens when you broadcast a message

Raymond Chen

Occasionally I catch people doing things like broadcasting a WM_COMMAND message to all top-level windows. This is one of those things that is so obviously wrong I don’t see how people even thought to try it in the first place.

Suppose you broadcast the message

SendMessage(HWND_BROADCAST, WM_COMMAND, 100, 0);

What happens?

Every top-level window receives the message with the same parameters, and every top-level window starts interpreting those parameters in their own idiosyncratic way. As you know (since you’ve written them yourself), each window procedure defines its own menu items and child windows and there is no guarantee that command 100 will mean the same thing to each window. A dialog box with the template

#define IDC_USEDEFAULT 100
...
    AUTORADIOBUTTON "Use &default color",
                    IDC_USEDEFAULT, 14, 38, 68, 10, WS_TABSTOP

would interpret the message as

id  IDC_USEDEFAULT (100)
command  BN_CLICKED (0)
window  NULL (0) — illegal parameter

Depending on how the dialog procedure is written, it might try to send a message back to the button control (and fail since you passed NULL as the window handle), or it might update some dialog state like disabling the color customization controls (since it was told that the user clicked the “User default color” radio button).

Another dialog box might have the template

#define IDC_CHANGE 100
...
    PUSHBUTTON      "C&hange", IDC_CHANGE, 88, 95, 50, 14

This dialog procedure would interpret the message as

id  IDC_CHANGE (100)
command  BN_CLICKED (0)
window  NULL (0) — illegal parameter

The reaction would probably be to apply the changes that were pending in the dialog.

Meanwhile, another window might have a menu that goes like this:

#define IDC_REFRESH 100
...
        MENUITEM "&Refresh", IDC_REFRESH

It is going to interpret the message as the user having selected “Refresh” from the window menu.

id  IDC_REFRESH (100)
command  0 — illegal parameter, must be 1 for menu items
window  NULL (0)

Not only is the command code invalid for a menu item, the window might be in a state where the program had disabled the “Refresh” option. Yet you sent the window a message as if to say that the user selected it anyway, which is impossible. Congratulations, you just presented the program with an impossible situation and it very well may crash as a result. For example, the program may have disabled the “Refresh” option since there is no current object to refresh. When you send it the “Refresh” command, it will try to refresh the current object and crash with a null pointer error.

Obviously, then, you cannot broadcast the WM_COMMAND message since there is no universal meaning for any of the command IDs. A command ID that means “Refresh” to one window might mean “Change” to another.

The same logic applies to nearly all of the standard Windows messages. The ones that are actually designed to be broadcast are as follows:

WM_SYSCOLORCHANGE
WM_SETTINGCHANGE (= WM_WININICHANGE)
WM_DEVMODECHANGE
WM_FONTCHANGE
WM_TIMECHANGE
WM_DDE_INITIATE

If you try to broadcast a message in the WM_USER or WM_APP ranges, then you’re even crazier than I thought. As we’ve already seen, the meaning of window messages in those ranges are defined by the window class or the application that created the window. Not only are the parameters to the message context-sensitive, the message itself is! This means that sending a random window a WM_USER+1 message (say) will result in extremely random behavior. (We saw this before in the context of broadcasts, but it applies to directed delivery, too.) If it’s a dialog box, it will think you sent a DM_SETDEFID message, and you just changed that dialog’s default ID. If it’s a common dialog box, it will think you sent a WM_CHOOSEFONT_GETLOGFONT message, and if you’re lucky, it will crash trying to return the LOGFONT through an invalid pointer. (If you’re not lucky, the parameter you passed will happen to be a valid pointer and the program will merely corrupt its own memory in some strange way, only to behave erratically later on.) If it’s a tooltip control, then you just sent it the TTM_ACTIVATE message and you just manipulated the tooltip’s activation state.

The same caution applies, using the same logic, to sending messages without universal meaning to windows whose window class you do not have an interface contract with. For example, I’ll see people sending the PSM_PRESSBUTTON message to a window on the blind-faith assumption that it is a property sheet.

Remember, then, that when you send a message to a window, you need to be sure that the window will interpret it in the manner you intend.

0 comments

Discussion is closed.

Feedback usabilla icon