带有WH_keyboard_LL和keybd_event(windows)的全局键盘挂钩

Global keyboard hook with WH_KEYBOARD_LL and keybd_event (windows)

本文关键字:全局 windows 键盘 event keyboard WH LL 带有 keybd      更新时间:2023-10-16

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

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

第一个问题是程序可以正确地阻止"A",但如果我在键盘上点击"A"一次,回调函数中的printf和keybd_event将被执行两次。因此,如果我打开一个txt文件,我点击一次"a",就会有两个"B"的输入。为什么?

第二个问题是,为什么使用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 an");
            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);
}

非常感谢!

由于WM_KEYDOWNWM_KEYUP,回调函数执行了两次。当你按下键盘上的一个键时,windows会用WM_KEYDOWN消息调用回调函数;当你松开键时,windows会用WM_KEYUP消息调用回调功能。这就是回调函数执行两次的原因。

您应该将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 an");
            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;
}

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

第一个很容易。你可以得到一个键向下,另一个键向上。:)

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

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

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

    每当一个新的键盘输入事件即将发布到线程输入队列中时,系统就会调用此函数。此消息是特殊情况。您还需要一个DLL来为其他消息制作一个真正的全局挂钩。