TN006: Message Maps
The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.
The latest version of this topic can be found at TN006: Message Maps.
This note describes the MFC message map facility.
The Problem
Microsoft Windows implements virtual functions in window classes that use its messaging facility. Due to the large number of messages involved, providing a separate virtual function for each Windows message would create a prohibitively large vtable.
Because the number of system-defined Windows messages changes over time, and because applications can define their own Windows messages, message maps provide a level of indirection that prevents interface changes from breaking existing code.
Overview
MFC provides an alternative to the switch statement that was used in traditional Windows-based programs to handle messages sent to a window. A mapping from messages to methods can be defined so that when a message is received by a window, the appropriate method is called automatically. This message-map facility is designed to resemble virtual functions but has additional benefits not possible with C++ virtual functions.
Defining a Message Map
The DECLARE_MESSAGE_MAP macro declares three members for a class.
A private array of
AFX_MSGMAP_ENTRY
entries called_messageEntries
.A protected
AFX_MSGMAP
structure calledmessageMap
that points to the_messageEntries
array.A protected virtual function called
GetMessageMap
that returns the address ofmessageMap
.
This macro should be put in the declaration of any class using message maps. By convention, it is at the end of the class declaration. For example:
class CMyWnd : public CMyParentWndClass
{ *// my stuff...
protected: *//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
*//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
This is the format generated by AppWizard and ClassWizard when they create new classes. The //{{ and //}} brackets are needed for ClassWizard.
The message map's table is defined by using a set of macros that expand to message map entries. A table starts with a BEGIN_MESSAGE_MAP macro call, which defines the class that is handled by this message map and the parent class to which unhandled messages are passed. The table ends with the END_MESSAGE_MAP macro call.
Between these two macro calls is an entry for each message to be handled by this message map. Every standard Windows message has a macro of the form ON_WM_MESSAGE_NAME that generates an entry for that message.
A standard function signature has been defined for unpacking the parameters of each Windows message and providing type safety. These signatures may be found in the file Afxwin.h in the declaration of CWnd. Each one is marked with the keyword afx_msg
for easy identification.
Note
ClassWizard requires that you use the afx_msg
keyword in your message map handler declarations.
These function signatures were derived by using a simple convention. The name of the function always starts with "On
". This is followed by the name of the Windows message with the "WM_" removed and the first letter of each word capitalized. The ordering of the parameters is wParam
followed by LOWORD
(lParam
) then HIWORD
(lParam
). Unused parameters are not passed. Any handles that are wrapped by MFC classes are converted to pointers to the appropriate MFC objects. The following example shows how to handle the WM_PAINT
message and cause the CMyWnd::OnPaint
function to be called:
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass) *//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT() *//}}AFX_MSG_MAP
END_MESSAGE_MAP()
The message map table must be defined outside the scope of any function or class definition. It should not be put in an extern "C" block.
Note
ClassWizard will modify the message map entries that occur between the //{{ and //}} comment bracket.
User Defined Windows Messages
User-defined messages may be included in a message map by using the ON_MESSAGE macro. This macro accepts a message number and a method of the form:
afx_msg LRESULT OnMyMessage(WPARAM wParam,
LPARAM lParam);
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd,
CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE,
OnMyMessage)
END_MESSAGE_MAP()
In this example, we establish a handler for a custom message that has a Windows message ID derived from the standard WM_USER
base for user-defined messages. The following example shows how to call this handler:
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
The range of user-defined messages that use this approach must be in the range WM_USER
to 0x7fff.
Note
ClassWizard does not support entering ON_MESSAGE
handler routines from the ClassWizard user interface. You must manually enter them from the Visual C++ editor. ClassWizard will parse these entries and let you browse them just like any other message-map entries.
Registered Windows Messages
The RegisterWindowMessage function is used to define a new window message that is guaranteed to be unique throughout the system. The macro ON_REGISTERED_MESSAGE
is used to handle these messages. This macro accepts a name of a UINT NEAR
variable that contains the registered windows message ID. For example
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
*//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam,
LPARAM lParam);
*//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd,
CMyParentWndClass) *//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND,
OnFind) *//}}AFX_MSG_MAP
END_MESSAGE_MAP()
The registered Windows message ID variable (WM_FIND in this example) must be a NEAR
variable because of the way ON_REGISTERED_MESSAGE
is implemented.
The range of user-defined messages that use this approach will be in the range 0xC000 to 0xFFFF.
Note
ClassWizard does not support entering ON_REGISTERED_MESSAGE
handler routines from the ClassWizard user interface. You must manually enter them from the text editor. ClassWizard will parse these entries and let you browse them just like any other message-map entries.
Command Messages
Command messages from menus and accelerators are handled in message maps with the ON_COMMAND
macro. This macro accepts a command ID and a method. Only the specific WM_COMMAND
message that has a wParam
equal to the specified command ID is handled by the method specified in the message-map entry. Command handler member functions take no parameters and return void
. The macro has the following form:
ON_COMMAND
(id, memberFxn)
Command update messages are routed through the same mechanism, but use the ON_UPDATE_COMMAND_UI
macro instead. Command update handler member functions take a single parameter, a pointer to a CCmdUI object, and return void
. The macro has the form
ON_UPDATE_COMMAND_UI
(id, memberFxn)
Advanced users can use the ON_COMMAND_EX
macro, which is an extended form of command message handlers. The macro provides a superset of the ON_COMMAND
functionality. Extended command-handler member functions take a single parameter, a UINT
that contains the command ID, and return a BOOL
. The return value should be TRUE
to indicate that the command has been handled. Otherwise routing will continue to other command target objects.
Examples of these forms:
- Inside Resource.h (usually generated by Visual C++)
#define ID_MYCMD 100
#define ID_COMPLEX 101
- Inside the class declaration
afx_msg void OnMyCommand();
afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
afx_msg BOOL OnComplexCommand(UINT nID);
- Inside the message map definition
ON_COMMAND(ID_MYCMD,
OnMyCommand)
ON_UPDATE_COMMAND_UI(ID_MYCMD,
OnUpdateMyCommand)
ON_COMMAND_EX(ID_MYCMD,
OnComplexCommand)
- In the implementation file
void CMyClass::OnMyCommand()
{ *// handle the command
}
void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI)
{ *// set the UI state with pCmdUI
}
BOOL CMyClass::OnComplexCommand(UINT nID)
{ *// handle the command
return TRUE;
}
Advanced users can handle a range of commands by using a single command handler: ON_COMMAND_RANGE or ON_COMMAND_RANGE_EX
. See the product documentation for more information about these macros.
Note
ClassWizard supports creating ON_COMMAND
and ON_UPDATE_COMMAND_UI
handlers, but it does not support creating ON_COMMAND_EX
or ON_COMMAND_RANGE
handlers. However, Class Wizard will parse and let you browse all four command handler variants.
Control Notification Messages
Messages that are sent from child controls to a window have an extra bit of information in their message map entry: the control's ID. The message handler specified in a message map entry is called only if the following conditions are true:
The control notification code (high word of
lParam
), such as BN_CLICKED, matches the notification code specified in the message-map entry.The control ID (
wParam
) matches the control ID specified in the message-map entry.
Custom control notification messages may use the ON_CONTROL macro to define a message map entry with a custom notification code. This macro has the form
ON_CONTROL(wNotificationCode,
id,
memberFxn)
For advanced usage ON_CONTROL_RANGE can be used to handle a specific control notification from a range of controls with the same handler.
Note
ClassWizard does not support creating an ON_CONTROL
or ON_CONTROL_RANGE
handler in the user interface. You must manually enter them with the text editor. ClassWizard will parse these entries and let you browse them just like any other message map entries.
The Windows Common Controls use the more powerful WM_NOTIFY for complex control notifications. This version of MFC has direct support for this new message by using the ON_NOTIFY
and ON_NOTIFY_RANGE
macros. See the product documentation for more information about these macros.