http://blog.chinaunix.net/uid-24862988-id-3432184.html
消息映射是MFC应用程序的运行核心,Windows系统以消息的形式把输入传递给应用程序的窗口过程,窗口通过窗口过程来接收和处理消息,并把控制返回Windows系统。
1. 消息结构
消息是一个将事件和数据结合起来的集合。MSG结构含有来自windows应用程序消息队列的消息信息。
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
1)hwnd---接收该消息的窗口句柄,用以检索消息队列中的消息。如果hwnd = 0,则应用程序检索所有消息队列中的消息。窗口可以是任何类型的屏幕对象(能在屏幕显示的对象),比如主窗口,按钮等。
2)message---消息标示符,该值由windows.h内的宏识别,它是用于区别其他消息的常量。
3)wParam和lParam---分别表示消息的第一和第二参数,确切的含义依赖与消息本身。wParam通常是一个于消息有关的常数量,也可能是窗口或控件的句柄;lParam通常是一个指向内存中数据的指针。
4)time---表示消息创建的时间
5)pt---消息创建时屏幕的光标坐标
2. 消息的种类
- MFC处理的三类消息
根据处理函数和处理过程的不同,MFC主要处理三类消息:
- Windows消息,前缀以“WM_”打头,WM_COMMAND例外。Windows消息直接送给MFC窗口过程处理,窗口过程调用对应的消息处理函数。一般,由窗口对象来处理这类消息,也就是说,这类消息处理函数一般是MFC窗口类的成员函数。
- 控制通知消息,是控制子窗口送给父窗口的WM_COMMAND通知消息。窗口过程调用对应的消息处理函数。一般,由窗口对象来处理这类消息,也就是说,这类消息处理函数一般是MFC窗口类的成员函数。需要指出的是,Win32使用新的WM_NOFITY来处理复杂的通知消息。WM_COMMAND类型的通知消息仅仅能传递一个控制窗口句柄(lparam)、控制窗ID和通知代码(wparam)。WM_NOTIFY能传递任意复杂的信息。
- 命令消息,这是来自菜单、工具条按钮、加速键等用户接口对象的WM_COMMAND通知消息,属于应用程序自己定义的消息。通过消息映射机制,MFC框架把命令按一定的路径分发给多种类型的对象(具备消息处理能力)处理,如文档、窗口、应用程序、文档模板等对象。能处理消息映射的类必须从CCmdTarget类派生。
1)系统定义消息和应用程序定义消息
根据
消息的来源可以把消息分为系统定义消息和应用程序定义消息。
消息标示符定义规则:前缀_后缀,前缀表示处理消息的窗体类型,后缀表示消息的具体内容
---WM:通用窗口消息
---BM:按钮空控制框
---DM:默认按钮控制框
---CB:组合框控制框
---EM:编辑控制框
---LB:列表控制框
---SBM:滚动控制框
系统定义消息的标示符号常量范围:0~WM_USER-1或者0x80000~0xBFFFF
应用程序定义消息---应用程序创建的消息,用在自己的窗口中或与其他应用程序交互的消息。
应用程序定义消息的标示符号常量范围:WM_USER(0x0400)~0x7FFF或者0xC000~0xFFFF。其中
WM_USER(0x0400)~0x7FFF范围的消息由应用程序自己使用。
0xC000~0xFFFF范围的消息用来和其他应用程序通讯。为了确保消息标示符唯一性,使用
::RegisterWindowMessage为消息注册。
系统定义消息及其对应的标示符号的范围
2)队列消息和非队列消息
根据消息发送的目的地,消息分为队列消息和非队列消息。
Windows系统有两类消息队列,一个是Windows系统维护的系统消息队列(System message queue),另一个是每个GUI线程有一个线程消息队列(Thread message queue)。
队列消息都被发送到系统消息队列,Windows每次从系统消息队列中取出一个消息,确定其送往那个窗口和这个窗口是由那个线程创建,之后把消息放进创建窗口的线程的消息队列。并由线程从线程消息队列中取出消息送到适当的窗口过程进行处理。
鼠标消息WM_LBUTTONDOWN的产生及传递流程
非队列消息将会绕过系统队列和消息队列,直接被送到目的窗口过程进行处理。
除
鼠标消息,键盘消息,和队列消息WM_PAINT, WM_TIMER和WM_QUIT外,绝大多数消息都是非队列消息。
3)窗口消息,命令消息和控制通知消息
根据MFC消息处理函数和处理过程的不同,可分为窗口消息,命令消息和控制通知消息。
A. 窗口消息由窗口对象处理,窗口消息被直接发送给MFC窗口过程,窗口过程调用相应的消息处理函数处理消息,消息处理函数就是MFC窗口类的成员函数。比如WM_CREATE,MoveWindow,DestroyWindow等消息。
B. 命令消息,一种特殊的窗口消息,用来处理从一个窗口发送到另一窗口的用户请求。命令消息属于应用程序自定义消息。比如来自菜单,工具栏按钮,加速键等用户接口对象的WM_COMMAND消息。
C. 控制通知消息,控制子窗口送给父窗口的WM_COMMAND通知消息,窗口过程调用相应的消息处理函数处理消息。控制通知消息只适用于标准的窗口控件,比如按钮,列表框,组合框,编辑框,以及Windows公共控件包括树状视图,列表视图等。
控制通知消息和命令消息的区别:命令消息主要是来自用户请求,处理用户命令,而控制通知消息为了使控件发生改变。比如:单纯的单击鼠标,Windows发出命令消息;而单击按钮控件,鼠标动作就使得按钮向其父窗口发送控制通知消息,父窗口处理此消息,再将其交给按钮控件处理。
窗口消息和控制通知消息,主要由窗口类处理,即由类CWnd及其派生类处理。而命令消息能够处理从CCmdTarget类派生的类的消息。
4)常用消息
A. 鼠标消息:反应鼠标的各种动作
B. 键盘消息
C. WM_COMMAND:既包括控制通知消息,也包括命令消息
D. WM_PAINT:用来通知窗口需要重新绘制
E. WM_CREATE:通知生成窗口,完成初始化
F. WM_CLOSE:通知关闭窗口
G. WM_QUIT:通知应用程序结束
3. 消息的发送和接收
1)消息发送
发送方式 使用的函数
发送 SendMessage
| SendNotifyMessage
| SendMessageTimeout
| SendMessageCallback
投递 PostMessage
| PostQuitMessage
| PostThreadMessage
广播 BroadcastSystemMessage
A. 发送消息
代表函数SendMessage:
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam);
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam);
调用此函数向一个或者多个窗口发送一条消息,
函数在消息被处理之后才返回。如果消息的目标窗口是应用程序的一部分,那么该窗口的窗口函数就被直接调用;如果接收消息的窗口是由其他线程创建的,那么系统就切换到相应的线程,并调用相应的窗口函数。
函数的返回值由接收函数的窗口的窗口函数返回,取决于发送的消息。
B. 投递消息
代表函数是PostMessage:
BOOL PostMessage(
__in_opt HWND hWnd,
__in UINT Msg,
__in WPARAM wParam,
__in LPARAM lParam);
BOOL PostMessage(
__in_opt HWND hWnd,
__in UINT Msg,
__in WPARAM wParam,
__in LPARAM lParam);
调用此函数把一条消息投递到创建窗口的线程的消息队列的最后,
并不等消息被处理就马上返回
。
C. 广播消息
代表函数BroadcastSystemMessage:
long BroadcastSystemMessage(
__in DWORD flags,
__inout_opt LPDWORD lpInfo,
__in UINT Msg,
__in WPARAM wParam,
__in LPARAM lParam);
__in DWORD flags,
__inout_opt LPDWORD lpInfo,
__in UINT Msg,
__in WPARAM wParam,
__in LPARAM lParam);
调用该函数可以向指定接收者发送一条消息,该消息可以是应用程序,可安装的驱动程序,网络驱动程序,系统级别的设备驱动消息或者他们的任意组合。广播消息成功返回一个正值,消息广播失败则返回-1。
如果flags = BSF_QUERY且至少有一个接收者返回了BROADCAST_QUERY_DENY,则返回值为0,如果flags != BSF_QUERY,则函数把消息发送给所有的接收者,并忽略其返回值。
2)消息接收
有三个主要的接收函数:GetMessage,PeekMessage,WaitMessage
BOOL GetMessage(
__out LPMSG lpMsg,
__in_opt HWND hWnd,
__in UINT wMsgFilterMin,
__in UINT wMsgFilterMax);
__out LPMSG lpMsg,
__in_opt HWND hWnd,
__in UINT wMsgFilterMin,
__in UINT wMsgFilterMax);
该函数用于获取与hWnd参数所指定的窗口相关联且在wMsgFilterMin/wMsgFilterMax参数所给消息值范围内的消息。如果hWnd = NULL,GetMessage获取属于调用该函数应用程序的任一窗口的消息。如果wMsgFilterMin = 0/wMsgFilterMax = 0,GetMessage返回所有可得到的消息。函数获取的如果不是消息WM_QUIT,则返回非零,否则返回零。
BOOL PeekMessage(
__out LPMSG lpMsg,
__in_opt HWND hWnd,
__in UINT wMsgFilterMin,
__in UINT wMsgFilterMax,
__in UINT wRemoveMsg);
__out LPMSG lpMsg,
__in_opt HWND hWnd,
__in UINT wMsgFilterMin,
__in UINT wMsgFilterMax,
__in UINT wRemoveMsg);
该函数用于查看消息队列,如果其中有消息就将其放入到lpMsg所指的结构中。PeekMessage与GetMessage不同的是,函数GetMessage只有队列有消息时才返回,PeekMessage无论队列中是否有消息都会返回。函数获取到消息返回非零,否则返回零。
BOOL WaitMessage(VOID);
该函数使应用程序挂起,直到一个新的消息被放入应用程序的消息队列之中才返回,函数执行成功返回非0,执行失败返回0
4. 消息循环
消息循环---从线程的消息队列中检索出消息,并将其发送到相应的窗口过程进行消息处理的机制。
MSG msg;
while (GetMessage(&msg, (HWND)NULL, 0, 0)) // 接收消息
{
if(TranslateAccelerator(hwndMain, haccel, &msg))
{
TranslateMessage(&msg); // 转换消息
DispatchMessage(&msg); // 分发消息
}
}
while (GetMessage(&msg, (HWND)NULL, 0, 0)) // 接收消息
{
if(TranslateAccelerator(hwndMain, haccel, &msg))
{
TranslateMessage(&msg); // 转换消息
DispatchMessage(&msg); // 分发消息
}
}
函数GetMessage从线程消息队列中获取一个消息并将他复制到MSG结构。TranslateAccelerator判断该消息是否为一个按键消息且为一个加速键消息,(如果是,则该函数把几个按键消息转换成一个加速键消息传递给窗口的回调函数)。消息处理完成后,函数DispatchMessage把消息发送给该消息指定窗口中的回调函数。
如果得到的消息是WM_QUIT,则
GetMessage返回0,应用程序退出消息循环。
5. 消息映射---消息处理机制,把消息和消息处理函数相互关联起来
1)消息映射的实现方法
A. 在类声明中,通过宏DECLARE_MESSAGE_MAP声明消息映射的关联方法,将消息和消息处理函数连接起来。
B. 在类实现的文件中,通过宏BEGIN_MESSAGE_MAP/END_MESSAGE_MAP来定义在宏DECLARE_MESSAGE_MAP中声明的方法。
C. 添加消息处理函数,并在里面添加相关的处理代码,实现需要的响应。
2)消息映射的宏
A. 用于窗口映射的宏---此类宏前缀为“ON_WM_”,如ON_WM_PAINT(),此类宏不带参数
B. 用于命令消息的宏---此类宏只有一种形式ON_COMMAND。命令ID/处理函数,分两类一对一:ON_COMMAND,多对一ON_COMMAND_RANGE。
C. 用于控制通知消息的宏---此类宏的形式由消息具体决定,也支持多对一:ON_CONTROL_RANGE/ON_NOTIFY_RANGE
D. 自定义消息映射的宏---此类前缀是“ON_”,后缀由用户自行命名,附带参数:命令ID/消息处理函数
6. 自定义消息的消息映射
一种利用ON_MESSAGE宏,另一种就是创建自己的消息映射宏
自定义消息的定义形式:WM_USER + X(X是一个非负整数)。
例如:
1)类声明文件中,声明一个消息命令ID,并在类声明体内声明一个响应函数
//自定义消息映射宏的命令ID
#define WM_MY_MESSAGE (WM_USER + 100)
#define WM_MY_MESSAGE (WM_USER + 100)
//自定义消息映射宏的相应函数
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
2)在类实现文件中,实现OnMyMessage函数
LRESULT CExMyMessageView::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
MessageBox(L" Hello!");
return NULL;
}
{
MessageBox(L" Hello!");
return NULL;
}
3)在BEGIN_MESSAGE_MAP/END_MESSAGE_MAP中,添加命令ID和响应函数的连接,实现消息映射
BEGIN_MESSAGE_MAP(CExMyMessageView, CView)
// the message is added by user
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage) //自定义消息映射宏
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
// the message is added by user
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage) //自定义消息映射宏
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()