字符消息
while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
这是 WinMain() 中典型的消息循环。
GetMessage() 从消息队列中取出下一条消息,填入 msg 结构的字段。DispatchMessage() 调用此消息的窗口过程。
TranslateMessage() 负责把击键消息转换为字符消息。
如果击键消息是 WM_KEYDOWN 或 WM_SYSKEYDOWN,且击键和转义状态组合产生了一个字符,
则 TranslateMessage() 把字符消息放入应用程序的消息队列。
这个字符消息将被放在击键消息之后,GetMessage() 可从消息队列中获取此字符消息。
四类字符消息:
| 字符 | 死字符 | |
| 非系统字符 | WM_CHAR | WM_DEADCHAR |
| 系统字符 | WM_SYSCHAR | WM_SYSDEADCHAR |
WM_CHAR 和 WM_DEADCHAR 来自于 WM_KEYDOWN 消息。
而 WM_SYSCHAR 消息 和 WM_SYSDEADCHAR 消息来自于 WM_SYSKEYDOWN 消息。
大多数情况下,Windows 程序会忽略其他三种字符消息,仅处理 WM_CHAR 消息。
消息排序:
因为 TranslateMessage() 从 WM_KEYDOWN 和 WM_SYSKEYDOWN 消息产生字符消息,
所以字符消息夹在击键消息中传给窗口过程。
如果 Caps Lock 键没有锁定,则在你按下再释放 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 | 'A'的虚拟键代码(0x41) |
| WM_CHAR | 'a'的字符编码(0x61) |
| WM_KEYDOWN | 'A'的虚拟键代码(0x41) |
| WM_CHAR | 'a'的字符编码(0x61) |
| ...... | ...... |
死字符通常在一些非美国英语的键盘上,某些键可以给字母加上音调。这些键称为“死键”,因为它们自己不产生字符。
KeyView 程序:
#include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("KeyView1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Keyboard Message Viewer #1"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int cxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar ; static int cLinesMax, cLines ; static PMSG pmsg ; static RECT rectScroll ; static TCHAR szTop[] = TEXT ("Message Key Char ") TEXT ("Repeat Scan Ext ALT Prev Tran") ; static TCHAR szUnd[] = TEXT ("_______ ___ ____ ") TEXT ("______ ____ ___ ___ ____ ____") ; static TCHAR * szFormat[2] = { TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"), TEXT ("%-13s 0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ; static TCHAR * szYes = TEXT ("Yes") ; static TCHAR * szNo = TEXT ("No") ; static TCHAR * szDown = TEXT ("Down") ; static TCHAR * szUp = TEXT ("Up") ; static TCHAR * szMessage [] = { TEXT ("WM_KEYDOWN"), TEXT ("WM_KEYUP"), TEXT ("WM_CHAR"), TEXT ("WM_DEADCHAR"), TEXT ("WM_SYSKEYDOWN"), TEXT ("WM_SYSKEYUP"), TEXT ("WM_SYSCHAR"), TEXT ("WM_SYSDEADCHAR") } ; HDC hdc ; int i, iType ; PAINTSTRUCT ps ; TCHAR szBuffer[128], szKeyName [32] ; TEXTMETRIC tm ; switch (message) { case WM_CREATE: case WM_DISPLAYCHANGE: // Get maximum size of client area cxClientMax = GetSystemMetrics (SM_CXMAXIMIZED) ; cyClientMax = GetSystemMetrics (SM_CYMAXIMIZED) ; // Get character size for fixed-pitch font hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cyChar = tm.tmHeight ; ReleaseDC (hwnd, hdc) ; // Allocate memory for display lines if (pmsg) free (pmsg) ; cLinesMax = cyClientMax / cyChar ; pmsg = malloc (cLinesMax * sizeof (MSG)) ; cLines = 0 ; // fall through case WM_SIZE: if (message == WM_SIZE) { cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; } // Calculate scrolling rectangle rectScroll.left = 0 ; rectScroll.right = cxClient ; rectScroll.top = cyChar ; rectScroll.bottom = cyChar * (cyClient / cyChar) ;
// 取(cyClient / cyChar)整数 InvalidateRect (hwnd, NULL, TRUE) ; return 0 ; case WM_KEYDOWN: case WM_KEYUP: case WM_CHAR: case WM_DEADCHAR: case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_SYSCHAR: case WM_SYSDEADCHAR: // Rearrange storage array for (i = cLinesMax - 1 ; i > 0 ; i--) { pmsg[i] = pmsg[i - 1] ;
// 丢弃最后一个结构中的数据,预留第一个结构。
// 可以这样理解,把全部结构数据向后顺移一位。 } // Store new message pmsg[0].hwnd = hwnd ; pmsg[0].message = message ; pmsg[0].wParam = wParam ; pmsg[0].lParam = lParam ;
//有按键消息来时,cLines加1,最大不超过窗口最大化时显示字符行 cLines = min (cLines + 1, cLinesMax) ; // Scroll up the display ScrollWindow (hwnd, 0, -cyChar, &rectScroll, &rectScroll) ; break ; // ie, call DefWindowProc so Sys messages work case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; //因为两个字符串绘画坐标一样,必须设定透明模式以防止第二个字符串擦除第一个字符串 SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; SetBkMode (hdc, TRANSPARENT) ; TextOut (hdc, 0, 0, szTop, lstrlen (szTop)) ; TextOut (hdc, 0, 0, szUnd, lstrlen (szUnd)) ; for (i = 0 ; i < min (cLines, cyClient / cyChar - 1) ; i++) {
//如果是字符消息则标记 1 ,如果是按键消息则标记 0 iType = pmsg[i].message == WM_CHAR || pmsg[i].message == WM_SYSCHAR || pmsg[i].message == WM_DEADCHAR || pmsg[i].message == WM_SYSDEADCHAR ; GetKeyNameText (pmsg[i].lParam, szKeyName, sizeof (szKeyName) / sizeof (TCHAR)) ; TextOut (hdc, 0, (cyClient / cyChar - 1 - i) * cyChar, szBuffer, wsprintf (szBuffer, szFormat [iType], szMessage [pmsg[i].message - WM_KEYFIRST], //计算得出消息类型,并索引 pmsg[i].wParam, (PTSTR) (iType ? TEXT (" ") : szKeyName),
//如果是按键消息就输出键名(szKeyName),并采用左对齐,宽度为15个字符。
//如果是字符消息就输出空格,宽度为1个字符。 (TCHAR) (iType ? pmsg[i].wParam : ' '),
// 如果是按键消息就输出空格。
// 如果是字符消息就输出该字符。 LOWORD (pmsg[i].lParam),
//低十六位记录着按键的重复计数次数。用十进制方式显示为无符号整数,宽度为6个字符。 HIWORD (pmsg[i].lParam) & 0xFF,
//取高十六位中的低八位,解释为键盘扫描码(OEM),显示为十进制有符号整数,宽度为4个字符。 0x01000000 & pmsg[i].lParam ? szYes : szNo,
// 0000 0001 0000 0000 0000 0000 0000 0000 读取 扩充键旗标(Extended Key Flag) 状态。 0x20000000 & pmsg[i].lParam ? szYes : szNo,
// 0010 0000 0000 0000 0000 0000 0000 0000 读取 内容代码(Context Code) 状态。 0x40000000 & pmsg[i].lParam ? szDown : szUp,
// 0100 0000 0000 0000 0000 0000 0000 0000 读取 键的先前状态(Previous Key State) 0x80000000 & pmsg[i].lParam ? szUp : szDown)) ;
// 1000 0000 0000 0000 0000 0000 0000 0000 读取 转换状态(Transition State) } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
GetKeyNameText() 介绍:
功能:该函数检取表示键名的字符串。
函数原型:int GetKeyNameText (LONG lParam, LPTSTR LpString, int nSize) ;
返回值:若函数调用成功,将拷贝一个以空结尾的字符串的指定缓冲区中,且返回值为串的长度(字符数),不计终止的空字符。
否则返回 0。

浙公网安备 33010602011771号