(based on "Programming Windows with MFC")
The Message Map

How is it that a WM_PAINT message from Windows turns into a call to CMainWindow::OnPaint? The answer lies in the message map. A message map is a table that correlates messages and member functions. When Hello's frame window receives a message, MFC scans the window's message map, sees that a handler exists for WM_PAINT messages, and calls OnPaint. The message map is MFC's way of avoiding the lengthy vtables that would be required if every class had a virtual function for every possible message it might receive. Any class derived from CCmdTarget can contain a message map. What MFC does internally to implement message maps is hidden behind some rather complex macros, but using a message map is exceedingly simple. Here's all you have to do to add a message map to a class:
 

Declare the message map by adding a DECLARE_MESSAGE_MAP statement to the class declaration.
 

Implement the message map by placing macros identifying the messages that the class will handle between calls to BEGIN_MESSAGE_MAP and END_MESSAGE_MAP.
 

Add member functions to handle the messages.

Hello's CMainWindow class handles just one message type, WM_PAINT, so its message map is implemented as follows:

BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
    ON_WM_PAINT ()
END_MESSAGE_MAP ()

 
 

BEGIN_MESSAGE_MAP begins the message map and identifies both the class to which the message map belongs and the base class. (Message maps are passed by inheritance just as other class members are. The base class name is required so that the framework can find the base class's message map when necessary.) END_MESSAGE_MAP ends the message map. In between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP are the message map entries. ON_WM_PAINT is a macro defined in the MFC header file Afxmsg_.h. It adds an entry for WM_PAINT messages to the message map. The macro accepts no parameters because it is hardcoded to link WM_PAINT messages to the class member function named OnPaint. MFC provides macros for more than 100 Windows messages, ranging from WM_ACTIVATE to WM_WININICHANGE. You can get the name of the message handler that corresponds to a given ON_WM macro from the MFC documentation, but it's fairly easy to deduce the name yourself by replacing WM_ with On and converting all the remaining letters except those at the beginning of the word to lowercase. Thus, WM_PAINT becomes OnPaint, WM_LBUTTONDOWN becomes OnLButtonDown, and so on.

You'll need to consult the MFC documentation to determine what kinds of arguments a message handler receives and what type of value it returns. OnPaint takes no arguments and returns no value, but OnLButtonDown is prototyped like this:

afx_msg void OnLButtonDown (UINT nFlags, CPoint point)

 
 

nFlags contains bit flags specifying the state of the mouse buttons and the Ctrl and Shift keys, and point identifies the location at which the click occurred. The arguments passed to a message handler come from the wParam and lParam parameters that accompanied the message. But whereas wParam and lParam are of necessity generic, the parameters passed to an MFC message handler are both specific and type-safe.

What happens if you want to process a message for which MFC doesn't provide a message-map macro? You can create an entry for the message using the generic ON_MESSAGE macro, which accepts two parameters: the message ID and the address of the corresponding class member function. The following statement maps WM_SETTEXT messages to a member function named OnSetText:

ON_MESSAGE (WM_SETTEXT, OnSetText)
 
 

OnSetText would be declared like this:

afx_msg LRESULT OnSetText (WPARAM wParam, LPARAM lParam);
 
 

Other special-purpose message-map macros provided by MFC include ON_COMMAND, which maps menu selections and other UI events to class member functions, and ON_UPDATE_COMMAND_UI, which connects menu items and other UI objects to "update handlers" that keep them in sync with the internal state of the application. You'll be introduced to these and other message-map macros in the chapters that follow.

Getting back to Hello for a moment, CMainWindow's OnPaint function and message map are declared with the following statements in Hello.h:

afx_msg void OnPaint ();
DECLARE_MESSAGE_MAP ()
 
 

afx_msg is a visual reminder that OnPaint is a message handler. You can omit it if you'd like because it reduces to white space when compiled. The term afx_msg is meant to connote a function that behaves as if it were a virtual function but does so without requiring a vtable entry. DECLARE_MESSAGE_MAP is usually the final statement in the class declaration because it uses C++ keywords to specify the visibility of its members. You can follow DECLARE_MESSAGE_MAP with statements declaring other class members, but if you do, you should also lead off with a public, protected, or private keyword to ensure the visibility you want for those members.

How Message Maps Work
You can find out how message maps work by examining the DECLARE_MESSAGE_MAP, BEGIN_MESSAGE_MAP, and END_MESSAGE_MAP macros in Afxwin.h and the code for CWnd::WindowProc in Wincore.cpp. Here's a synopsis of what goes on under the hood when you use message-mapping macros in your code, and how the framework uses the code and data generated by the macros to convert messages into calls to corresponding class member functions.

MFC's DECLARE_MESSAGE_MAP macro adds three members to the class declaration: a private array of AFX_MSGMAP_ENTRY structures named _messageEntries that contains information correlating messages and message handlers; a static AFX_MSGMAP structure named messageMap that contains a pointer to the class's _messageEntries array and a pointer to the base class's messageMap structure; and a virtual function named GetMessageMap that returns messageMap's address. (The macro implementation is slightly different for an MFC application that's dynamically rather than statically linked to MFC, but the principle is the same.) BEGIN_MESSAGE_MAP contains the implementation for the GetMessageMap function and code to initialize the messageMap structure. The macros that appear between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP fill in the _messageEntries array, and END_MESSAGE_MAP marks the end of the array with a NULL entry. For the statements

// In the class declaration
DECLARE_MESSAGE_MAP ()

// In the class implementation
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
    ON_WM_PAINT ()
END_MESSAGE_MAP ()
 
 

the compiler's preprocessor generates this:

// In the class declaration
private:
    static const AFX_MSGMAP_ENTRY _messageEntries[];
protected:
    static const AFX_MSGMAP messageMap;
    virtual const AFX_MSGMAP* GetMessageMap() const;

// In the class implementation
const AFX_MSGMAP* CMainWindow::GetMessageMap() const
    { return &CMainWindow::messageMap; }

const AFX_MSGMAP CMainWindow::messageMap = {
    &CFrameWnd::messageMap,
    &CMainWindow::_messageEntries[0]
};

const AFX_MSGMAP_ENTRY CMainWindow::_messageEntries[] = {
    { WM_PAINT, 0, 0, 0, AfxSig_vv,
        (AFX_PMSG)(AFX_PMSGW)(void (CWnd::*)(void))OnPaint },
    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};

 
 

With this infrastructure in place, the framework can call GetMessageMap to get a pointer to CMainWindow's messageMap structure. It can then scan the _messageEntries array to see if CMainWindow has a handler for the message, and if necessary it can grab a pointer to CFrameWnd's messageMap structure and scan the base class's message map, too.

That's a pretty good description of what happens when a message for CMainWindow arrives. To dispatch the message, the framework calls the virtual WindowProc function that CMainWindow inherits from CWnd. WindowProc calls OnWndMsg, which in turn calls GetMessageMap to get a pointer to CMainWindow::messageMap and searches CMainWindow::_messageEntries for an entry whose message ID matches the ID of the message that is currently awaiting processing. If the entry is found, the corresponding CMainWindow function (whose address is stored in the _messageEntries array along with the message ID) is called. Otherwise, OnWndMsg consults CMainWindow::messageMap for a pointer to CFrameWnd::messageMap and repeats the process for the base class. If the base class doesn't have a handler for the message, the framework ascends another level and consults the base class's base class, systematically working its way up the inheritance chain until it finds a message handler or passes the message to Windows for default processing. Figure 1-5 illustrates CMainWindow's message map schematically and shows the route that the framework travels as it searches for a handler to match a given message ID, beginning with the message map entries for CMainWindow.

What MFC's message-mapping mechanism amounts to is a very efficient way of connecting messages to message handlers without using virtual functions. Virtual functions are not space-efficient because they require vtables, and vtables consume memory even if the functions in them are not overridden. The amount of memory used by a message map, in contrast, is proportional to the number of entries it contains. Since it's extremely rare for a programmer to implement a window class that includes handlers for all of the different message types, message mapping conserves a few hundred bytes of memory just about every time a CWnd is wrapped around an HWND.

 

Figure 1-5. Message-map processing.