哎呀,所以我设法解决了它,尽管我对解决方案并不完全满意。至少没有什么神秘的,它的工作原理:)。
我没有收到快捷键 Ctrl+Alt+F11 和 Ctrl+Alt+F12 的原因
它们被注册为全局热键。我设法使用 stackoverflow 成员 moodforaday 的ActiveHotkeys程序找到了这一点(非常感谢!)。显然没有记录的方法可以找出哪个程序注册了特定的热键(并且它在我的系统上没有做任何事情)。请参阅moodforaday 关于该问题的主题。
解决方案
上述线程中的一个答案使我想到了另一个问题。Efotinis 的回答对我来说绝对是完美的。我没有设置低级键盘挂钩的经验,但这并不像听起来那么困难。为了将来的缘故,这是我在 Qt 应用程序中的做法:
在我的 mainwindow.h 中:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// ... code
private:
void tryLogin();
friend LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam);
};
LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam);
在我的 mainwindow.cpp 中:
// setting up the hook in the constructor
SetWindowsHookEx(WH_KEYBOARD_LL,
LowLevelKeyboardProc,
NULL,
0);
钩子代码(主要来自 efotinis 的回答):
LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam)
{
KBDLLHOOKSTRUCT* kllhs = reinterpret_cast<KBDLLHOOKSTRUCT*>(lparam);
if (code == HC_ACTION)
{
if (wparam == WM_KEYDOWN && kllhs->vkCode == VK_F12 &&
(GetAsyncKeyState(VK_MENU) < 0 && GetAsyncKeyState(VK_CONTROL) < 0))
{
MainWindow* w = dynamic_cast<MainWindow*>(qApp->activeWindow());
if (NULL != w)
{
w->tryLogin(); // this should not be blocking!
return 1;
}
}
}
return CallNextHookEx(0, code, wparam, lparam);
}
如您所见,我们从全局 QApplication 对象中获取指向应用程序窗口的指针。我们使用 dynamic_cast ,所以在活动窗口碰巧不是 MainWindow 实例时,我们会得到一个 NULL 指针。
如果您想知道为什么要检查 GetAsyncKeyState 调用是否为 < 0,这是因为如果键按下,此函数会返回并设置 MSB。并且当设置 MSB 时,SHORT 数为负数(在 x86/x64 和兼容平台上)。如果 Windows 被移植到有符号整数表示不同的平台,则此代码可能会中断。绝对正确的方法是创建一个 16 位掩码并使用它来检查 MSB,但我懒得这样做。:)
需要注意的一点是,当您从钩子调用函数时,Qt 事件循环才刚刚开始处理。这意味着直到你没有从你的函数返回,它会阻塞 UI(冻结它几秒钟)。如果您想像我一样显示一个对话框,而不是exec()
调用raise, activateWindow
and show
,同时将对话框的窗口模式设置为模式(可能在其构造函数中)。
如果需要,您可以使用UnHookWindowsHookEx取消注册该钩子(当卸载包含该钩子的模块时会发生这种情况)。为此,请保存 SetWindowsHookEx 调用的返回值。