Well, a lot has been said already about message maps and how the handler you wrote in your derived Wnd class gets called. Here I present a simulation on how to understand it from a C++ viewpoint. What the C++ code takes to achieve this is presented below. Of course, I present only the bare bones.
A point to keep in mind is that MFC avoids virtual functions (so will I) for calling handlers. That way, a lot of memory in terms of vTable pointers is saved. You need to have a few virtual functions, though.
Let us take the example of a dialog. A dialog class is derived form CWnd, which in turn is derived from CCmdTarget, the main guy who runs through the message map and invokes the handler in many cases. To simulate it, the derived class will be represented by DervDialog and the representation of CCmdTarget will be baseWnd. To the DervDialog class, you do exactly what you do to handle a message; for this, you will call a function foo() in the baseWnd class which runs through the message map and calls each handler one by one. Does it not simulate how the message first comes to the WndProc (foo() in our case) and routes to the CCmdtarget (baseWnd in our case) which runs through the message map structure and calls the handler that is in the derived class (DervDialog in our case)? There, the messages are automatically trapped by the Wndproc; here, you simulate it by invoking the foo() function.
Let us go through this one by one. First I take a structure myStruct (simulating the AFX_MSGMAP_ENTRY structure in MFC); one of its members is a pointer to a member function of the baseWnd. Then, I write DECLARE_SAI_MAP(); in it I sneak in a virtual function, namely virtual myStruct* Moo(), which I also define in my baseWnd class (look at the source code of DECLARE_MESSAGE_MAP(). MFC also sneaks in a virtual function. It does many more things than this; I avoid all that for the sake of simplicity). I will explain the duty of Moo() a little later.
Next, to the second macro, BEGIN_SAI_MAP(..). This macro declares an array of the type myStruct namely ss, opens the bracket as though it wants to populate it, but stops right there. Who will populate the array? That is the job of the ON_SAI_MESSAGE(...) macro; this one's a bit tricky. If you write it like this, ON_SAI_MESSAGE(10,12,roo), what happens is that the 10 and 12 go into the myStruct's j and k, respectively, and the roo will be typecast as though it is a pointer to the base class baseWnd member function.
roo() will actually be a member function of the derived class DervDialog, but then what will be roo? It is precisely the handler that you will write in the DervDialog class. ON_SAI_MESSAGE simulates a typical ON_COMMAND(..) macro. Supposing you write ON_SAI_LABAMBA(700007,800008) then the integers get stored in myStruct as explained above; a third predefined function, Labamba in the DervDialog, will be typecast to a member function of the base class baseWnd. Now who will write Labamba()? You will, in the DervDialog; this is a simulation of a typical ON_WM_PAINT(), which does not have a handler that is passed in to the macro.
Finally, to the third macro, END_SAI_MAP(). This one just closes the bracket of the ss array, telling you that populating the array is over.
In main I write:
DervDialog dd; baseWnd *ptr=ⅆ ptr->foo();
Let us see what happens when I call foo(). foo makes a call to Moo(). Remember, it was sneaked in as a virtual function in the derived class DervDialog. So, that implementation, the DervDialog implementation, of Moo is called. This Moo returns the starting address of the ss array (_myStruct *ptrM=Moo() ) that was ceremoniously populated by BEGIN_SAI_MAP(DervDialog), ON_SAI_MESSAGE(10,20,Kzoo), ON_SAI_MESSAGE(770,130,NULL), and END_SAI_MAP().
The returned address is stored in the ptrM pointer. A while loop comes into play and runs through the array of structures until it sees the j and k variables of the myStruct to be zero and zero. This is just a stopping condition; MFC checks for an all-NULL situation. An intimidating C++ syntax like this (this->*(ptrM[index].ptrzoo))(), comes to our aid that calls the handler in DervDialog one by one.
To experiment, add your own member function to the DervDialog and put in an ON_SAI_MESSAGE(170,720,yourfunction) between the BEGIN_SAI_MAP(DervDialog) and END_SAI_MAP() and get it called by the base class baseWnd. One point to remember is that the so-called handler function you write in the DervDialog must have a pre-assigned prototype.
I hope now the bare bones of the message map's inner workings get simulated successfully. In fact, by using the same technique, you can wire up a window/dialog to a WndProc. Maybe I will present that simulation as a continuation of this article. However. Paul Dilascia does the wiring up by using virtual functions in his legendry Windows++ architecture; see http://www.dilascia.com/wpp.htm.