Windows消息(二):消息的分类以及模拟发送控件通知消息

转自: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.

  • No return value.
lpnmhdr
Address of an NMHDR structure. The hwndFrom member is the handle to the tab control. The idFrommember is the child window identifier of the tab control. The code member is TCN_SELCHANGE.

由上看出TCN_SELCHANGE确实是以WM_NOTIFY呈现的,它包含以一个结构体指针的形式包含在lParam中:

typedef struct tagNMHDR { HWND hwndFrom; UINT idFrom; UINT code; } NMHDR;

Contains information about a notification message.

hwndFrom
Window handle to the control sending a message.
idFrom
Identifier of the control sending a message.
code
Notification code. This member can be a control-specific notification code or it can be one of the common notification codes.

注:关于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_COMMANDWM_NOTIFY中呢?

WM_NOTIFY

idCtrl = (int) wParam;

pnmh = (LPNMHDR) lParam;

idCtrl
Identifier of the common control sending the message.
pnmh
Address of an NMHDR structure that contains the notification code and additional information.

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)));

原文地址:https://www.cnblogs.com/qinfengxiaoyue/p/2910631.html