C++ 带有 WH_KEYBOARD_LL 和 keybd_event 的全局键盘钩子(windows)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22975916/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 00:17:37  来源:igfitidea点击:

Global keyboard hook with WH_KEYBOARD_LL and keybd_event (windows)

c++hookkeyboard-eventskeyboard-hook

提问by user1722361

I am trying to write a simple global keyboard hook program to redirect some keys. For example, when the program is executed, I press 'a' on the keyboard, the program can disable it and simulate a 'b' click. I do not need a graphic ui, just a console is enough (keep it running)

我正在尝试编写一个简单的全局键盘挂钩程序来重定向某些键。例如,当程序执行时,我按键盘上的“a”,程序可以禁用它并模拟“b”点击。我不需要图形用户界面,一个控制台就足够了(保持运行)

My plan is to use global hook to catch the key input, and then use keybd_event to simulate the keyboard. But I have some problems.

我的计划是使用全局钩子来捕捉按键输入,然后使用keybd_event来模拟键盘。但我有一些问题。

The first problem is that the program can correctly block 'A' but if I hit 'A' on the keyboard once, the printf in the callback function is executed twice, as well as the keybd_event. So if i open a txt file, i click 'A' once, there are two 'B's input. why is that?

第一个问题是程序可以正确地阻止'A',但是如果我在键盘上按了一次'A',则回调函数中的printf 以及keybd_event 会执行两次。所以如果我打开一个txt文件,我点击'A'一次,有两个'B'的输入。这是为什么?

The second question is that why the hook using of WH_KEYBOARD_LL can work on other process without a dll? I thought that we had to use a dll to make a global hook until I wrote this example...

第二个问题是为什么使用WH_KEYBOARD_LL的钩子可以在没有dll的其他进程上工作?我以为我们必须使用 dll 来制作全局钩子,直到我写了这个例子......

#include "stdafx.h"
#include <Windows.h>
#define _WIN32_WINNT 0x050

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    BOOL fEatKeystroke = FALSE;

    if (nCode == HC_ACTION)
    {
        switch (wParam)
        {
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
        case WM_KEYUP:
        case WM_SYSKEYUP:
            PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
            if (fEatKeystroke = (p->vkCode == 0x41)) {     //redirect a to b
            printf("Hello a\n");
            keybd_event('B', 0, 0, 0);
            keybd_event('B', 0, KEYEVENTF_KEYUP, 0);
            break;
            }
            break;
        }
    }
    return(fEatKeystroke ? 1 : CallNextHookEx(NULL, nCode, wParam, lParam));
}

int main()
{
    // Install the low-level keyboard & mouse hooks
    HHOOK hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, 0, 0);

    // Keep this app running until we're told to stop
    MSG msg;
    while (!GetMessage(&msg, NULL, NULL, NULL)) {    //this while loop keeps the hook
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(hhkLowLevelKybd);

    return(0);
}

Many thanks!

非常感谢!

回答by Ivan Danilov

First one is easy. You get one for key down and another for key up. :)

第一个很容易。你得到一个键按下和另一个键。:)

As for the why it can work without a DLL - that's because it is a global hook. Unlike thread-specific ones it is executed in your own process, not in the process where keyboard event happened. It is done via message sending to the thread which has installed the hook - that's precisely why you need message loop here. Without it your hook can't be ran as there would be no one to listen for incoming messages.

至于为什么它可以在没有 DLL 的情况下工作 - 那是因为它是一个全局钩子。与线程特定的不同,它在您自己的进程中执行,而不是在发生键盘事件的进程中执行。它是通过向安装了钩子的线程发送消息来完成的——这正是您在这里需要消息循环的原因。没有它,您的钩子就无法运行,因为没有人可以监听传入的消息。

The DLL is required for thread-specific hooks because they're called in the context of another process. For this to work, your DLL should be injected into that process. It is just not the case here.

特定于线程的挂钩需要 DLL,因为它们是在另一个进程的上下文中调用的。为此,您的 DLL 应该被注入到该进程中。情况并非如此。

回答by Forhad Reza

Your callback function execute twice because of WM_KEYDOWNand WM_KEYUP. When you down a key of your keyboard, windows calls the callback function with WM_KEYDOWNmessage and when you release the key, windows calls the callback function with WM_KEYUPmessage. That's why your callback function execute twice.

由于WM_KEYDOWN和 ,您的回调函数执行两次WM_KEYUP。当您按下键盘上的某个键时,windows 会通过WM_KEYDOWN消息调用回调函数,当您松开按键时,windows 会通过WM_KEYUP消息调用回调函数。这就是您的回调函数执行两次的原因。

You should change your switch statement to this:

您应该将 switch 语句更改为:

switch (wParam)
{
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
    case WM_KEYUP:
    case WM_SYSKEYUP:
        PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
        if (fEatKeystroke = (p->vkCode == 0x41))  //redirect a to b
        {     
            printf("Hello a\n");

            if ( (wParam == WM_KEYDOWN) || (wParam == WM_SYSKEYDOWN) ) // Keydown
            {
                keybd_event('B', 0, 0, 0);
            }
            else if ( (wParam == WM_KEYUP) || (wParam == WM_SYSKEYUP) ) // Keyup
            {
                keybd_event('B', 0, KEYEVENTF_KEYUP, 0);
            }
            break;
        }
        break;
}

About your second question, I think you have already got from @Ivan Danilov answer.

关于你的第二个问题,我想你已经从@Ivan Danilov 那里得到了答案。

回答by Hung Minh Tran

  1. I have run your code but nothing happend? What wrong with me?
  2. Base on msdnthat WH_KEYBOARD_LL message is "Global only" It mean more than that.

    The system calls this function .every time a new keyboard input event is about to be posted into a thread input queue.This message is special case. You also need an DLL to make a real global hook for other message.

  1. 我已经运行了你的代码,但什么也没发生?我怎么了?
  2. 基于msdn,WH_KEYBOARD_LL 消息是“仅限全局”这意味着更多。

    每次新的键盘输入事件即将发布到线程输入队列时,系统都会调用此函数。此消息是特例。您还需要一个 DLL 来为其他消息创建一个真正的全局钩子。