60. What's the best way to send myself a message in MFC?
Those message maps in MFC do help tidy up a program, but they pose a
problem for an SDK developer coming new to MFC - how to send my own program a
message? This used to be easy in the old days - you just posted yourself a message value in the
range WM_USER + N, and handled the message in your WndProc in the normal way. In
this brave new world however, there are a few changes.
First of all, the value to be used. WM_USER messages have always caused some
subtle bugs, because Windows itself uses a few messages in this range for dialog
management (duh ?!?!). So, Microsoft added the new range beginning with WM_APP,
which was guaranteed to be outside the range used by Windows itself - but
not outside the range that might be used by another app - we'll come to that in
a minute.
Well, that's fine if you're writing an SDK app, but what about MFC message
maps ? Well, the special ON_MESSAGE macro was invented for this. Let's say we
want to send ourselves a message of WM_APP+1000.
We have to add the macro to the message map, being careful to put it outside
the markers for ClassWizard message handlers:
....
ON_WM_INITMENU()
ON_WM_INITMENUPOPUP()
//}}AFX_MSG_MAP
ON_MESSAGE (WM_APP+1000, OnMyMessage)
END_MESSAGE_MAP()
Then we add a prototype for the message to our class header:
afx_msg LRESULT OnMyMessage (WPARAM wParam, LPARAM lParam);
Then we can create a body for the handler, and invoke it by simply posting
ourselves the message with whatever parameters we want:
PostMessage (WM_APP+1000, 1, 27);
This is all fine and dandy. But there's a fly in this ointment - a fly that's
been around since the earliest days of Windows and messages. It is perfectly
possible for one app to broadcast a message to all Windows, and therefore it is
possible for another app to broadcast the same carefully-chosen WM_APP value to
you. Thereby invoking your lovely handler when the event you're looking for has
not happened at all. Oh calamity ! This is especially likely if you choose a
nice round number like I did above...
The solution is to use REGISTERED messages. I moved ALL my self-messaging
code to registered messages a long time ago, and haven't suffered as a result.
This requires a slightly different technique. First we have to
"register" the message with windows using the Windows api call
RegisterWindowMessage. This takes a textual name for the message, and returns an
ID in a special range, which is guaranteed to be unique UNLESS another app calls
RegisterWindowMessage with the same textual key. To lower this already low
probability I use GUIDGEN.EXE (gui) or UUIDGEN.EXE (command line), which ship with the Platform SDK, to generate a
GUID - Globally Unique IDentifier - value, then use this string with
RegisterWindowMessage. I usually add a readable prefix to the GUID just for my
own convenience. By the way, yes, these are the same GUID values that COM uses
for identifying its classes.
static UINT g_uAppBarSlide = 0;
....
g_uAppBarSlide = RegisterWindowMessage
("APPBAR_da725800-71f1-11d4-8437-00c04f9d75f4");
The next thing to note is that the variable used to store the message value
is a module global, NOT a class member. This is because the
ON_REGISTERED_MESSAGE macro requires that. You declare the handler to the system
much as we did before, but now using this new macro:
ON_REGISTERED_MESSAGE (g_uAppBarSlide, OnSlide)
END_MESSAGE_MAP()
Our prototype looks much like it did before:
afx_msg LRESULT OnSlide (WPARAM, LPARAM);
Note that in this case I'm not using the parameters, so I use the type-only
notation. So all I have to do now is to use the value stored in our global to
send the message:
PostMessage (g_uAppBarSlide);