1、前言
最近项目中需要录制键鼠命令,然后再通过注入的方式回放录制过程。一般来说,普通的一些点击命令完全可以通过python的一些包来完成键鼠模拟操作,比如通过pyautogui包。但是,鼠标移动过程中,如果采用频率很高的话,比如1s之类采样50个点像素坐标,那么就会发现,如果是通过python进行回放的话,整个回放过程,相比于原来的录制过程,就如同慢放一样。
整个过程抽象如下:
在录制过程每隔时间T进行一次采样(记录下该点的坐标),由于采样本身所消耗的时间极少(小于0.1ms),可以忽略,因此,录制过程假如采样n个点,需要的时间为:nT
回放过程,需要依次移动到采样的的每个点p1、p2…,由于在回放过程移动到每个点是通过python函数moveTo来实现的,而函数moveTo执行一次的时间为t(大概在100ms左右,不能忽略),所以回放过程消耗的时间为:nT + nt 。
回放过程与录制过程的时间比比:(nT + nt)/nT = 1 + t/T,因此回放过程相对于录制过程,相当于是1 + t/T倍慢放。并且采样频率越高,即T越小,慢放的比例越大!
为了让回放过程更加接近录制过程,即减小慢放的比例,就需要缩放moveTo的执行时间。由于通过python实现moveTo的时间消耗较长,可以通过c/c++来实现moveTo,就可以大幅缩短moveTo需要的时间。
2、生成dll
为了在python中调用dll,需要先生成moveTo的dll文件。对应的源码如下:
// mouseMove.h
#ifdef MYDLL_EXPORTS
#define MATH_API __declspec(dllexport)
#else
#define MATH_API __declspec(dllimport)
#endif
#include <windows.h>
extern "C" {
MATH_API void mouseClick(int x, int y);
MATH_API void LeftDown(int x, int y);
MATH_API void LeftUp(int x, int y);
MATH_API void mouseMove(int x, int y); // 对应moveTo函数
}
// mouseMove.cpp
void mouseClick(int x, int y) {
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP;
// 计算屏幕上的坐标位置
input.mi.dx = x * 65535 / GetSystemMetrics(SM_CXSCREEN);
input.mi.dy = y * 65535 / GetSystemMetrics(SM_CYSCREEN);
SendInput(1, &input, sizeof(INPUT));
}
void LeftDown(int x, int y) {
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN;
// 计算屏幕上的坐标位置
input.mi.dx = x * 65535 / GetSystemMetrics(SM_CXSCREEN);
input.mi.dy = y * 65535 / GetSystemMetrics(SM_CYSCREEN);
SendInput(1, &input, sizeof(INPUT));
}
void LeftUp(int x, int y) {
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTUP;
// 计算屏幕上的坐标位置
input.mi.dx = x * 65535 / GetSystemMetrics(SM_CXSCREEN);
input.mi.dy = y * 65535 / GetSystemMetrics(SM_CYSCREEN);
SendInput(1, &input, sizeof(INPUT));
}
void mouseMove(int x, int y) {
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
// 计算屏幕上的坐标位置
input.mi.dx = x * 65535 / GetSystemMetrics(SM_CXSCREEN);
input.mi.dy = y * 65535 / GetSystemMetrics(SM_CYSCREEN);
SendInput(1, &input, sizeof(INPUT));
}
注意:在dll文件中不仅实现mouseMove(对应moveTo),还顺带实现了mouseClick等操作。
3、通过pyton调用dll
在python中调用dll文件,只需要先导入ctypes包,然后通过ctypes包加载dll文件,然后调用dll中的函数即可。
import ctypes
import pyautogui
# 加载dll文件
lib = ctypes.cdll.LoadLibrary("E:\\program\\cplusplus\\myDll\\x64\\Release\\myDll.dll")
# 调用dll中的函数
lib.mouseMove(100,200)
4、python库与dll库运行速度对比
都知道比速度python肯定是比不上c/c++,但是运行速度到底差了多少呢?如果单纯是就论本问题提到的移动鼠标这个功能的话,说出来可能吓人!!!
无图说个。。。。,
import time
import ctypes
import pyautogui
lib = ctypes.cdll.LoadLibrary("E:\\program\\cplusplus\\myDll\\x64\\Release\\myDll.dll")
t = time.time()
# dll执行1000次mouseMove
for i in range(0,1000):
lib.mouseMove(i,2)
t1 = time.time()
print("mouseMove 1000 times by dll: ",t1 - t)
# 通过pyautogui执行100次moveTo
t = time.time()
for i in range(0,100):
pyautogui.moveTo(i,300)
t1 = time.time()
print("moveTo 100 times by pyautogui:",t1 - t)
从图中可以看到,通过dll执行1000次mouseMove消耗的时间为0.11s,平均一次0.1ms左右
通过pyautogui执行100 mouseTo消耗时间为10s,平均一次消耗时间100ms
居然相差了1000倍???
难怪录制过程转换为python回放都变慢放了~
参考文章:
【1】python 调用 c++ dll库
【2】C++模拟鼠标移动及单击实现代码
【3】[python] python调用C++ 程序