汇编语言课程

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Lesson 09-10 鼠标键盘消息
鼠标和键盘是系统的基本输入设备。

当应用程序需要从用户处得到某些信息时,必须用到输入设备。

由于鼠标和键盘是用户同计算机打交道的主要工具,本课主要学习如何响应鼠标以及键盘的输入信息。

一、使用鼠标
鼠标是一种定位输入设备,一般来说,常用的鼠标有两个键(左键、右键)的,也有三个键(左键、中键、右键)的,鼠标的操作主要有以下三种基本方式:
单击(Click):迅速地按下并释放鼠标键。

双击(Double Click):连续两次迅速地按下和放开鼠标键。

移动(Move):移动鼠标的位置。

当鼠标进行上面的操作时,就会激发鼠标事件,也就会向Windows系统发送相应的鼠标消息,应用程序就可以根据不同的消息进行不同的处理。

1.常用鼠标消息
根据鼠标在窗口中所处位置不同,鼠标消息可以被分为以下两类:
■客户区鼠标消息
当鼠标处于窗口的客户区时发送的鼠标消息。

如视图窗口区域整个都是客户区,也是框架窗口的客户区。

■非客户鼠标消息
当鼠标处于窗口的非客户区域时所发送的消息。

非客户区一般包括标题栏、最大最小化按钮、关闭窗口按钮、系统菜单和窗口边框等。

①客户区鼠标消息
一般在Windows应用程序中使用最多的是客户区鼠标消息,大部分应用程序都不处理非客户区域鼠标消息,它们会被Windows操作系统本身妥善处理。

下面列出Windows常用的10个客户区鼠标消息:
鼠标移动:
WM_MOUSEMOVE 鼠标在客户区移动
鼠标左键:
WM_LBUTTONUP左键在客户区释放
WM_LBUTTONDOWN左键在客户区按下
WM_LBUTTONDBCLK左键在客区双击
鼠标右键:
WM_RBUTTONUP右键在客户区释放
WM_RBUTTONDOWN左键在客户区按下
WM_RBUTTONDBCLK右键在客户区双击
鼠标中键:
WM_MBUTTONUP中键在客户区释放
WM_MBUTTONDOWN中键在客户区按下
WM_MBUTTONDBCLK中键在客户区双击
除鼠标移动消息WM_MOUSEMOVE以外,每个鼠标键都有三种消息:按下、释放和双击。

当用户在客户区单击一下鼠标左键时,实际上发送了两个鼠标消息:在鼠标左键按下时发送了WM_LBUTTOND O WN消息;在鼠标左键释放时发送了WM_LBUTTONUP消息;而当鼠标在客户区移动时,实际上在不停向Windows系统发送WM_MOUSEMOVE消息。

区分两次单击和一次双击,取决于两次按下鼠标键之间的时间间隔,只有当时间隔小于一定值时才被认为是一次双击,Windows默认的时间间隔为500毫秒,应用程序可以调用全局函数::SetDoubleClickTime来重新设定此值。

鼠标消息被放在系统的消息队列,由Windows系统的用户模块协助,发送给相应的窗口进行处理。

②非客户区鼠标消息
一般情况下,应用程序可以不考虑非客户区鼠标消的处理,下面列出了10个非客户区鼠标消息:
鼠标移动:
WM_NCMOUSEMOVE鼠标在客户区移动
鼠标左键:
WM_NCLBUTTONUP左键在非客户区释放
WM_NCLBUTTONDOWN左键在非客户区按下
WM_NCLBUTTONDBCLK左键在非客户区双击鼠标右键:
WM_NCRBUTTONUP右键在非客户区释放
WM_NCRBUTTONDOWN右键在非客户区按下
WM_NCRBUTTONDBCLK右键在非客户区双击鼠标中键:
WM_NCMBUTTONUP中键在非客户区释放
WM_NCMBUTTONDOWN中键在非客户区按下
WM_NCMBUTTONDBCLK中键在非客户区双击
③接收鼠标消息
一般情况下,鼠标处在哪个窗口中,鼠标消息就发送到该窗口。

当鼠标移动到某个应用程序窗口的外面时,应用程序则停止接收鼠标消息,Wihdows此时会将鼠标消息发送给鼠标下面的窗口(包括桌面)。

当鼠标移回到应用程序窗口中时,Windows会重新把鼠标消息发送方向指向应用程序。

2.处理客户鼠标消息
使用Class Wizard可为应用程序添加鼠标消息处理,为2DCAD程序添加左键按下鼠标消息处理,具体步骤如下:
第1步:打开2DCAD工程,打开MFC ClassWizard对话框。

第2步:在ClassWizard对话框的Message Maps选项卡中,先在Class Name下拉列表中选择CMy2DCADView视图类,然后在Obiect Ids列表框中选择第一个列表项CMy2DCADView,此时Messages列表框中有许多消息(按字母顺序排列),找到WM_LBUTTONDOWN消息,为视图类添加鼠标左键按下的消息处理函数。

第3步:编辑OnLButtonDown函数。

鼠标左键消息处理代码添加在三个地方:
(1)在类的头文件中(2DCADView.h)声明函数:
函数带有两个参数,没有返回值。

(2)在类的执行文件中(2DCADView.cpp)使用消息映射宏建立消息映射:处理鼠标左键被按下消息,使用ON_WM_LBUTTONDOWN宏,其他客户区鼠
标消息映射宏如下表所示:
(3)在类的执行文件(2DCADView.cpp)中定义函数体。

函数中调用了CMy2DCADView的基类Cview中的成员函数OnLButtonDown,
用来处理默认消息。

客户区鼠标消息映射宏
MFC把鼠标消息处理函数封装在CWnd类中,都是虚函数,拥有相同的返回值类型和相同的参数:返回值类型为void,两个参数为:
■CPoint point
Point参数代表鼠标光标的坐标位置,point.x为横坐标,point.y为纵坐标。

采用的是客户坐标系统。

在这个坐标系统中,默认的坐标原点(0,0)位于客户区的左上角。

■UNIT nFlages
鼠标键和键盘组合使用标志,用来描述鼠标消息产生时鼠标键与Ctrl、Shift键的组合状态。

nFlags中包含的标志可以有以下5个:
MK_LBUTTON:鼠标左键被按下。

MK_RBUTTON:鼠标右键被按下。

MK_MBUTTON:鼠标中键被按下。

MK_SHIFT:键盘上的Shift键被按下。

MK_CONTROL:键盘上的Ctrl键被按下。

第一个参数的意义非常明确,代表了鼠标在当前窗口中的位置;第二个参数nFlags相对难于理解一些,举个简单的例子,当用户在使用Windows的资源管理器查看文件时,可以按住键盘上的Ctrl键,然后单击多个文件,将这些文件都选中。

此时发送给Windows的鼠标消息WM_LBUTTONDOWN中的nFlags参数包含有MK_CONTROL标志。

在应用程序中判断nFlags参数中包含的标志要使用到位逻辑运算,位运算将操作数按照二进制位逐位进行逻辑运算。

要知道某个按钮是否被按下,可以用对应的常量标志与nFlages参数作按位逻辑“与”运算,所得结果若为非零值,则表示按钮被按下。

如2DCAD程序,当用户在客户区中只单击鼠标左键时,不会输出“Shift键被按下”的文本。

但如果先按住键盘的Shift键,再在客户区中单击鼠标左键,就会输出该文本,使用位运算同样可以处理其他的按键状态。

3.使用鼠标画点画线
①画点
画点的交互较简单,一般的画图程序只需要在视图区中单击一下鼠标左键,如果此时处于画点状态,就可以在鼠标单击的位置画出一个点来。

画点是一步交互过程,只需要处理鼠标左键按下的消息WM_LBUTTONDOWN,并在消息处理函数里添加相应的画点代码就可以了。

需要有一个变量来标志程序是否处于画点状态。

编译并运行2DCAD程序,当用户直接在客户区单鼠标左键时,不会有什么反映。

先单击一下工具栏上的画点按钮,然后再在客户区单击鼠标左键,可见每单击一次,就在视图区中画上了一个中圆圈。

②画线段
在视图中画一条线段的交互过程要比画点复杂,通常可以有两种交互方式来画一条线段:
■两次单击鼠标左键的方式
第一次单击鼠标左键定义线段的起点,移动鼠标就会拖出一条橡皮条,然后第二次单击鼠标左键定义线段的终点。

这种交互方式需要处理的鼠标消息依次是WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONDOWN。

■“单击-拖动-释放”鼠标左键的方式
先按下鼠标左键定义线段的起点,然后拖动鼠标拉出一个橡皮条,最后释放鼠标左键定义线段的终点。

这种交互方式需要处理的鼠标消息三个,依次为WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP。

画线段涉及到一系列鼠标消息的处理。

在2DCAD程序中将采用第一种交互方式来画线段,为了简化画线,将WM_MOUSEMOVE消息的处理放在后面综合实例中,此处只需要处理WM_LBUTTONDOWN消息。

需要有一个标志画线状态的BOOL变量m_bIsLine,还需要一个步数变量来标志鼠标左键按下时定义的是线段的起点还是终点,如果是起点,则将该点坐标值记录下来,供后续操作中画线段用;如果是终点,就将线段画出。

因此,还需要有两个变量来记录线段的起点X 和Y坐标值。

2DCAD程序画线段功能的具体步骤如下:
第1步:打开2DCAD工程,向视图类CMy2DCADView中添加记录线段起点坐标值的整型成员变量m_nStartX和m_nStartY,然后再添加一个记录画线段操作步数的整型成员变量m_nStep(第一步为起点,第二步为终点)。

第2步:在视图类的构造函数中为新增加的成员变量赋初始值。

第3步:在视图类的OnLButtonDown函数中添加具体的画线段代码。

为2DCAD程序添加画点和画线功能总结为以下4步:
第1步:分析交互过程。

第2步:添加成员变量,成员变量初始化。

第3步:使用ClassWizard向视图类中添加相对应的鼠标消息处理函数。

第4步:在函数中添加代码。

为应用程序添加新的功能时,首先要对功能以及交互过程进行分析,得出需要添加什么成员变量和成员函数(即消息处理函数),然后按照分析,添加源代码。

4.鼠标捕捉
有时候,用户需要将鼠标消息集中在一个窗口中,用户可以通过捕捉鼠标来完成上述的工作。

捕捉鼠标会强迫所有的鼠标消息被送到一个窗口中。

在窗口捕捉了鼠标后,尽管鼠标已经不在此窗口范围内,但该窗口仍可以继续获得鼠标的输入。

在许多文本编辑窗口中(包括VisualC++6.0的编辑窗口),当用户单击一个充满文本的窗口并拖动鼠标越出窗口边界时,这些窗口会滚动,以便能对多行文本进行选择。

这些窗口能够连续地接到鼠标的输入(在WM_MOUSEMOVE消息所对应的函数体中),这是因为鼠标已经被捕捉。

前面的画线段中,当用户在画线段时定义了起点后,应用程序就可以捕捉鼠标,等待用户定义线段终点后(或者按ESC键终止绘图),再释放对鼠标的捕捉。

捕捉和释放鼠标方法非常简单,只需调用CWnd的成员函数SetCapture就可以捕捉鼠标;调用全局函数ReleaseCapture可以释放对鼠标的捕捉:
■CWnd∷SetCaptuer()
CWnd的成员函数SetCapture用来捕捉鼠标。

当调用此函数以后,无论光标位于何处,所有的鼠标输入消息都被发送给应用程序的当前窗口对象。

当该窗口对象不再需要所有的鼠标输入时,应用程序应调用ReleaseCapture函数来释放捕捉,以便应用程序的其他窗口能够接收到鼠标输入消息。

当在不同应用程序之间的窗口进行切换时,鼠标的捕捉会被Windows操作系统自动释放。

■∷ReleaseCapture()函数
全局函数ReleaseCapture用于释放鼠标捕捉,函数没有参数。

为2DCAD应用程序添加鼠标捕捉和释放功能,代码放在视图类的OnLButtonDown函数中。

二、改变光标
许多Windwos应用程序中,当鼠标光标通过某个特定控件或者执行某个功能时,为了立即给用户提供反馈信息,常常需要改变光标的类型。

要在应用程序中改变光标的形状,首先要有相应的光标资源。

1.光标资源
光标资源有两种,一种是系统预先定义好的光标,比如常见的箭头光标、十字光标、沙漏光标等等;另外一种是由用户自己定义的光标资源。

①预定义光标
Windows系统中,已经预定义了相当一部分常用的光标资源,这些预定义的系统光标如下所示:
使用系统预定义光标时,可以使用CWinApp::LoadStandardCursor成员函数来加载。

②自定义光标
用户可以在应用程序中自己定义新的光标资源,2DCAD程序添加一个新的IDC_POINTER光标资源,然后在RescourceView窗口中的IDC_POINTER项上单击鼠标右键,从弹出的菜单中选择Properties…项,弹出光标属性对话框,将资源语言类型选择为中文。

每个光标都有一个Hot spot(热点)。

由于光标实际上是一幅小的位图,热点表明了光
标指针的实际位置。

在编辑窗口顶部,有Hot spot:1,1式样的文本,同时在文本右边有一个Set Hotspot (设置热点)按钮,前面的文本表明当前光标的热点在光标位图的(1,1)坐标处,这正好是箭头的顶端。

如果想重新定义热点位置,首先单击设置热点按钮,然后在光标位图的适当位置单击鼠标左键,热点被定义到鼠标左键单击的位置了。

使用CWinApp::LoadCursor成员函数来加装一个用户自定义光标资源。

2.显示光标
改变光标的形状,最简单的方法是响应WM_SETCURSOR消息。

当光标在未被捕捉时,从一个窗口移到另一个窗口,Windows向窗口发送WM_SETCURSOR消息。

MFC应用程序可以在该消息的处理函数OnSetCursor中加装并显示新的光标。

当已经准备好光标资源以后,在一个应用程序的视图窗口中改变光标的形状可以归纳为以下三步:
第1步:处理WM_SETCURSOR消息
打开2DCAD工程,使用ClassWizard工具为视图类CMy2DCADView添加WM_SETCURSOR消息的处理函数OnSetCursor。

第2步:加载光标资源
在OnSetCursor函数中,可以使用CWinApp::LoadCursor函数加载一个自定义光标,或者使用CWinApp::LoadStandardCursor函数来加载一个系统预定义光标。

这两个函数的参数是光标资源的ID号或者名称,而返回值是一个指向光标的句柄HCURSOR。

HCURSOR LoadCursor( LPCTSTR lpszResourceName ) const;
HCURSOR LoadCursor( UINT nIDResource ) const;
HCURSOR LoadStandardCursor( LPCTSTR lpszCursorName ) const;
第3步:设置光标
使用全局函数SetCursor可以为窗口设置一个新的光标,该函数的原型如下:
HCURSOR SetCursor(
HCURSOR hCursor// handle to cursor
);
参数是将要设置的新光标,返回值是以前的旧光标(如果没有,就返回NULL)。

参数和返回值都是一个指向光标的句柄HCURSOR。

在OnSetCursor函数中加入代码后,编译并运行2DCAD程序,在画点时,视图窗口中光标变为一个带点的箭头形状,即自定义的IDC_POINTER光标;而在画线时,光标变为IDC_CROSS十字光标;其他情况下,还是Windows系统的默认光标。

三、键盘消息
键盘作为输入设备,是Windows应用程序的另外一个重要的输入手段。

当用户按下或释放一个键时,键盘驱动程序KEYBOARD.DRV中的键盘中断处理程序对所击键进行编码并调用Windows的用户模块USER32.EXE中的有关程序来生成键盘消息,最终发送该消息到应用程序的消息队列中等待处理。

键盘上每一个有意义的键都对应着一个唯一的标识值,称为扫描码。

当用户按下或释放一个键时,都会产生扫描码。

键的扫描码可以用来作为键的标识,但它是依赖于具体设备的。

因此,在应用程序中,使用的往往是与具体设备无关的虚拟码。

虚拟码是由Windows系统定义的与设备无关的键标识值。

设备驱动程序截取键的扫描码,然后把它翻译为对应的虚拟码。

这样,由于键盘输入,就产生了一条消息,它含有扫描码、虚拟码以及其他与击键有关的信息。

1.输入焦点
键盘必须由所有在Windows下运行的应用程序所共享。

由于某些应用程序有好几个窗口,而键盘必须由同一应用程序的这些窗口共享。

当在键盘上按下一个键时,只有一个窗口过程能接收到该键盘消息。

接收到这个键盘消息的窗口称为有“输入焦点”的窗口,具有输入焦点的窗口可能是活动窗口,也可能是活动窗口的子窗口。

当某一个窗口为活动窗口时,Windows会加亮显示其标题条。

如果活动窗口为一图标,则此时没有一个窗口拥有输入焦点,但Windows仍将发送键盘消息给图标,但此时的消息格式有所不同。

窗口函数通过捕获WM_SETFOCUS和WM_KILLFOCUS消息以确定当前窗口是否具有输入焦点。

WM_SETFOCUS表明窗口正在接收输入焦点,而WM_KILLFOCUS表示窗口正失去输入焦点。

2.常用的键盘消息
键盘上的键分为两种:系统键和非系统键。

系统键指Alt键和F10键,而其他键则是非系统键,两种键所发送的消息是不同的,平时使用较多的是非系统键。

常用的键盘消息有以下三个:
■WM_KEYDOWN:非系统键被按下时发送。

■WM_KEYUP:非系统键被释放时发送。

■WM_CHAR:字符消息,由上两种消息转化而来。

注意:WM_KEYDOWN和WM_KEYUP是由键盘动作直接产生的消息,所有的非系统键都可以产生这两种消息。

而WM_CHAR则是由前两种键盘消息转化而来,并非所有的键按下都可以产生字符消息,比如按下End键时就没有WM_CHAR消息产生。

一般来说,当用户按下并释放一个键盘键时,往往会产生好几个消息,如下列出了不同击键方法产生的消息:
当按下系统键的同时再按下其他键(比如Alt+F),则有相应的系统键消息产生。

分别是:WM_SYSKEYDOWN、WM_SYSKEYUP和WM_SYSCHAR。

一般情况下,多数应用程序只使用到很少的键盘消息,WM_SYSKEYDOWN和WM_SYSKEYUP消息一般由Windows系统内部处理,应用程序可不必处理。

3.处理键盘消息
可以使用ClassWizard为键盘消息添加处理函数,对应于常用的3个非系统键消息,其消息处理函数的原型如下:
WM_KEYDOWN:afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
WM_KEYUP:afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
WM_CHAR:afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
可见这3个函数有相同的3个参数,3个参数的意义如下:
①UINT nChar:虚拟键代码
此参数可以通知应用程序哪个键按下或释放了。

虚拟键代码(virtual key code)是一种与设备无关的键盘编码,它的值存放在键盘消息的wParam参数中。

最常用的虚拟键代码已被定义在系统的头文件中,例如:VK_LEFT表示“←”键,VK_F1表示功能键F1,VK_SHIFT表示Shift键等等。

注意:在Visual C++中,数字和字母键的虚拟键值VK_0~VK_9、VK_A~VK_Z没有定义,应该用字符(如“A”“Z”等)直接代替。

②UINT nRepCnt:重复计数
实际上,在Windows操作系统中,按下一个键不放不会连续不断的产生单独的键盘按下消息WM_KEYDOWN,Windows会自动解释消息,并把重复次数存放在nRepCnt参数中传递给应用程序。

对于一般的按键,nRepCnt参数始终为1。

③UINT nFlags:其他信息
在nFlags参数中包含了许多信息,比如键的扫描码、转换状态、前一键的状态等等。

这些信息都按位存放在nFlags中。

如下列出了nFlags参数各个位代表的意义。

在这三个参数中,一般用户使用第一个参数虚拟键代码nChar的情况较多,而后面两个参数使用较少。

如果要使用第三个参数nFlags,就必须使用位操作符将各个位分离出来。

4.为2DCAD添加取消绘图功能
2DCAD应用程序已经可以自由的画点和画线了,但一旦程序处于绘图状态时,目前没有办法取消绘图,回到初始状态。

由于一般的应用程序都使用Esc键来退出某个状态或者操作,下面就为程序添加上Esc键的处理,并添加相应的代码来取消绘图功能。

具体操作步骤如下:
第1步:打开2DCAD程序,使用ClassWizard为视图类CMy2DCADView添加WM_KEYDOWN消息的处理函数OnKeyDown。

第2步:在编辑窗口中修改OnKeyDown函数如下:
先判断nChar参数是否等于Esc键的虚拟键代码VK_ESCAPE,如果相等,则表明Esc 键被按下,接下来就进行相应的清除绘图状态的工作;如果不相等,表明按下的不是Esc 键,2DCAD程序不作任何处理,最后,将消息交给基类的成员函数CView::OnKeyDown进行其他默认处理。

四、综合实例:橡皮线的实现
许多画图程序中(比如AutoCAD或者是Windws系统自带的画图工具),当画一条线段的时候,会有一条线跟随鼠标移动,以便指示线段将画在什么地方。

这一条动态移动的线就是橡皮线(Rubber Line)。

使用橡皮线可以使应用程序的交互性更加友好。

1.橡皮线工作原理
在画线段时,当在某个位置按下鼠标键后,拖动鼠标,鼠标指针后面就跟随着一条橡皮线。

因此可知,橡皮线功能必然在鼠标移动消息WM_MOUSEMOVE的处理函数中实现的。

画橡皮线本身还是使用画线函数MoveTo、LineTo来实现的,为了画出橡皮线,就必须不停地在新位置画出线段,并把以前画出的线段擦掉。

擦掉线段的方法很多,其中一种就是使用与背景相同的颜色重新画一下将要被擦除的线段。

比如,在白色的背景上有一条黑色的,如果要擦除这些线,在原来的黑线上画一条相同的白线就可以了。

因此,如果背景是单一的颜色,只需要构造一支背景颜色的画笔和一支前景颜色的画笔,然后首先用背景色画笔擦掉上次的线段,再用前景色画笔画出新的线段,就可以实现橡皮线了。

由于橡皮线的起点是不变的,而终点在不停的改变,因此,还需要记录下前一条线段的终点位置。

但是,假如背影色不是单一的,而又无法构造一个可变颜色的画笔。

另一个问题是就算是单一背景色,移动的橡皮线仍然会把已经存在的图形破坏掉。

如果在画橡皮线时直接将线条混合模式设置为R2_NOT(反色)模式,则负负得正,画两次正好回到屏幕上原来的图形,这样就可以不破坏已经存在的图形。

2.添加代玛
添加橡皮线分以下三步:
第1步:添加鼠标移动消息处理
打开2DCAD程度,使用ClassWizard为视图类CMy2DCADView添加鼠标移动消息WN_MOUSEMOVE处理函数OnMouseMove。

第2步:添加记录终点坐标的成员变量
第3步:修改消息处理函数代码
同时,还需要在每次画完一条线段后将终点坐标也归零。

同时由于设置了鼠标捕捉,当鼠标移动到窗口的非视图区时,仍然有橡皮线跟着移动,并且光标仍然是十字形光标。

四、练习
1.理解并实现橡皮线的绘制。

2.为动态画线程序添加功能:①用键盘的“Esc”键停止画线;②当鼠标在屏幕曲线绘制坐标区内双击时,如果画线处于停止状态,则重新开始画线,否则不作任何处
理;③实现可用鼠标拖动坐标系的功能,当鼠标拖动坐标系时不处理键盘取消处理,并且画线停止,但是原来坐标系和曲线存在;④但拖动过程中,光标形状改变。

相关文档
最新文档