<知識分享>
輸入事件中的鍵盤事件通常有字符事件和按鍵事件,這些事件的附帶信息構成了鍵盤輸入的信息,而想要讀取這些信息,是要通過API函數ReadConsoleInput來獲取的,函數原型如下:
- BOOL ReadConsoleInput( //讀取輸入信息
- HANDLE hConsoleInput, //句柄
- PINPUT_RECORD lpBuffer, //輸入事件結構體的指針
- DWORD nLength, //要讀取的記錄數
- LPDWORD lpNumberOfEventsRead //用來接受成功讀取記錄數的指針
- ); //如果該函數成功調用,返回非零值
- //輸入事件結構體的指針可以是結構體數組的首地址,這樣就可以一次性讀取多個記錄數。
下面介紹幾個和讀取鍵盤輸入事件有關的結構體,各結構體原型如下:
- typedef struct _INPUT_RECORD //輸入事件結構體
- {
- WORD EventType; //事件類型
- union
- {
- KEY_EVENT_RECORD KeyEvent; //按鍵事件
- MOUSE_EVENT_RECORD MouseEvent; //鼠標事件
- WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
- MENU_EVENT_RECORD MenuEvent;
- FOCUS_EVENT_RECORD FocusEvent;
- } Event; //具體的事件
- } INPUT_RECORD;
- /*
- 其中事件類型EventType的值有5種
- KEY_EVENT 代表Event包含一個KEY_EVENT_RECODE結構體
- MOUSE_EVENT 代表Event包含一個MOUSE_EVENT_RECODE結構體
- WINDOW_BUFFER_SIZE_EVENT 代表Event包含一個WINDOW_BUFFER_SIZE_EVENT_RECORD結構體
- MENU_EVENT 代表Event包含一個MENU_EVENT_RECORD結構體
- FOCUS_EVENT 代表Event包含一個FOCUS_EVENT_RECORD結構體
- */
- typedef struct _KEY_EVENT_RECORD //鍵盤事件結構體
- {
- BOOL bKeyDown; //按鍵狀態,true代表鍵按下,false代表鍵釋放
- WORD wRepeatCount; //按鍵次數
- WORD wVirtualKeyCode; //虛擬鍵
- WORD wVirtualScanCode; //虛擬鍵掃描碼
- union
- {
- WCHAR UnicodeChar; //解釋成Unicode寬字符
- CHAR AsciiChar; //解釋成ASCII碼字符
- } uChar;
- DWORD dwControlKeyState; //控制鍵狀態
- } KEY_EVENT_RECORD;
- /*
- 控制鍵各狀態的值
- ENHANCED_KEY 擴展鍵被按下
- LEFT_ALT_PRESSED 左Alt鍵被按下
- LEFT_CTRL_PRESSED 左Ctrl鍵被按下
- RIGHT_ALT_PRESSED 右Alt鍵被按下
- RIGHT_CTRL_PRESSED 右Ctrl鍵被按下
- NUMLOCK_ON 數字鎖定被打開
- SCROLLLOCK_ON 滾動鎖定被打開
- CAPSLOCK_ON 大寫鎖定被打開
- SHIFT_PRESSED Shift鍵被按下
- */
當輸入事件為鍵盤事件時,事件類型就為鍵盤事件,為其他事件時,事件類型就為對應的事件。另外,鍵盤上每個有意義的鍵都對應着一個唯一的掃描碼,雖然掃描碼可以作為鍵的標識,但是它是依賴於具體的設備的。因此,在應用程序中,使用的往往是與具體設備無關的虛擬鍵代碼。這種虛擬鍵代碼是一種與具體設備無關的鍵盤編碼。而控制鍵狀態比如大寫鎖定開啟狀態,Ctrl鍵按下狀態等、、、
下面是部分常用虛擬鍵代碼表:
- /*
- 虛擬鍵代碼 值 鍵名稱
- -----------------------------------------------------
- VK_BACK 0x08 退格鍵
- VK_TAB 0x09 Tab鍵
- VK_RETURN 0x0D 回車鍵
- VK_SHIFT 0x10 Shift鍵
- VK_LSHIFT 0xA0 左Shift鍵
- VK_RSHIFT 0xA1 右Shift鍵
- VK_CONTROL 0x11 Ctrl鍵
- VK_LCONTROL 0xA2 左Ctrl鍵
- VK_RCONTROL 0xA3 右Ctrl鍵
- VK_MENU 0x12 Alt鍵
- VK_LMENU 0xA4 左Alt鍵
- VK_RMENU 0xA5 右Alt鍵
- VK_PAUSE 0x13 Pause鍵
- VK_CAPITAL 0x14 Caps Lock鍵
- VK_NUMLOCK 0x90 Num Lock鍵
- VK_SCROLL 0x91 Scroll Lock鍵
- VK_ESCAPE 0x1B Esc鍵
- VK_SPACE 0x20 空格鍵
- VK_PRIOR 0x21 Page Up鍵
- VK_NEXT 0x22 Page Down鍵
- VK_END 0x23 End鍵
- VK_HOME 0x24 Home鍵
- VK_LEFT 0x25 左方向鍵
- VK_UP 0x26 上方向鍵
- VK_RIGHT 0x27 右方向鍵
- VK_DOWN 0x28 下方向鍵
- VK_DELETE 0x2E Delete鍵
- VK_INSERT 0x2D Insert鍵
- '0' 0x30 0鍵(非小鍵盤)
- '1' 0x31 1鍵(非小鍵盤)
- '2' 0x32 2鍵(非小鍵盤)
- ... ... ...
- '9' 0x39 9鍵(非小鍵盤)
- 'A' 0x41 A鍵
- 'B' 0x42 B鍵
- ... ... ...
- 'Z' 0x5A Z鍵
- VK_SLEEP 0x5F Sleep鍵
- VK_NUMPAD0 0x60 小鍵盤0鍵
- VK_NUMPAD1 0x61 小鍵盤1鍵
- VK_NUMPAD2 0x62 小鍵盤2鍵
- ... ... ...
- VK_NUMPAD9 0x69 小鍵盤9鍵
- VK_MULTIPLY 0x6A 小鍵盤乘鍵*
- VK_ADD 0x6B 小鍵盤加鍵+
- VK_SUBTRACT 0x6D 小鍵盤減鍵-
- VK_DIVIDE 0x6F 小鍵盤除鍵/
- VK_DECIMAL 0x6E 小鍵盤點鍵.
- VK_F1 0x70 F1鍵
- VK_F2 0x71 F2鍵
- ... ... ...
- VK_F12 0x7B F12鍵
- VK_F13 0x7C F13鍵 注:別問我,我也不知道什么電腦有這么多鍵
- ... ... ...
- VK_F24 0x87 F24鍵
- VK_OEM_1 0xBA ;:鍵
- VK_OEM_2 0xBF /?鍵
- VK_OEM_3 0xC0 ·~鍵
- VK_OEM_4 0xDB [{鍵
- VK_OEM_5 0xDC \|鍵
- VK_OEM_6 0xDD ]}鍵
- VK_OEM_7 0xDE '"鍵
- VK_OEM_PLUS 0xBB =+鍵
- VK_OEM_MINUS 0xBD -_鍵
- VK_OEM_COMMA 0xBC ,<鍵
- VK_OEM_PERIOD 0xBE .>鍵
- */
以上是部分常用的微軟虛擬鍵盤碼表,想要知道更詳細的,請參見MSDN。其中各個虛擬鍵的具體使用情況根據各人編譯器或IDE等的不同而有所差異。下面是一個實現按Esc鍵就輸出Esc的樣例程序:
- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
- #include <conio.h>
- #define true 1
- #define false 0
- int main()
- {
- HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); //獲得標准輸入設備句柄
- INPUT_RECORD keyrec; //定義輸入事件結構體
- DWORD res; //定義返回記錄
- for (;;)
- {
- ReadConsoleInput(handle_in, &keyrec, 1, &res); //讀取輸入事件
- if (keyrec.EventType == KEY_EVENT) //如果當前事件是鍵盤事件
- {
- if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) //當前事件的虛擬鍵為Esc鍵
- {
- printf("Esc ");
- }
- }
- }
- return 0;
- }
在上面的樣例程序中,當你按下Esc鍵后又馬上釋放,程序會輸出兩次Esc,因為有兩次事件的虛擬鍵代碼都是Esc鍵的代碼,一次是按下,一次是釋放。如果要實現按下鍵后出現反應,釋放不出現反應,那么將上例程序中第18行代碼的條件改成
- if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE
- && keyrec.Event.KeyEvent.bKeyDown == true) //表示當前為鍵按下而不是鍵釋放
就行了。
根據控制鍵的狀態我們可以實現不同的狀態輸出不同的值以及組合鍵的實現,下面的樣例程序在大寫鎖定打開時輸入A鍵則輸出大寫字母A,否則輸出小寫字母a。而在Shift鍵被按下的狀態是則輸出Shift+A以及Shift+a。樣例程序如下
- #include <stdio.h>
- #include <stdlib.h>
- #include <windows.h>
- #include <conio.h>
- #define true 1
- #define false 0
- int main()
- {
- HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE); //獲得標准輸入設備句柄
- INPUT_RECORD keyrec; //定義輸入事件結構體
- DWORD res; //定義返回記錄
- for (;;)
- {
- ReadConsoleInput(handle_in, &keyrec, 1, &res); //讀取輸入事件
- if (keyrec.EventType == KEY_EVENT) //如果當前事件是鍵盤事件
- {
- if (keyrec.Event.KeyEvent.wVirtualKeyCode == 'A'
- && keyrec.Event.KeyEvent.bKeyDown == true) //當按下字母A鍵時
- {
- if (keyrec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) //Shift鍵為按下狀態
- {
- printf("Shift+");
- }
- if (keyrec.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) //大寫鎖定為打開狀態
- {
- printf("A ");
- }
- else //大寫鎖定關閉狀態
- {
- printf("a ");
- }
- }
- }
- }
- return 0;
- }
由上例需要了解到的是,各個控制鍵狀態的的確定並不是使用等於符號==而是按位與&運算符,因為在同一時刻可能有多種控制鍵狀態值,比如各種鎖定都被打開且各種控制鍵也被同時按下。使用&運算符則顯得尤其高明,方便查詢各個控制鍵的狀態而不使之出現沖突。呵呵,不服不行啊,感慨一下,還是要多學習一下別人高明的地方,比如靈活運用位運算符實現各種功能等等······