Win10使用虚拟USB鼠标实现自动挂机测试功能
昨天要测试一个软件不停的整机的功能:在整机软件中需要不停地点击某个软件的按钮,启动会议功能,然后隔一段时间后,需要再次呼出关闭按钮,并点击将该按钮停止会议,如此往复的操作。当然这些操作是需要人工操作点击软件界面的某些按钮实现的。
本以为是一个很简单的东西,没想到还是弄出了花样。
初始版本 - 脚本方案
第一想法本是想通过批处理实现,无奈没有找到相应的鼠标功能接口,只好放弃。
第二想法是通过python实现,发现网上的代码不知道是版本问题还是别的,就是无法运行。模块无法识别,使用PIP安装库时需要更新python,竟把python环境给升挂了。算了,懒地弄了,就此放弃此方案。
网上下的代码版本1
import win32api
def shubiao(x,y):
win32api.SetCursorPos([x,y])
# 鼠标点击
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
# time.sleep(1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
# time.sleep(1)
# print("dianji_shubiao")
shubiao(100,200)
网上下的代码版本2
from ctypes import *
import time
time.sleep(5)
for i in range(1,5):
windll.user32.SetCursorPos(900,50);
windll.user32.SetCursorPos(900,300);
windll.user32.mouse_event(windll.user32.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
windll.user32.mouse_event(windll.user32.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
time.sleep(1)
第二版本 - 应用程序模拟事件方案
上面的功能无法实现,只好自己写一个应用。
使用wind32提供的API接口实现,如实现在某个位置点击的代码
void DoClick(int x, int y)
{
SetCursorPos(x, y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
为了后续的调试的配置方便,自己这个应用运行时读取script.txt文件中的内容,然后解析脚本,执行。
自己定义的命令就几个:
script.txt
loop 10
move 100,200
sleep 100
click 120,432
然后使用C进行解析执行:
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <vector>
enum ENUM_ACTION
{
ACTION_MOVE =0,
ACTION_CLICK,
ACTION_SLEEP
};
typedef struct _ACTION_STRUCT
{
ENUM_ACTION action;
int x;
int y;
int sleep;
}ACTION_STRUCT,*PACTION_STRUCT;
void DoMove(int x, int y)
{
SetCursorPos(x, y);
}
void DoClick(int x, int y)
{
SetCursorPos(x, y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
void DoSleep(int x)
{
Sleep(x);
}
编译运行,使用多命令运行程序拾取各个按钮的坐标后,编写真正使用脚本,然后执行运行。一切都是很美好,自己看了一会后,发现有的时候偶尔有问题(见注释),不过下一次执行的时候好了。
前面的软件运行了大概2个小时就不行了,又是晚上,所以整个晚上挂了一个寂寞。
注释:出现的问题是由于我们的产品是大屏,软件并且是全屏运行的,需要在屏幕点击一击鼠标或者触摸屏时才弹出操作界面,然后再在操作界面中点击对应的按钮弹出相应的选择菜单,然后再在菜单中选择对应的关闭按钮。不过由于这是一系列的连贯操作,前几次还可以正常弹出选择菜单,后面的就不弹出了。
一旦出现关闭失败后,后面就是大概率就不再成功了。可能的问题是由于是全屏,默认隐藏鼠标,但是只发送鼠标事件,又没见相应的鼠标动作(如移动),事件发生后系统又要隐藏鼠标指针,导致前面的事件被湮灭。
终极方案- 真实的“鼠标”方案
想着即然鼠标事件不行,那不如来个“真正”的鼠标,有移动轨迹,有操作。
前一段时间使用Windows驱动虚拟了一个USB HID鼠标功能,对这个驱动加以利用,不就可以了吗?就干就干。
整个方案的框架如下:
首先对鼠标的事件进行封装,这里我发的是鼠标的报表内容:
描述符的内容解析可见:USB鼠标HID报告描述符数据格式分析 http://www.usbzh.com/article/detail-327.html
void CVMouseDlg::MouseLeft()
{
ULONG rtn = 0;
UCHAR data[4] = { 0x00,0xFF,0x00,0x00 };
m_usb.SetMouseData(data, 4, &rtn);
}
void CVMouseDlg::MouseRight()
{
ULONG rtn = 0;
UCHAR data[4] = { 0x00,0x01,0x00,0x00 };
m_usb.SetMouseData(data, 4, &rtn);
}
void CVMouseDlg::MouseUp()
{
ULONG rtn = 0;
UCHAR data[4] = { 0x00,0x00,0xff,0x00 };
m_usb.SetMouseData(data, 4, &rtn);
}
void CVMouseDlg::MouseDown()
{
ULONG rtn = 0;
UCHAR data[4] = { 0x00,0x00,0x01,0x00 };
m_usb.SetMouseData(data, 4, &rtn);
}
void CVMouseDlg::MouseLeftClickDown()
{
ULONG rtn = 0;
UCHAR data[4] = { 0x01,0x00,0x000,0x00 };
m_usb.SetMouseData(data, 4, &rtn);
}
void CVMouseDlg::MouseLeftClickUp()
{
ULONG rtn = 0;
UCHAR data[4] = { 0x00,0x00,0x000,0x00 };
m_usb.SetMouseData(data, 4, &rtn);
}
由于鼠标的相对移动,所以为了移动鼠标到具体的位置,进行代码封装:
void CVMouseDlg::DoMove(int x, int y)
{
POINT pt;
GetCursorPos(&pt);
do
{
if (x > pt.x)
{
MouseRight();
}
else if (x < pt.x)
{
MouseLeft();
}
if (y > pt.y)
{
MouseDown();
}
else if (y < pt.y)
{
MouseUp();
}
Sleep(1);
GetCursorPos(&pt);
printf("%d,%d\n", pt.x, pt.y);
if (x == pt.x && y == pt.y)
{
break;
}
} while (1);
}
虚拟的鼠标:
应用程序的软件界面: