Click here to Skip to main content
15,867,939 members
Articles / Desktop Programming / MFC

Windows Message Handling - Part 2

Rate me:
Please Sign up or sign in to vote.
4.77/5 (70 votes)
7 Jun 2000 454.7K   201   41
Message Maps, User defined messages and Registered Messages

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

C++
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

C++
protected:
	//{{AFX_MSG(CAboutDlg)
	afx_msg void OnClose();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()

Function Implementation: Located in the Class Implementation

C++
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 its 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 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 two 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 manually 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

C++
afx_msg LRESULT OnMessage(WPARAM wParam, LPARAM lParam);

where OnMessage is the name of your handler function. The ON_MESSAGE macro takes two parameters: The address of the handler, and the message it should handle. For example: The following statement maps WM_GETTEXTLENGTH to OnGetTextLength():

C++
ON_MESSAGE (WM_GETTEXTLENGTH, OnGetTextLength)

OnGetTextLength is prototyped as:

C++
afx_msg LRESULT OnGetTextLength(WPARAM wParam, LPARAM lParam);

User-Defined Messages

Sometimes, you will need to communicate between two windows in your application or between two 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:

C++
#define WM_DELETEALL WM_APP + 0x100
//...
pYourDialog->SendMessage(WM_DELETEALL, 0, 0);

Handling a user-defined message is done with the ON_MESSAGE macro:

C++
#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:

C++
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:

C++
static UINT WM_FIND = RegisterWindowMessage("YOURAPP_FIND_MSG");
//...
pFindWindow->SendMessage(WM_FIND, lParam, wParam);

History

  • 8th June, 2000: Initial version

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.


Written By
Web Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questiondifference between user-defined and registered window messages Pin
sepinaz29-Aug-13 15:18
sepinaz29-Aug-13 15:18 
QuestionHandle message of windows applications Pin
staler.ideas14-Jul-11 22:11
staler.ideas14-Jul-11 22:11 
GeneralMy vote of 4 Pin
m_tv6-Mar-11 17:23
m_tv6-Mar-11 17:23 
Questionwho will create message loop and message queue? Pin
vasu_sri17-Oct-10 21:36
vasu_sri17-Oct-10 21:36 
GeneralMy vote of 4 Pin
LaxmikantYadav5-Aug-10 0:51
LaxmikantYadav5-Aug-10 0:51 
General2 SyncInvoke calls from one method are serial. [modified] Pin
alex.buzunov29-Jan-10 11:00
alex.buzunov29-Jan-10 11:00 
I was looking for a way to kill a message window that popups when I try to use a trial library.
SyncInvoke looked like a great way to do it but when I did 2 calls in a row one SyncInvoke was waiting for another thread to finish.

SyncInvoke(CPricingServerDlg, &CPricingServerDlg::KillPopup, &params);
SyncInvoke(CPricingServerDlg, &CPricingServerDlg::UseTrialLibrary, &params);

Trial message popup killer:

void CPricingServerDlg::KillPopup(LPVOID params)
{
	HWND  MessageWin; 
	MessageWin=0;
	while(MessageWin==0){
		MessageWin  = ::FindWindow(_T("#32770"), _T("SQLAPI++ Registration Reminder"));
	}
	::SendMessage(MessageWin, WM_NCDESTROY, 0, 0);
	MessageWin = 0; 
}


So, instead, I've decided to create user message and post it just before the call to UseTrialLibrary():

PostMessage(WM_KILLPOPUP, 0, 0);
SyncInvoke(CPricingServerDlg, &CPricingServerDlg::UseTrialLibrary, &params);


Here's user message definition:

#define WM_KILLPOPUP WM_APP + 0x100
...
ON_MESSAGE (WM_KILLPOPUP, OnKillPopup)
...
afx_msg  LRESULT  OnKillPopup(WPARAM wParam, LPARAM lParam);
...
LRESULT CPricingServerDlg::OnKillPopup(WPARAM wParam, LPARAM lParam){
	LPVOID params = 0;
	KillPopup(params);
	return 0;
}

Works like a charm.
A.

(Pardon me for the lack of the failsafe in the infinite loop - it's just the proof of the concept project)

modified on Friday, January 29, 2010 5:11 PM

Questionwho sends the message? Pin
tiankng22-Jul-09 22:40
tiankng22-Jul-09 22:40 
GeneralDSFDS Pin
mahbubabbas28-May-08 22:15
mahbubabbas28-May-08 22:15 
Generalmake example Pin
supfar31-Oct-07 22:42
supfar31-Oct-07 22:42 
QuestionWhere can I get the Part 1 part of this series Windows Message Handling - Part 1 Pin
tom groezer1-May-07 21:31
tom groezer1-May-07 21:31 
AnswerRe: Where can I get the Part 1 part of this series Windows Message Handling - Part 1 Pin
ilikeopensource24-May-07 4:44
ilikeopensource24-May-07 4:44 
GeneralGood Article Pin
JPandya5-Dec-06 22:28
JPandya5-Dec-06 22:28 
GeneralUser Defined Messages Pin
JPandya5-Dec-06 0:31
JPandya5-Dec-06 0:31 
GeneralMouse Position on non-Client Area of MDIChild Window Pin
fayaz var9-Jan-06 0:21
fayaz var9-Jan-06 0:21 
GeneralRegistered Message. Pin
Jewel Nandy1-Dec-05 3:03
Jewel Nandy1-Dec-05 3:03 
Generalmessage from worker thread to main window Pin
Norman Hines29-Oct-04 12:22
Norman Hines29-Oct-04 12:22 
GeneralRe: message from worker thread to main window Pin
22-Dec-04 1:24
suss22-Dec-04 1:24 
GeneralRe: message from worker thread to main window Pin
Norman Hines22-Dec-04 4:22
Norman Hines22-Dec-04 4:22 
QuestionRe: message from worker thread to main window Pin
tom groezer1-May-07 23:23
tom groezer1-May-07 23:23 
Generalmessage in console app .... Pin
dharani3-Sep-04 2:28
dharani3-Sep-04 2:28 
Generaluser defined messages : convert-problem Pin
Applicator19-Jul-04 1:18
Applicator19-Jul-04 1:18 
QuestionCan we handle the messages of other applications? Pin
shashike30-Dec-03 16:17
shashike30-Dec-03 16:17 
GeneralIntercept message at control level Pin
Davide Zaccanti21-Aug-03 22:27
Davide Zaccanti21-Aug-03 22:27 
GeneralA Powerful tool made easy Pin
Ajay Sancheti1-Aug-03 3:23
Ajay Sancheti1-Aug-03 3:23 
GeneralMessages vs. Commands Pin
SysPro7-Jul-03 7:54
SysPro7-Jul-03 7:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.