1.首先建立一個基于對話框的MFC程式,我這裡建立的項目名為“Testlili”。
2.然後“項目”--“類向導”--“消息”,選擇類名為“CTestliliDlg”,找到WM_HOTKEY消息添加,添加後“現有處理程式”的框框下就會多出一個“OnHotKey”。
3.在“BOOL ×××::OnInitDialog()”函數中加入注冊熱鍵的代碼,比如注冊一個Ctrl+X的熱鍵:
ATOM HotKeyId=GlobalAddAtom(L"Chun_ge")- 0xC000; //取得熱鍵唯一辨別符
//GetSafeHwnd();取得本程式的視窗句柄
RegisterHotKey(GetSafeHwnd(),HotKeyId,MOD_ALT①,'X'②);
RegisterHotKey(GetSafeHwnd(),HotKeyId,NULL,VK_F10);
當然還可以像上面這樣把①中的參數NULL掉,②中的參數用虛拟鍵代碼比如此處的按鍵是F10。這樣設定後實作的就是單熱鍵。順帶一提RegisterHotKey()函數是系統API這樣設定後的熱鍵是全局熱鍵,也就是當我們的窗戶最小化或者處于未激活狀态下按下設定的熱鍵也會執行回調函數中的代碼,而不是像下面的方法設定的那樣是局部熱鍵,一旦我們的程式處于未激活狀态按下熱鍵就不會有反應。
①:參數3可以疊加,比如MOD_CONTROL|MOD_ALT,加上②處的參數4組合起來就是熱鍵Ctrl+Alt+X
注冊熱鍵的函數:
BOOL WINAPI RegisterHotKey(
__in_opt HWND hWnd,
__in int id,
__in UINT fsModifiers,
__in UINT vk
);
參數1:為視窗的句柄
參數2:定義熱鍵的辨別符。調用線程中的其他熱鍵,不能使用同樣的辨別符。應用程式必須定義一個0X0000-0xBFFF範圍的值。一個共享的動态連結庫(DLL)必須定義一個範圍為0xC000-0xFFFF的值(GlobalAddAtom函數傳回該範圍)。為了避免與其他動态連結庫定義的熱鍵沖突,一個DLL必須使用GlobalAddAtom函數獲得熱鍵的辨別符。
參數3:定義為了産生WM_HOTKEY消息而必須與由nVirtKey參數定義的鍵一起按下的鍵。
該參數可以是如下值的組合:
鍵 值 含意
MOD_ALT 0x0001 按下的可以是任一(左邊或者右邊的)Alt鍵。
MOD_SHIFT 0x0004 按下的可以是任一(左邊或者右邊的)Shift鍵。
MOD_WIN 0x0008 按下的可以是任一(左邊或者右邊的)Windows徽标鍵。
MOD_NOREPEAT 0x4000 更改熱鍵行為,以便鍵盤自動重複不會産生多個熱鍵通知。
MOD_CONTROL 0x0002 按下的可以是任意一個Ctrl鍵(左邊或者右邊的)。
參數4:定義熱鍵的虛拟鍵碼。
4.在OnHotKey函數中添加觸發熱鍵後要執行的代碼:
void CEternityDlg::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2)
{
// TODO: 在此添加消息處理程式代碼和/或調用預設值
if (nHotKeyId==HotKeyId) //這個If不加也行
{
MessageBox(L"春哥純爺們");
}
CDialogEx::OnHotKey(nHotKeyId, nKey1, nKey2);
}
5.如果有2個以上熱鍵:
BOOL ×××::OnInitDialog()
{
HotKeyId=GlobalAddAtom(L"Chun_ge")- 0xC000; //取得熱鍵唯一辨別符
HotKeyId2=GlobalAddAtom(L"Zeng_ge")- 0xC000;
RegisterHotKey(GetSafeHwnd(),HotKeyId,NULL,VK_F10); //熱鍵F10
RegisterHotKey(GetSafeHwnd(),HotKeyId2,NULL,VK_F11); //再注冊一個F11
}
void CNeverDlg::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2)
{
// TODO: 在此添加消息處理程式代碼和/或調用預設值
if (nHotKeyId==HotKeyId) //如果有1個以上熱鍵可以用這個方法來選擇
{ //F10熱鍵
MessageBox(L"春哥純爺們");
}
else if (nHotKeyId==HotKeyId2)
{ //F11熱鍵
MessageBox(L"鐵血真漢子");
}
CDialogEx::OnHotKey(nHotKeyId, nKey1, nKey2);
}
然後編譯運作,當按下Alt+X組合鍵時就會彈出一個消息框。當然為了嚴謹還可以用類向導添加OnDestroy()消息:
void CGhostDlg::OnDestroy()
{
CDialogEx::OnDestroy();
// TODO: 在此處添加消息處理程式代碼
UnregisterHotKey(GetSafeHwnd(),HotKeyId);
}
這樣在MFC程式退出時就會登出熱鍵
關于MFC對話框程式不能響應OnKeyDown和OnChar函數的一些說明
(1)現象
在MFC的對話框中,映射了WM_CHAR和WM_KEYDOWN消息響應函數後,還是不能響應OnKeyDown和OnChar。需要說明的是WM_CHAR消息響應的是鍵盤上的數字鍵和A~Z的字母鍵,WM_KEYDOWN消息響應的是除了數字鍵和字母鍵以外的那些鍵盤按鍵比如Esc鍵,Enter鍵,空格鍵什麼的。
(2)原因
因為MFC在進行設計的時候,這兩個消息被對話框上的控件截獲了,不能到達這兩個消息響應函數,對于OnKeyDown來說,隻要把對話框上的控件都删除了,就可以接收到WM_KEYDOWN消息,但是還是接收不到WM_CHAR消息。
(3)解決
方法1:重載PreTranslateMessage這個虛函數;在裡面加上SendMessage(pMsg->message,pMsg->wParam,pMsg->lParam);這一句後,對話框的OnKeyDown和OnChar函數就生效了。
方法2:在PreTranslateMessage虛函數裡直接處理WM_KEYDOWN或WM_CHAR。
eg:對話框屏蔽esc鍵
BOOL Cmfc_testDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加專用代碼和/或調用基類
if(pMsg->message == WM_KEYDOWN)
{
if(pMsg->wParam == VK_ESCAPE)
return TRUE;
}
return CDialogEx::PreTranslateMessage(pMsg);
}
(4)執行順序
三個消息的執行順序為WM_KEYDOWN、WM_CHAR、WM_KEYUP
(5)關于PreTranslateMessage()函數
PreTranslateMessage是消息在送給TranslateMessage函數之前被調用的,絕大多數視窗的消息都要通過這裡,比較常用,當你需要在 MFC之前處理某些消息時,常常要在這裡添加代碼。通過重載這個函數,我們可以改變MFC的消息控制流程,甚至可以作一個全新的控制流出來。
前面說了絕大多數視窗的消息都可以通過這個虛函數進行控制,這個絕大數其實是隻有穿過消息隊列的消息才受PreTranslateMessage ()影響,采用SendMessage()或其他類似的方式向視窗直接發送的而不經過消息隊列的消息根本不會理睬PreTranslateMessage()的存在,故切記SendMessage 發送的消息是不能用PreTranslate來截取的,應該重寫WindowProc視窗過程來響應消息!
(6)SendMessage()和PostMessage()
SendMessage函數發送的消息會直接發送到視窗過程而不經過消息隊列,且直到消息處理完成後,SendMessage才傳回。函數傳回值指定 消息處理的結果,依賴于所發送的消息。
PostMessage函數将一個消息寄送到指定視窗的消息隊列裡,不等待線程處理消息就立刻傳回。函數執行成功傳回TRUE,否則傳回 FALSE。
LRESULT WINAPI SendMessage(
HWND hWnd, //視窗句柄
UINT Msg, //消息值
WPARAM wParam,//消息附加資訊
LPARAM lParam //消息附加資訊
);
BOOL WINAPI PostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
(6)對話框特殊按鍵消息的處理
預設情況下對話框響應Enter鍵的處理是調用基類的OnOK()函數,Esc鍵會調用基類的OnCancel()函數,這兩個函數都是虛函數,故要 對Enter或Esc鍵做相應處理的話可以重寫這兩個函數。
單擊對話框标題欄上的關閉按鈕時會先後産生WM_CLOSE消息,故需要對标題欄關閉按鈕按下做處理的時候可以在WM_CLOSE消息的消息響應函數中進行。
鍵盤消息介紹
系統消息:
ALT,F1,——F24等,是由系統内部處理的,程式本身就存在,比如F1是幫助鍵。
WM_SYSKEYDOWN
WM_SYSKEYUP
WM_SYSCHAR
非系統消息:
是由我們自己加上去的,
WM_KEYDOWN
WM_KEYUP
WM_CHAR
虛拟鍵代碼
符号形式 十六進制形式 說明
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鍵
VK_LWIN 5B Left Windows 鍵 (Microsoft自然鍵盤)
VK_RWIN 5C Right Windows 鍵 (Microsoft自然鍵盤)
VK_APPS 5D Applications 鍵 (Microsoft自然鍵盤)
VK_NUMPAD0 60 數字小鍵盤上的 0 鍵
VK_NUMPAD1 61 數字小鍵盤上的 1 鍵
VK_NUMPAD2 62 數字小鍵盤上的 2 鍵
VK_NUMPAD3 63 數字小鍵盤上的 3 鍵
VK_NUMPAD4 64 數字小鍵盤上的 4 鍵
VK_NUMPAD5 65 數字小鍵盤上的 5 鍵
VK_NUMPAD6 66 數字小鍵盤上的 6 鍵
VK_NUMPAD7 67 數字小鍵盤上的 7 鍵
VK_NUMPAD8 68 數字小鍵盤上的 8 鍵
VK_NUMPAD9 69 數字小鍵盤上的 9 鍵
VK_MULTIPLY 6A Multiply 鍵
VK_ADD 6B Add鍵
VK_SEPARATOR 6C Separator 鍵
VK_SUBTRACT 6D Subtract 鍵
VK_DECIMAL 6E Decimal 鍵
VK_DIVIDE 6F 數字小鍵盤 / 鍵
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 7 E 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 鍵
如何設定單鍵快捷鍵(熱鍵)
下面為這個按鈕添加快捷鍵ctrl+o。左下角切換到“資源視圖”,然後項目名上右鍵>添加>資源

其中IDR_ACCELERATOR1 為加速鍵資源ID,在它上面右鍵>屬性,打開“快捷鍵節點”視窗,可以在這個視窗中修改這個ID,這個ID在後面的代碼中會用到
此時視窗中間是快捷鍵編輯視窗,點選其中一行則右側顯示這一行響應的編輯器,注意右側視窗的标題
切換到“類視圖”,在CTestDlg上右鍵>添加>添加變量
CTestDlg的構造函數中添加加速鍵初始化代碼,注意LoadAccelerators()參數中的IDR_ACCELERATOR1要和之前提到的加速鍵資源ID要一樣
CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CTestDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
//加速鍵初始化
hAccKey=LoadAccelerators(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_ACCELERATOR1));
}
為CTestDlg類添加PreTranslateMessage虛函數,項目>類向導
BOOL CTestDlg::PreTranslateMessage(MSG* pMsg)
{
if(TranslateAccelerator(m_hWnd,hAccKey,pMsg)){
return true;
}
return CDialogEx::PreTranslateMessage(pMsg);
}
Ctrl+F5,程式運作起來之後點選Ctrl+o就會彈出對話框,和點選“打開”效果一樣。
下面通過響應鍵盤消息的方式讓這個程式對按下字母a也響應“打開”事件。
項目>類向導
void CTestDlg::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if('a' == nChar){
MessageBox(L"春哥純爺們");
}
CDialogEx::OnChar(nChar, nRepCnt, nFlags);
}
但是這樣是不能響應按鍵消息的,因為對話框程式中鍵盤消息會被攔截。還需要在剛才的PreTranslateMessage(MSG*pMsg)函數中用SendMessage重新發送鍵盤消息。修改之後的PreTranslateMessage(MSG*pMsg)函數如下:
BOOL CkeysDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加專用代碼和/或調用基類
SendMessage(pMsg->message,pMsg->wParam,pMsg->lParam);
return 0;
//return CDialogEx::PreTranslateMessage(pMsg);
}
編譯後啟動,我們按下a鍵就會彈出:
參考文章:http://blog.csdn.net/xing_yufei/article/details/8573774
參考文章:http://blog.csdn.net/whereyougo/article/details/38407339
示例工程: http://download.csdn.net/download/l198738655/9986530
單熱鍵示例工程:http://download.csdn.net/download/l198738655/10265598