1,虛擬鍵(VK_*) 鍵盤上每一個鍵對應(yīng)一個掃描碼,掃描碼是OEM廠商制定的,不同的廠商生產(chǎn)的鍵盤同樣一個按鍵的掃描碼都有可能出現(xiàn)不一致的情況,為了擺脫由于系統(tǒng)設(shè)備不一致的情況,通過鍵盤驅(qū)動程序?qū)呙璐a映射為統(tǒng)一的虛擬鍵碼表示,從而達到所有的設(shè)備都有一個統(tǒng)一的虛擬鍵,比如回車鍵的虛擬鍵是VK_RETURN。 Windows定義的虛擬鍵都定義在WinUser.h這個頭文件里面,都是以VK_作為前綴。 2,激活/關(guān)閉窗口對鍵盤的消息 激活/關(guān)閉消息:WM_SETFOCUS/WM_KILLFOCUS 創(chuàng)建光標:CreateCaret(...) 設(shè)置光標位置:SetCaretPos(…) 在窗口中顯示光標:ShowCaret(…) 銷毀光標:DestroyCaret() 3,鍵盤消息 1)字符消息 系統(tǒng)字符消息 WM_SYSCHAR:系統(tǒng)字符 WM_SYSDEADCHAR:系統(tǒng)死字符 非系統(tǒng)按鍵消息 WM_CHAR:非系統(tǒng)字符 WM_DEADCHAR:非系統(tǒng)死字符 2)按鍵消息 系統(tǒng)按鍵消息:與ALT鍵相組合的組合鍵(無論用戶處理否,都需要最后調(diào)用DefWindowProc(hWnd,iMessage,wParam,lParam)) WM_SYSKEYDOWN WM_SYSKEYUP 非系統(tǒng)按鍵消息: WM_KEYDOWN WM_KEYUP 注意: a) b) 所有鍵都存在“彈起”消息。 c)
我們是怎么收到WM_CHAR的呢?就是因為我們在消息循環(huán)時調(diào)用了TranslateMessage對鍵盤消息進行翻譯, 如果消息為WM_KEYDOWN或者WM_SYSKEYDOWN,并且按鍵與位移狀態(tài)相組合產(chǎn)生一個字符,則TranslateMessage把字符消息放入消息隊列中。此字符消息將是GetMessage從消息隊列中得到的按鍵消息之后的下一個消息。 在我們處理這個消息時,對應(yīng)的wParam不是虛擬鍵,而是ANSI或Unicode字符代碼,一般情況下我們可以這樣用: 4,消息順序 因為TranslateMessage函數(shù)從WM_KEYDOWN和WM_SYSKEYDOWN消息產(chǎn)生了字符消息,所以字符消息是夾在按鍵消息之間傳遞給窗口消息處理程序的。例如,如果Caps Lock未打開,而使用者按下再釋放A鍵,則窗口消息處理程序?qū)⒔邮盏饺绫?-10所示的三個消息: 表6-10
如果您按下Shift鍵,再按下A鍵,然后釋放A鍵,再釋放Shift鍵,就會輸入大寫的A,而窗口消息處理程序會接收到五個消息,如表6-11所示: 表6-11
Shift鍵本身不產(chǎn)生字符消息。 如果使用者按住A鍵,以使自動重復(fù)產(chǎn)生一系列的按鍵,那么對每條WM_KEYDOWN消息,都會得到一條字符消息,如表6-12所示: 表6-12
如果某些WM_KEYDOWN消息的重復(fù)計數(shù)大于1,那么相應(yīng)的WM_CHAR消息將具有同樣的重復(fù)計數(shù)。 組合使用Ctrl鍵與字母鍵會產(chǎn)生從0x01(Ctrl-A)到0x1A(Ctrl-Z)的ASCII控制代碼,其中的某些控制代碼也可以由表6-13列出的鍵產(chǎn)生: 表6-13
最右列給出了在ANSI C中定義的控制字符,它們用于描述這些鍵的字符代碼。 我們一般可以這樣處理WM_CARH消息: case WM_CHAR: { } 我們可以在WM_CHAR里面判斷當前是否有指定的鍵被按下: BOOL bIsCtrl = (::GetAsyncKeyState(VK_CONTROL) & 0x8000); (MFC源碼 afxcolordialog.cpp 460行) 或 BOOL bIsCtrl = (::GetKeyState(VK_CONTROL) & 0x8000); 下面我解釋一下鍵盤消息的lParam參數(shù),這個參數(shù)在MSDN上面都可以查到,只是英文,我這里作一些簡單的說明:(以WM_KEYDOWN為例) WPARAM:虛擬鍵值,VT_*等值。 LPARAM:根據(jù)其不同的位數(shù)表示的含義不同可以分以下幾部分: (1) 重復(fù)計數(shù)位(0 - 15 位):表示消息按鍵數(shù)據(jù)。一般情況下為1,當鍵一直按下,窗口過程就會連續(xù)收到W_KEYDOWN消息,但有可能窗口過程來不及處理這些按鍵消息,那么Windows就會把幾個按鍵消息組合成一個,并增加重復(fù)計數(shù)。比如你處理WM_KEYDOWN時Sleep(200),那么得到的這個數(shù)字就可能大于1,一般可以這樣來得到這個計數(shù): DWORD count = (((DWORD)lParam) & 0x0000FFFF); (2) OEM掃描碼(16~23位):OEM掃描碼是鍵盤發(fā)送的碼值,由于此域是設(shè)備相關(guān)的,因而此值往往被忽略。 (3) 擴展鍵標志(24位):擴展鍵標志在有Alt鍵(或Ctrl鍵)按下時為1,否則為0。 (4) 保留位(25~28位):保留位是系統(tǒng)缺省保留的,一般不用。 (5) 關(guān)聯(lián)碼(29位):關(guān)聯(lián)碼用來記錄某鍵與Alt鍵的組合狀態(tài),若按下Alt,當WM_SYSKEYDOWN消息送到某個激活的窗口時,其值為1,否則為0。 (6) 鍵的先前狀態(tài)(位30):鍵的先前狀態(tài)用于記錄先前某鍵的狀態(tài),對于WM_SYSKEYUP消息,其值始終為1。 (7) 轉(zhuǎn)換狀態(tài)(31位):轉(zhuǎn)換狀態(tài)的消息是始終按著某鍵所產(chǎn)生的消息,若某鍵原來是按下的,則其先前狀態(tài)為0。轉(zhuǎn)換狀態(tài)指示鍵被按下還是被松開。當鍵被按下時,對應(yīng)于者WM_SYSKEYDOWN消息,其值始終為0,當鍵被松開時,其轉(zhuǎn)換狀態(tài)為1,對應(yīng)于WM_SYSKEYUP消息,其值始終為1。 5,死字符消息 Windows程序經(jīng)常忽略WM_DEADCHAR和WM_SYSDEADCHAR消息,但您應(yīng)該明確地知道死字符是什么,以及它們工作的方式。 在某些非U.S.英語鍵盤上,有些鍵用于給字母加上音調(diào)。因為它們本身不產(chǎn)生字符,所以稱之為「死鍵」。例如,使用德語鍵盤時,對于U.S.鍵盤上的+/=鍵,德語鍵盤的對應(yīng)位置就是一個死鍵,未按下Shift鍵時它用于標識銳音,按下Shift鍵時則用于標識抑音。 當使用者按下這個死鍵時,窗口消息處理程序接收到一個wParam等于音調(diào)本身的ASCII或者Unicode代碼的WM_DEADCHAR消息。當使用者再按下可以帶有此音調(diào)的字母鍵(例如A鍵)時,窗口消息處理程序會接收到WM_CHAR消息,其中wParam等于帶有音調(diào)的字母「a」的ANSI代碼。 因此,使用者程序不需要處理WM_DEADCHAR消息,原因是WM_CHAR消息已含有程序所需要的所有信息。Windows的做法甚至還設(shè)計了內(nèi)部錯誤處理。如果在死鍵之后跟有不能帶此音調(diào)符號的字母(例如「s」),那么窗口消息處理程序?qū)⒃谝恍薪邮盏絻蓷lWM_CHAR消息-前一個消息的wParam等于音調(diào)符號本身的ASCII代碼(與傳遞到WM_DEADCHAR消息的wParam值相同),第二個消息的wParam等于字母s的ASCII代碼。 當然,要感受這種做法的運作方式,最好的方法就是實際操作。您必須加載使用死鍵的外語鍵盤,例如前面講過的德語鍵盤。您可以這樣設(shè)定:在「控制臺」中選擇「鍵盤」,然后選擇「語系」頁面標簽。然后您需要一個應(yīng)用程序,該程序可以顯示它接收的每一個鍵盤消息的詳細信息。下面的KEYVIEW1就是這樣的程序。 符號常量 十六進制值 指定的鼠標或鍵盤按鍵 VK_LBUTTON 01 鼠標左鍵 VK_RBUTTON 02 鼠標右鍵 VK_CANCEL 03 Control-break 過程 VK_MBUTTON 04 鼠標中鍵 VK_BACK 08 BACKSPACE 鍵 VK_TAB 09 TAB 鍵 VK_CLEAR 0C CLEAR 鍵 VK_RETURN 0D ENTER 鍵 VK_SHIFT 10 SHIFT 鍵 VK_CONTROL 11 CTRL 鍵 VK_MENU 12 ALT 鍵 VK_PAUSE 13 PAUSE 鍵 VK_CAPITAL 14 CAPS LOCK 鍵 VK_ESCAPE 1B ESC 鍵 VK_SPACE 20 SPACEBAR VK_PRIOR 21 PAGE UP 鍵 VK_NEXT 22 PAGE DOWN 鍵 VK_END 23 END 鍵 VK_HOME 24 HOME 鍵 VK_LEFT 25 LEFT ARROW 鍵 VK_UP 26 UP ARROW 鍵 VK_RIGHT 27 RIGHT ARROW 鍵 VK_DOWN 28 DOWN ARROW 鍵 VK_SELECT 29 SELECT 鍵 VK_EXECUTE 2B EXECUTE 鍵 VK_SNAPSHOT 2C PRINT SCREEN鍵(用于Windows 3.0及以后版本) VK_INSERT 2D INS 鍵 VK_DELETE 2E DEL 鍵 VK_HELP 2F HELP 鍵 /////////////////////////////////////////////////// 對于字母鍵和非小鍵盤上的數(shù)字鍵,直接在單引號中加入該鍵就行. 比如:a鍵:'A' 1鍵:'1' ////////////////////////////////////////////// VK_LWIN 5B Left Windows 鍵 (Microsoft自然鍵盤) VK_RWIN 5C Right Windows 鍵 (Microsoft自然鍵盤) VK_APPS 5D Applications 鍵 (Microsoft自然鍵盤) VK_NUMPAD0 60 數(shù)字小鍵盤上的 0 鍵 VK_NUMPAD1 61 數(shù)字小鍵盤上的 1 鍵 VK_NUMPAD2 62 數(shù)字小鍵盤上的 2 鍵 VK_NUMPAD3 63 數(shù)字小鍵盤上的 3 鍵 VK_NUMPAD4 64 數(shù)字小鍵盤上的 4 鍵 VK_NUMPAD5 65 數(shù)字小鍵盤上的 5 鍵 VK_NUMPAD6 66 數(shù)字小鍵盤上的 6 鍵 VK_NUMPAD7 67 數(shù)字小鍵盤上的 7 鍵 VK_NUMPAD8 68 數(shù)字小鍵盤上的 8 鍵 VK_NUMPAD9 69 數(shù)字小鍵盤上的 9 鍵 VK_MULTIPLY 6A Multiply 鍵 VK_ADD 6B Add 鍵 VK_SEPARATOR 6C Separator 鍵 VK_SUBTRACT 6D Subtract 鍵 VK_DECIMAL 6E Decimal 鍵 VK_DIVIDE 6F Divide 鍵 VK_F1 70 F1 鍵 VK_F2 71 F2 鍵 VK_F3 72 F3 鍵 VK_F4 73 F4 鍵 VK_F5 74 F5 鍵 VK_F6 75 F6 鍵 VK_F7 76 F7 鍵 VK_F8 77 F8 鍵 VK_F9 78 F9 鍵 VK_F10 79 F10 鍵 VK_F11 7A F11 鍵 VK_F12 7B F12 鍵 VK_F13 7C F13 鍵 VK_F14 7D F14 鍵 VK_F15 7E F15 鍵 VK_F16 7F F16 鍵 VK_F17 80H F17 鍵 VK_F18 81H F18 鍵 VK_F19 82H F19 鍵 VK_F20 83H F20 鍵 VK_F21 84H F21 鍵 VK_F22 85H F22 鍵 VK_F23 86H F23 鍵 VK_F24 87H F24 鍵 VK_NUMLOCK 90 NUM LOCK 鍵 VK_SCROLL 91 SCROLL LOCK 鍵 VK_ATTN F6 Attn 鍵 VK_CRSEL F7 CrSel 鍵 VK_EXSEL F8 ExSel 鍵 VK_EREOF F9 Erase EOF 鍵 VK_PLAY FA Play 鍵 VK_ZOOM FB Zoom 鍵 VK_OEM_CLEAR FE Clear 鍵 舉例: (一)響應(yīng)單獨的按鍵: 先添加PreTranslateMessage()(響應(yīng)WM_CHAR)也是同樣的效果,因為本例只捕捉鍵盤) BOOL CMydilog::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message == WM_KEYDOWN) { if(pMsg->wParam=='M')//直接用上面的虛碼代替就可以響應(yīng)所指鍵 MessageBox("hello");//如果按下M鍵彈出消息.比如想當按下小鍵盤1時 //彈出就用VK_NUMPAD1代替'M' } return CDialog::PreTranslateMessage(pMsg); } (二)組合鍵的用法:(本例響應(yīng)Ctrl+X鍵) BOOL CMydilog::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message == WM_KEYDOWN) { switch (pMsg->wParam) { case VK_ESCAPE: SetFocus (); return TRUE; case 'X': if(::GetKeyState(VK_CONTROL) < 0)//如果是Shift+X這里就 //改成VK_SHIFT MessageBox("hello"); return TRUE; } } return CDialog::PreTranslateMessage(pMsg); } SetCapture 函數(shù)功能:該函數(shù)在屬于當前線程的指定窗口里設(shè)置鼠標捕獲。一旦窗口捕獲了鼠標,所有鼠標輸入都針對該窗口,無論光標是否在窗口的邊界內(nèi)。同一時刻只能有一個窗口捕獲鼠標。如果鼠標光標在另一個線程創(chuàng)建的窗口上,只有當鼠標鍵按下時系統(tǒng)才將鼠標輸入指向指定的窗口。 LRESULT CALLBACK TestProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN: //左鍵按下
{
SetCapture(hwnd);
}
break;
case WM_MOUSEMOVE: //鼠標按下移動
{
if(GetCapture() == hwnd)
{
//鼠標按下并移動...
}
}
break;
case WM_LBUTTONUP: //左鍵彈起
{
if(GetCapture() == hwnd)
ReleaseCapture(); //當前線程中的窗口釋放鼠標捕獲,并恢復(fù)通常的鼠標輸入處理。
}
break;
return 0;
}
} |
|