home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK11 / MFC / DOC / TN006.TX$ / tn006
Encoding:
Text File  |  1992-02-16  |  8.8 KB  |  271 lines

  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.     DECLARE_MESSAGE_MAP()
  238. };
  239.  
  240. BEGIN_MESSAGE_MAP(CMyParentWnd, CFrameWnd)  // correct
  241.    ...
  242. END_MESSAGE_MAP()
  243.  
  244. class CMyWnd : public CMyParentWnd
  245.     ... 
  246.     DECLARE_MESSAGE_MAP()
  247. };
  248.  
  249. BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd)    // incorrect
  250.   ...
  251. END_MESSAGE_MAP()
  252.  
  253. The first message map is defined correctly.  CFrameWnd is an
  254. immediate base class of CMyParentWnd, and the BEGIN_MESSAGE_MAP 
  255. macro reflects this relationship.
  256.  
  257. The second message map is defined incorrectly.  While CFrameWnd
  258. is a base class of CMyWnd, it is not an immediate base class.  If
  259. a message is sent to a CMyWnd object that the object does not
  260. know how to handle, the message will be passed on to the CFrameWnd
  261. message map for processing.  The message should have been passed
  262. to the CMyParentWnd message map first.  There is no way for this
  263. error to be caught at compile time -- it will only be evident
  264. by runtime misbehavior.
  265.  
  266.  
  267.  
  268.  
  269.