|
|
1.1 ! root 1: Microsoft Foundation Classes Microsoft Corporation ! 2: Technical Notes ! 3: ! 4: #6 : Message Maps ! 5: ! 6: This note describes the Foundation message map facility. ! 7: ! 8: ----------------------------------------------------------------------------- ! 9: The Problem ! 10: =========== ! 11: ! 12: Microsoft Windows implements what are essentially virtual functions ! 13: in window classes using its messaging facility. Due to the large ! 14: number of messages involved, providing a separate virtual function ! 15: for each Windows message results in a prohibitively large ! 16: vtable. ! 17: ! 18: ----------------------------------------------------------------------------- ! 19: Overview ! 20: ======== ! 21: ! 22: The Foundation provides an alternative to the switch statement usually ! 23: used in Windows programs to handle messages sent to a window. A ! 24: mapping from messages to member-functions may be defined so that when ! 25: a message is to be handled by a window, the appropriate member ! 26: function is called automatically. This message map facility was ! 27: designed to be as similar to virtual functions as possible without a ! 28: large vtable overhead. ! 29: ! 30: ! 31: Defining a Message Map ! 32: ====================== ! 33: ! 34: ! 35: The DECLARE_MESSAGE_MAP macro declares a private array for the ! 36: message map entries called _messageEntries, a protected CMessageMap ! 37: called messageMap, and a protected virtual function called ! 38: GetMessageMap that returns the address of messageMap. This macro ! 39: should be placed in the declaration of any class using message maps. ! 40: By convention, it is at the end of the class declaration. ! 41: ! 42: For example: ! 43: ! 44: class CMyWnd : public CMyParentWndClass ! 45: { ! 46: // my stuff... ! 47: afx_msg void OnPaint(); ! 48: ! 49: DECLARE_MESSAGE_MAP() ! 50: }; ! 51: ! 52: ! 53: The message map's table is defined with a set of macros that expand ! 54: to message map entries. A table begins with the BEGIN_MESSAGE_MAP ! 55: macro that defines the class that is handled by this message map and ! 56: the parent class to pass unhandled messages to. The table ends with ! 57: the END_MESSAGE_MAP macro. ! 58: ! 59: Between these two lines is an entry for each message to be handled by ! 60: this message map. Every standard Windows message has a macro of the ! 61: form ON_WM_xxx (where xxx is the name of the message) that generates ! 62: an entry for that message. ! 63: ! 64: A standard function signature has been defined for unpacking the ! 65: parameters of each Windows message and providing type safety. These ! 66: signatures may be found in the file AFXWIN.H in the declaration of ! 67: CWnd. Each one is marked with the word afx_msg for easy identification. ! 68: ! 69: These function signatures were derived using a simple convention to ! 70: make them easier to deduce. The name of the function always starts ! 71: with On. This is followed by the name of the Windows message with ! 72: the WM_ removed and only the first letter of each word capitalized. ! 73: The ordering of the parameters is wParam followed by LOWORD(lParam) ! 74: then HIWORD(lParam). Unused parameters are not passed. Any handles ! 75: that are wrapped by Foundation classes are converted to pointers to ! 76: the appropriate Foundation objects. ! 77: ! 78: The following example shows how to handle the WM_PAINT message ! 79: and cause the CMyWnd::OnPaint function to get called: ! 80: ! 81: BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass) ! 82: ON_WM_PAINT() ! 83: END_MESSAGE_MAP() ! 84: ! 85: The message map table must be defined outside the scope of any ! 86: function or class definition. It should not be placed within ! 87: an extern "C" block. ! 88: ! 89: ! 90: User Defined Messages ! 91: ===================== ! 92: ! 93: User defined messages may be included in a message map by using the ! 94: ON_MESSAGE macro. This macro accepts a message number and a member ! 95: function of the form: ! 96: ! 97: // inside the class declaration ! 98: afx_msg LONG OnMyMessage(UINT wParam, LONG lParam); ! 99: ! 100: For example: ! 101: ! 102: #define WM_MYMESSAGE 5 ! 103: ! 104: BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass) ! 105: ON_MESSAGE(WM_USER + WM_MYMESSAGE, OnMyMessage) ! 106: END_MESSAGE_MAP() ! 107: ! 108: In this example, we establish a handler for a custom message with ! 109: a Windows message ID derived from the standard WM_USER base for ! 110: user-defined messages. You might invoke this handler with code ! 111: such as: ! 112: ! 113: extern CMyWnd myWnd; ! 114: myWnd->SendMessage(WM_USER + WM_MYMESSAGE); ! 115: ! 116: ! 117: Registered Windows Messages ! 118: =========================== ! 119: ! 120: The ::RegisterWindowMessage function is used to define a new window ! 121: message that is guaranteed to be unique throughout the system. ! 122: The macro ON_REGISTERED_MESSAGE is used to handle these messages. ! 123: This macro accepts a the name of a near UINT variable that contains ! 124: the registered windows message ID. ! 125: ! 126: ! 127: For example: ! 128: ! 129: class CMyWnd : public CMyParentWndClass ! 130: { ! 131: public: ! 132: CMyWnd(); ! 133: ! 134: afx_msg LONG OnFind(UINT wParam, LONG lParam); ! 135: ! 136: DECLARE_MESSAGE_MAP() ! 137: }; ! 138: ! 139: ! 140: static UINT _near wm_Find = RegisterWindowMessage("commdlg_Find"); ! 141: ! 142: BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass) ! 143: ON_REGISTERED_MESSAGE(wm_Find, OnFind) ! 144: END_MESSAGE_MAP() ! 145: ! 146: ! 147: Note: the registered Windows message ID variable (wm_Find in ! 148: the example above) must be a _near variable because of the ! 149: way ON_REGISTERED_MESSAGE is implemented. If your program ! 150: is designed to run in an ambient far data model (large or compact) ! 151: you must explicitly qualify the variable declaration with the ! 152: __near modifier, as shown above. You are not required to qualify ! 153: the declaration if your program uses small or medium model, but it ! 154: does not hurt to make the declaration explicit. ! 155: ! 156: The Foundation library header file 'afx.h' #defines NEAR to be '_near'. ! 157: ! 158: ! 159: User Defined Command Messages ! 160: ============================= ! 161: ! 162: Command messages from menus and accelerators are handled in message ! 163: maps with the ON_COMMAND macro. This macro accepts a command id as ! 164: well as a member function. Only WM_COMMAND messages with a wParam ! 165: equal to the id match these table entries. The macro has the form: ! 166: ! 167: ON_COMMAND(id, memberFxn) ! 168: ! 169: Command handler member functions must take no parameters and ! 170: return void. ! 171: ! 172: For example: ! 173: ! 174: // inside a resource header (usually generated by DLGEDIT) ! 175: #define IDM_MYCMD 100 ! 176: ! 177: // inside the class declaration ! 178: afx_msg void OnMyCommand(); ! 179: ! 180: // inside the message map definition ! 181: ON_COMMAND(IDM_MYCMD, OnMyCommand) ! 182: ! 183: ! 184: Control Notification Messages ! 185: ============================= ! 186: ! 187: Messages that are sent from child controls to a window have an extra ! 188: bit of information in their message map entry: the control's id. The ! 189: message map entry only matches the message if the id in the entry ! 190: matches the id sent with the notification message. ! 191: ! 192: Custom control notification messages may use the ON_CONTROL macro to ! 193: define a message map entry with a custom notification code. This ! 194: macro has the form: ! 195: ! 196: ON_CONTROL(wNotificationCode, id, memberFxn) ! 197: ! 198: ! 199: How a Message is Translated ! 200: =========================== ! 201: ! 202: The WindowProc member function of class CWnd is the workhorse of the ! 203: message handler. It is the default window procedure for every window ! 204: created with the Foundation. ! 205: ! 206: When a message is to be handled, WindowProc searches the message map ! 207: attached to the window receiving the message for an appropriate ! 208: entry. ! 209: ! 210: If an entry is found, the wSig field of the entry is used to ! 211: call the function pointer, pfn, in the entry with the appropriate ! 212: signature. ! 213: ! 214: If an entry is not found, the message map of the window's parent ! 215: class is searched. This process continues until a handler is found ! 216: or the top of the CWnd class hierarchy is found at which point the ! 217: message is passed to DefWindowProc so the system can perform any ! 218: default processing. ! 219: ! 220: A cache of recently handled messages is used to speed up searches ! 221: through the message map. ! 222: ! 223: Registered windows messages are handled as a special case of the ! 224: general mechanism. ! 225: ! 226: ! 227: NOTE: ! 228: It is important to realize that the message map parent class-child ! 229: class relationship described above is established through the ! 230: BEGIN_MESSAGE_MAP macro and *NOT* through the normal C++ language ! 231: inheritance mechanism. ! 232: ! 233: For example: ! 234: ! 235: class CMyParentWnd : public CFrameWnd ! 236: { ! 237: ... ! 238: DECLARE_MESSAGE_MAP() ! 239: }; ! 240: ! 241: BEGIN_MESSAGE_MAP(CMyParentWnd, CFrameWnd) // correct ! 242: ... ! 243: END_MESSAGE_MAP() ! 244: ! 245: class CMyWnd : public CMyParentWnd ! 246: { ! 247: ... ! 248: DECLARE_MESSAGE_MAP() ! 249: }; ! 250: ! 251: BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd) // incorrect ! 252: ... ! 253: END_MESSAGE_MAP() ! 254: ! 255: The first message map is defined correctly. CFrameWnd is an ! 256: immediate base class of CMyParentWnd, and the BEGIN_MESSAGE_MAP ! 257: macro reflects this relationship. ! 258: ! 259: The second message map is defined incorrectly. While CFrameWnd ! 260: is a base class of CMyWnd, it is not an immediate base class. If ! 261: a message is sent to a CMyWnd object that the object does not ! 262: know how to handle, the message will be passed on to the CFrameWnd ! 263: message map for processing. The message should have been passed ! 264: to the CMyParentWnd message map first. There is no way for this ! 265: error to be caught at compile time -- it will only be evident ! 266: by runtime misbehavior. ! 267: ! 268: ! 269: ! 270:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.