简体   繁体   English

使用WH_KEYBOARD_LL和keybd_event(windows)的全局键盘钩子

[英]Global keyboard hook with WH_KEYBOARD_LL and keybd_event (windows)

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. 例如,当程序执行时,我按键盘上的'a',程序可以禁用它并模拟'b'点击。 I do not need a graphic ui, just a console is enough (keep it running) 我不需要图形ui,只需一个控制台即可(保持运行)

My plan is to use global hook to catch the key input, and then use keybd_event to simulate the keyboard. 我的计划是使用全局钩子来捕获键输入,然后使用keybd_event来模拟键盘。 But I have some problems. 但我有一些问题。

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. 第一个问题是程序可以正确地阻止'A',但是如果我在键盘上点击'A'一次,回调函数中的printf会被执行两次,以及keybd_event。 So if i open a txt file, i click 'A' once, there are two 'B's input. 所以,如果我打开一个txt文件,我点击“A”一次,有两个'B'输入。 why is that? 这是为什么?

The second question is that why the hook using of WH_KEYBOARD_LL can work on other process without a dll? 第二个问题是为什么使用WH_KEYBOARD_LL的钩子可以在没有dll的情况下在其他进程上工作? I thought that we had to use a dll to make a global hook until I wrote this example... 在我写这个例子之前,我认为我们必须使用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! 非常感谢!

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. 至于为什么它可以在没有DLL的情况下工作 - 这是因为它是一个全局钩子。 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. 如果没有它,你的钩子就无法运行,因为没有人可以收听传入的消息。

The DLL is required for thread-specific hooks because they're called in the context of another process. DLL是特定于线程的钩子所必需的,因为它们是在另一个进程的上下文中调用的。 For this to work, your DLL should be injected into that process. 为此,您的DLL应该注入该进程。 It is just not the case here. 事实并非如此。

Your callback function execute twice because of WM_KEYDOWN and WM_KEYUP . 由于WM_KEYDOWNWM_KEYUP您的回调函数执行两次。 When you down a key of your keyboard, windows calls the callback function with WM_KEYDOWN message and when you release the key, windows calls the callback function with WM_KEYUP message. 当您按下键盘的键时,Windows会使用WM_KEYDOWN消息调用回调函数,当您释放该键时,Windows会使用WM_KEYUP消息调用回调函数。 That's why your callback function execute twice. 这就是你的回调函数执行两次的原因。

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回答了。

  1. I have run your code but nothing happend? 我运行了你的代码,但没有发生任何事情? What wrong with me? 我怎么了?
  2. Base on msdn that WH_KEYBOARD_LL message is "Global only" It mean more than that. 基于msdn ,WH_KEYBOARD_LL消息是“仅限全局”这意味着更多。

    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. 您还需要一个DLL来为其他消息创建一个真正的全局钩子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM