home *** CD-ROM | disk | FTP | other *** search
- Microsoft Foundation Classes Microsoft Corporation
- Technical Notes
-
- #6 : Message Maps
-
- This note describes the Foundation message map facility.
-
- -----------------------------------------------------------------------------
- The Problem
- ===========
-
- Microsoft Windows implements what are essentially virtual functions
- in window classes using its messaging facility. Due to the large
- number of messages involved, providing a separate virtual function
- for each Windows message results in a prohibitively large
- vtable.
-
- -----------------------------------------------------------------------------
- Overview
- ========
-
- The Foundation provides an alternative to the switch statement usually
- used in Windows programs to handle messages sent to a window. A
- mapping from messages to member-functions may be defined so that when
- a message is to be handled by a window, the appropriate member
- function is called automatically. This message map facility was
- designed to be as similar to virtual functions as possible without a
- large vtable overhead.
-
-
- Defining a Message Map
- ======================
-
-
- The DECLARE_MESSAGE_MAP macro declares a private array for the
- message map entries called _messageEntries, a protected CMessageMap
- called messageMap, and a protected virtual function called
- GetMessageMap that returns the address of messageMap. This macro
- should be placed in the declaration of any class using message maps.
- By convention, it is at the end of the class declaration.
-
- For example:
-
- class CMyWnd : public CMyParentWndClass
- {
- // my stuff...
- afx_msg void OnPaint();
-
- DECLARE_MESSAGE_MAP()
- };
-
-
- The message map's table is defined with a set of macros that expand
- to message map entries. A table begins with the BEGIN_MESSAGE_MAP
- macro that defines the class that is handled by this message map and
- the parent class to pass unhandled messages to. The table ends with
- the END_MESSAGE_MAP macro.
-
- Between these two lines is an entry for each message to be handled by
- this message map. Every standard Windows message has a macro of the
- form ON_WM_xxx (where xxx is the name of the message) that generates
- an entry for that message.
-
- A standard function signature has been defined for unpacking the
- parameters of each Windows message and providing type safety. These
- signatures may be found in the file AFXWIN.H in the declaration of
- CWnd. Each one is marked with the word afx_msg for easy identification.
-
- These function signatures were derived using a simple convention to
- make them easier to deduce. The name of the function always starts
- with On. This is followed by the name of the Windows message with
- the WM_ removed and only the first letter of each word capitalized.
- The ordering of the parameters is wParam followed by LOWORD(lParam)
- then HIWORD(lParam). Unused parameters are not passed. Any handles
- that are wrapped by Foundation classes are converted to pointers to
- the appropriate Foundation objects.
-
- The following example shows how to handle the WM_PAINT message
- and cause the CMyWnd::OnPaint function to get called:
-
- BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
- ON_WM_PAINT()
- END_MESSAGE_MAP()
-
- The message map table must be defined outside the scope of any
- function or class definition. It should not be placed within
- an extern "C" block.
-
-
- User Defined Messages
- =====================
-
- User defined messages may be included in a message map by using the
- ON_MESSAGE macro. This macro accepts a message number and a member
- function of the form:
-
- // inside the class declaration
- afx_msg LONG OnMyMessage(UINT wParam, LONG lParam);
-
- For example:
-
- #define WM_MYMESSAGE 5
-
- BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
- ON_MESSAGE(WM_USER + WM_MYMESSAGE, OnMyMessage)
- END_MESSAGE_MAP()
-
- In this example, we establish a handler for a custom message with
- a Windows message ID derived from the standard WM_USER base for
- user-defined messages. You might invoke this handler with code
- such as:
-
- extern CMyWnd myWnd;
- myWnd->SendMessage(WM_USER + WM_MYMESSAGE);
-
-
- Registered Windows Messages
- ===========================
-
- The ::RegisterWindowMessage function is used to define a new window
- message that is guaranteed to be unique throughout the system.
- The macro ON_REGISTERED_MESSAGE is used to handle these messages.
- This macro accepts a the name of a near UINT variable that contains
- the registered windows message ID.
-
-
- For example:
-
- class CMyWnd : public CMyParentWndClass
- {
- public:
- CMyWnd();
-
- afx_msg LONG OnFind(UINT wParam, LONG lParam);
-
- DECLARE_MESSAGE_MAP()
- };
-
-
- static UINT _near wm_Find = RegisterWindowMessage("commdlg_Find");
-
- BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
- ON_REGISTERED_MESSAGE(wm_Find, OnFind)
- END_MESSAGE_MAP()
-
-
- Note: the registered Windows message ID variable (wm_Find in
- the example above) must be a _near variable because of the
- way ON_REGISTERED_MESSAGE is implemented. If your program
- is designed to run in an ambient far data model (large or compact)
- you must explicitly qualify the variable declaration with the
- __near modifier, as shown above. You are not required to qualify
- the declaration if your program uses small or medium model, but it
- does not hurt to make the declaration explicit.
-
- The Foundation library header file 'afx.h' #defines NEAR to be '_near'.
-
-
- User Defined Command Messages
- =============================
-
- Command messages from menus and accelerators are handled in message
- maps with the ON_COMMAND macro. This macro accepts a command id as
- well as a member function. Only WM_COMMAND messages with a wParam
- equal to the id match these table entries. The macro has the form:
-
- ON_COMMAND(id, memberFxn)
-
- Command handler member functions must take no parameters and
- return void.
-
- For example:
-
- // inside a resource header (usually generated by DLGEDIT)
- #define IDM_MYCMD 100
-
- // inside the class declaration
- afx_msg void OnMyCommand();
-
- // inside the message map definition
- ON_COMMAND(IDM_MYCMD, OnMyCommand)
-
-
- Control Notification Messages
- =============================
-
- Messages that are sent from child controls to a window have an extra
- bit of information in their message map entry: the control's id. The
- message map entry only matches the message if the id in the entry
- matches the id sent with the notification message.
-
- Custom control notification messages may use the ON_CONTROL macro to
- define a message map entry with a custom notification code. This
- macro has the form:
-
- ON_CONTROL(wNotificationCode, id, memberFxn)
-
-
- How a Message is Translated
- ===========================
-
- The WindowProc member function of class CWnd is the workhorse of the
- message handler. It is the default window procedure for every window
- created with the Foundation.
-
- When a message is to be handled, WindowProc searches the message map
- attached to the window receiving the message for an appropriate
- entry.
-
- If an entry is found, the wSig field of the entry is used to
- call the function pointer, pfn, in the entry with the appropriate
- signature.
-
- If an entry is not found, the message map of the window's parent
- class is searched. This process continues until a handler is found
- or the top of the CWnd class hierarchy is found at which point the
- message is passed to DefWindowProc so the system can perform any
- default processing.
-
- A cache of recently handled messages is used to speed up searches
- through the message map.
-
- Registered windows messages are handled as a special case of the
- general mechanism.
-
-
- NOTE:
- It is important to realize that the message map parent class-child
- class relationship described above is established through the
- BEGIN_MESSAGE_MAP macro and *NOT* through the normal C++ language
- inheritance mechanism.
-
- For example:
-
- class CMyParentWnd : public CFrameWnd
- {
- ...
- DECLARE_MESSAGE_MAP()
- };
-
- BEGIN_MESSAGE_MAP(CMyParentWnd, CFrameWnd) // correct
- ...
- END_MESSAGE_MAP()
-
- class CMyWnd : public CMyParentWnd
- {
- ...
- DECLARE_MESSAGE_MAP()
- };
-
- BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd) // incorrect
- ...
- END_MESSAGE_MAP()
-
- The first message map is defined correctly. CFrameWnd is an
- immediate base class of CMyParentWnd, and the BEGIN_MESSAGE_MAP
- macro reflects this relationship.
-
- The second message map is defined incorrectly. While CFrameWnd
- is a base class of CMyWnd, it is not an immediate base class. If
- a message is sent to a CMyWnd object that the object does not
- know how to handle, the message will be passed on to the CFrameWnd
- message map for processing. The message should have been passed
- to the CMyParentWnd message map first. There is no way for this
- error to be caught at compile time -- it will only be evident
- by runtime misbehavior.
-
-
-
-
-