Qt模拟键盘点击分为两种情况:
情况一:模拟键盘输入到系统窗口,整个系统都接收这个模拟输入。
情况二:模拟键盘输入到指定的软件,只有指定的软件接收模拟输入。
情况一实现方式:
头文件:windows.h
使用
void keybd_event(BYTE bVk, BYTE bScan, DWORD dwFlags,DWORD dwExtraInfo);
参数:
第一个为按键的虚拟键值,如回车键为 vk_return, tab 键为 vk_tab(其他具体的参见附录:常用模拟键的键值对照表跟ASCII码表相同;
第二个参数为扫描码,一般不用设置,用 0 代替就行;
第三个参数为选项标志,如果为 keydown 则置 0 即可,如果为 keyup 则设成"KEYEVENTF_KEYUP";
第四个参数一般也是置 0 即可。
或者
Uint SendInput(UINT nInputs, LPINPUT pInputs, int cbSize );
参数:
nInput 指定ninput 数组中元素的个数。就是插入事件的个数。
pInput 指向一个类型为INPUT的数组变量,该数组中的每个元素代表一个将要插入到线程事件中去的键盘或鼠标事件。
cbSize 指定INPUT结构的大小。如果cbSize不是INPUT结构的大小,则函数将失败返回。
详细使用方法参考sendinput详细介绍
,ketbd_event详细介绍
代码示例:
例子1:模拟按下’A’键
keybd_event(65,0,0,0);
keybd_event(65,0,KEYEVENTF_KEYUP,0);
例子2:模拟按下’ALT+F4’键
keybd_event(18,0,0,0);
keybd_event(115,0,0,0);
keybd_event(115,0,KEYEVENTF_KEYUP,0);
keybd_event(18,0,KEYEVENTF_KEYUP,0);
例子3:单键输入
void SendUnicode(wchar_t data)
{
INPUT input[2];
memset(input, 0, 2 * sizeof(INPUT));
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0;
input[0].ki.wScan = data;
input[0].ki.dwFlags = 0x4;//KEYEVENTF_UNICODE;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0;
input[1].ki.wScan = data;
input[1].ki.dwFlags = KEYEVENTF_KEYUP | 0x4;//KEYEVENTF_UNICODE;
SendInput(2, input, sizeof(INPUT));
}
例子4:多键输入
void keyboardTreeKey(unsigned short key1, unsigned short key2, unsigned short key3){
INPUT input[6];
memset(input, 0, sizeof(input));
input[0].type = input[1].type = input[2].type = input[3].type = input[4].type = input[5].type = INPUT_KEYBOARD;
input[0].ki.wVk = input[3].ki.wVk = key1;// VK_CONTROL;
input[1].ki.wVk = input[4].ki.wVk = key2;//VK_MENU;
input[2].ki.wVk = input[5].ki.wVk = key3;
input[3].ki.dwFlags = input[4].ki.dwFlags = input[5].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(6, input, sizeof(INPUT));
}
键盘命令分按下和抬起,都要传入,例如input[0]和input[3]代表key1的按下和抬起。
情况二实现方式:
头文件:windows.h
使用
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam;
参数:
hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。
Msg:指定被发送的消息。
wParam:指定附加的消息指定信息。
IParam:指定附加的消息指定信息。
返回值:返回值指定消息处理的结果,依赖于所发送的消息。
备注:需要用HWND_BROADCAST通信的应用程序应当使用函数RegisterWindowMessage来为应用程序间的通信取得一个唯一的消息。
例子1:模拟按下’A’键
HWND hwnd = 0;
for(int i = 0 ;i < 7;++i){
hwnd = FindWindow(NULL,L"yjd"); //获取指定程序yjd的窗口句柄
qDebug() << __FUNCTION__ << __LINE__ << hwnd;
if(hwnd != NULL){
break;
}
::Sleep(1000);
}
if (NULL != m_hwnd)
{
std::thread th([=](){ //单独启动一个线程进行数据传递
::SendMessage(m_hwnd, WM_SYSKEYDOWN, 65, 0)
});
th.detach();//传递结束后,进行关闭线程
}
例子2:模拟按下Shift+'A’键
HWND hwnd = 0;
for(int i = 0 ;i < 7;++i){
hwnd = FindWindow(NULL,L"yjd"); //获取指定程序yjd的窗口句柄
qDebug() << __FUNCTION__ << __LINE__ << hwnd;
if(hwnd != NULL){
break;
}
::Sleep(1000);
}
if (NULL != m_hwnd)
{
std::thread th([=](){ //单独启动一个线程进行数据传递
PostMessage(m_hwnd, WM_KEYDOWN, VK_SHIFT, 0x20380001);
PostMessage(m_hwnd, WM_KEYDOWN, 65, 0x20210001);
QThread::msleep(20);
// PostMessage(m_hwnd, WM_SYSCHAR, 65, 0x20210001);
PostMessage(m_hwnd, WM_SYSKEYUP, 65, 0xE0210001);
PostMessage(m_hwnd, WM_SYSKEYUP, VK_SHIFT, 0xC0380001);
});
th.detach();//传递结束后,进行关闭线程
}
这里使用的SendMessage,其实也可以使用PostMessage,它们的区别为:
主要在于是否等待其他程序消息处理。PostMessage只是把消息放入队列,不管其他程序是否处理都返回,然后继续执行;而SendMessage必须等待其他程序处理消息后才返回,继续执行。这两个函数的返回值也不同,PostMessage的返回值表示PostMessage函数执行是否正确,而SendMessage的返回值表示其他程序处理消息后的返回值,跟Qt的sendEvent和postEvent差不多的原理。
鼠标移动模拟,这里介绍鼠标针对整个系统窗口移动,想要实现限定到指定窗口移动暂时还没有思路。
SetCursorPos实现方式
POINT cp;
GetCursorPos(&cp); //获取光标位置
SetCursorPos(cp.x+3,cp.y); //设置光标位置
mouse_event实现方式
mouse_event(MOUSEEVENTF_MOVE,1,0,0,0); //默认相对于当前位置水平向右移动
1、鼠标左键按下和松开两个事件的组合即一次单击(右键类似):
mouse_event (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 )
2、两次连续的鼠标左键单击事件 构成一次鼠标双击事件:
mouse_event (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 )
mouse_event (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 )
3、使用绝对坐标移动鼠标:
mouse_event (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, 500, 500, 0, 0)
需要说明的是,如果没有使用MOUSEEVENTF_ABSOLUTE,函数默认的是相对于鼠标当前位置的点,如果dx,和dy,用0,0表示,这函数认为是当前鼠标所在的点。
SendIput实现方式
INPUT input[1];
memset(input, 0, 1 * sizeof(INPUT));
input[0].type = INPUT_MOUSE;
input[0].mi.dx = 1;
input[0].mi.dy = 0;
input[0].mi.dwFlags = MOUSEEVENTF_MOVE;;
SendInput(1, input, sizeof(INPUT));
SendIputhe的dwFlags和mouse_event操作方式是一样的。
const int MOUSEEVENTF_MOVE = 0x0001; 移动鼠标
const int MOUSEEVENTF_LEFTDOWN = 0x0002; 模拟鼠标左键按下
const int MOUSEEVENTF_LEFTUP = 0x0004; 模拟鼠标左键抬起
const int MOUSEEVENTF_RIGHTDOWN = 0x0008; 模拟鼠标右键按下
const int MOUSEEVENTF_RIGHTUP = 0x0010; 模拟鼠标右键抬起
const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; 模拟鼠标中键按下
const int MOUSEEVENTF_MIDDLEUP = 0x0040; 模拟鼠标中键抬起
const int MOUSEEVENTF_ABSOLUTE = 0x8000; 标示是否采用绝对坐标
为什么我没有使用PostMessage来实现,因为它们似乎是无效的。