|
|
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.