原文链接: http://blog.sina.com.cn/s/blog_5f8817250100taab.html

本文大部分来自MSDN和网友的博客,我在实践的基础上再作了一些总结。

1,虚拟键(VK_*)

键盘上每一个键对应一个扫描码,扫描码是OEM厂商制定的,不同的厂商生产的键盘同样一个按键的扫描码都有可能出现不一致的情况,为了摆脱由于系统设备不一致的情况,通过键盘驱动程序将扫描码映射为统一的虚拟键码表示,从而达到所有的设备都有一个统一的虚拟键,比如回车键的虚拟键是VK_RETURN。

Windows定义的虚拟键都定义在WinUser.h这个头文件里面,都是以VK_作为前缀。

2,激活/关闭窗口对键盘的消息

激活/关闭消息:WM_SETFOCUS/WM_KILLFOCUS

创建光标:CreateCaret(...)

设置光标位置:SetCaretPos(…)

在窗口中显示光标:ShowCaret(…)

销毁光标:DestroyCaret()

3,键盘消息

1)字符消息

系统字符消息

WM_SYSCHAR:系统字符

WM_SYSDEADCHAR:系统死字符

非系统按键消息

WM_CHAR:非系统字符

WM_DEADCHAR:非系统死字符

2)按键消息

系统按键消息:与ALT键相组合的组合键(无论用户处理否,都需要最后调用DefWindowProc(hWnd,iMessage,wParam,lParam))

WM_SYSKEYDOWN

WM_SYSKEYUP

非系统按键消息:

WM_KEYDOWN

WM_KEYUP

注意:

a)  除Print键之外都有“按下”消息。

b) 所有键都存在“弹起”消息。

c)  根据MSDN说明,只有下面这些键才会产生字符消息:

  • 任何字符键
  • 回退键(BACKSPACE)
  • 回车键(carriage return)
  • ESC
  • SHIFT + ENTER (linefeed 换行)
  • TAB

我们是怎么收到WM_CHAR的呢?就是因为我们在消息循环时调用了TranslateMessage对键盘消息进行翻译,

如果消息为WM_KEYDOWN或者WM_SYSKEYDOWN,并且按键与位移状态相组合产生一个字符,则TranslateMessage把字符消息放入消息队列中。此字符消息将是GetMessage从消息队列中得到的按键消息之后的下一个消息。

在我们处理这个消息时,对应的wParam不是虚拟键,而是ANSI或Unicode字符代码,一般情况下我们可以这样用:  (TCHAR)wParam;

4,消息顺序

因为TranslateMessage函数从WM_KEYDOWN和WM_SYSKEYDOWN消息产生了字符消息,所以字符消息是夹在按键消息之间传递给窗口消息处理程序的。例如,如果Caps Lock未打开,而使用者按下再释放A键,则窗口消息处理程序将接收到如表6-10所示的三个消息:

表6-10

消息

按键或者代码

WM_KEYDOWN

「A」的虚拟键码(0x41)

WM_CHAR

「a」的字符代码(0x61)

WM_KEYUP

「A」的虚拟键码(0x41)

如果您按下Shift键,再按下A键,然后释放A键,再释放Shift键,就会输入大写的A,而窗口消息处理程序会接收到五个消息,如表6-11所示:

表6-11

消息

按键或者代码

WM_KEYDOWN

虚拟键码VK_SHIFT (0x10)

WM_KEYDOWN

「A」的虚拟键码(0x41)

WM_CHAR

「A」的字符代码(0x41)

WM_KEYUP

「A」的虚拟键码(0x41)

WM_KEYUP

虚拟键码VK_SHIFT(0x10)

Shift键本身不产生字符消息。

如果使用者按住A键,以使自动重复产生一系列的按键,那么对每条WM_KEYDOWN消息,都会得到一条字符消息,如表6-12所示:

表6-12

消息

按键或者代码

WM_KEYDOWN

「A」的虚拟键码(0x41)

WM_CHAR

「a」的字符代码(0x61)

WM_KEYDOWN

「A」的虚拟键码(0x41)

WM_CHAR

「a」的字符代码(0x61)

WM_KEYDOWN

「A」的虚拟键码(0x41)

WM_CHAR

「a」的字符代码(0x61)

WM_KEYDOWN

「A」的虚拟键码(0x41)

WM_CHAR

「a」的字符代码(0x61)

WM_KEYUP

「A」的虚拟键码(0x41)

如果某些WM_KEYDOWN消息的重复计数大于1,那么相应的WM_CHAR消息将具有同样的重复计数。

组合使用Ctrl键与字母键会产生从0x01(Ctrl-A)到0x1A(Ctrl-Z)的ASCII控制代码,其中的某些控制代码也可以由表6-13列出的键产生:

表6-13

按键

字符代码

产生方法

ANSI C控制字符

Backspace

0x08

Ctrl-H

\b

Tab

0x09

Ctrl-I

\t

Ctrl-Enter

0x0A

Ctrl-J

\n

Enter

0x0D

Ctrl-M

\r

Esc

0x1B

Ctrl-[

最右列给出了在ANSI C中定义的控制字符,它们用于描述这些键的字符代码。

我们一般可以这样处理WM_CARH消息:

case WM_CHAR:

{

switch (wParam)

{

case 0x08:

// Process a backspace.

break;

case 0x0A:

// Process a linefeed.

break;

case 0x1B:

// Process an escape.

break;

case 0x09:

// Process a tab.

break;

case 0x0D:

// Process a carriage return.

break;

default:

// Process displayable characters.

break;

}

}

我们可以在WM_CHAR里面判断当前是否有指定的键被按下:

BOOL bIsCtrl = (::GetAsyncKeyState(VK_CONTROL) & 0x8000); (MFC源码 afxcolordialog.cpp 460行)

BOOL bIsCtrl = (::GetKeyState(VK_CONTROL) & 0x8000);

下面我解释一下键盘消息的lParam参数,这个参数在MSDN上面都可以查到,只是英文,我这里作一些简单的说明:(以WM_KEYDOWN为例)

WPARAM:虚拟键值,VT_*等值。

LPARAM:根据其不同的位数表示的含义不同可以分以下几部分:

(1) 重复计数位(0 - 15 位):表示消息按键数据。一般情况下为1,当键一直按下,窗口过程就会连续收到W_KEYDOWN消息,但有可能窗口过程来不及处理这些按键消息,那么Windows就会把几个按键消息组合成一个,并增加重复计数。比如你处理WM_KEYDOWN时Sleep(200),那么得到的这个数字就可能大于1,一般可以这样来得到这个计数:

DWORD count = (((DWORD)lParam) & 0x0000FFFF);

(2) OEM扫描码(16~23位):OEM扫描码是键盘发送的码值,由于此域是设备相关的,因而此值往往被忽略。

(3) 扩展键标志(24位):扩展键标志在有Alt键(或Ctrl键)按下时为1,否则为0。

(4) 保留位(25~28位):保留位是系统缺省保留的,一般不用。

(5) 关联码(29位):关联码用来记录某键与Alt键的组合状态,若按下Alt,当WM_SYSKEYDOWN消息送到某个激活的窗口时,其值为1,否则为0。

(6) 键的先前状态(位30):键的先前状态用于记录先前某键的状态,对于WM_SYSKEYUP消息,其值始终为1。

(7) 转换状态(31位):转换状态的消息是始终按着某键所产生的消息,若某键原来是按下的,则其先前状态为0。转换状态指示键被按下还是被松开。当键被按下时,对应于者WM_SYSKEYDOWN消息,其值始终为0,当键被松开时,其转换状态为1,对应于WM_SYSKEYUP消息,其值始终为1。

5,死字符消息

Windows程序经常忽略WM_DEADCHAR和WM_SYSDEADCHAR消息,但您应该明确地知道死字符是什么,以及它们工作的方式。

在某些非U.S.英语键盘上,有些键用于给字母加上音调。因为它们本身不产生字符,所以称之为「死键」。例如,使用德语键盘时,对于U.S.键盘上的+/=键,德语键盘的对应位置就是一个死键,未按下Shift键时它用于标识锐音,按下Shift键时则用于标识抑音。

当使用者按下这个死键时,窗口消息处理程序接收到一个wParam等于音调本身的ASCII或者Unicode代码的WM_DEADCHAR消息。当使用者再按下可以带有此音调的字母键(例如A键)时,窗口消息处理程序会接收到WM_CHAR消息,其中wParam等于带有音调的字母「a」的ANSI代码。

因此,使用者程序不需要处理WM_DEADCHAR消息,原因是WM_CHAR消息已含有程序所需要的所有信息。Windows的做法甚至还设计了内部错误处理。如果在死键之后跟有不能带此音调符号的字母(例如「s」),那么窗口消息处理程序将在一行接收到两条WM_CHAR消息-前一个消息的wParam等于音调符号本身的ASCII代码(与传递到WM_DEADCHAR消息的wParam值相同),第二个消息的wParam等于字母s的ASCII代码。

当然,要感受这种做法的运作方式,最好的方法就是实际操作。您必须加载使用死键的外语键盘,例如前面讲过的德语键盘。您可以这样设定:在「控制台」中选择「键盘」,然后选择「语系」页面标签。然后您需要一个应用程序,该程序可以显示它接收的每一个键盘消息的详细信息。下面的KEYVIEW1就是这样的程序。

Windows键盘消息处理的更多相关文章

  1. .Net WinForm 控件键盘消息处理剖析

    在WinForm控件上我们可以看到很多关于键盘消息处理的方法,比如OnKeyDown, OnKeyPress, ProcessCmdKey, ProcessDialogKey,IsInputKey等等 ...

  2. WinForm 控件键盘消息处理剖析(转)

    一直想整理键盘事件的调用顺序,刚好看见园子里的这篇文章,写的不错,就转载了:http://www.cnblogs.com/tedzhao/archive/2010/09/07/1820557.html ...

  3. MFC之键盘和键盘消息处理

    今天学习了最基本的MFC操作,键盘及键盘消息函数,跟着书中的内容自己编了个小程序.检测用户是否同时按下SHIFT和B键 第一步:先是建了一个单文档模式的MFC程序,添加了键盘消息及处理函数.键盘消息处 ...

  4. Windows 键盘快捷键

    Windows 键盘快捷键 标签页和窗口快捷键 Ctrl+N 打开新窗口. Ctrl+T 打开新标签页. Ctrl+Shift+N 在隐身模式下打开新窗口. 按 Ctrl+O,然后选择文件. 通过 G ...

  5. android源码 键盘消息处理机制

    键盘消息处理模型: .WMS统一管理Window, 它包含了InputManager变量,其对应c++层的NativeInputManager2.c++层的NativeInputManager包含 i ...

  6. Windows键盘无法调起

    Windows 键盘无法调起 经常使用触摸屏幕的小伙伴肯定都遇到过屏幕键盘怎么也唤不起来(在桌面模式下,非平板模式).以下收集了一些常见的解决方案: 注:本文基于 Windows 10 v1903,其 ...

  7. 6.文件所有权和权限----免费设置匿名----Windows键盘记录器----简介和python模块

    文件所有权和权限 touch --help cd Desktop mkdir Folder cd Folder clear touch Test1 Test2 Test3 Test4 ls ls -l ...

  8. windows鼠标消息处理与键盘模拟函数

    1.鼠标坐标问题 BOOL GetWindowRect(   HWND hWnd,   LPRECT lpRect  ); RECT x;//定义一个二维数组x ::GetWindowRect(hwn ...

  9. Windows 键盘操作快捷方式积累

    复制.粘贴: CTRL+C 复制被选择的项目到剪贴板 CTRL+V 粘贴剪贴板中的内容到当前位置 CTRL+X 剪切被选择的项目到剪贴板 Alt+ space + E + P CMD 窗口快速粘贴 关 ...

随机推荐

  1. Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC

    在之前的<跟我学SpringMVC>中的<第七章 注解式控制器的数据验证.类型转换及格式化>中已经介绍过SpringMVC集成Bean Validation 1.0(JSR-3 ...

  2. Android studio 自己定义打包APK名称

    Android Studio打包应用默认生成的apk名称是:app-release.apk .假设我们要让生成的apk名跟我们版本号包名有联系的话.那我们就要自己定义生成的apk名了,要怎么做呢. 我 ...

  3. Unlicensed ARC session – terminating!

    问题描述 近日,发现ArcGIS10.4中存在很多bug,而且费了好多时间去测试它,最终决定改用10.1.在降级程序时遇到许可问题. 重装ArcGIS10.1后,打开工程,所有引用都自动映射,没报任何 ...

  4. 如何随机获取数据库不连续ID的数据?

    这个问题的来由是我朋友要为一网站实现一个标签云功能,和我交流后我给出了一个方案,在此略作记录,亦求拍砖. 大概需求这是样的: 在数据库有一张表A如下图: 其中id字段的值未必是连续的,现在我朋友要做的 ...

  5. java 对视频和图片进行加密解密[转]

    是时候回到文件加密与解密的具体实现这个主题上来了.后续的举例均采用图片(包括GIF动画)类型,而其他类型资源的实现原理相同,就不一一给出了.首先来看对一幅JPG类型图片进行异或加密的Java实现,由于 ...

  6. eclipse 运行简单JAVA程序事例

    开发JAVA已经有一段时间了,不过要运行JAVA程序,还是在命令行敲命令,很不方便,很麻烦,突然想到eclipse应该也有这个功能,于是百度了一下,将步骤晒出来,供大家参考. 1.创建JAVA工程 单 ...

  7. python globals和locals

    文章里面说globals和locals函数返回的是命名空间 - 一个存有对应作用域的所有的变量.方法的字典,注意这里和dir函数返回数组的不一样 Python命名空间的本质 class Test(ob ...

  8. psql 查询表大小

    select schemaname,tablename,pg_relation_size(schemaname||'.'||tablename) as tabsize from pg_tables o ...

  9. Dubbo源码解读:appendAnnotation [01]

    import java.lang.reflect.Method; public class AppendAnnotation { /** * 获取某个Annotation实例的所有方法值(实际是Ann ...

  10. kettle 如何将excel文件导入oracle数据库?

      1.情景展示 昨日,有一批数据需要导入数据库,但是,plsql不知为何不能导了,于是,我选择使用kettle完成excel数据的导入. 2.准备工作 将对应的数据库所需的jar包拷贝至其lib目录 ...