Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 616
user1722361 Avatar asked Apr 09 '14 23:04

user1722361


Video Answer


3 Answers

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.

like image 182
Farhad Reza Avatar answered Oct 20 '22 21:10

Farhad Reza


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.

like image 41
Ivan Danilov Avatar answered Oct 20 '22 22:10

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.

    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.

like image 36
Hung Minh Tran Avatar answered Oct 20 '22 20:10

Hung Minh Tran