Shaikh Sonny Aman’s Blog

Lets learn and share!

Why message maps, How message maps

Posted on | August 8, 2007 |

( The article is taken from Kanitkar’s book)

//comserver visitors … :D )

Instead of using message maps we can think of using virtual functions to convert a message into a call to an appropriate handler. To understand how virtual functions can be used, look at the following code segment:

Class CFrameWnd

{

virtual void OnPaint(){}

};

Class myframe : public CFrameWnd

{

Myframe()

{

Create(0,”Draw”);

}

Void OnPaint(){}

};

class myapp : public CWinApp

{

public:

BOOL InitInstance()

{

Myframe *p;

p = new myframe;

m_pMainWnd = p;

p->ShowWindow(SW_SHOWMAXIMIZED);

return TRUE;

}

};

myapp a;

Here, myapp is the application class and a is the global application object. myframe is the frame window class. The compiler creates a table called VTABLE for each class that contains virtual functions and for the classes derived from it. The compiler places the address of the virtual functions for the particular class in the VTRABLE. If you don’t redefine a function that was declared virtual in the base class, the compiler uses the address of the base class version in the derived class’s VTABLE. When objects of the base class or derived class are created the compiler secretly places a pointer called vpointer (abbreviated as vptr) in the object. This pointer points to the class’s VTABLE.

The vptr is placed at the beginning of the object and is initialized to point to the starting address of its class’s VTABLE. This initialization is done in the constructor. All of this – setting up the VTABLE for each class, initializing the vptr, inserting the code for the virtual function call – all happens automatically. The VTABLEs for the two classes CFrameWnd and myframe are shown in the following figure:

VTABLE of CFrameWnd

VTABLE of myframe

&CFrameWnd::OnPaint

&myframe::OnPaint

In the 16-bit version of MFC, the MFC window classes were registered with AfxWndProc() as the message handler. In this function all the messages used to get handled. Since the 32-bit version of MFC, instead of AfxWndProc () the MFC window classes are registered with DefWindowProc () as a window procedure. Even now all the messages are ultimately routed to AfxWndProc () from where they are dispatched to various CWnd-derived objects. Why all messages still end up in AfxWndProc() has something to do with MFC’s message hook mechanism

Had a virtual function been used for converting a message into a call to the appropriate handler, the window procedure would have looked like this:

CFrameWnd *p;

p = AfxGetApp()->m_pMainWnd;

p->OnPaint();

Here, AfxGetApp () fetches the address of the global application object (created from myapp). m_pMainWnd is a public data member of the CWinApp class. This data member contains the address of the myframe object assuming that the window is built using the myframe class. In the statement,

p = AfxGetApp()->m_pMainWnd

we are storing the address of a derived class object in a pointer to the base class object. When we call the OnPaint () it must be determined whether the OnPaint () handler CFrameWnd class or one in the myframe class should get called. To determine this, contents of p issued. p contains address of the myframe object. First two bytes starting at this address contain the vptr. The vptr points to the address of the VTABLE belonging to myframe class. From this VTABLE, the address of myframe::OnPaint() is obtained and the control is transferred to this address. Thus, it is the derived class’s (myframe’s) OnPaint() which gets called. Had OnPaint() not been declared as virtual in CFrameWnd class then through

p->OnPaint();

the CFrameWnd::OnPaint () would have been called.

This in effect means that for every possible message that CFrameWnd object is going to receive there must be a virtual function in it. Otherwise we would never be able to override it in a class derived from CFrameWnd. This would lead to a huge virtual table. Message maps are a MFC’s way avoiding this lengthy virtual table.

Another difficulty with virtual tables arises in case of user-defined messges. Suppose we write a handler, say fun() in the myframe class. And now to call it if we say p->fun(), an error will result. This is because fun() has not been defined as virtual in the CFrameWnd class. The difficulty is we do not have the source code of the CFrameWnd class hence cannot make the virtual declaration. In effect it means that we cannot tackle user-defined messages through the mechanism of virtual functions.

Let’s now understand the mechanism of message maps. Any class derived from CCmdTarget can contain a message map. What MFC does internally to implement a message map hidden behind some rather complex macros. To understand the message mechanism consider the following small code snippet:

class myframe:public CFrameWnd

{

Void OnPaint(){}

DECLARE_MESSAGE_MAP()

};

BEGIN_MESSAGE_MAP()

ON_WM_PAINT()

END_MESSAGE_MAP()

The macros DECLARE_MESSAGE_MAP(),BEGIN_MESSAGE_MAP() AND END_MESSAGE_MAP() have been defined in the file ‘afxwin.h’. During preprocessing the DECLARE_MESSAGE_MAP macro adds three members to the class declaration:

(a) a private array of structures called _messageEntries[]. Each element of this array is a structure called AFX_MSGMAP_ENTRY. In addition to other elements, this structure contains a message id and a pointer to a function that should get called when a message with this id arrives.

(b) a structure called messageMap. This structure contains a pointer to class’s _messageEntries[] aray and a pointer to base class/s messageMap structure.

( c) A virtual function called GetMessageMap () that returns messageMap structure’s address.

Not that the _messageEntries[] arrray and the messageMap structure are static members of the class. This means there is one instance of them. The begin BEGIN_MESSAGE_MAP() macro contains the implementation of the GetMessageMap() function and the code to initialize the messageMap structure. The macros that appears between BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP() fill in the _messageEntries[] array. Finally,END_MESSAGE_MAP() marks the end of the _messageEntries[] array with a NULL entry.

With this infrastructure in place let us now understand how a message passed to the application window gets converted into a call to the appropriate message handler.

Suppose a message, say WM_PAINT, gets posted in the message queue. This message would be retrieved by the GetMEssage() function and would be dispatched to the AfxWndProc() function by the DispatchMessage() function.

Inside the AfxWndProc() function, the framework retrieves the C++ object associated with the window handle using CWnd::FromHandlePermanent() function. AfxWndProc() then calls that object’s ( the one derived using FromHandlePermanent() ) WindowProc() function. The WindowProc() function gets the address of myframe’s messageMap structure.

As discussed earlier, this structure contains the address of _messageEntrioes[] array. The id of the message picked up from the message queue is now searched in this array. If the entry is found then the handler corresponding to it is immediately called (as you may recollect, the _messageEntries[] contains message ids and the names of their handlers).

If the message id is not found in the _messageEntries[] array of the mainframe class then the _messageEntries[] array of the myframe’s base class is searched.

But how can we reach the _messageEntries[] array of base class? This is achieved using the pointer to the base class’s messageMap structure, which is stored in myframe’s messageMap structure. If the base class does not have the handler for the message the framework ascends another level and consults base class’s base class, systematically working its way to the inheritance chain until it finds the message handler or it has reached the father of all classes. If it still cannot find the message handler then the framework passes the misusage to Windows for default processing.

The MFC’s message mapping mechanism amounts to an efficient way of connecting messages to message handlers without using virtual functions. In contrast to virtual tables the amount of memory used by message maps is proportional to the number of message entries it contains.

Comments

One Response to “Why message maps, How message maps”

  1. gonBembreemn
    December 30th, 2008 @ 9:25 pm

    tkbwbrrhbqjzxbzswell, hi admin adn people nice forum indeed. how’s life? hope it’s introduce branch ;)

Leave a Reply