0%

在应用层模拟键盘按键(1)

简介

在某些情况下,我们需要模拟键盘按键,用程序替代人工按键操作,
可以使用的方法包含:使用 SendMessagePostMessage 发送按键消息,使用 keybd_event 函数
发送按键动作(该函数被 SendInput 取代),使用 SendInput 函数发送按键动作,等等。

发送按键消息

使用 WM_KEYDOWN 或者 WM_KEYUP 并不好使,推测可能是 lParam 参数设置错误,
使用 WM_CHAR 消息都能够成功,只不过发送消息之前,需要先获取窗口句柄。

1
2
3
4
5
6
HWND hWnd = FindWindow(NULL, "无标题 - 记事本");
HWND hEdit = FindWindowEx(hWnd, NULL, "EDIT", NULL);
SendMessage(hEdit, WM_CHAR, 0x41, 0); // 输入A
SendMessage(hEdit, WM_KEYDOWN, VK_SPACE, 0); // 按下空格
SendMessage(hEdit, WM_KEYUP, VK_SPACE, 0); // 弹起空格
SendMessage(hEdit, WM_CHAR, 0x41, 0); // 输入A

调用按键事件函数

该函数格式如下所示,其中参数 bVk 设置输入的虚拟按键,参数 dwFlags 设置按下或弹起动作

1
2
3
4
5
6
VOID WINAPI keybd_event(
_In_ BYTE bVk,
_In_ BYTE bScan,
_In_ DWORD dwFlags,
_In_ ULONG_PTR dwExtraInfo
);
1
2
3
4
5
// 模拟按下Shift+A组合键,可以使用Sleep()增加按键间隔
keybd_event(VK_SHIFT, 0, 0, 0); // 按下Shift键
keybd_event(0x41, 0, 0, 0); // 按下A键
keybd_event(0x41, 0, KEYEVENTF_KEYUP, 0); // 松开A键
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); // 松开Shift键

调用发送输入函数

该函数格式如下所示,参数 nInputs 描述输入 INPUT 结构体数组的数量,参数 cbSize 表示对应总大小

1
2
3
4
5
UINT WINAPI SendInput(
_In_ UINT nInputs,
_In_ LPINPUT pInputs,
_In_ int cbSize
);
1
2
3
4
5
6
7
8
typedef struct tagINPUT {
DWORD type;
union {
MOUSEINPUT mi;
KEYBDINPUT ki;
HARDWAREINPUT hi;
};
} INPUT, *PINPUT;

结构体中的参数 type 可取值 INPUT_MOUSE INPUT_KEYBOARD INPUT_HARDWARE 来对应后续参数

1
2
3
4
5
6
7
8
typedef struct tagMOUSEINPUT {
LONG dx;
LONG dy;
DWORD mouseData;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} MOUSEINPUT, *PMOUSEINPUT;
1
2
3
4
5
6
7
typedef struct tagKEYBDINPUT {
WORD wVk;
WORD wScan;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT;
1
2
3
4
5
typedef struct tagHARDWAREINPUT {
DWORD uMsg;
WORD wParamL;
WORD wParamH;
} HARDWAREINPUT, *PHARDWAREINPUT;

我们这里使用 INPUT_KEYBOARD 来发送键盘按键,结构体KEYBDINPUTkeybd_event 取值相同

1
2
3
4
5
6
INPUT input = { 0 };
input.type = INPUT_KEYBOARD;
input.ki.wVk = VK_SHIFT;
SendInput(1, &input, sizeof(INPUT)); // 按下Shift
input.ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, &input, sizeof(INPUT)); // 弹起Shift

经实验发现每次发送1个信息时能够成功,但是改为同时发送2个信息就不成功,不太清楚原因。