Я пытаюсь написать простую глобальную программу для подключения клавиатуры, чтобы перенаправить некоторые клавиши. Например, когда программа выполняется, я нажимаю «a» на клавиатуре, программа может отключить ее и симулировать щелчок «b». Мне не нужен графический интерфейс, достаточно консоли (продолжайте работать)
Мой план состоит в том, чтобы использовать глобальный хук для перехвата ввода с клавиатуры, а затем использовать keybd_event для имитации клавиатуры. Но у меня есть некоторые проблемы.
Первая проблема заключается в том, что программа может правильно заблокировать «A», но если я нажму «A» на клавиатуре один раз, printf в функции обратного вызова будет выполнен дважды, а также keybd_event. Так что, если я открываю текстовый файл, я нажимаю «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_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;
}
По поводу вашего второго вопроса, я думаю, вы уже получили ответ @Ivan Danilov.
Система вызывает эту функцию. Каждый раз, когда новое событие ввода с клавиатуры собирается публиковаться в очереди ввода потока.
Это сообщение является частным случаем. Вам также нужна DLL, чтобы создать реальный глобальный хук для другого сообщения.