《Windows API每日一练》5.3 字符消息

在本章前面讲过,通过转义状态信息可把击键消息转换为字符消息。并且提到,仅仅利用转义状态信息是不够的。还必须知道与国家/地区相关的键盘配置。其实不必担心这个问题。Windows程序主程序中的消息循环可以完美的解决这个问题。

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

这是WinMain函数中典型的消息循环。GetMessage函数从消息队列中取出下一条消息,填入msg结构的字段。DispatchMessage函数调用此消息的窗口过程。

在这两个函数之间是TranslateMessage函数,它负责把击键消息转换为字符消息。如果击键消息是WM_KEYDOWN或WM_SYSKEYDOWN,且击键和转义状态组合产生了一个字符,则TranslateMessage函数把字符消息放入应用程序的消息队列。这个字符消息将被放在按键消息之后,GetMessage函数可从消息队列中获取此字符消息。

本节必须掌握的知识点:

四类字符消息

消息排序

控制字符的处理

死字符处理

5.3.1 四类字符消息

字符消息可分为四类。

|-------|------------|----------------|
| | 字符 | 死字符 |
| 非系统字符 | WM_CHAR | WM_DEADCHAR |
| 系统字符 | WM_SYSCHAR | WM_SYSDEADCHAR |

WM_CHAR消息和WM_DEADCHAR消息来自于WM_KEYDOWN消息。而 WM_SYSCHAR 消息和 WM_SYSDEADCHAR 消息来自于 WM_SYSKEYDOWN 消息。

大多数情况下,Windows程序会忽略其他三种字符消息,仅处理 WM_CHAR消息。四类字符消息中的lParam参数和产生此字符码消息的按键消息(WM_KEYDOWN)中的 IParam参数是一样的(重复计数、扫描代码、扩展键标志、上下文代码、上一个键状态标志和转换状态标志)。但是,WM_CHAR消息的wParam参数不是虚拟键代码。实际上,它是ANSI或Unicode 字符码。

这些字符消息是我们遇到的将文本传递给窗口过程的第一个消息。但它们绝不是唯一 的一个。其他字符消息是将以零结束的文本串传递给窗口过程的消息。窗口过程怎么知道哪些字符数据是8位的ANSI字符编码,哪些是16位的Uincode字符编码呢?很简单,任何窗口过程,只要包含用RegisterClassA(RegisterClass的ANSI版本)注册的窗口类,就得到包含ANSI字符编码的消息。若包含用RegisterClassW(RegisterClass的宽字符版本)注册的窗口类,则得到Unicode字符编码的消息。如果你的程序用RegisteiClass注册窗口类,且Unicode标识符被定义,则你使用的实际是RegisterClassW,否则为RegisterClassA。

除非你明确地对窗口过程做了 ANSI和Unicode函数的混合编码,否则WM_CHAR消息(以及其他三个字符消息)所传递的字符码是:

(TCHAR) wParam

同一个窗口过程可能会用到两个窗口类,一个用RegisterClassA注册,另一个用 RegisterClassW注册。这意味着窗口过程会接收到用ANSI字符编码的消息和用Unicode字 符编码的消息。如果你的窗口过程需要确认是哪种字符编码,它能调用:

fUnicode = IsWindowUnicode(hvmd);

如果hwnd的窗口过程获得Unicode消息,则fUnicode变量为TRUE。这意味着此窗口基于用RegisterClassW注册的窗口类。

5.3.2 消息排序

因为 TranslateMessage 函数从 WM_KEYDOWN 和 WM_SYSKEYDOWN 消息产生字符消息,所以字符消息夹在击键消息中传给窗口过程。例如,如果CapsLock键没有锁定, 则在你按下再释放A键时,相应的窗口过程会接收以下三个消息:

|------------|----------------|
| 消息 | 按键或代码 |
| WM_KEYDOWN | 'A'的虚拟键码(0x41) |
| WM_CHAR | 'a'的字符编码(0x61) |
| WM_KEYUP | 'A'的虚拟键码(0x41) |

如果你通过以下几步输入大写字母A:按下Shift键,再按下A键,释放A键,再释放Shift 键,则窗口过程接收五个消息:

|------------|--------------------|
| 消息 | 按键或代码 |
| 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 消息,可以得到一个字符消息:

|------------|----------------|
| 消息 | 按键或代码 |
| 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键和字母键的组合会产生ASCII控制字符,范围从0x01(Ctrl-A)到0x1A(Ctrl_Z)。其中的某些控制码也可由下表中的按键产生(win10系统环境下已经不同于winNT或98):

|---------|---------|----------|--------------------|
| 按键 | 字符码 | 产生方法 | ANSI C 转义码 |
| 空格键 | 0x08 | Ctrl-H | \b |
| Tab键 | 0x09 | Ctrl-I | \t |
| Ctrl+回车 | 0x0A | Ctrl-J | \n |
| 回车键 | 0x0D | Ctrl-M | \r |
| Esc键 | 0x1B | Ctrl-[ | |

最右面一列显示了在ANSI C中定义的转义码,用来表明这些键的字符码。

Windows程序有时采用Ctrl键和字母键的组合作为菜单快捷键(我们将在第九章中讨 论)。在这种情况下,字母键不能转换为字符消息。

5.3.3 控制字符的处理

我们按照以下的基本规则来处理击键和字符消息:如果你需要读取输入到窗口中的键 盘字符,就处理WM_CHAR消息:如果你需要读取光标键、功能键、Delete键、Insert键、 Shift键、Ctrl键和Alt键,则处理WM_KEYDOWN消息。

但是Tab键怎么办?回车键、空格键、Esc键呢?从传统意义上讲,这些键产生ASCII 控制字符,像上表中所显示的那样。但是在Windows中,它们也产生虚拟键代码。这些键应该在处理WM_CHAR消息或者在处理WM_KEYDOWN消息时被处理吗?

通常我们更偏向把Tab键、回车键、空格键 和Esc键看作控制字符,而不是虚拟键。我通常这样处理WM_CHAR消息:

case WM_CHAR:

//其他行程式

switch (wParam)

{

case '\b': // backspace

//其他行程式

break ;

case '\t': // tab

//其他行程式

break ;

case '\n': // linefeed

//其他行程式

break ;

case '\r': // carriage return

//其他行程式

break ;

default: // character codes

//其他行程式

break ;

}

return 0 ;

5.3.4 死字符处理

Windows程序通常忽略WM_DEADCHAR和WM_SYSDEADCHAR消息,但是你应该明确地了解什么是死字符和它们怎样工作的。

在一些非美国英语的键盘上,某些键可以给字母加上音调。这些键称为"死键",因为它们自己不产生字符。例如,当安装了德语键盘时,对于美国式键盘上的+/=键,德语键盘上相应的位置为一个死键:

未按下 Shift 键时它用于标识锐音,按下 Shift 键时则用于标识抑音。

当用户按下此键时,相应的窗口过程会接收到wParam参数等于音调本身的ASCII或 Unicode码的WM_DEADCHAR消息。若接着按下可带有音调的字母键时(例如A键),窗口 过程接收到的将是wParam参数等于带有音调的字母'a'的ANSI码的WM_CHAR消息。

因而,你的程序不必去处理WM_DEADCHAR消息,因为WM_CHAR消息已包含了程序所需要的所有信息。Windows程序甚至有内置的差错处理:如果你按下死键,接着按下不能携带音调的字母键(如's'键),那么窗口过程会连续接收两个WM_CHAR消息。第一个消息的wParam参数等于音调本身的ASCII码(与传递给WM_DEADCHAR消息的 wParam参数相同),第二个消息的wParam参数等于字母's'的ASCII码。

当然,理解它的最好方式就是实际操作。你需要加载使用死键的非英语键盘,比如我前面描述过的德国键盘。你可在控制面板中通过选择【键盘】中的【语言】选项卡来设置它。然后你需要一个应用程序,它能显示程序接收的每一个键盘消息的细节。下面将要讨论的KEYVIEW1就是这样的一个程序。

相关推荐
蓝田~41 分钟前
观察者模式和订阅模式
windows·观察者模式
陌小呆^O^1 小时前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
qystca3 小时前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法
梓仁沐白7 小时前
ubuntu+windows双系统切换后蓝牙设备无法连接
windows·ubuntu
网易独家音乐人Mike Zhou9 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
九鼎科技-Leo11 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
搬砖的小码农_Sky12 小时前
C语言:数组
c语言·数据结构
Yang.9914 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
我不瘦但很逗14 小时前
Windows下使用DBeaver连接云数据库(MySQL)
数据库·windows
ashane131415 小时前
Java list
java·windows·list