MFC的消息处理模式

在Windows中发生的一切都可以用消息来表示,消息用于告诉操作系统发生了什么,所有的Windows应用程序都是消息驱动的,消息机制是Windows应用程序的核心。

在Windows中,不同的消息由应用程序的不同部分进行处理。MFC库将很多底层的消息都屏蔽了,使用户更加方便、简易地处理消息。例如,用户接收到诸如移动鼠标键(WM_MOUSEMOVE)消息或单击鼠标键(WM_LRBUTTONDOWN)消息时不必处理窗口和鼠标的重画工作,MFC及应用程序框架会替用户做这些工作。在使用MFC进行编程时,用户只需处理一些高层的消息,例如,“用户在单击窗口中的OK按扭”,“用户现在选中了下拉列表框中的第五项”等等,这样就大大减轻了程序员的负担。

一个消息是由消息的名称(UINT)和两个参数(WPARAM, LPARAM)组成。消息的参数中包含有重要的信息。例如对鼠标消息而言,LPARAM中一般包含鼠标的位置信息,而WPARAM参数中包含了发生该消息时,SHIFT、CTRL等键的状态信息,对于不同的消息类型来说,两个参数也都相应地具有明确意义。

消息与输入焦点

Windows是一个以消息为导向的系统,应用程序只能被动地等待用户按键的消息,不能主动地去读键盘的状态,也就是说,每当键盘上有个键被按下,系统就会发出一个按键消息给窗口,告诉它某个键被按下去了,只要鼠标移动一下,系统也会发出相应的消息,并把鼠标的坐标信息传给窗口。

Windows可以同时执行许多程序,但键盘只有一个,怎么判断由哪个窗口接收键盘及鼠标的消息呢?采用“输入焦点”(inpuut focus)技术可以解决这个问题。只要某个窗口取得输入焦点,它不但会被提升到屏幕的最前面,颜色也会有所不同,所有的键盘消息就会导向该窗口,该窗口也成为“活动窗口”。

窗口如何取得输入焦点?通常被鼠标单击的窗口会得到输入焦点,除此之外,程序本身也可以利用SetFocus()来指定哪个窗口拥有输入焦点。

CWnd*CWnd::SetFocus();

如果调用某窗口的SetFocus()成员函数,该窗口就可以取得输入焦点,该函数返回前一个拥有输入焦点的窗口。

如果某个窗口的输入焦点被抢走,Windows系统就会发出WM_KILLFOCUS消息给这个失去输入焦点的窗口,同时还会告诉该窗口下一个取得输入焦点的窗口的指针。而获得输入焦点的窗口则会收到WM_SETFOCUS消息。

消息响应函数分别为:

afx_msgvoid OnKillFocus(CWnd* pNewWnd);

其中的参数为得到输入焦点的窗口的指针。

Afx_msgvoid OnSetFocus(CWnd* pOldWnd);

其中的参数为失去输入焦点的窗口的指针。

消息的分类

Windows系统预定义了许多消息,每个消息都拥有一个宏定义,即用形象的字符串来标识消息,一系列#define 语句将消息与特定数值联系起来,可以在头文件WinUser.h中找到这些宏定义,例如

#defineWM_PAINT 120

可以在程序中通过消息名“WM_PAINT”来访问它。其他消息如:

#define WM_MOUSEMOVE                    0x0200

#defineWM_LBUTTONDOWN                  0x0201

#define WM_LBUTTONUP                    0x0202

#defineWM_LBUTTONDBLCLK                0x0203

#defineWM_RBUTTONDOWN                  0x0204

#define WM_RBUTTONUP                    0x0205

#defineWM_RBUTTONDBLCLK                0x0206

#defineWM_MBUTTONDOWN                  0x0207

#define WM_MBUTTONUP                    0x0208

#defineWM_MBUTTONDBLCLK                0x0209

系统定义的消息有不同的前缀,不同的前缀有不同的含义。

1. 标准的Windows消息

除了WM_COMMAND消息,所有以WM_为前缀的消息都是标准的Windows消息,如窗口、鼠标移动、窗口大小改变等,程序启动或退出甚至每一段固定的时间都会产生标准Windows消息。如

1) 键盘消息

对于窗口而言,来自用户的按键输入可分为两类,一类是系统键(system key),另一类则是非系统键。凡是ALT和其它键一同按下的组合称为“系统键”,窗口收到系统键之后,会自动地将它解释成系统事件,或者查阅键盘加速表,将系统键翻译成加速表指定的信息。如:ALT+F4的组合会迫使窗口关闭,“ALT+字母”的组合可能会拉下某个菜单。

当用户按下某个键时,Windows系统会先发出WM_KEYDOWN消息给窗口,这个消息的意思是“按键被压下去”。接着Windows系统会发出WM_CHAR给同一个窗口,这个消息代表的意义是“系统送来某个字符”,如果用户放开此键,Windows系统会发出WM_KEYUP消息,表示“按键被放开”。如果用户一直按住某个键不放,经过一段时间之后会产生“连发”的效果,造成Windows系统不停地发出WM_KEYDOWN与WM_CHAR消息。

计算机内部以ASCII码的规则来记录所有的英文字母和数字符号。不过不是键盘上每个按键都可以对应成ASCII码中的字符,如大小写键、CTRL键、F1到F12键等。

每个按键都有对应的扫描码,PC BIOS收到键盘的中断消息后,会自动将扫描码翻译成ASCII码,但有些控制键无法译成ASCII码,如Page UP、Page Down等。Windows定义了一套与硬件无关的“虚拟键码”来表示键盘上所有的按键,如A键就是VK_A、ESC键就是VK_ESC、F1键是VK_F1、ALT键是VK_MENU等。因为“虚拟键码”定义的规则与硬件无关,所以有些虚拟键在通常的键盘上根本就找不着。

#defineVK_LBUTTON        0x01

#defineVK_RBUTTON        0x02

#defineVK_CANCEL         0x03

#defineVK_MBUTTON        0x04    /* NOT contiguous with L & RBUTTON */

#defineVK_BACK           0x08

#defineVK_TAB            0x09

#defineVK_CLEAR          0x0C

#defineVK_RETURN         0x0D

#defineVK_SHIFT          0x10

#defineVK_CONTROL        0x11

#defineVK_MENU           0x12

#defineVK_PAUSE          0x13

#defineVK_CAPITAL        0x14

#defineVK_F1             0x70

#defineVK_F2             0x71

#defineVK_F3             0x72

#defineVK_F4             0x73

#defineVK_F5             0x74

#defineVK_F6             0x75

#defineVK_F7             0x76

#defineVK_F8             0x77

#defineVK_F9             0x78

#defineVK_F10            0x79

#

#defineWM_CHAR               0x0102 //字符消息

WM_CHAR也称为键盘消息,如果某窗口拥有输入焦点,当用户在应用程序运行时按下一个键时,系统就会产生一个键盘消息WM_CHAR,告诉此窗口键盘上哪个键被按下了。该消息的处理函数为OnChar()。具体形式为:

afx_msgvoid OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

各参数含义为:

nChar:键盘所输入的ASCII码。

nRepCnt:按键的重复次数,当用户按下某个键不放时,该参数将持续增加。

nFlag:用于传递按键的其它一些信息,如扫描码,上一次按键状态等。具体如下:

字节

说明

0-7

键盘扫描码

8

此按键为扩充按键,如F1,F12等功能键,此字节等于1时为真

9-12

保留

13

此字节为1表示按下键的同时,ALT键也被按住了

14

前一个按键状态。此字节为1代表信息在按键被按下之前就送出来了

15

此字节为1表示这个按键已经被放开了,反之就表示还被按着

此外还有两个常用的键盘消息:WM_KEYDOWN和WM_KEYUP.

WM_KEYDOWN消息是当用户按下一个非系统键时产生的,非系统键就是不按下ALT键时的按键。

WM_KEYUP消息是当用户释放一个非系统键时产生的。

2) 鼠标消息

① #define WM_MOUSEMOVE       0x0200  //鼠标移动消息

当鼠标在某个窗口内移动时,Windows会不断地发出鼠标移动消息WM_MOUSEMOVE,并把鼠标的最新位置传给该窗口。如果在窗口的范围内按下鼠标左键,系统就会发出“按下左键”的WM_LBUTTONDOWN消息给该窗口,等到用户放开按键后,再发出“放开左键”的WM_LBUTTONUP消息给该窗口。

鼠标移动消息的消息响应函数为:

afx_msg void OnMouseMove(UINT nFlags, CPoint point)

其中的参数含义如下:

UINT nFlag:此事件发生时,鼠标按键、键盘控制键的状态,可以是以下值的任意组合:

当用户按下CTRL键时,nFlags设置为MK_CONTROL。

当用户按下鼠标左键时,nFlags设置为MK_LBUTTON。

当用户按下鼠标中键时,nFlags设置为MK_MBUTTON。

当用户按下鼠标右键时,nFlags设置为MK_RBUTTON。

当用户按下SHIFT键时,nFlags设置为MK_SHIFT。

CPoint point:该参数为一个CPoint结构,用于指示当前鼠标光标所在的x, y坐标。

鼠标除了移动会产生消息之外,鼠标上面的按键也会象键盘按键一样产生消息,以左键为例,当它被压下去、按住未放开时,Windows会发出WM_LBUTTONDOWN消息;当该键被放开后,Windows会发出WM_LBUTTONUP消息;当鼠标左键双击之后,Windows会发出WM_LBUTTONDBLCLK消息;

#defineWM_LBUTTONDOWN    0x0201  //鼠标左键按下消息

当用户在窗口客户区中按下鼠标左键时,产生WM_LBUTTONDOWN消息,该消息的处理函数为:OnLButtonDown(),它带有两个参数。

afx_msg void OnLButtonDown(UINT nFlags, CPoint point)

#defineWM_LBUTTONUP         0x0202 //鼠标左键抬起消息

消息处理函数为:

afx_msg void OnLButtonUP(UINT nFlags, CPoint point)

#defineWM_LBUTTONBLCLK     0x0203 //双击鼠标左键消息

消息处理函数为:

afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point)

其它鼠标中键、右键与此类似。

标准的Windows消息由窗口和视图处理,即CWnd和它的派生类都可以接收标准Windows消息。这类消息中通常含有如何对消息进行处理的一些参数。标准的Windows消息都有默认的处理函数,这些处理函数在CWnd类中进行了预定义。

2. 控件通知消息

控件通知消息是由编辑框、列表框等控件或子窗口产生并传向父窗口的消息,它以WM_COMMAND为消息名,在消息的参数中包含有具体的控件通知代码,以区别具体的控件通知消息。

控件通知消息必须由窗口类的对象,即直接或间接由CWnd类或其派生类的对象进行处理。比如主框架窗口类、子边框窗口类或窗口类等。

不同的控件产生的通知消息有不同的前缀,如:

1)文本控件消息

文本控件消息以EM_为前缀,如:

#define EM_GETSEL      0X00B0       //获得选择文本

#define EM_SETSEL      0X00B1       //设置选择文本

#define EM_GETRECT    0X00B2      //获得文本区域

#define EM_SETRECT     0X00B2      //设置文本区域

2按扭控件消息

按扭控件消息以BM_为前缀,如

#define BM_GETCHECK  0X00F0   //确定单选按扭或复选按扭是否被选中

#define BM_SETCHECK  0X00F1   //设置按扭控件的选中标记

#define BM_GETSTATE  0X00F2   //获得按扭控件的状态

#define BM_SETSTATE  0X00F3   //设置按扭显示的高亮状态

#define BM_SETSTYLE  0X00F4   //设置按扭控件的风格

#define BM_CLICK      0X00F5   //单击按扭控件

3)列表框控件消息

列表框控件消息以LB_为前缀

#define LB_ADDSTRING  0x0180   //向列表框中加入字符串

#define LB_INSERTSTRING   0x0181   //向列表框的指定位置中加入字符串

#define LB_GETTEXT       0x0189   //获取列表框中的文本

4组合框控件消息

组合框控件消息以CB_为前缀

#define CB_ADDSTRING     0x0143   //把字符串加入到组合框的列表部分

#define CB_DELETESTRING   0x0144   //从组合框列表部分删除字符串

5滚动栏控件消息

滚动栏控件消息以SBM_为前缀

#define SBM_SETPOS     0x00E0   //设置滚动栏滑块的位置

#define SBM_GETPOS     0x00E1   //获取滚动栏滑块的位置

#define SBM_SETRANGE  0x00E2   //设置滚动栏滑块的范围

6默认下压式按扭消息

#define DM_GETDEFID   (WM_USER+0)  //获取对话框默认按扭控件的ID

#define DM_SETDEFID   (WM_USER+0)  //改变对话框默认按扭控件的ID

3. 命令消息

命令消息是一类特殊的消息。每当用户选择一个菜单项、单击一个按钮、工具栏和加速键时,就发出一个WM_COMMAND命令消息。

所有命令消息都包含一个共同的参数,那就是该命令消息需要操作的资源ID值。例如当我们单击菜单项File|New时,产生的命令消息中将包含该菜单项的资源ID值,如ID_FILE_NEW。

命令消息的处理与前两种消息不同,命令消息可以被更广泛的对象处理,而不仅仅局限于窗口类。所有派生自CCmdTarget的类,如文档类、文档模板类和应用程序类等都可以处理命令消息。

命令消息的处理函数为:OnCommand()。

4. 消息标志符取值范围

系统定义的消息            0x0000到0x03FF

用户定义内部消息          0x4000到0x07FF

系统定义的消息            0x8000到0x0BFF

用户定义外部消息          0xC000到0xFFFF

下面是几个重要的与窗口操作有关的消息:

1. WM_CREATE消息

当一个应用程序调用CreateWindow函数创建一个窗口时,会产生WM_CREATE消息,该消息的处理函数为:

afx_msgint OnCreate(LPCREATESTRUCT lpCreateStruct)

2. WM_PAINT消息

当Windows或另一个应用程序要求重画当前应用程序中的窗口或部分窗口时,会发出WM_PAINT消息。通常在调用UpdateWindow函数或RedrawWindow()之后会产生WM_PAINT消息。

该消息的处理函数为:OnPaint()。该函数没有参数。

在Windows编程中与窗口相关的消息还有很多,如WM_HSCROLL、WM_VSCROLL等。

定时消息

定时消息也是Windows中的一类重要的消息。当用户希望应用程序每隔一个特定的时间间隔执行某项指定操作时,就需要用到定时消息WM_TIMER。

在进行定时操作时,用户需要先调用SetTimer函数来创建一个定时器,并设置该定时器的事件标志nIDEvent以及时间间隔uElapse。然后编写WM_TIMER消息的处理函数OnTimer()。此函数拥有一个参数,即定时器的事件标志nIDEvent,用于指定使用哪个定时器。用户可以在OnTimer()函数中编写定时操作的程序代码。

当应用程序开始运行后,每过uElapse毫秒,就发出一个WM_TIMER消息,从而进入OnTimer()函数进行处理。

如  SetTimer(ID_TIMER, 1000, NULL); //该定时器ID,时间间隔,调用函数

所有的消息处理函数原型前面都有关键字 afx_msg前缀,用于把消息处理函数与其它函数区分开来。

消息映射

以前的程序员编写Windows程序时,需要使用WinMain()例程来向WndProc函数发送消息,对消息的处理是通过switch-case结构实现的,当要处理的消息很多时,switch-case结构的分支很多,影响程序的可读性。

在编写MFC应用程序时,不再采用这种古老的方法,而是采用一种相当巧妙的方法,通过一些宏将特定的消息映射到派生类中相应的成员函数上,这种方法被称作消息映射机制。所谓消息映射,就是将消息与其处理函数相对应,即当系统产生一条消息时,它能够找到处理该消息的函数。

在MFC中,凡是从CcmdTarget(命令发送类)派生的类都可以有消息映射,如果要建立消息映射,需要进行以下操作:

1 在类的相应头文件(.H)中申明消息映射表,它通常情况下被写在一个类定义的最后。

DECLARE_MESSAGE_MAP()

2 在类的实现代码文件(.cpp)中加入消息映射表,如下所示:

BEGIN_MESSAGE_MAP(CfirstAppApp, CWinApp)

ON_COMMAND(ID_APP_ABOUT, OnAppAbout)

ON_COMMAND(ID_FILE_NEW, CwinApp::OnFileNew)

ON_COMMAND(ID_FILE_OPEN,CwinApp::OnFileOpen)

ON_COMMAND(ID_FILE_PRINT_SETUP,CwinApp::OnFilePrintSetup)

END_MESSAGE_MAP()

VC++中提供了三种消息映射宏:DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP和END_MESSAGE_MAP。DECLARE_MESSAGE_MAP用于类申明的末尾,即在.h文件中;在实现文件(.cpp)中,使用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP来完成消息映射的任务。这两个宏总是配合使用。在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏之间列出了消息映射的各个入口。

DECLARE_MESSAGE_MAP:用于申明在源文件中存在消息映射。

BEGIN_MESSAGE_MAP:用于标志消息映射的开始。

END_MESSAGE_MAP:标志消息映射的结束。

ON_COMMAND:用于将一个特定的命令消息映射到类的成员函数,即使用该成员函数来处理该命令消息。即指定命令消息和它的处理函数名称。

ON_COMMAND_UPDATE_UI:用于将一个特定的更新命令消息映射到类的成员函数。例如菜单的灰化、命令按钮的禁止等。

其中,BEGIN_MESSAGE_MAP宏有两个参数,第一个参数指出该消息映射所属类的名称,第二个参数指出前一个类的直接基类。

当使用ClassWizard创建新类时,该向导为新类提供一个消息映射,但向导支持的映射宏是有限的,有时需要用户手动创建消息映射。

消息传递

Windows应用程序中的大多数消息都是用户与应用程序相互作用而产生的。当产生消息时,系统使用CwinApp类的Run函数来检索消息,并把消息发送到适当的窗口。在MFC程序中,命令消息与非命令消息采用的是两种不同的传递方式。

非命令消息的传递

非命令消息(即标准Windows消息和控件通知消息)必须由窗口类的对象,即直接或间接由CWnd类或其派生类的对象进行处理。这个窗口可能是主框架窗口、标准控件、对话框、视图或其它类型的子窗口。在程序运行过程中,每个Windows窗口都与窗口对象联系在一起。每个窗口对象都有自己的消息映射和处理函数。MFC利用消息映射把消息与其对应的处理函数相匹配。

命令消息的传递

命令消息的处理与其它消息不同,命令消息可以被更广泛的对象处理,而不仅仅局限于窗口类。所有派生自CCmdTarget的类,如文档类、文档模板类和应用程序类等都可以处理命令消息。当某个命令消息产生后,它被应用程序框架沿着命令消息的传递路径发送。命令消息的传递路径见下表。传递路径上的每一个对象都按顺序检查自己的消息映射,看看是否能够处理这个命令消息。如果消息与处理函数相匹配,则调用处理函数处理该消息并不再搜索后面的对象,不然就继续搜索下去。

若程序为SDI应用程序,则传递路径从第二栏开始。

从表中可以看出,消息的传递尊循一定的规则:一个对象接收到命令消息后,先发给当前活动的子对象,然后交给自己处理,如果都不能处理,最后再发给其它对象。

命令消息的传递路径

收到消息的对象及其所属类

传递路径

MDI主框架窗口对象

(CMDIFrameWnd及其派生类)

活动的MDI子边框窗口(CMDIChildWnd)

主框架窗口,即其自身

应用程序(CWinApp)对象

文档边框窗口对象

(CMDIChildWnd或CframeWnd及其派生类)

活动视图

文档边框窗口,即其自身

应用程序(CWinApp)对象

视图对象

(Cview及其派生类)

视图

与视图关联的文档

文档对象

(CDocument及其派生类)

文档

与文档关联的文档模板

对话框对象

(CDialog及其派生类)

对话框

拥有此对话框的窗口

应用程序(CWinApp)对象

MFC的消息处理模式相关推荐

  1. MFC的消息处理函数和消息过程函数的区别

    MFC的消息处理函数(例如:OnCreate)和消息过程函数(例如:WindowProc)的区别? 并不是说OnCreate和WindowProc的区别,只是分别举个消息映射函数和窗口过程函数的例子. ...

  2. MFC—对话框程序—模式对话框与非模式对话框

    一.根据主窗口类型,MFC软件工程可以分为以下几种架构模型: 1.SDI(Single Document Interface):单文档界面,一个主框架窗口下只能编辑一份文档. 例如:记事本和画笔等. ...

  3. [编织消息框架][消息处理模式]管道模式

    proxy server 提供外部公开访问服务 client向proxy server访问时,proxy server分发N个任务调用工作服 而client无需要关心proxy server 如何工作 ...

  4. 在vs2008 vc++ 中添加mfc中消息处理函数

    初学vc++,想在vs2008 c++中添加一个按钮需要添加消息处理函数,可是不像6.0,右键添加的选项里只有变量和函数,没有消息处理函数,找了一圈还是在msdn里,具体步骤如下: 1.在类视图中,右 ...

  5. MFC消息处理学习总结

    Windows消息机制概述 http://www.cppblog.com/suiaiguo/archive/2009/07/18/90412.html 消息是指什么?      消息系统对于一个win ...

  6. MFC框架机制详细论述

    1.1 Windows消息机制要点 1.1.1 窗口过程 ​ 每个窗口会有一个称为窗口过程的回调函数(WndProc),它带有四个参数,分别为:窗口句柄(Window Handle), 消息ID(Me ...

  7. 深入理解MFC消息循环和消息泵的原理

    首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...

  8. MFC 教程【12_对话框和对话框类CDialog 】

    对话框和对话框类CDialog 对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的. 模式和无模式对话框 对话框分两种类型,模式对话框和无模式对话框. 模式对 ...

  9. MFC 教程【2_MFC和Win32 】

    MFC和Win32 MFC Object和Windows Object的关系 MFC中最重要的封装是对Win32 API的封装,因此,理解Windows Object和MFC Object (C++对 ...

  10. MFC进修笔记2——MFC和Win32

    1.MFC Object和Windows Object的关系 MFC中最首要的封装是对Win32 API的封装,是以,懂得Windows Object和MFC Object (C++对象,一个C++类 ...

最新文章

  1. 机器学习的教训:5家公司分享的错误经验
  2. Python 语言介绍
  3. HTML ol 标签的 type 属性
  4. java面试第十四天
  5. 扑克牌比大小c语言,算了算学了有一个月c语言了,写了个扑克牌程序
  6. 0421 AutoLayout的实践/基本使用
  7. JavaMonitor 监视器
  8. 2022 USNews全美大学排行榜出炉!普林斯顿霸榜,哥大哈佛MIT并列第二
  9. .NET下的验证码控件John.Controls.ValidateCode2V for .NET beta1
  10. moodle架构分析---表现层的设计(一)
  11. java 学习案例之英汉字典
  12. 电子科技大学《图论及其应用》复习(史上最全汇总)
  13. 博主力推!!NRF52832 BLE 抓包sniffer来了!附带安装使用说明
  14. springboot 操作es 之elasticsearch-rest-high-level-client
  15. 微博热搜数据变化趋势视频化展示
  16. 时序违例的原因及其解决办法
  17. 关于脑电波的黑科技,离我们生活还有多远。
  18. 销售报表案例--如何应用Excel创建销售漏斗分析仪
  19. 汇编中的串操作指令(MOVS,CMPS,SCAS,LODS,STOS)
  20. Hadoop第七天--MapReduceYarn详解(二)

热门文章

  1. 计算机基础知识进制的转换,计算机基础之各进制间的相互转换
  2. 自动刷百度下拉词优化工具
  3. h5可拖动悬浮按钮_移动端可拖拽悬浮窗+点击事件
  4. WIZ ConfigTool-批量配置WIZnet S2E模块
  5. 怎样往阿里云windows服务器传文件
  6. matlab gui矩阵计算器,matlab-gui矩阵计算器.doc
  7. java 开根号_Java实现开根号运算(不使用数组和String)
  8. C语言入门--状态机编程
  9. 状态机编程实例及适用范围
  10. 15个磁性材料相关概念解释(基础版)