while(GetMessage(&msg,NULL,0,0))
TranslateMessage(&msg);//Windows将根据击键消息、转义状态和键盘的国家/地区配置等信息,将扫描码转换成相应的字符码,如果可以组合成一个字符,则把字符消息放入应用程序的消息队列,产生WM_CHAR、WM_DEADCHAR等消息。
DispatchMessage(&msg);
6.3.1 四类字符消息(WM_CHAR\WM_DEADCHAR、WM_SYSCHAR\WM_SYSDEADCHAR)
(1)lParam——与击键消息的lParam相同
(2)wParam——ANSI或Unicode字符码
①如何区别是8位的ANSI还是16位的Unicode?——看RegisterClass是A或W版的。
②获取字符的方法:(TCHAR)wParam;
③判断窗口过程用的是哪种编码:fUnicode= IsWindowsUnicode(hwnd);
6.3.2 消息排序
(1)发送顺序
非系统消息发送顺序
系统消息发送顺序
WM_KEYDOWN
WM_CHAR
WM_KEYUP
WM_SYSKEYDOWN
WM_SYSCHAR
WM_SYSKEYUP
(2)应用举例
①输入大写字母A:Shift+A
消息顺序
击键或键码
WM_KEYDOWN
VK_SHIFT(0x10);//shift只产生击键,而不产生字符消息
WM_KEYDOWN
‘A’的虚拟键(0x41)
WM_CHAR
‘A’的字符编码(0x41)
WM_KEYUP
‘A’的虚拟键(0x41)
WM_KEYUP
VK_SHIFT(0x10)
②持续按住A不放
消息顺序
击键或键码
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)
6.3.3 控制字符的处理
(1)处理击键和字符消息的原则:
①读取字符到窗口时,就处理WM_CHAR消息
②读取光标键、功能键、Delete、Insert、Shift、Ctrl、Alt时处理WM_KEYDOWN。
③Tab、回车、空格、Esc键。根据情况,一般在WM_CHAR中处理,如下:
case WM_CHAR:
switch(wParam);
Case ‘\b’: //退格键
break;
Case ‘\t’: //Tab键
break;
Case ‘\n’: //换行符
break;
Case ‘\r’: //回车键
break;
default:
break;
6.3.4 死字符消息
(1)死键:在非美国英语键盘上,某些键可以给字母加音调,但本身不产生字符,因此被称为“死键”。
(2)死字符的工作方式:
①当按下死键(如德语键盘的+=键),相应的窗口过程会收到wParam为音调本身的ASCII码的WM_DEADCHAR消息。若接着按可带音调的字母(如A键),窗口过程会收到参数wParam为‘á’的ANSI码的WM_CHAR消息。因而不必自己去处理WM_DEADCHAR消息,因为WM_CHAR消息己经可以获得所有的信息。
②容错机制:如果按下死键,再按不可再音调的字母键(如‘S’键),那么窗口过程会连接收到两个WM_CHAR消息。第1个消息的wParam等于音调本身的ASCII码,第2个消息等于‘s’字母的ASCII码。
6.4 键盘消息和字符集
6.4.1 KEYVIEW1程序
(1)各列说明——为了便于分栏,采用等宽字体。
(3)GetKeyNameText——根据lParam里的扫描码获取按键名称。
6.4.2 非英语键盘问题
(1)切换不同键盘布局——KeyView1编译为非Unicode的多字节版本
①实验
A、在美国英语版的Windows下,切换不同键盘布局时字符显示的问题
键盘
WM_KEYDOWN
虚拟键代码
(十进制)
扫描码
(十进制)
美国英语键盘
(WM_CHAR)
希腊键盘
(WM_CHAR)
俄语键盘
(WM_CHAR)
65
30
0x61(a)
0xE1(á)
0xF4(ô)
66
48
0x62(b)
0xE2(â)
0xE8(è)
67
46
0x63(c)
0xF8(ø)
0xF1(ñ)
68
32
0x64(d)
0xE4(ä)
0xE2(â)
69
18
0x65(e)
0xE5(å)
0xF3(ó)
B、在希腊语版的Windows下,切换不同键盘布局时字符显示的问题
键盘
WM_KEYDOWN
虚拟键代码
(十进制)
扫描码
(十进制)
美国英语键盘(WM_CHAR)
希腊键盘(WM_CHAR)
俄语键盘(WM_CHAR)
65
30
0x61(a)
0xE1(α)
0xF4(τ)
66
48
0x62(b)
0xE2(β)
0xE8(θ)
67
46
0x63(c)
0xF8(ψ)
0xF1(ρ)
68
32
0x64(d)
0xE4(δ)
0xE2(β)
69
18
0x65(e)
0xE5(ε)
0xF3(σ)
②结论及原因——以A实验为例说明。
A、绿色为正确的显示结果,红色为不正确的显示结果。
B、当切换键盘布局时,同一个的按键会发出同样的WM_KEYDOWN消息和扫描码(因为扫描码是根据键盘的自然布局发出的,与硬件有关)。但WM_CHAR中收到的字符码,会因切换不同的键盘而不同。此时,因在同一个美国英语Windows版本下试验,所以三种键盘下通过字符码读取的字符都是来自同一个美国英语版本的字符集。从而导致希腊语键盘和俄语键盘下读出来的字符是错的,因为这些字符不是希腊字母和西里尔文字母。(注意:SYSTEM_FONT得到的字符集因Windows语言的版本不同而不同,有美国英语版、希腊版、俄语版的之分)。如果要正确显示出正确的希腊字母和西里尔字母,就要工作在不同语言的Windows里,这就是非Unicode字符集的不便之处。
(2)死键在不同版本的Windows下的字符显示问题
①死键的产生——以德语键盘为例。
先按“=”发送WM_DEADCHAR消息;B、再按字符,发送带有音调的WM_CHAR字符)
键盘
WM_KEYDOWN
虚拟键代码
(十进制)
扫描码
(十进制)
美国英语Windows
(WM_CHAR)
希腊语版Windows
(WM_CHAR)
65
30
0xE1(á)
0xE1(α)
69
18
0xE9(é)
0xE9(ι)
73
23
0xED(í)
0xED(ν)
79
24
0xF3(ó)
0xF3(σ)
85
22
0xFA(ú)
0xFA(ϊ)
②结论与原因
A、绿色为正确的显示结果,红色为不正确的显示结果。
B、同一种键盘布局下,同一按键发送WM_CHAR的字符码是一样的。
C、在不同的Windows下,因字符集不同,所以显示的字符也是不同的。
6.4.3 字符集和字体
(1)Windows支持的三种字体:
①位图字体——Windows标题栏、菜单、按钮、对话框中使用
②矢量字体:过时
③TrueType字体:可缩放。
(2)系统字体(位图字体)
GetStockObject
标识符
字样名称
(CreateFont的参数)
备注
SYSTEM_FONT
System
DC默认。得到的字符集因语言不同
SYSTEM_FIXED_FONT
FixedSys
等宽,得到的字符集因语言不同
OEM_FIXED_FONT
Terminal
用于MS_DOS下,得到的字符集因语言不同
DEFAULT_GUI_FONT
MS Sans Serif或宋体等
标准控件、用户界面
ANSI_FIXED_FONT
Courier
tmCharSet=0
ANSI_VAR_FONT
MS Sans Serif
DEVICE_DEFAULT_FONT
System
得到的字符集因语言不同
注意:①SYSTEM_FONT、OEM_FIXED_FONT等会因Windows语言版本不同,而得到不同字符集。
②ANSI_FIXED_FONT、ANSI_VAR_FONT得到的字符集不会因Windows不同而不同。
(3)不同语言版本下得到的ANSI字符集对比
①通过SYSTEM_FONT得到的系统默认字符集的不同
美国英语版Windows
希腊语版Windows
俄语版Windows
日语版Windows
②从上面可以看出,调用GetStockObject(SYSTEM_FONT)会因Windows版本的不同,而得到不同的字符集。但同一版本的Windows下得到的系统字符集,不会因键盘布局而不同。这也是导致KeyView1程序(非Unicode版)在美国英语版的Windows里切换到非英语键盘时,显示字符仍是美国版ANSI字符集(CharSet=0),而不是相应国家字符集的原因。
6.4.4 Unicode解决方案
(1)切换不同键盘布局——KeyView1编译为Unicode的版本
键盘
WM_KEYDOWN
虚拟键代码
(十进制)
扫描码
(十进制)
美国英语键盘
(WM_CHAR)
希腊键盘
(WM_CHAR)
俄语键盘
(WM_CHAR)
65
30
0x0061(a)
0x03B1(α)
0x0444(ф)
66
48
0x0062(b)
0x03B2(β)
0x0438(и)
67
46
0x0063(c)
0x03C8(ψ)
0x0441(с)
68
32
0x0064(d)
0x03B4(δ)
0x0432(в)
69
18
0x0065(e)
0x03B5(ε)
0x0443(у)
说明:①Unicode下WM_CHAR为16位的字符码,而不是8位的,WM_CHAR的值可能高于0xFF。
②为什么定义了Unicode版本的KeyView1,切换希腊键盘和俄语键盘显示还会出错呢?
因为位图字体(除中日韩版)最多可包含256个字符,所以尽管希腊键盘和俄语键盘得到的字符码是正确的,但显示为实心块的错误。当使用SYSTEM_FONT或SYSTEM_FIXED_FONT总有某些语言的字符不能正确显示。(中日韩版的系统字体为TrueType,可多于256个字符)。
6.4.5 TrueType字体和大字体
(1)创建逻辑字体——CreateFont和CreateFrontIndirect
①CreateFont的参数:第9个为“字符集ID”,第13个设置等宽FIXED_PITCH。
②CreateFontIndirect参数为LOGFONT结构指针,与CreateFont的14个参数一一对应。
(2)WM_INPUTLANGECHANGE消息:表示输入法或键盘布局发生了改变
参数
含义
备注
wParam
字符集ID
可以使用TranslateCharsetInfo这个API得到字符集的信息