问题
I am trying to write a program for Windows system that swaps the A and B keys, i.e. when I press the A key, B
gets typed, and vice versa.
To do so, I first map the A key to behave like the B key. Here is the code I wrote.
#include <stdio.h>
#include <windows.h>
HHOOK hook;
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *) lParam;
DWORD newVkCode;
INPUT inputs[1];
UINT ret;
char wParamStr[16];
char vkStr[16] = "";
if (wParam == WM_KEYDOWN)
strcpy(wParamStr, "KEYDOWN");
else if (wParam == WM_KEYUP)
strcpy(wParamStr, "KEYUP");
else if (wParam == WM_SYSKEYDOWN)
strcpy(wParamStr, "SYSKEYDOWN");
else if (wParam == WM_SYSKEYUP)
strcpy(wParamStr, "SYSKEYUP");
else
strcpy(wParamStr, "UNKNOWN");
if (p->vkCode == 10)
strcpy(vkStr, "<LF>");
else if (p->vkCode == 13)
strcpy(vkStr, "<CR>");
else
vkStr[0] = p->vkCode;
printf("%d - %s - %lu (%s) - %d - %lu\n",
nCode, wParamStr, p->vkCode, vkStr, p->scanCode, p->time);
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wScan = 0;
inputs[0].ki.dwFlags = 0;
inputs[0].ki.time = 0;
inputs[0].ki.dwExtraInfo = 0;
if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
}
if (p->vkCode == 'A') {
inputs[0].ki.wVk = 'B';
ret = SendInput(1, inputs, sizeof (INPUT));
return 1;
}
/*
else if (p->vkCode == 'B') {
inputs[0].ki.wVk = 'A';
ret = SendInput(1, inputs, sizeof (INPUT));
return 1;
}
*/
return CallNextHookEx(hook, nCode, wParam, lParam);
}
int main(int argc, char **argv)
{
MSG messages;
hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHook, NULL, 0);
if (hook == NULL) {
printf("Error %d\n", GetLastError());
return 1;
}
printf("Waiting for messages ...\n");
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return 0;
}
This works as expected. I first compile this code with the following commands.
"%vs80comntools%\vsvars32.bat"
cl /D_WIN32_WINNT=0x0401 /EHsc foo.c /link user32.lib
Now, I run the program. I go to another program, say, Notepad, and press the A key and B
gets typed. I see the following output from the above program when this happens.
C:\foo>foo.exe
Waiting for messages ...
0 - KEYUP - 13 (<CR>) - 28 - 874702239
0 - KEYDOWN - 65 (A) - 30 - 874703752
0 - KEYDOWN - 66 (B) - 0 - 874703752
0 - KEYUP - 65 (A) - 30 - 874703877
0 - KEYUP - 66 (B) - 0 - 874703877
Now, I move on to the next step of mapping the B key to behave like the A key. To do this, I simply uncomment the five lines of code that is commented above, compile it and run it again.
This also behaves as expected. When I press the A key, B
gets typed, and when I press the B key, A
gets typed. However, when I check the output of the program, I see too many events.
Here is the output of the program, when I press the A key exactly once.
C:\foo>foo.exe
Waiting for messages ...
0 - KEYUP - 13 (<CR>) - 28 - 874746621
0 - KEYDOWN - 65 (A) - 30 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYDOWN - 66 (B) - 0 - 874749008
0 - KEYDOWN - 65 (A) - 0 - 874749008
0 - KEYUP - 65 (A) - 30 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
0 - KEYUP - 66 (B) - 0 - 874749101
0 - KEYUP - 65 (A) - 0 - 874749101
After this, when I press the B exactly once, I see the following output.
0 - KEYDOWN - 66 (B) - 48 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYDOWN - 65 (A) - 0 - 874824590
0 - KEYDOWN - 66 (B) - 0 - 874824590
0 - KEYUP - 66 (B) - 48 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
0 - KEYUP - 65 (A) - 0 - 874824637
0 - KEYUP - 66 (B) - 0 - 874824637
Why are there so many events here? I am guessing this happens because when I press the A key,
- The
keyboardHook
function is called with the keyboard input event for the A key press. - It processes the keyboard input event and sends a keyboard input event for the B key.
- The
keyboardHook
function is called again for the B key. - It processes it and sends an event for the A now.
- The whole cycle repeats.
- After 10 such cycles, the Windows system probably interferes and stops the cycle.
The above six point explanation is pure imagination. I have a few questions about this.
- Can we explain why so many events are generated using an official documentation from Microsoft? If an official documentation is not available, can we prove the reason behind the generation of so many events by writing another program or performing some experiment?
- If the events are generated indeed due to repetition of the same cycle (i.e. consuming
A
, generatingB
, consumingB
, generatingA
, repeat), then how does it stop automatically after 10 cycles? Can this be also explained using an official documentation, another program or any experiment?
By the way, I know how to work around this. For the fake key events sent using the SendInput
function in the keyboardHook
function, the hardware scan code is set to 0
in the following statement.
inputs[0].ki.wScan = 0;
So I can use this fact to ignore events sent by the keyboardHook
function in the keyboardHook
function by modifying the key mapping part of the code as follows.
if (p->vkCode == 'A' && p->scanCode != 0) {
inputs[0].ki.wVk = 'B';
ret = SendInput(1, inputs, sizeof (INPUT));
return 1;
}
else if (p->vkCode == 'B' && p->scanCode != 0) {
inputs[0].ki.wVk = 'A';
ret = SendInput(1, inputs, sizeof (INPUT));
return 1;
}
Now when I press the A key, I get the familiar output.
C:\foo>foo.exe
Waiting for messages ...
0 - KEYUP - 13 (<CR>) - 28 - 875183112
0 - KEYDOWN - 65 (A) - 30 - 875186310
0 - KEYDOWN - 66 (B) - 0 - 875186310
0 - KEYUP - 65 (A) - 30 - 875186388
0 - KEYUP - 66 (B) - 0 - 875186388
Is there a better way to solve this problem?
回答1:
Following the suggestion of Raymond Chen in the comments to the question, here is a complete program that swaps A and B in the right way, i.e. by checking the injected flag of the low-level keyboard input event. In other words, I perform the following test to decide whether to consume a low-level keyboard input event and replace it with another.
(p->flags & LLKHF_INJECTED) == 0
Here is the complete program.
#include <stdio.h>
#include <windows.h>
HHOOK hook;
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *) lParam;
DWORD newVkCode;
INPUT inputs[1];
UINT ret;
char wParamStr[16];
char vkStr[16] = "";
if (wParam == WM_KEYDOWN)
strcpy(wParamStr, "KEYDOWN");
else if (wParam == WM_KEYUP)
strcpy(wParamStr, "KEYUP");
else if (wParam == WM_SYSKEYDOWN)
strcpy(wParamStr, "SYSKEYDOWN");
else if (wParam == WM_SYSKEYUP)
strcpy(wParamStr, "SYSKEYUP");
else
strcpy(wParamStr, "UNKNOWN");
if (p->vkCode == 10)
strcpy(vkStr, "<LF>");
else if (p->vkCode == 13)
strcpy(vkStr, "<CR>");
else
vkStr[0] = p->vkCode;
printf("%d - %s - %lu (%s) - %d - %lu\n",
nCode, wParamStr, p->vkCode, vkStr, p->scanCode, p->time);
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wScan = 0;
inputs[0].ki.dwFlags = 0;
inputs[0].ki.time = 0;
inputs[0].ki.dwExtraInfo = 0;
if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
}
if (p->vkCode == 'A' && (p->flags & LLKHF_INJECTED) == 0) {
inputs[0].ki.wVk = 'B';
ret = SendInput(1, inputs, sizeof (INPUT));
return 1;
} else if (p->vkCode == 'B' && (p->flags & LLKHF_INJECTED) == 0) {
inputs[0].ki.wVk = 'A';
ret = SendInput(1, inputs, sizeof (INPUT));
return 1;
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
int main(int argc, char **argv)
{
MSG messages;
hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHook, NULL, 0);
if (hook == NULL) {
printf("Error %d\n", GetLastError());
return 1;
}
printf("Waiting for messages ...\n");
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return 0;
}
This can be compiled as follows.
"%vs80comntools%\vsvars32.bat"
cl /D_WIN32_WINNT=0x0401 foo.c /link user32.lib
Now when I run this program, go to another program, say, Notepad, and press the A key, B
gets typed. I see the following output from the above program when this happens.
C:\lab\foo>foo.exe
Waiting for messages ...
0 - KEYUP - 13 (<CR>) - 28 - 588708476
0 - KEYDOWN - 65 (A) - 30 - 588711206
0 - KEYDOWN - 66 (B) - 0 - 588711206
0 - KEYUP - 65 (A) - 30 - 588711284
0 - KEYUP - 66 (B) - 0 - 588711284
来源:https://stackoverflow.com/questions/27303878/when-i-swap-keys-using-setwindowshookex-wh-keyboard-ll-why-does-my-program-get