Windows Message Handling - Part 2


How do MFC message handlers work?

Whenever your window receives a message, MFC will call a member function of your class. But how does MFC know what function to call?
MFC uses a technique called Message Maps. A Message Map is a table that associates messages with functions. When you receive a message, MFC will go through your Message Map and search for a corresponding Message Handler. I have showed in Part 1 how you add a Message Handler to the Message Map by using ClassWizard, but what really happens code-wise?
MFC uses a large set of rather complicated macros that add the Message Map to your classes. When you use ClassWizard to create a Message Handler, it will first add the function to your class, and add the corresponding macro to your Message Map. For example, examine the following ClassWizard generated WM_CLOSE handler:

Message Map: located in the class implementation

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)

ON_WM_CLOSE()
//}}AFX_MSG_MAP

END_MESSAGE_MAP()

Function Declaration: located in the class declaration.

protected:
//{{AFX_MSG(CAboutDlg)

afx_msg void OnClose();
//}}AFX_MSG

DECLARE_MESSAGE_MAP()

Function Implementation: located in the class implementation

void CAboutDlg::OnClose() 
{
// TODO: Add your message handler code here and/or call default


CDialog::OnClose();
}
By adding a DECLARE_MESSAGE_MAP statement to the class declaration, MFC adds the required code to declare the message map. The BEGIN_MESSAGE_MAP tells MFC where the Message Map begins, and identifies your class and it's base class. The reason it needs the base class is because Message Handlers are passed through c++ inheritance, just like any other function. END_MESSAGE_MAP obviously, tells MFC where the Message Map ends. In between these two macros is where your declare the Message Map entry for your Message Handler. MFC has many predefined macros, which associate messages with your member function. Take the the ON_WM_CLOSE macro as an example: It associates the WM_CLOSE message with your OnClose() member function. The macro takes no parameters since it always expects a function called OnClose() which is prototyped as afx_msg void OnClose(). This method gives you 2 advantages:
  1. It is easy to keep track of Message Handlers and the messages they handle
  2. MFC screens out any irrelevant and will break up lParam and wParam to parameters relevant to the message.
Also the return value is simplified, and the Message Handler is prototyped according to the message. For example: If the value should always be zero, MFC simplifies the process and allows you to declare the function as a void, and MFC will be responsible for returning 0. To find the name of the message handler that correlates with a given Message Handler macro you should look it up in the MFC documentation.
There are some messages that ClassWizard doesn't support, but you can manualy add your message handler by adding the function and Message Map macro as described above. If you add message-map entries manually, you may not be able to edit them with ClassWizard later. If you add them outside the bracketing comments //{{AFX_MSG_MAP(classname) and //}}AFX_MSG_MAP, ClassWizard cannot edit them at all. Note that by the same token ClassWizard will not touch any entries you add outside the comments, so feel free to add messages outside the comments if you do not want them to be modified. Messages that are not recognized by ClassWizard, such as message-map ranges, must be added outside the comments.

The all mighty ON_MESSAGE

Sometimes you will find yourself trying to handle a message that ClassWizard doesn't support, and it doesn't have a Message Map macro. MFC has a generic macro just for this kind of situation ON_MESSAGE. ON_MESSAGE allows you to handle any message that exists. The prototype of Message Handlers that use ON_MESSAGE is
afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);
where OnMessage is the name of your handler function. The ON_MESSAGE macro takes 2 parameters: The address of the handler, and the message it should handle. For example: The following statement Maps WM_GETTEXTLENGTH to OnGetTextLength():
ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)
OnGetTextLength is prototyped as
afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);

User-defined messages

Sometimes, you will need to communicate between 2 windows in your application or between 2 windows from different applications. An easy way to do this is by using User-defined messages. The name "User-defined" can be confusing at first; you define a User-defined message and not the user of your program. I have stated in Part 1 that messages are identified by numbers, and that Windows predefines standard messages. The way of using predefined messages is to simply use a number. To make sure that you don't conflict with the system defined messages you should use a number in the range of WM_APP through 0xBFFF:
#define WM_DELETEALL WM_APP + 0x100
//...

pYourDialog->SendMessage(WM_DELETEALL, 0, 0);
Handling a user-defined message is done with the ON_MESSAGE macro:
#define WM_DELETEALL WM_APP + 0x100
//...

//Message Map entry:

ON_MESSAGE (WM_DELETEALL, OnDeleteAll)
//OnDeleteAll is prototyped as

afx_msg LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam);
//And is implemented as

LRESULT OnDeleteAll(WPARAM wParam, LPARAM lParam){
//Do What ever you want

return somevalue;
}

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 name of a UINT variable that contains the registered Windows message ID. For example:
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();

//{{AFX_MSG(CMyWnd)

afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG


DECLARE_MESSAGE_MAP()
};

static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)

ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP

END_MESSAGE_MAP()
The range of user defined messages using this approach will be in the range 0xC000 to 0xFFFF. And you send it using the regular SendMessage() method:
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
//...

pFindWindow->SendMessage(WM_FIND, lParam, wParam);

Tags:

About author

Freshersplanet is exclusively for listing fresher jobs, and attracts nearly a million fresh job seekers from different streams. Recruiters can publish any fresher openings, Internships and walk-ins via Freshersplanet to reach to the top quality freshers talent available in the country.

0 comments

Leave a Reply