简体   繁体   中英

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. I do not need a graphic ui, just a console is enough (keep it running)

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.

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?

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...

#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. 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. For this to work, your DLL should be injected into that process. It is just not the case here.

Your callback function execute twice because of WM_KEYDOWN and WM_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. That's why your callback function execute twice.

You should change your switch statement to this:

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.

  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.

    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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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