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键,回车键,空格键什么的。
(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