在python中通过调用dll来提高运行速度

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回放都变慢放了~
------------------------- 更新 ------------------------------------
之前一直很难相信python和c++ dll的速度会相差这么,事实上却是也没有这么大的差距,应该本质上pyautogui也是通过调用windows API相关dll。那么问题出在哪里呢?
研究pyautogui源码发现,pyautogui的moveTo接口有装饰器,绝大部分时间都消耗在了装饰器上。
在这里插入图片描述
将装饰器去掉的话,就和调用dll的速度相当了。

参考文章:
【1】python 调用 c++ dll库
【2】C++模拟鼠标移动及单击实现代码
【3】[python] python调用C++ 程序

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值