Webocreation

Monday, November 23, 2009

Message Mapping vs. Virtual Functions

Message Mapping vs. Virtual Functions

Once a message handler is created, it can be called by the application. In a purely object-oriented C++ environment, handlers are implemented as virtual member functions. MFC uses message maps to simulate this behavior. The message map avoids the lengthy virtual function tables that would be required if every class had a virtual function for every possible message it might receive. To maximize performance, the most current message map entries are cached by the application framework.

Note that there are a few handlers that are implemented as virtual member functions in MFC, such as CView::OnDraw.

d 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 message entries it contains. Since it's extremely rare for a developer to implement a window class that includes handlers for all of the different types of messages, message mapping conserves a few hundred bytes of memory just about every time a CWnd is wrapped around an HWnd.

Message Mapping SystemThe message mapping system used by MFC is made up of two basic components: the CCmdTarget class and message maps. These two pieces work together to provide the same message handling capabilities as a regular Windows-based application.

The CCmdTarget class is the base class for any object that needs to receive Windows messages, command messages, or both. Any class derived from CCmdTarget can use a message map and only classes derived from CCmdTarget can receive messages. If you look at The MFC Class Hierarchy illustrations in Chapter 2, you will notice that a number of classes are derived from CCmdTarget, including CWnd, CDocument, and CWinApp.

A message map is the mechanism that connects a Windows message to the class member functions that are handling the message. In this section, you will look at several key elements of message maps so you can gain an understanding of how message maps work. This section includes the following topics:

Message Map Macros

MFC provides three macros to generate message maps: DECLARE_MESSAGE_MAP, BEGIN_MESSAGE_MAP, and END_MESSAGE_MAP. These macros expand into code that defines and implements a message map for a CCmdTarget-based class.

When using message maps in your classes, the basic strategy is to include the DECLARE_MESSAGE_MAP macro in your class header file, xxx.h, and to add the message mapping entries enclosed by the BEGIN_MESSAGE_MAP and END_MESSAGE_MAP macros to your implementation file, xxx.cpp.

The preprocessor uses the message map macros to generate message mapping support code. When used together in an implementation file, these macros actually implement the message map. The definitions for the message map macros found in the Afxwin.h file will help you understand how the message mapping system works.

To see the definition of the DECLARE_MESSAGE_MAP macro, click this icon.

#define DECLARE_MESSAGE_MAP() \

private: \

static const AFX_MSGMAP_ENTRY_messageEntries(); \

protected: \

static const AFX_MSGMAP messageMap; \

virtual const AFX_MSGMAP* GetMessageMap() const;

To see the definition of the BEGIN_MESSAGE_MAP macro, click this icon.

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \

const AFX_MSGMAP* the Class::GetMessageMap() const \
{ return & the Class::messageMap; } \
AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
const AFX_MSGMAP_ENTRY theClass::_messageEntries[ ] = \
{
To see the definition of the END_MESSAGE_MAP macro, click this icon.

#define END_MESSAGE_MAP() \

{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
};

The message map entries (_messageEntries) and the message map structure (messageMap) are static members of the class. These elements of the message map are described in detail in the topics that follow.

Message Handlers

A message handler is a function that is called when a particular message is sent to an application window. MFC provides a number of macros that can be used to connect messages to their handlers in the message map. The following table lists common Windows messages and their corresponding macros and handlers.

Windows message Macro Message handler


WM_PAINT ON_WM_PAINT OnPaint

WM_CREATE ON_WM_CREATE OnCreate

WM_RBUTTONDOWN ON_WM_RBUTTONDOWN OnRButtonDown


The ON_WM_xxx macros are hard coded to link the Windows messages to the corresponding MFC message handlers. Every standard Windows message has a macro of the form ON_WM_xxx, where xxx is the name of the message. A simple convention is used to generate the name of the message handler function. The name of the handler function starts with "On." This is followed by the name of the message with the "WM_" removed and only the first letter of each word capitalized. Thus, the ON_WM_PAINT macro corresponds to the OnPaint message handler, as shown in the preceding table.

Some message handlers have parameters that provide additional information for processing the message. You can refer to the MFC online documentation to determine what kinds of parameters you can pass to a particular message handler and what kind of values it returns. For example, the OnLButtonDblClk message handler is prototyped like this:

afx_msg void CMsgView::OnLButtonDblClk(UINT nFlags, CPoint point)

The nFlags argument specifies the states of the mouse buttons as well as the states of the CTRL and SHIFT keys, and the point argument 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 Windows message. The wParam and lParam parameters are of necessity generic, whereas the parameters passed to an MFC handler are both specific and type-safe.

Note afx_msg is appended to the handler for use by ClassWizard. It currently evaluates to nothing and has no effect upon the execution of the application.

For more information, see "Message Map Macros" in the Visual C++ online documentation.

Message Map Entry Macros

In your source files, a message map consists of a sequence of predefined macros. The macros inside the message map are called "entry macros." The entry macros used in a message map depend upon the category of the message to be handled.

Each entry macro accepts zero or more parameters as predefined by the MFC Library. The following table summarizes the various kinds of entry macros used in message maps.

Message type Macro form Parameters


Predefined Windows message ON_WM_XXX None

Command message ON_COMMAND Command ID, Handler name

Update command ON_UPDATE_COMMAND_UI Command ID, Handler name

Control notification ON_XXX Control ID, Handler name

User-defined message ON_MESSAGE User-defined message ID, Handler name

Registered Windows message ON_REGISTERED_MESSAGE Registered message ID variable, Handler name

A range of command IDs ON_COMMAND_RANGE Start and end of a contiguous range of command IDs

A range of command IDs for updating ON_UPDATE_COMMAND_UI_RANGE Start and end of a contiguous range of command IDs

A range of control IDs ON_CONTROL_RANGE A control-notification code and the start and end of a contiguous range of command IDs


The following example code shows a message map for the CMyView class using several common entry macros with their parameters:

BEGIN_MESSAGE_MAP(CMyView, CView)
//{{AFX_MSG_MAP(CMyView)
ON_WM_MOUSEACTIVATE()
ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll)
ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR_ALL, OnUpdateEditClearAll)
ON_BN_CLICKED(ID_MY_BUTTON, OnMyButton)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

For detailed information about message map entry macros and their parameters, see "Technical Note 6: Message Maps," available in the Microsoft Foundation Class Reference section of the Visual C++ online documentation.

How Message Maps Work

In this topic, you'll take a look at what goes on behind the scenes when you use message mapping macros in your code, and how MFC uses the code and data that are generated by the macros to convert messages into calls to the corresponding message handlers.

When you use the DECLARE_MESSAGE_MAP macro in the class declaration, it adds three members to the class:

® An array of data structures that contain information to link messages to message handlers

® A static data structure that contains a pointer to the array of message map entry macros for the class and a pointer to the message map for the base class

® A virtual function named GetMessageMap

Where the Message Map Begins

Let's look at an example. The following example code is the class declaration for the application class CMyClass:
class CMyClass : public CWinApp{
...
DECLARE_MESSAGE_MAP
};

Based on this class declaration, the preprocessor generates the following example code for message mapping:

private: static const AFX_MSGMAP_ENTRY _messageEntries[];
protected: static const AFX_MSGMAP messageMap;
virtual const AFX_MSGMAP* GetMessageMap() const;

To generate preprocessor output, use the cl /P filename on a command line. It writes preprocessor output to a file with the same base name as the source file, but with the .i extension.

The first statement from the code generated by the preprocessor is the _messageEntries array and contains information linking messages to message handlers. The next statement is the messageMap structure, which contains a pointer to the class's _messageEntries array and a pointer to the base class's messageMap structure. The last statement adds the virtual function GetMessageMap and returns the address of the messageMap for the base class.

Where the Message Macros Fit In
The BEGIN_MESSAGE_MAP macro 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.

Message Mapping Data Structures
To keep things simple, the implementation details of the message mapping data structures and functions are not shown here. You can look at the definitions of the message map macros in the Afxwin.h file and examine the code in the source files to find out more about the message mapping data structures.

Putting It All Together
The following illustration shows how the key elements of a message map fit together and indicates the path the framework follows to search for a message handler to match a given message ID.



To start processing a message, the framework calls the GetMessageMap function to get a pointer to CMainFrame's message map structure. It then scans the messageEntries array to see if there is a handler for the message. In the preceding illustration, the message being processed is WM_PAINT. If necessary, GetMessageMap can use the pointer to the base class (in this case, the CFrameWnd class) to search its message map to see if there is a message handler available. GetMessageMap will continue to search through the class hierarchy for a message handler until it reaches the last base class. The pointer for the last base class in the hierarchy is NULL. If GetMessageMap gets a NULL pointer, no message handler was found and the message is passed to Windows for processing

No comments:

Post a Comment