| < Free Open Study > |
|
Every ATL class must supply a MSG map, using the BEGIN_MSG_MAP and END_MSG_MAP macros. Together, these macros define an implementation of Process- WindowMessage() for your class:
// ProcessWindowMessage() is placed in your class using the BEGIN_MSG_MAP macro. #define BEGIN_MSG_MAP(theClass) \ public: \ BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, \ WPARAM wParam, LPARAM lParam, LRESULT& lResult, \ DWORD dwMsgMapID = 0) \ { \ BOOL bHandled = TRUE; \ hWnd; \ uMsg; \ wParam; \ lParam; \ lResult; \ bHandled; \ switch(dwMsgMapID) \ { \ case 0: // END_MSG_MAP closes the initial definition of ProcessWindowMessage(). #define END_MSG_MAP() \ break; \ default: \ ATLTRACE2(atlTraceWindowing, 0, \ _T("Invalid message map ID (%i)\n"), dwMsgMapID); \ ATLASSERT(FALSE); \ break; \ } \ return FALSE; \ }
Now, if you pull these two macros together, what do you have? A gigantic switch statement! Yes indeed, ProcessWindowMessage() is doing the same thing we did by hand in the previous Win32 example. As you can see, dwMsgMapID is the test point of the switch. Case 0 is a catchall for any and all standard Windows messages (WM_CREATE, WM_PAINT, and so forth). All of these "WM_" messages end up under the case 0 condition, and are tested using a simple if...else syntax. While you could implement this logic by hand, ATL provides a number of macros to do so on your behalf.
The MSG_MAP of CMainWindow has a single entry for WM_PAINT, which was placed there using the MESSAGE_HANDLER macro. The two parameters of this macro are the message we are handling (WM_PAINT) and the function to call in response (OnPaint). When expanded, MESSAGE_HANDLER makes the call to the correct member function, and returns a BOOL (bHandled) indicating if the message should be sent into the DefWindowProc(). This BOOL is declared by the BEGIN_MSG_MAP macro. Here is the expansion of MESSAGE_HANDLER:
// This macro is used to fill up all the "case 0" conditions (i.e., windows messages) #define MESSAGE_HANDLER(msg, func) \ if(uMsg == msg) \ { \ bHandled = TRUE; \ lResult = func(uMsg, wParam, lParam, bHandled); \ if(bHandled) \ return TRUE; \ }
Notice that bHandled will be sent in by reference to the message handler function. This gives you the chance to change the value of this parameter before exiting the handler, to determine if the current message should be sent into DefWindowProc(). A value of TRUE indicates the message has been fully handled, and does not need to enter the default windows procedure, whereas FALSE says DefWindowProc() needs to be called.
Also note that END_MSG_MAP always returns FALSE, signifying that your message map did not handle the message, which allows CWindowImplBaseT<>::WindowProc() to call DefWindowProc() on your behalf.
If you handle a message, but still wish to pass it into the DefWindowProc(), you can set the bHandled parameter yourself in a given message handler:
// Set the handled flag to inform WindowProc() if it should send this to the // DefWindowProc() LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { ... // I did some work, but still send this into the default window's proc. bHandled = FALSE; return 0; }
The MESSAGE_HANDLER macro is not the only valid macro that may appear in an ATL message map. Like most ATL maps, your message map may be populated with any number of macros. MESSAGE_HANDLER is the most common of all your options, and is used to handle all standard windows message. Some of the more useful macros are listed in the following table:
ATL MSG_MAP Macro | Meaning in Life |
---|---|
CHAIN_MSG_MAP | Allows your class to "inherit" the messages found in the MSG_MAP of a base class. |
MESSAGE_ RANGE_HANDLER | Routes a contiguous numerical range of messages to a single function. |
COMMAND_ID_HANDLER | Maps a WM_COMMAND message to some function, using the ID of the control (or menu item). |
COMMAND_HANDLER COMMAND_ RANGE_HANDLER | Like COMMAND_ID_HANDLER, but takes the ID as well as the notification code of the control (or menu item). |
NOTIFY_HANDLER NOTIFY_RANGE_HANDLER NOTIFY_ID_HANDLER | Like the set of _COMMAND_ macros, but for a WM_NOTIFY event rather than a WM_COMMAND event. |
Each of these alternative MSG_MAP entries will tweak the code behind ProcessWindow- Message(). Many of these macros will be automatically inserted based off your selections of the Add Windows Message Handler Wizard. We will see examples of some of these alternatives when we discuss ATL's support for building interactive dialog boxes, as well as ActiveX controls in Chapter 14.
| < Free Open Study > |
|