转自:http://blog.sina.com.cn/s/blog_4b3c1f950100nten.html
1. 标准消息(队列消息)
除WM_COMMAND之外,所有以WM_开头的消息都是标准消息,如WM_MOUSEMOVE、WM_LBUTTONUP、
WM_KEYDOWN、WM_CHAR。
从CWnd派生的类都可以接收到这类消息。
Windows每次从系统消息队列移走一个消息,确定它是送给哪个窗口的和这个窗口是由哪个线程创建的,然后,把它放进窗口
创建线程的线程消息队列。线程消息队列接收送给该线程所创建窗口的消息。线程从消息队列取出消息,通过Windows 把它送
给适当的窗口过程来处理。除了键盘、鼠标消息以外,队列消息还有WM_PAINT、WM_TIMER 和WM_QUIT。
注意:标准消息并不需要我们指定处理函数名称,是默认的对应关系。
例如:
宏名称 对应消息 消息处理函数
ON_WM_CHAR WM_CHAR OnChar
ON_WM_CLOSE WM_CLOSE OnClose
ON_WM_CREATE WM_CREATE OnCreate
ON_WM_DESTROY WM_DESTROY OnDestroy
ON_WM_LBUTTONDO WM_LBUTTONDOWN OnLButtonDown
ON_WM_LBUTTONUP WM_LBUTTONUP OnLButtonUp
ON_WM_MOUSEMOVE WM_MOUSEMOVE OnMouseMove
ON_WM_PAINT WM_PAINT OnPaint
......... ............ .......
2.命令消息
来自菜单、加速键或工具栏按钮的消息均是命令消息。
这类消息都以WM_COMMAND形式呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息
的wParam参数识别。从CCmdTarget派生的类都可以接收到这类消息,其wParam 记录着该消息来自哪一个菜单项目。
例如:
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_COMMAND(IDM_FILENEW, OnFileNew)
ON_COMMAND(IDM_FILEOPEN, OnFileOpen)
ON_COMMAND(IDM_FILESAVE, OnFileSave)
...........
3.通告消息
由控件产生的消息,例如按钮,列表框的选择等都会产生通告消息,目的是为了向其父窗口(通常是对话框)通知事件的发
生。
这类消息是以WM_COMMAND或WM_NOTIFY形式呈现的。从CCmdTarget派生的类(如CDocument可以接受命令消息和
通告消息,但不能接收标准消息(队列消息)),都可以接收到这类消息。
注意:由于CWnd类派生于CCmdTarget类,所以凡是从CWnd派生的类,他们既可以接收标准消息,也可以接收命令消息和
通告消息。而对于从CCmdTarget类派生的类只能接收命令消息和通告消息,不能接受标准消息。
例如:
控件 宏 消息处理函数
Button ON_BN_CLICKED(<id>,<memberFxn>) memberFxn
ComboBox ON_CBN_DBLCLK(<id>,<memberFxn>) memberFxn
Edit ON_EN_SETFOCUS(<id>,<memberFxn>) memberFxn
ListBox ON_LBN_DBLCLK(<id>,<memberFxn>) memberFxn
......... ...................... ...........
标准消息和非标准消息的区分:
标准消息:代有控制后后续操作;
非标准消息:只是简单提示。
MFC命令消息的路由:
AfxWndProc(替换了窗口过程函数)->AfxCallWndProc->WindowProc->OnWnddMsg->(如果是命令消息则调用Oncommand;如果是
通告消息则调用OnNotify)->OnCmdMsg
那么通告消息到底是WM_COMMAND还是WM_NOTIFY呢?
解释一:WM_NOTIFY比WM_COMMAND 功能更强大,可以存储一些额外的信息,WM_COMMAND 并不被所有的控件所支
持。
解释二:Edit,Button,ListBox等发送WM_COMMAND消息,ListView,Toolbar,Tree等编译时如果不联接comctl32.lib就通
不过的。Common Controls发送WM_NOTIFY消息,因为需要提供的信息更多。
给对话框中的控件发送消息:
想要给CTreeCtrl控件模拟发送一个TCN_SELCHANGE消息。
想要给CButton控件模拟发送一个BN_CLICKED消息。
★ 由上面对windows消息的分类,我们得知,这两个消息都是通告消息。那是用 WM_COMMAND还是WM_NOTIFY呢?
根据上面的解释,我们使用 TCN_SELCHANGE--WM_NOTIFY ,BN_CLICKED--WM_COMMAND。
是不是这样呢?咱们参看MSDN:
TCN_SELCHANGE
lpnmhdr = (LPNMHDR) lParam;
Notifies a tab control's parent window that the currently selected tab has changed. This message is sent in the form of
a WM_NOTIFY message.
由上看出TCN_SELCHANGE确实是以WM_NOTIFY呈现的,它包含以一个结构体指针的形式包含在lParam中:
typedef struct tagNMHDR { HWND hwndFrom; UINT idFrom; UINT code; } NMHDR;
Contains information about a notification message.
注:关于NMHDR以及ON_NOTIFY(转自:http://www.cnblogs.com/a-peng/archive/2007/11/18/963533.html)
NMHDR结构的引进就是消息统一起来,利用它可以传递复杂的信息。这个结构的布局如下:
NMHDR
{
HWnd hWndFrom ; 相当于原WM_COMMAND传递方式的lParam
UINT idFrom ; 相当于原WM_COMMAND传递方式的wParam(low-order)
UINT code ; 相当于原WM_COMMAND传递方式的Notify Code(wParam"s high-order)
};
对于这个结构的应用于WM_NOTIFY信息结构,结果WM_NOTIFY就变成了:
A、无附加信息。结构变得很简单,就是一个NMHDR结构。
B、有附加信息。定义一个大的结构,它的第一个元素就是NMHDR结构,它的后面放置附加信息。
WM_NOTIFY结构的好处:
由于在大结构中,第一个成员为NMHDR,这样一来,我们就可以利用指向NMHDR的指针来传递结构地址,根据指针的特性,无论消息有没有附加信息,这个指针都适用,也能够很方便的进行强制转换。
分析ON_NOTIFY:
类向导可以创建ON_NOTIFY消息映射入口并提供一个处理函数的框架,来处理 WM_NOTIFY类型的消息。ON_NOTIFY消息映射宏有如下语法:
ON_NOTIFY(wNotifyCode,id,memberFxn)
其中:
wNotifyCode:要处理的通知消息通知码。比如上面我们提到的LVN_KEYDOWN;
Id:控件标识ID;
MemberFxn:处理此消息的成员函数。
此成员函数有如下的原型声明:
afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result);
比如:假设你想成员函数OnKeydownList1处理ClistCtrl(标识ID=IDC_LIST1)的 LVN_KEYDOWN消息,你可以使用类向导添加如下的消息映射:
ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )
在上面的例子中,类向导提供如下函数:
void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_KEYDOWN* pLVKey= (LV_KEYDOWN*)pNMHDR;
*pResult = 0;
}
这时类向导提供了一个适当类型的指针,你既可以通过pNMHDR,也可以通过 pLVKey来访问这个通知结构。
ON_NOTIFY_RANGE:
有时我们可能需要为一组控件处理相同的WM_NOTIFY消息。这时需要使用ON_NOTIFY_RANGE而不是ON_NOTIFY。不过,很不幸的是,VC6的ClassWizard并不支持这个消息,所以我们必须手工添加。方法和一般的手工添加的消息一样,不过需要注意的是:
(1)当你使用 ON_NOTIFY_RANGE时,你需要指定控件的ID范围.其消息映射入口及函数原型如下:
ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
其中:wNotifyCode:消息通知码.比如:LVN_KEYDOWN。id: 第一控件的标识ID。
idLast:最后一个控件的标识ID。(标识值一定要连续)memberFxn: 消息处理函数。
(2)成员函数必须有如下原型申明:afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );
BN_CLICKED
The BN_CLICKED notification message is sent when the user clicks a button. The parent window of the button receives
this notification message through the WM_COMMAND message.
BN_CLICKED
idButton = (int) LOWORD(wParam); // identifier of button
hwndButton = (HWND) lParam; // handle to button
由上看出BN_CLICKED确实是包含在WM_COMMAND 中的。
★ 怎样把通告消息溶到WM_COMMAND 和 WM_NOTIFY中呢?
WM_NOTIFY
idCtrl = (int) wParam;
pnmh = (LPNMHDR) lParam;
WM_COMMAND
wNotifyCode = HIWORD(wParam); // notification code
wID = LOWORD(wParam); // item, control, or accelerator identifier
hwndCtl = (HWND) lParam; // handle of control
组装参数:
LPARAM MAKELPARAM(
WORD wLow, // low-order word
WORD wHigh // high-order word
);
或者
DWORD MAKELONG(
WORD wLow, // low-order word of long value
WORD wHigh // high-order word of long value );
★ 向控件发消息我们可以使用以下两个方法:
LONG SendDlgItemMessage(
HWND hDlg, // handle of dialog box
int nIDDlgItem, // identifier of control
UINT Msg, // message to send
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
或者
LRESULT SendMessage(
HWND hWnd, // handle of destination window
UINT Msg, // message to send
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
★ 实例,验证成功:
//模拟发送TCN_SELCHANGE消息
NMHDR nmhdr;
nmhdr.code = TCN_SELCHANGE;
nmhdr.hwndFrom = g_pMainDlg->m_TabCtrl.GetSafeHwnd();
nmhdr.idFrom= g_pMainDlg->m_TabCtrl.GetDlgCtrlID();
::SendDlgItemMessage(g_pMainDlg->m_hWnd,IDC_TAB1,WM_NOTIFY,MAKELONG(TCN_SELCHANGE,0),(LPARAM)(&nmhdr));
//发送BN_CLICKED消息
::SendMessage(g_pMainDlg->m_VNOnLine.m_hWnd,WM_COMMAND,MAKELPARAM(IDC_RANG_OFF,BN_CLICKED),(LPARAM)(::GetDlgItem(g_pMainDlg->m_VNOnLine.m_hWnd,IDC_RANG_OFF)));