如何使用Qt在Windows系统中全局禁用按键?

4

是否可以禁用或屏蔽一些键,例如 Print Screen 键?

我的事件过滤器:

bool EventFilter::eventFilter(QObject *object, QEvent *event)
{
    qDebug() << "object:" << object << "type:" << event->type();
    return false;
}

我尝试使用qApp

ui->setupUi(this);

EventFilter *evt = new EventFilter;

qApp->installEventFilter(evt);

仅返回应用程序小部件的事件:
object: QWidgetWindow(0x175bae50, name = "QWidgetClassWindow") type: QEvent::Type(PlatformSurface)
object: QWidget(0x175b02c0) type: QEvent::Type(PlatformSurface)
object: QWidget(0x175b02c0) type: QEvent::Type(WinIdChange)
object: QWidget(0x175b02c0) type: QEvent::Type(Create)
...

同时:

ui->setupUi(this);

EventFilter *evt = new EventFilter;

QDesktopWidget *c = new QDesktopWidget;
c->installEventFilter(evt);

但是仅返回2个事件:
object: QDesktopWidget(0x174e0260, name = "desktop") type: QEvent::Type(PolishRequest)
object: QDesktopWidget(0x174e0260, name = "desktop") type: QEvent::Type(Polish)

无法拦截和/或阻止事件吗?谢谢。

如果你想全局禁用按键,那么Qt将几乎没有任何帮助。你需要深入了解键盘钩子和相关的低级别内容。 - user7860670
此外,最新的Windows对应用程序的限制相当严格。如果您想要更改类似于屏幕截图键之类的东西,您可能需要向用户请求管理员权限。不确定,也许一个应用程序可以禁用它,但我猜测不行。 - hyde
你为什么想做那个? - Cheers and hth. - Alf
我认为你可以做到,但需要重新启动操作系统。如果这对你来说足够好,我会将其作为答案提供给你。 - Farhad
@Alf 为了防止应用程序屏幕被打印,我知道我无法完全阻止它,但我想让它变得困难一些。当应用程序窗口处于焦点状态时,我可以拦截“打印”键,但如果我单击“任务栏”并按下“打印”键,则不会发生“打印”事件,因此我希望在应用程序打开时全局阻止它。 - Guilherme Nascimento
1个回答

4
你可以安装 低级键盘钩子 来拦截并阻止所有 Print Screen 按键。以下示例已在我的 Windows 7 设备上测试过。
这是一个简单的示例:

ScreenShot

#include <QtWidgets>
#include <windows.h>
//link against user32.lib when compiling in MSVC
#ifdef _MSC_VER
#pragma comment(lib, "User32.lib")
#endif

class GlobalPrintScreenBlocker {
public:
    GlobalPrintScreenBlocker():mHKeyboardHook(NULL) {}
    //to avoid leaking the hook procedure handle
    ~GlobalPrintScreenBlocker(){ unblock(); }
    //hook callback function (called on every system-wide key press)
    static LRESULT CALLBACK LowLevelKeyboardProc(int nCode,
                                                 WPARAM wParam, LPARAM lParam) {
        if(nCode == HC_ACTION) {
            PKBDLLHOOKSTRUCT p = reinterpret_cast<PKBDLLHOOKSTRUCT>(lParam);
            if(p->vkCode == VK_SNAPSHOT) return 1; //block print-screen key
        }
        //this is not a message we are interested in
        return CallNextHookEx(NULL, //ignored paramater
                              nCode,
                              wParam,
                              lParam);
    }
    void block(){
        mHKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, //low-level keyboard hool
                                          &LowLevelKeyboardProc, //callback
                                          GetModuleHandle(NULL), 
                                          0);
    }
    void unblock(){
        if(mHKeyboardHook) UnhookWindowsHookEx(mHKeyboardHook);
        mHKeyboardHook = NULL;
    }

private:
    HHOOK mHKeyboardHook;
};

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);

    GlobalPrintScreenBlocker blocker;
    QPushButton button("Disable ScreenShot");
    button.setCheckable(true);
    QObject::connect(&button, &QPushButton::toggled, [&](bool isChecked){
        if(isChecked)
            blocker.block();
        else
            blocker.unblock();
    });
    button.show();

    return a.exec();
}

挂钩程序在安装它的线程中被调用。这样做的好处是,您不必担心回调函数中的线程安全问题,并且32位和64位进程之间没有区别(所有进程最终都会通过向安装挂钩的线程的事件循环发送消息来间接调用挂钩函数)。但是,这也有一个缺点,如果您的线程正在忙于执行其他任务,则可能无法调用回调挂钩:

挂钩过程应该在低级挂钩超时值指定的数据输入时间内处理消息...该值以毫秒为单位。如果挂钩过程超时,则系统将消息传递给下一个挂钩。但是,在Windows 7及更高版本上,挂钩会在不调用的情况下被静默删除。应用程序无法知道挂钩是否已删除。

您可以通过为安装/执行挂钩分配单独的线程来解决此限制。此外,请注意,您的挂钩回调函数应尽可能简洁,以便能够在时间限制内完成。

P.S.:我不得不使用剪切工具创建上面的屏幕截图...


Mike,你的示例在MSVC和Mingw中都很好用,谢谢!我会给你发送赏金的。 :D (23小时后) - Guilherme Nascimento
还有一种方法可以绕过这个防御措施。如果有人设置了另一个键盘钩子来获取屏幕截图,它可能会在你的钩子之前被调用。更简单的方法是使用任何屏幕捕获工具而不使用打印屏幕键。我认为你无法完全禁止屏幕截图。 - Vasily Ryabov
@VasilyRyabov,你说的没错。我不认为完全禁止这种情况是可能的。毕竟,没有什么可以阻止用户将屏幕输出定向到任何其他设备上,在那里他们可以拍照,这将使您花费在实现禁用系统级别屏幕捕获的视频驱动程序的时间变得毫无意义。您采取的任何方法来实现这种功能都应该只是旨在使用户难以捕获屏幕,安装一个用于禁用快照按钮的钩子就是这样一种方法。. . - Mike
1
虽然我完全承认可以使用“截图工具”完全绕过这种方法(如我的答案中所提到的)。但我想专注于问题范围,即在Windows全局范围内禁用键。 - Mike
1
没问题。答案很好(+我的点赞)。低级全局钩子在其他方面也非常有用。例如,它可以帮助记录所有用户操作以生成GUI自动化脚本,尽管除了钩子之外还有许多陷阱。 - Vasily Ryabov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接