簡體   English   中英

使用WH_KEYBOARD_LL和keybd_event(windows)的全局鍵盤鈎子

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

我正在嘗試編寫一個簡單的全局鍵盤鈎子程序來重定向一些鍵。 例如,當程序執行時,我按鍵盤上的'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);
}

非常感謝!

第一個很容易。 你得到一個用於按鍵,另一個用於按鍵。 :)

至於為什么它可以在沒有DLL的情況下工作 - 這是因為它是一個全局鈎子。 與特定於線程的線程不同,它在您自己的進程中執行,而不是在發生鍵盤事件的進程中執行。 它是通過消息發送到安裝了鈎子的線程完成的 - 這正是你需要消息循環的原因。 如果沒有它,你的鈎子就無法運行,因為沒有人可以收聽傳入的消息。

DLL是特定於線程的鈎子所必需的,因為它們是在另一個進程的上下文中調用的。 為此,您的DLL應該注入該進程。 事實並非如此。

由於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 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;
}

關於你的第二個問題,我想你已經從@Ivan Danilov回答了。

  1. 我運行了你的代碼,但沒有發生任何事情? 我怎么了?
  2. 基於msdn ,WH_KEYBOARD_LL消息是“僅限全局”這意味着更多。

    系統調用此函數。每次新的鍵盤輸入事件即將發布到線程輸入隊列中。 此消息是特例。 您還需要一個DLL來為其他消息創建一個真正的全局鈎子。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM