How do MFC Message Handlers Work?
Whenever your window receives a message, MFC will call a member function of your class. But how does MFC know what function to call?
MFC uses a technique called Message Maps. A Message Map is a table that associates messages with functions. When you receive a message, MFC will go through your Message Map and search for a corresponding Message Handler. I have showed in Part 1 how you add a Message Handler to the Message Map by using ClassWizard
, but what really happens code-wise?
MFC uses a large set of rather complicated macros that add the Message Map to your classes. When you use ClassWizard
to create a Message Handler, it will first add the function to your class, and add the corresponding macro to your Message Map. For example, examine the following ClassWizard
generated WM_CLOSE
handler:
Message Map: Located in the Class Implementation
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
ON_WM_CLOSE()
END_MESSAGE_MAP()
Function Declaration: Located in the Class Declaration
protected:
afx_msg void OnClose();
DECLARE_MESSAGE_MAP()
Function Implementation: Located in the Class Implementation
void CAboutDlg::OnClose()
{
CDialog::OnClose();
}
By adding a DECLARE_MESSAGE_MAP
statement to the class declaration, MFC adds the required code to declare the message map. The BEGIN_MESSAGE_MAP
tells MFC where the Message Map begins, and identifies your class and its base class. The reason it needs the base class is because Message Handlers are passed through C++ inheritance, just like any other function. END_MESSAGE_MAP
obviously, tells MFC where the Message Map ends. In between these two macros is where your declare the Message Map entry for your Message Handler. MFC has many predefined macros, which associate messages with your member function. Take the ON_WM_CLOSE
macro as an example: It associates the WM_CLOSE
message with your OnClose()
member function. The macro takes no parameters since it always expects a function called OnClose()
which is prototyped as afx_msg void OnClose()
. This method gives you two advantages:
- It is easy to keep track of Message Handlers and the messages they handle.
- MFC screens out any irrelevant and will break up
lParam
and wParam
to parameters relevant to the message.
Also the return value is simplified, and the Message Handler is prototyped according to the message. For example: If the value should always be zero, MFC simplifies the process and allows you to declare the function as a void
, and MFC will be responsible for returning 0
. To find the name of the message handler that correlates with a given Message Handler macro, you should look it up in the MFC documentation.
There are some messages that ClassWizard
doesn't support, but you can manually add your message handler by adding the function and Message Map macro as described above. If you add message-map entries manually, you may not be able to edit them with ClassWizard
later. If you add them outside the bracketing comments //{{AFX_MSG_MAP(classname)
and //}}AFX_MSG_MAP
, ClassWizard
cannot edit them at all. Note that by the same token, ClassWizard
will not touch any entries you add outside the comments, so feel free to add messages outside the comments if you do not want them to be modified. Messages that are not recognized by ClassWizard
, such as message-map ranges, must be added outside the comments.
The All Mighty ON_MESSAGE
Sometimes, you will find yourself trying to handle a message that ClassWizard
doesn't support, and it doesn't have a Message Map macro. MFC has a generic macro just for this kind of situation ON_MESSAGE
. ON_MESSAGE
allows you to handle any message that exists. The prototype of Message Handlers that use ON_MESSAGE
is
afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
where OnMessage
is the name of your handler function. The ON_MESSAGE
macro takes two parameters: The address of the handler, and the message it should handle. For example: The following statement maps WM_GETTEXTLENGTH
to OnGetTextLength()
:
ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)
OnGetTextLength
is prototyped as:
afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);
User-Defined Messages
Sometimes, you will need to communicate between two windows in your application or between two windows from different applications. An easy way to do this is by using User-defined messages. The name "User-defined
" can be confusing at first; you define a User-defined message and not the user of your program. I have stated in Part 1 that messages are identified by numbers, and that Windows predefines standard messages. The way of using predefined messages is to simply use a number. To make sure that you don't conflict with the system defined messages, you should use a number in the range of WM_APP
through 0xBFFF:
#define WM_DELETEALL WM_APP + 0x100
pYourDialog->SendMessage(WM_DELETEALL, 0, 0);
Handling a user-defined message is done with the ON_MESSAGE
macro:
#define WM_DELETEALL WM_APP + 0x100
ON_MESSAGE (WM_DELETEALL, OnDeleteAll)
afx_msg LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam);
LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam){
return somevalue;
}
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
variable that contains the registered Windows message ID. For example:
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
END_MESSAGE_MAP()
The range of user defined messages using this approach will be in the range 0xC000 to 0xFFFF. And you send it using the regular SendMessage()
method:
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
pFindWindow->SendMessage(WM_FIND, lParam, wParam);
History
- 8th June, 2000: Initial version
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.