响应键盘事件

------------------------

在开始学习有关键盘事件的知识前首先来看一段代码(回调函数这部分的代码):

1 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )2 {3 HDC hdc ;4 PAINTSTRUCT ps ;5

6 switch( message )7 {8 caseWM_PAINT:9 hdc = BeginPaint( hwnd, &ps ) ;10 EndPaint( hwnd, &ps ) ;11 return 0;12

13 case WM_KEYDOWN: //键盘按键被按下

14 switch(wParam)15 {16 case VK_UP: //方向键上

17 MessageBox( hwnd, TEXT("你按下了方向键 上"), TEXT("键盘消息"), MB_OK ) ;18 break;19 case VK_DOWN: //方向键下

20 MessageBox( hwnd, TEXT("你按下了方向键 下"), TEXT("键盘消息"), MB_OK ) ;21 break;22 case VK_LEFT: //方向键左

23 MessageBox( hwnd, TEXT("你按下了方向键 左"), TEXT("键盘消息"), MB_OK ) ;24 break;25 case VK_RIGHT: //方向键右

26 MessageBox( hwnd, TEXT("你按下了方向键 右"), TEXT("键盘消息"), MB_OK ) ;27 break;28 }29 return 0;30

31 caseWM_DESTROY:32 PostQuitMessage( 0) ;33 return 0;34 }35

36 returnDefWindowProc( hwnd, message, wParam, lParam ) ;37 }

完整的示例代码:

View Code

1 #include

2

3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;4

5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, intiCmdShow )6 {7 static TCHAR szAppName[] = TEXT( "UseKeyboard") ;8 HWND hwnd ;9 MSG msg ;10 WNDCLASS wndclass ;11

12 wndclass.style = CS_HREDRAW |CS_VREDRAW ;13 wndclass.lpfnWndProc =WndProc ;14 wndclass.cbClsExtra = 0;15 wndclass.cbWndExtra = 0;16 wndclass.hbrBackground =(HBRUSH) GetStockObject( WHITE_BRUSH ) ;17 wndclass.hCursor =LoadCursor( NULL, IDC_ARROW ) ;18 wndclass.hIcon =LoadIcon( NULL, IDI_APPLICATION ) ;19 wndclass.lpszMenuName =NULL ;20 wndclass.hInstance =hInstance ;21 wndclass.lpszClassName =szAppName ;22

23 if( !RegisterClass( &wndclass ) )24 {25 MessageBox( NULL, TEXT("窗口类注册失败!"), TEXT("错误"), MB_OK ) ;26 return 0;27 }28

29 hwnd = CreateWindow( szAppName, TEXT("响应键盘事件"),30 WS_OVERLAPPEDWINDOW,31 CW_USEDEFAULT, CW_USEDEFAULT,32 CW_USEDEFAULT, CW_USEDEFAULT,33 NULL, NULL, hInstance, NULL34 );35

36 ShowWindow( hwnd, iCmdShow ) ;37 UpdateWindow( hwnd ) ;38

39 while( GetMessage( &msg, NULL, 0, 0) )40 {41 TranslateMessage( &msg ) ;42 DispatchMessage( &msg ) ;43 }44

45 returnmsg.wParam ;46 }47

48 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )49 {50 HDC hdc ;51 PAINTSTRUCT ps ;52

53 switch( message )54 {55 caseWM_PAINT:56 hdc = BeginPaint( hwnd, &ps ) ;57 EndPaint( hwnd, &ps ) ;58 return 0;59

60 caseWM_KEYDOWN:61 switch(wParam)62 {63 caseVK_UP:64 MessageBox( hwnd, TEXT("你按下了方向键 上"), TEXT("键盘消息"), MB_OK ) ;65 break;66 caseVK_DOWN:67 MessageBox( hwnd, TEXT("你按下了方向键 下"), TEXT("键盘消息"), MB_OK ) ;68 break;69 caseVK_LEFT:70 MessageBox( hwnd, TEXT("你按下了方向键 左"), TEXT("键盘消息"), MB_OK ) ;71 break;72 caseVK_RIGHT:73 MessageBox( hwnd, TEXT("你按下了方向键 右"), TEXT("键盘消息"), MB_OK ) ;74 break;75 }76 return 0;77

78 caseWM_DESTROY:79 PostQuitMessage( 0) ;80 return 0;81 }82

83 returnDefWindowProc( hwnd, message, wParam, lParam ) ;84 }

在这段代码中, 编译运行如果没有错误的话当你在按下方向键上下左右时应该都会弹出一个对话框, 重点看一下case WM_KEYDOWN:部分的代码:

caseWM_KEYDOWN:switch(wParam)

{caseVK_UP:

MessageBox( hwnd, TEXT("你按下了方向键 上"), TEXT("键盘消息"), MB_OK ) ;break;caseVK_DOWN:

MessageBox( hwnd, TEXT("你按下了方向键 下"), TEXT("键盘消息"), MB_OK ) ;break;caseVK_LEFT:

MessageBox( hwnd, TEXT("你按下了方向键 左"), TEXT("键盘消息"), MB_OK ) ;break;caseVK_RIGHT:

MessageBox( hwnd, TEXT("你按下了方向键 右"), TEXT("键盘消息"), MB_OK ) ;break;

}return 0 ;

如果对前一部分中提到的"Windows向应用程序发送了一条消息"有较为深刻的理解的话, 那么此时你应该能够明白了, 所谓的响应键盘的按键事件不过也是处理系统发来的按键消息罢了。

当然, 的确是这样, 当有按键被按下时系统就会向应用程序发送一个按键被按下的消息, 在发来的消息中的wParam字段中包涵有有关按键被按下的必要信息, 正如上面代码中写到的那样, 首先程序先case到一个按键被按下的消息WM_KEYDOWN, 然后再进一步处理到底是哪个按键被按下了, 于是就嵌套一个switch语句, 通过wParam中的信息判断到底是哪个按键被按下了, 当信息为VK_UP时就说明是方向键上被按下了, 这样我们就可以根据这样消息作出相应的动作, 在这个示例中, 程序是通过显示一个对话框"你按下了方向键 上"来响应方向键上的, 同样, 你还可以同理推导出其他按键的响应方式, 这只是我们所看到的表面现象, 既然学习还是要更进一步了解下响应按键的具体情况。

一、这条消息属于谁?

通常来说, 一台电脑上一般只配有一个键盘, 而运行在Windows系统上的应用程序却有很多, 也就是说, 除了你的应用程序外还有其他一些应用程序在和你共用这个键盘, 那么系统是如何知道当前用户的按下的按键是针对你的应用程序的呢?

Windows通过一个被称为"输入焦点"的名词来确定该消息属于哪个窗口, 接收到这个键盘事件的窗口称为有输入焦点的窗口。一个具有输入焦点的窗口必须是活动窗口或者活动窗口的子孙窗口。

何为活动窗口? 活动窗口很容易鉴别, 系统会对这个窗口的标题框或者窗口边框进行加亮提示, 同样会在任务栏突出显示, 图示如下:

当活动窗口有子窗口时, 具有输入焦点的窗口可以是活动窗口, 也可能是他的子孙窗口中的一个, 例如当一个窗口中有许多控件(按钮、输入框、文本框、编辑框、滚动条等), 这些控件通常通过显示一个虚线框或插入符号以及闪烁的光标等表示自己获得了输入焦点。

在程序中, 可以通过捕获WM_SETFOCUS和WM_KILLFOCUS来确定自己的窗口是否获得了焦点。

当程序最小化到系统托盘时, 系统同样能够将消息发送给程序, 只不过此时消息的形式与发给活动窗口时不同。

二、队列和同步

当一个按键被按下时到你的应用程序得到这个消息期间需要经过一个"中转站", 这个中转站就是系统的消息队列, 也就是说, 当用户按下按键后, Windwows和键盘设备的驱动程序将硬件的扫描码转化成格式化后的消息, 但这个消息并不会直接发送给应用程序的消息队列, 而是存储在系统的一个独立的消息队列当中, 这个系统的消息队列注要用来存储用户从键盘和鼠标输入的消息。

为什么要这样做? 可以想象一下, 当系统这会正在运行许多的应用程序导致系统超负荷运作时, 恰好你的程序也在被运行并且获取了输入焦点, 用户正好要使用你的程序完成一些操作, 但是用户的输入速度快于此时系统处理的速度, 也就是说如果不先存放在系统的消息队列中那么此时用户的输入已经大于你的程序的处理速度了, 当键盘上某一个特殊的按键被按下时系统可能会将输入焦点转换到另一个窗口上, 后面的操作系统会认为用户的击键操作已经属于另一个窗口。当先存放在系统的队列, 再把这些消息发给这些消息发给当前活动的窗口就可以避免消息接收混乱的情况。

三、击键消息

当一个按键被按下时, Windows将WM_KEYDOWN或WM_SYSKEYDOWN消息放进具有输入焦点的窗口的消息队列中, 当按键被释放时, Windows又会把WM_KWYUP或WM_SYSKEYUP放进当前具有输入焦点的消息队列中。

具体是WM_KEYDOWN还是WM_SYSKEYDOWN要看击键的类型, 击键分为"系统击键"和"非系统击键", 带上"SYS"标识符的表示为系统击键, 与非系统击键的区别是系统击键表示该击键对于Windows系统更重要, 例如, 当使用Alt键与其他输入按键进行组合时产生的消息即为系统击键消息, 这些消息一般具有特殊的用途, 例如转换活动窗口: Alt + Tab或Alt + Esc, 关闭当前活动的应用程序, Alt + F4等。

对于这些系统击键消息一般是交给DefWindowProc即默认的消息处理函数处理, 当然, 我们也可以自己处理这些系统击键消息, 处理的方法同普通消息相同, 例如阻止所有系统击键操作:

caseWM_SYSKEYDOWN:caseWM_SYSKEYUP:caseWM_SYSCHAR:return 0 ;

四、虚拟键代码

虚拟键代码存储在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP消息的wParam参数中, 常用的虚拟键代码如下:

十进制

十六进制

WINUSER.H中的标识符

是否必须

IBM兼容键盘

1

1

VK_LBUTTON

鼠标左键

2

2

VK_RBUTTON

鼠标右键

3

3

VK_CANCEL

Ctrl + Break

4

4

VK_MBUTTON

鼠标中键

8

8

VK_BACK

退格键

9

9

VK_TAB

Tab键

12

C

VK_CLEAR

Clear键

13

D

VK_RETURN

回车键(任意一个)

16

10

VK_SHIFT

Shift键(任意一个)

17

11

VK_CONTROL

Ctr键(任意一个)

18

12

VK_MENU

Alt键(任意一个)

19

13

VK_PAUSE

Pause键

20

14

VK_CAPITAL

大写锁定键

27

1B

VK_ESCAPE

Esc键

32

20

VK_SPACE

空格键

33

21

VK_PRIOR

PageUp键

34

22

VK_NEXT

PageDown键

35

23

VK_END

End键

36

24

VK_HOME

HOME键

37

25

VK_LEFT

左箭头

38

26

VK_UP

上箭头

39

27

VK_RIGHT

右键头

40

28

VK_DOWN

下箭头

41

29

VK_SELECT

42

2A

VK_PAINT

43

2B

VK_EXECUTE

44

2C

VK_SNAPSHOT

Paint Screen键

45

2D

VK_INSERT

Insert键

46

2E

VK_DELETE

Del键

47

2F

VK_HELP

48 - 57

30 - 39

主键盘上的0到9

65 - 90

41 - 5A

A - Z

91

5B

VK_LWIN

左Windows键

92

5C

VK_RWIN

右Windows键

93

5D

VK_APPS

Applicatin键

96 - 105

60 - 69

VK_NUMPAD0 - VK_NUMPAD9

小键盘区的0 - 9

106

6A

VK_MULTIPLY

小键盘区的*

107

6B

VK_ADD

小键盘区的+

108

6C

VK_SEPARATOR

109

6D

VK_SUBTRACT

小键盘区的-

110

6E

VK_DECIMAL

小键盘区的.

111

6F

VK_DIVIDE

小键盘区的/

112 - 121

70 - 79

VK_F1 - VK_F10

功能键F1 - F10

122 - 135

7A- 79

VK_F11 - VK_F24

功能键F11 - F24

144

90

VK_NUMLOCK

数字锁定键

145

91

VK_SCROLL

Scroll Lock键

五、转义状态

当需要知道用户是否按下了转义键时(Shift键、Ctrl键和Alt键)或者切换键(Caps Lock键和Scroll Lock键), 可以通过调用GetKeyState函数来获得该信息:

iState = GetKeyState(VK_SHIFT) ;

如果Shift键被按下, 函数的返回值为负, 即最高位为1

如果需要判断Caps Lock键是否打开, 则

iState = GetKeyState(VK_CAPITAL) ;

当返回值得最低位为1时说明Caps Lock键被打开了。

需要注意的是, GetKeyState函数获取的按键状态并不是实时状态下的, 也就是说当你函数获取时是这种状态, 而你刚刚获取后用户就可能又已经释放了这个按键, 不过在大多数情况下通过GetKeyState获得的消息还是符合我们的目的的。

一个响应键盘的实例:

实例的内容为在窗口的客户区画一个直径为100的圆, 然后通过上下左右方向键操作这个圆进行相应的移动, 回调函数相应的代码如下:

1 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )2 {3 HDC hdc ;4 PAINTSTRUCT ps ;5 static POINT pt = { 500, 300 }; //初始显示坐标为(500, 300)

6

7 switch( message )8 {9 caseWM_PAINT:10 hdc = BeginPaint( hwnd, &ps ) ;11 Ellipse( hdc, pt.x, pt.y, pt.x + 100, pt.y + 100 ) ; //画一个直径为100的圆

12 EndPaint( hwnd, &ps ) ;13 return 0;14

15 case WM_KEYDOWN: //处理按键按下消息

16 switch(wParam)17 {18 case VK_UP: //上

19 pt.y -= 5;20 break;21 case VK_DOWN: //下

22 pt.y += 5;23 break;24 case VK_LEFT: //左

25 pt.x -= 5;26 break;27 case VK_RIGHT: //右

28 pt.x += 5;29 break;30 }31 InvalidateRect( hwnd, NULL, TRUE ) ; //使窗口无效化

32 return 0;33

34 caseWM_DESTROY:35 PostQuitMessage( 0) ;36 return 0;37 }38

39 returnDefWindowProc( hwnd, message, wParam, lParam ) ;40 }

完整的代码:

View Code

1 #include

2

3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;4

5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, intiCmdShow )6 {7 static TCHAR szAppName[] = TEXT( "UseKeyboard") ;8 HWND hwnd ;9 MSG msg ;10 WNDCLASS wndclass ;11

12 wndclass.style = CS_HREDRAW |CS_VREDRAW ;13 wndclass.lpfnWndProc =WndProc ;14 wndclass.cbClsExtra = 0;15 wndclass.cbWndExtra = 0;16 wndclass.hbrBackground =(HBRUSH) GetStockObject( WHITE_BRUSH ) ;17 wndclass.hCursor =LoadCursor( NULL, IDC_ARROW ) ;18 wndclass.hIcon =LoadIcon( NULL, IDI_APPLICATION ) ;19 wndclass.lpszMenuName =NULL ;20 wndclass.hInstance =hInstance ;21 wndclass.lpszClassName =szAppName ;22

23 if( !RegisterClass( &wndclass ) )24 {25 MessageBox( NULL, TEXT("窗口类注册失败!"), TEXT("错误"), MB_OK ) ;26 return 0;27 }28

29 hwnd = CreateWindow( szAppName, TEXT("响应键盘事件"),30 WS_OVERLAPPEDWINDOW,31 CW_USEDEFAULT, CW_USEDEFAULT,32 CW_USEDEFAULT, CW_USEDEFAULT,33 NULL, NULL, hInstance, NULL34 );35

36 ShowWindow( hwnd, iCmdShow ) ;37 UpdateWindow( hwnd ) ;38

39 while( GetMessage( &msg, NULL, 0, 0) )40 {41 TranslateMessage( &msg ) ;42 DispatchMessage( &msg ) ;43 }44

45 returnmsg.wParam ;46 }47

48 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )49 {50 HDC hdc ;51 PAINTSTRUCT ps ;52 static POINT pt = { 500, 300 }; //初始显示坐标为(500, 300)

53

54 switch( message )55 {56 caseWM_PAINT:57 hdc = BeginPaint( hwnd, &ps ) ;58 Ellipse( hdc, pt.x, pt.y, pt.x + 100, pt.y + 100 ) ; //画一个直径为100的圆

59 EndPaint( hwnd, &ps ) ;60 return 0;61

62 case WM_KEYDOWN: //处理按键按下消息

63 switch(wParam)64 {65 case VK_UP: //上

66 pt.y -= 5;67 break;68 case VK_DOWN: //下

69 pt.y += 5;70 break;71 case VK_LEFT: //左

72 pt.x -= 5;73 break;74 case VK_RIGHT: //右

75 pt.x += 5;76 break;77 }78 InvalidateRect( hwnd, NULL, TRUE ) ; //使窗口无效化

79 return 0;80

81 caseWM_DESTROY:82 PostQuitMessage( 0) ;83 return 0;84 }85

86 returnDefWindowProc( hwnd, message, wParam, lParam ) ;87 }

在这段代码中首先定义了一个static类型的POINT结构记录圆的坐标, 然后通过方向键去操作它, 使POINT的对象pt中的x或y的值改变, 再通过InvalidateRect函数使窗口的客户区无效化触发WM_PAINT消息使其对窗口进行重绘, 这样就达到了看起来移动圆的目的。

--------------------

wid, 2012.11.20

c语言windows程序设计 - 第十天,C语言Windows程序设计 - 第十天 - 响应键盘事件...相关推荐

  1. C语言程序设计笔记(浙大翁恺版) 第十周:字符串

    按照中国大学MOOC上浙江大学翁恺老师主讲的版本所作,B站上也有资源.原课程链接如下: https://www.icourse163.org/course/ZJU-9001 由于是大三抽空回头整理的, ...

  2. java语言程序设计基础篇课后答案_《Java语言程序设计:基础篇》课后复习题答案-第十五章.pdf...

    <Java语言程序设计:基础篇>课后复习题答案-第十五章 Chapter15Graphics 1. Theycoordinateshouldincreaseandthexcoordinat ...

  3. 各取所需:Microsoft Windows XP with sp3 十五种语言的MSDN原版光盘镜像

    各取所需:Microsoft Windows XP with sp3 十五种语言的MSDN原版光盘镜像 2008年09月29日 星期一 10:30 这篇文章,是有关Windows XP下载的" ...

  4. c语言程序设计华北电力大学,华北电力大学 高级语言程序设计C

    <高级语言程序设计(C)>是为全校理工科非计算机专业开设的必修课,是一门培养和提高学生信息素养.逻辑思维方法.科学研究和创新能力的重要基础课.<高级语言程序设计(C)>的课程建 ...

  5. 计划完成提醒系统C语言,通信录管理计划系统C语言知识程序设计.doc

    #+ C语言程序设计 --通讯录管理系统 实 习 报 告 组长: 姚士平 组员: 胡恒山 芮青荣 南京邮电大学电气信息工程B100509班 2011 ※实习前论: 实习的目的.要求与内容 一.实习的目 ...

  6. 高级语言程序设计用c语言描述答案,清华大学出版社-图书详情-《高级语言程序设计(C语言描述)第2版》...

    近年来,随着各类程序(软件)设计大赛的广泛开展,C语言程序设计教学中"重语言轻设计"的教学状况得到了有效改善.本教材一直致力于改善这种教学缺陷,始终强调设计的重要性,因而受到广大相 ...

  7. python语言程序设计实践教程答案实验六_Python程序设计实践教程

    书名:Python程序设计实践教程 定价:29.8 ISBN:9787115532602 作者:储岳中 薛希玲 版次:*1版 出版时间:2020-04 内容提要: 本书是Python语言程序设计的配套 ...

  8. java程序设计_十分钟说课—Java程序设计(耿祥义)

    "十分钟说课"-Java程序设计 耿祥义视频说课: 课程概述 1. Java入门 1) 重点内容,三个基本步骤: 编写源文件,编译,运行. 2)JDK,使用JDK11以上版本,注意 ...

  9. 2c语言程序设计_大学生学C语言的理由是什么

    不止一个学生问到我:"老师,为什么我们的应用程序设计要学C语言而不是别的?C语言不是已经过时了吗?如果现在要写一个Windows程序,用VB或Dephi开发多快呀,用C行吗? 这个问题三言两 ...

最新文章

  1. SpringCloud配置文件 application.yml和 bootstrap.yml区别,执行顺序
  2. MySQL内存预估_mysql时该如何估算内存的消耗,公式如何计算?
  3. 用python实现矩阵乘法
  4. 算法入门开灯问题,新做法
  5. 更改apk安装包对android系统等级要求
  6. 错误:readline/readline.h:没有那个文件或目录解决方法
  7. md5不是对称密码算法_密码学中的消息摘要算法5(MD5)
  8. Deep Residual Network
  9. 导入android工程@Override报错
  10. UWP 自然灾害App在刷新数据后卡死的解决方案
  11. 针对大的sql文件删除行操作
  12. 有了BBdoc文档搜索,就不要使用DocFetcher 全文搜索了
  13. python cnn 股市_荐python3绘制股票K线图的那些坑【一】mpl_finance迁向mplfinance,以及plot独立窗口视图问题!...
  14. 深入理解设计模式之模板模式
  15. c语言笔段式,走势划分笔段中枢的区别及缠论操作核心技术
  16. 怎样把excel中的图表插入PPT并且可以在PPT中编辑图表
  17. 动态规划—钢条切割问题与01背包问题
  18. 19 Three.js实现雾化效果
  19. 单反相机风景摄影易记易学口诀
  20. 最常用的65条正则表达式

热门文章

  1. JavaScript Array reverse 方法:颠倒数组中元素的顺序
  2. ServiceStack 项目实例 001 建立项目结构
  3. 简单实现ReplaceAll(转)
  4. 转:AIX rcp跨主机远程拷贝数据
  5. xenserver 管理口显示重复IP的处理方法
  6. 2011年使用天正建筑8.0注册版(附注册机)
  7. java 正则_认识正则表达式(Java语言基础)
  8. 计算机网络原理 大型实验,计算机网络原理实验 实验一
  9. mysql for 语句执行顺序_MySQL查询语句的执行流程
  10. kicad绿油开窗_GitHub - OS-Q/S05: KiCad EDA