字符消息

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。

 

posted @ 2018-07-19 07:07  M-Anonymous  阅读(208)  评论(0)    收藏  举报