发布网友 发布时间:2022-04-28 16:41
共1个回答
热心网友 时间:2022-06-03 16:45
展开1全部1.在Microsoft Windows 中,键盘和鼠标是两个标准的用户输入源,在一些交叠的操作中通常相互补充使用。当然,鼠标在今天的应用程序中比10年前使用得更为广泛。甚至在一些应用程序中,我们更习惯于使用鼠标,例如在游戏、画图程序、音乐程序,以及Web创览器等程序中就是这样。然而,我们可以不使用鼠标,但绝对不能从一般的PC中拆掉键盘。 Windows程序获得键盘输入的方式:键盘输入以消息的形式传递给程序的窗口过程。实际上,第一次学习消息时,键盘就是一个明显的例子:消息应该传递给应用程序的信息类型。 Windows用8种不同的消息来传递不同的键盘事件。这好像太多了,但是(就像我们所看到的一样)程序可以忽略其中至少一半的消息而不会有任何问题。并且,在大多数情况下,这些消息中包含的键盘信息会多于程序所需要的。处理键盘的部分工作就是识别出哪些消息是重要的,哪些是不重要的。 2.键盘基础知识 虽然应用程序在很多情况下可以通过鼠标实现信息的输入,但到现在为止键盘仍然是PC机中不可替代的重要输入设备。 用键盘当作输入设备,每当用户按下或释放某一个键时,会产生一个中断,该中断激活键盘驱动程序KEYBOARD.DRV来对键盘中断进行处理。 KEYBOARD.DRV程序会根据用户的不同操作进行编码,然后调用Windows用户模块USER.EXE生成键盘消息,并将该消息发送到消息队列中等候处理。 1.扫描码和虚拟码 扫描码对应着键盘上的不同键,每一个键被按下或释放时,都会产生一个唯一的扫描码作为本身的标识。扫描码依赖于具体的硬件设备,即当相同的键被按下或释放时,在不同的机器上可能产生不同的扫描码。在程序中通常使用由Windows系统定义的与具体设备无关的虚拟码。在击键产生扫描码的同时,键盘驱动程序KEYBOARD.DRV截取键的扫描码,然后将其翻译成对应的虚拟码,再将扫描码和虚拟码一齐编码形成键盘消息。所以,最后发送到消息队列的键盘消息中,既包含了扫描码又包含了虚拟码。 经常使用的虚拟码在WINDOWS.H文件中定义,常用虚拟码的数值、常量符号和含义如表所示。 取值(16进制) 常量符号 含义 01 VK_LBUTTON 鼠标左键 02 VK_RBUTTON 鼠标右键 03 VK_CANCEL Break中断键 04 VK_MBUTTON 鼠标中键 05-07 -- 未定义 08 VK_BACK (BackSpace)键 09 VK_TAB Tab键 0A-0B -- 未定义 0C VK_CLEAR Clear键 0D VK_RETURN Enter键 0E-0F -- 未定义 10 VK_SHIFT Shift键 11 VK_CONTROL Ctrl键 12 VK_MENU Alt键 13 VK_PAUSE Pause键 14 VK_CAPTIAL CapsLock键 15-19 -- 汉字系统保留 1A -- 未定义 1B VK_ESCAPE Esc键 1C-1F -- 汉字系统保留 20 VK_SPACE 空格键 21 VK_PRIOR PageUp键 22 VK_NEXT PageDown键 23 VK_END End键 24 VK_HOME Home键 25 VK_LEFT ←(Left Arrow)键 26 VK_UP ↑(Up Arrow)键 27 VK_RIGHT →(Right Arrow)键 28 VK_DOWN ↓(Down Arrow)键 29 VK_SELECT Select键 2A -- OEM保留 2B VK_EXECUTE Execute键 2C VK_SNAPSHOT Print Screen键 2D VK_INSERT Insert键 2E VK_DELETE Delete键 2F VK_HELP Help键 30-39 VK_0-VK_9 数字键0-9 3A-40 -- 未定义 41-5A VK_A-VK_Z 字母键A-Z 5B-5F -- 未定义 60-69 VK_NUMPAD0-VK_NUMPAD9 小键盘数字键0-9 6A VK_MULTIPLY *(乘号)键 6B VK_ADD +(加号)键 6C VK_SEPAPATOR 分隔符键 6E VK_SUBTRACT -(减号)键 6F VK_DECIMAL .(小数点)键 70-87 VK_DIVIDE /(除号)键 88-8F VK_F1-VK_F24 F1-F24功能键 90 VK_NUMBERLOCK Number lock键 91 VK_SCROLL Scroll lock键 92-B9 -- 未定义 BA-C0 -- OEM保留 C1-DA -- 未定义 DB_E4 -- OEM保留 E5 -- 未定义 E6 -- OEM保留 E7-E8 -- 未定义 E9-F5 -- OEM保留 F6-FE -- 未定义 2.输入焦点 同一时刻,Windows中可能有多个不同的程序在运行,也就是说有多个窗口同时存在。这时,键盘由多个窗口共享,但只有一个窗口能够接收到键盘消息,这个能够接收键盘消息的窗口被称为拥有输入焦点的窗口。 拥有输入焦点的窗口应该是当前的活动窗口,或者是活动窗口的子窗口,其标题和边框会以高亮度显示,以区别于其他窗口。拥有输入焦点的也可以是图标而不是窗口,此时,Windows也将消息发送给图标,只是消息的格式略有不同。 窗口过程可以通过发送WM_SETFOCUS和 WM_KILLFOCUS消息使窗体获得或失去输入焦点。程序也可以通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来判断窗体何时获得或失去输入焦点。其中WM_SETFOCUS消息表示窗口正获得输入焦点,WM_ KILLFOCUS消息表示窗口正失去输入焦点。 3.键盘消息 键盘消息分为系统键消息和非系统键消息。系统键消息是指由Aft键和其他键组合而产生的按键消息。当系统键被按下时产生WM_ SYSKEYDOWN消息,当系统键被释放时产生WM_SYSKEYUP消息。 Aft键与其他键形成的组合键通常用于对程序菜单和系统菜单进行选择,或用于在不同的程序之间进行切换。因此,系统键消息应该交由Windows进行处理,用户所编制的程序一般不处理系统键消息,而是将这些消息交由DefWindowProc函数进行处理。如果用户想对系统键消息进行处理,应该在处理完这些消息后,再将其发送给DefWindowProc函数,使得Windows系统能够正常工作。 某些击键消息可以被转换成字符消息,例如字母键、数字键等。而有些键只能产生按键消息而没有字符消息,例如 Shift键、Insert键等。消息循环中的 TranslateMessage函数可以实现从击键消息向字符消息的转化。当GetMessage函数捕获一个WM_SYSKEYDOWN消息或 WM_KEYDOWN消息后,TranslateMessage函数判断产生该消息的键是否能够被转换成字符消息,如果能,就将该消息转换成字符消息,再通过DispatchMessape函数将转换后的字符消息发送到消息队列中去。字符消息共有以下四种,如表所示。 字符 系统字符 非系统字符 普通字符 WM_SYSCHAR WM_CHAR 死字符 WM_SYSDEADCHAR WM_DEADCHAR 其中死字符是由某些特殊键盘上的按键所造成的,Windows一般忽略死字符所产生的消息。 Windows的消息一般是通过一个MSG结构体变量传送给消息处理函数的。对于键盘消息, MSG结构体变量的各个域中较重要的是lParam域和 wParam域。wParam域用于保存按键的虚拟键代码或字符的ASCII码。对于非字符消息,wParam域保存按键的虚拟健代码;对于字符消息, wParam域不保存字符的ASCII码。lParam域则用于保存击键时产生的附加信息,实际上一个32位的lParam变量被分为六部分,记录了以下相关信息:重复次数、OEM扫描码、扩展键标志、关联键标志、前一击键状态和转换状态。lParam域各位的含义如表所示。 位数 含义 0-15 击键重复次数累加 16-23 OEM扫描码 24 是否为扩展键 25-28 未定义 29 是否便用关联键,及Alt键是否同时按下。 30 前一次击键状态,0表示该键前一次状态为抬起,1表示前一次状态为按下 31 转换状态 按键的次序不同,产生的消息也不相同。例如,按下并释放1键,读过程依次产生如表所示三条消息。按下1键所产生的消息和wParam的取值 消息 wParam变量取值 WM_KEYDOWN 虚拟码1 WM_CHAR ASCII码“1” WM_KEYUP 虚拟码1 如果按下Shift键后再按下1键并释放,则依次产生如表所示的消息。按下 Shift键后按 1健所产生的消息和 wParam的取值 消息 wParam变量取值 WM_KEYDOWN 虚拟码 VK_SHIFT WM_KEYDOWN 虚拟码 VK_1 WM_CHAR ASCII码 “1” WM_KEYUP 虚拟码 VK_1 WM_KEYUP 虚拟码 VK_SHIF 键盘应用实例 下面通过一个应用程序实例来说明在实际编程中如何处理键盘消息。 #include #include // 全局变量 RECT rc; //记录滚屏的矩形区域 int xChar, yChar; //文本输入点坐标 WNDCLASSEX wnd; //窗口类结构变量 char szAppName[] = "键盘消息监视程序"; //窗口类名 //函数声明 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE hInstance,int iCmdShow); //函数:WinMain //作用:入口函数 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow) { MSG msg; if(!MyRegisterClass(hInstance)) { return FALSE; } if(!InitInstance(hInstance,iCmdShow)) { return FALSE; } while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam; } //函数:ShowKey //作用:实现在窗口中显示按键信息 void ShowKey (HWND hwnd, int iType,char *szMessage,WPARAM wParam,LPARAM lParam) { static char *szFormat[2] ={"%-14s %3d %c %6u %4d %5s %5s %6s %6s", "%-14s %3d %c %6u %4d %5s %5s %6s %6s" }; char szBuffer[80]; HDC hdc; ScrollWindowEx(hwnd, 0, -yChar, &rc,&rc,NULL,NULL,SW_INVALIDATE); hdc = GetDC (hwnd); SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)); TextOut (hdc, xChar, rc.bottom - yChar, szBuffer, wsprintf szBuffer, szFormat[iType], szMessage, //消息 wParam, //虚拟键代码 (BYTE) (iType ? wParam :‘ ’),//显示字符值 LOWORD (lParam), // 重复次数 HIWORD (lParam) & 0xFF, // OEM键盘扫描码 //判断是否为增强键盘的扩展键 (PSTR) (0x01000000 & lParam ? “是” : “否”), //判断是否同时使用了ALT键 (PSTR) (0x20000000 & lParam ? “是” : “否”), (PSTR) (0x40000000 & lParam ? “按下” : “抬”), //判断前一次击键状 (PSTR)(0x80000000 & lParam ? “按下” : “抬起”)) //判断转换状态? ); ReleaseDC (hwnd, hdc); ? ValidateRect (hwnd, NULL); ? } //函数:WndProc //作用:处理主窗口的消息 LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static char szTop[] ="消息键 字符 重复数 扫描码 扩展码 ALT 前一状态 转换状态"; static char szUnd[] ="_______ __ ____ _____ ______ ______ ___ _______ ______"; //在窗口中输出文字作为信息标题 HDC hdc; PAINTSTRUCT ps; TEXTMETRIC tm; switch (iMsg) { case WM_CREATE://处理窗口创建的消息 hdc = GetDC (hwnd); //设定字体 SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)); //检取当前字体的度量数据 GetTextMetrics (hdc, &tm); xChar = tm.tmAveCharWidth;//保存字体平均宽度 yChar = tm.tmHeight; //保存字体高度 ReleaseDC (hwnd, hdc); rc.top = 3 * yChar / 2; return 0; case WM_SIZE://处理窗口大小改变的消息 //窗体改变后保存新的滚屏区域右下角坐标 rc.right = LOWORD (lParam); rc.bottom = HIWORD (lParam); UpdateWindow (hwnd); return 0; case WM_PAINT: //处理窗口重绘消息 InvalidateRect (hwnd, NULL, TRUE); hdc = BeginPaint (hwnd, &ps); SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; SetBkMode (hdc, TRANSPARENT) ; TextOut (hdc, xChar, yChar / 2, szTop, (sizeof szTop) - 1) ; TextOut (hdc, xChar, yChar / 2, szUnd, (sizeof szUnd) - 1) ; EndPaint (hwnd, &ps); return 0; case WM_KEYDOWN: //处理键盘上某一键按下的消息 ShowKey (hwnd, 0, "WM_KEYDOWN",wParam, lParam); return 0; case WM_KEYUP: //处理键盘上某一按下键被释放的消息 ShowKey (hwnd, 0, "WM_KEYUP", wParam, lParam); return 0; case WM_CHAR: //处理击键过程中产生的非系统键的可见字符消息 howKey (hwnd, 1, "WM_CHAR", wParam, lParam); return 0; case WM_DEADCHAR: //处理击键过程中产生的非系统键"死字符"消息 ShowKey (hwnd, 1, "WM_DEADCHAR", wParam, lParam); return 0; case WM_SYSKEYDOWN: //处理系统键按下的消息 ShowKey (hwnd, 0, "WM_SYSKEYDOWN",wParam, lParam); break; case WM_SYSKEYUP: //处理系统键抬起的消息 ShowKey (hwnd, 0, "WM_SYSKEYUP", wParam, lParam); break; case WM_SYSCHAR://处理系统键可见字符消息 ShowKey (hwnd, 1, "WM_SYSCHAR", wParam, lParam); break; case WM_SYSDEADCHAR://处理系统键"死字符"消息 ShowKey (hwnd, 1, "WM_SYSDEADCHAR", wParam, lParam); break; case WM_DESTROY: //处理结束应用程序的消息 PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, iMsg, wParam, lParam); } //函数:MyRegisterClass //作用:注册窗口类 BOOL MyRegisterClass(HINSTANCE hInstance) { wnd.cbSize= sizeof (wnd); wnd.style = CS_HREDRAW | CS_VREDRAW; wnd.lpfnWndProc = WndProc; wnd.cbClsExtra = 0; wnd.cbWndExtra = 0; wnd.hInstance = hInstance; wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION);? wnd.hCursor = LoadCursor (NULL, IDC_ARROW); wnd.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); wnd.lpszMenuName = NULL; wnd.lpszClassName = szAppName; wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION); return RegisterClassEx (&wnd); } //函数:InitInstance //作用:创建主窗口 BOOL InitInstance(HINSTANCE hInstance,int iCmdShow) { HWND hwnd; hwnd = CreateWindow (szAppName, "键盘消息监视程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, NULL,NULL,hInstance,NULL ); if(!hwnd) { return FALSE; } ShowWindow (hwnd, iCmdShow); UpdateWindow (hwnd); return TRUE;