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

use*_*361 7 c++ hook keyboard-hook keyboard-events

我正在尝试编写一个简单的全局键盘钩子程序来重定向一些键.例如,当程序执行时,我按键盘上的'a',程序可以禁用它并模拟'b'点击.我不需要图形ui,只需一个控制台即可(保持运行)

我的计划是使用全局钩子来捕获键输入,然后使用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 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);
}
Run Code Online (Sandbox Code Playgroud)

非常感谢!

Iva*_*lov 7

第一个很容易.你得到一个用于按键,另一个用于按键.:)

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

DLL是特定于线程的钩子所必需的,因为它们是在另一个进程的上下文中调用的.为此,您的DLL应该注入该进程.事实并非如此.

  • 这都是错误的。所有全局挂钩都必须在 DLL 中,除非它是低级挂钩。缺乏这方面的文档。这里的语言是 C++,但 C# 可以使用低级钩子,因为它们不需要 DLL。 (2认同)

For*_*eza 6

由于WM_KEYDOWN和,你的回调函数执行两次WM_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 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;
}
Run Code Online (Sandbox Code Playgroud)

关于你的第二个问题,我想你已经从@Ivan Danilov回答了.