web-dev-qa-db-ja.com

WH_KEYBOARD_LLおよびkeybd_eventを使用したグローバルキーボードフック(Windows)

いくつかのキーをリダイレクトするための単純なグローバルキーボードフックプログラムを作成しようとしています。たとえば、プログラムの実行時にキーボードの「a」を押すと、プログラムはそれを無効にして「b」クリックをシミュレートできます。グラフィックUIは必要ありません。コンソールだけで十分です(実行し続けます)

私の計画は、グローバルフックを使用してキー入力をキャッチし、keybd_eventを使用してキーボードをシミュレートすることです。しかし、私にはいくつかの問題があります。

最初の問題は、プログラムが「A」を正しくブロックできることですが、キーボードで「A」を1回押すと、コールバック関数のprintfとkeybd_eventが2回実行されます。したがって、txtファイルを開いて、「A」を1回クリックすると、「B」の入力が2つあります。何故ですか?

2番目の質問は、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);
}

どうもありがとう!

7
user1722361

最初のものは簡単です。 1つはキーダウン用、もう1つはキーアップ用です。 :)

DLL-それはグローバルフックであるためです。スレッド固有のフックとは異なり、キーボードイベントが発生したプロセスではなく、独自のプロセスで実行されます。これは、フックをインストールしたスレッドへのメッセージ送信を介して行われます。そのため、ここでメッセージループが必要になります。これがないと、着信メッセージをリッスンする人がいないため、フックを実行できません。

DLLは、別のプロセスのコンテキストで呼び出されるため、スレッド固有のフックに必要です。これを機能させるには、DLLをに注入する必要があります。そのプロセス。ここではそうではありません。

7
Ivan Danilov

WM_KEYDOWNWM_KEYUPのため、コールバック関数は2回実行されます。キーボードのキーを押すと、WindowsはWM_KEYDOWNメッセージでコールバック関数を呼び出し、キーを離すと、WindowsはWM_KEYUPメッセージでコールバック関数を呼び出します。そのため、コールバック関数は2回実行されます。

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

2番目の質問については、@ IvanDanilovの回答からすでに得ていると思います。

6
Forhad Reza
  1. コードを実行しましたが、何も起こりませんでしたか?どうした俺?
  2. msdn に基づいて、WH_KEYBOARD_LLメッセージは「グローバルのみ」です。それ以上の意味があります。

    システムはこの関数を呼び出します。新しいキーボード入力イベントがスレッド入力キューにポストされるたびに。 このメッセージは特殊なケースです。他のメッセージの実際のグローバルフックを作成するには、DLL)も必要です。

0
Hung Minh Tran