Sus*_*Pal 2 c windows keyboard-hook visual-studio
我正在尝试为 Windows 系统编写一个交换A和B键的程序,即当我按下该A键时,B就会输入内容,反之亦然。
为此,我首先将A键映射为与键类似的行为B。这是我写的代码。
#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;
}
Run Code Online (Sandbox Code Playgroud)
这按预期工作。我首先使用以下命令编译此代码。
"%vs80comntools%\vsvars32.bat"
cl /D_WIN32_WINNT=0x0401 /EHsc foo.c /link user32.lib
Run Code Online (Sandbox Code Playgroud)
现在,我运行该程序。我转到另一个程序,例如记事本,然后按下该A键并B进行输入。发生这种情况时,我会看到上述程序的以下输出。
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
Run Code Online (Sandbox Code Playgroud)
现在,我继续下一步,将B键映射为与键类似的行为A。为此,我只需取消上面注释的五行代码的注释,编译它并再次运行它。
这也符合预期。当我按下该A键时,B就会输入内容,当我按下该B键时,A就会输入内容。但是,当我检查程序的输出时,我看到太多事件。
这是当我按下该键一次时程序的输出A。
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
Run Code Online (Sandbox Code Playgroud)
之后,当我按B一次时,我会看到以下输出。
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
Run Code Online (Sandbox Code Playgroud)
为什么这里有这么多活动?我猜会发生这种情况是因为当我按下按键A时
keyboardHook函数通过按键的键盘输入事件来调用A。keyboardHook再次针对该键调用该函数B。以上六点解释纯属想象。我对此有几个问题。
A、生成B、消耗B、生成A、重复)而生成的,那么它如何在10个周期后自动停止?这也可以使用官方文档、另一个程序或任何实验来解释吗?顺便说一句,我知道如何解决这个问题。SendInput对于使用函数中的函数发送的假按键事件keyboardHook,硬件扫描码设置0为以下语句。
inputs[0].ki.wScan = 0;
Run Code Online (Sandbox Code Playgroud)
因此我可以利用这个事实通过修改代码的键映射部分来忽略函数keyboardHook中函数发送的事件keyboardHook,如下所示。
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;
}
Run Code Online (Sandbox Code Playgroud)
现在,当我按下该A键时,我会得到熟悉的输出。
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
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来解决这个问题?
按照 Raymond Chen 在问题评论中的建议,这里是一个完整的程序,它以正确的方式交换A和B,即通过检查低级键盘输入事件的注入标志。换句话说,我执行以下测试来决定是否使用低级键盘输入事件并将其替换为另一个事件。
(p->flags & LLKHF_INJECTED) == 0
Run Code Online (Sandbox Code Playgroud)
这是完整的程序。
#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;
}
Run Code Online (Sandbox Code Playgroud)
这可以编译如下。
"%vs80comntools%\vsvars32.bat"
cl /D_WIN32_WINNT=0x0401 foo.c /link user32.lib
Run Code Online (Sandbox Code Playgroud)
现在,当我运行该程序时,转到另一个程序(例如记事本),然后按下该A键,B即可输入内容。发生这种情况时,我会看到上述程序的以下输出。
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2638 次 |
| 最近记录: |