按键精灵作为一款模拟鼠标以及键盘操作的软件来说,其有着相当强大的功能。然而可惜的是,按键精灵使用相当过时的VB语言,同时其语法还是老版本的语法,新版VB的特性并不能完全的支持。这使得我有一种想用python来实现的冲动。

下面是我使用按键精灵模拟鼠标点击玩别踩白块的视频。从视频中可以看出来,按键精灵提供的窗口api性能并不算的上太好。(也许是因为我没有进行优化吧)。但是我将整个逻辑搬到python上,并提供了自己所写的api后,速度有了很大的提升。(视频)下面我来简单的谈谈如何使用python完成按键精灵的部分功能。

首先是完成窗口的获取以及窗口大小的判断。这里我不使用python提供的api,而是通过直接加载windows的dll文件来实现的。用的是python提供的ctypes。

获取窗口:

User32 = ctypes.windll.LoadLibrary("User32.dll")

User32.FindWindowW.argtypes=[ctypes.wintypes.LPCWSTR,ctypes.wintypes.LPCWSTR]

User32.FindWindowW.restypes=[ctypes.wintypes.HWND]

wHWND = User32.FindWindowW(None,'Bluestacks App Player')

以及定义窗口大小结构体:

class CRECT(Structure):

_fields_ = [ ("left",c_long),

("top",c_long),

("right",c_long),

("bottom",c_long)]

获取窗口大小:

rect = CRECT()

User32.GetWindowRect.argtypes=[ctypes.wintypes.HWND,ctypes.wintypes.c_void_p]

if not User32.GetWindowRect(wHWND,byref(rect)):

exit(1)

移动鼠标:

def SetCursePos(x,y):

User32.SetCursorPos.argtypes=[ctypes.wintypes.c_int,ctypes.wintypes.c_int]

User32.SetCursorPos(int(x),int(y))

模拟鼠标事件:

def EmuCursorEvent(x,y,event,Abs):

User32.mouse_event.argtypes=[ctypes.wintypes.DWORD,

ctypes.wintypes.DWORD,

ctypes.wintypes.DWORD,

ctypes.wintypes.DWORD,

ctypes.wintypes.c_void_p]#ULONG_PTR

if Abs:

User32.mouse_event(event|0x8000,x,y,0,None)

else:

User32.mouse_event(event,x,y,0,None)

模拟鼠标点击:

def EmuCursorClick(x,y):

SetCursePos(x,y)

#EmuCursorEvent(x,y,MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_LEFTUP,True)

EmuCursorEvent(0,0,MOUSEEVENTF_LEFTDOWN,False)

time.sleep(0.01)

EmuCursorEvent(0,0,MOUSEEVENTF_LEFTUP,False)

接下来是实现一个速度更快的GetPixel功能。我选择使用GDI的方法,对窗口进行截图,在窗口内使用GetDIBits的方法实现特定位置单点以及多点颜色的查询。新建一个VC++项目,指定生成dll文件,其核心实现部分为:

1. 截屏

SCREENFUNCTION_API HBITMAP __stdcall GetWindowImg(HWND hWnd)

{

HDC dcSrc = GetWindowDC(hWnd);

RECT wRect = { 0, };

GetWindowRect(hWnd, &wRect);

long dwWidth = wRect.right - wRect.left;

long dwHigh = wRect.bottom - wRect.top;

HDC dcDest = CreateCompatibleDC(dcSrc);

HBITMAP hBitmap = CreateCompatibleBitmap(dcSrc, dwWidth, dwHigh);

HGDIOBJ hObj = SelectObject(dcDest, hBitmap);

if (!BitBlt(dcDest, 0, 0, dwWidth, dwHigh, dcSrc, 0, 0, SRCCOPY))

{

OutputDebugString(L"Error While Copy Image!");

}

SelectObject(dcDest, hObj);

DeleteDC(dcDest);

ReleaseDC(hWnd, dcSrc);

return hBitmap;

}

2.单一像素查找:

SCREENFUNCTION_API DWORD32 __stdcall GetWindowPixel(HWND hWnd,int x, int y) {

HBITMAP m_bmp = GetWindowImg(hWnd);

BITMAP bmp;

GetObject(m_bmp, sizeof(BITMAP), &bmp);

HDC hdc = GetDC(NULL);

PBYTE pBits = NULL;

BITMAPINFO bi = { 0, };

bi.bmiHeader.biSize = sizeof(bi.bmiHeader);

bi.bmiHeader.biWidth = bmp.bmWidth;

bi.bmiHeader.biHeight = -bmp.bmHeight;

bi.bmiHeader.biPlanes = 1;

bi.bmiHeader.biBitCount = bmp.bmBitsPixel;

bi.bmiHeader.biCompression = BI_RGB;

bi.bmiHeader.biClrUsed = 0;

bi.bmiHeader.biClrImportant = 0;

if (!GetDIBits(hdc, m_bmp, 0, bmp.bmHeight, pBits, &bi, DIB_RGB_COLORS))

{

OutputDebugString(L"Error While Get Header Size");

return 0;

}

pBits = (PBYTE)malloc(bi.bmiHeader.biSizeImage);

ZeroMemory(pBits, bi.bmiHeader.biSizeImage);

if (!GetDIBits(hdc, m_bmp, 0, bmp.bmHeight, pBits, &bi, DIB_RGB_COLORS))

{

free(pBits);

pBits = NULL;

}

int offset = (y - 1)*bmp.bmWidth + x - 1;//可能越界 添加越界检查

DWORD32 RGB = 0;

if (offset > bi.bmiHeader.biSizeImage >> 2 || offset < 0) {

RGB = 0;

OutputDebugString(L"Access Violation!");

}

else

RGB = *((PDWORD32)(pBits)+offset);

free(pBits);

pBits = NULL;

return RGB;

}

3.多像素查找:

SCREENFUNCTION_API int __stdcall GetWindowMultiPixel(HWND hWnd, PINT PosArr, PDWORD32 pRGB)

{

//PosArr end with 0

if (PosArr == NULL)

return 0;

int bufferlen = 0;

while (*(PosArr + bufferlen))

bufferlen++;

if (pRGB == NULL)

{

return (bufferlen + 2)/2;

}

int x, y;

x = 0;

y = 0;

HBITMAP m_bmp = GetWindowImg(hWnd);

BITMAP bmp;

GetObject(m_bmp, sizeof(BITMAP), &bmp);

HDC hdc = GetDC(NULL);

PBYTE pBits = NULL;

BITMAPINFO bi = { 0, };

bi.bmiHeader.biSize = sizeof(bi.bmiHeader);

bi.bmiHeader.biWidth = bmp.bmWidth;

bi.bmiHeader.biHeight = -bmp.bmHeight;

bi.bmiHeader.biPlanes = 1;

bi.bmiHeader.biBitCount = bmp.bmBitsPixel;

bi.bmiHeader.biCompression = BI_RGB;

bi.bmiHeader.biClrUsed = 0;

bi.bmiHeader.biClrImportant = 0;

if (!GetDIBits(hdc, m_bmp, 0, bmp.bmHeight, pBits, &bi, DIB_RGB_COLORS))

{

OutputDebugString(L"Error While Get Header Size");

return 0;

}

pBits = (PBYTE)malloc(bi.bmiHeader.biSizeImage);

ZeroMemory(pBits, bi.bmiHeader.biSizeImage);

if (!GetDIBits(hdc, m_bmp, 0, bmp.bmHeight, pBits, &bi, DIB_RGB_COLORS))

{

free(pBits);

pBits = NULL;

}

for (int i = 0; i < bufferlen >> 1; i++)

{

x = *(PosArr + 2 * i);

y = *(PosArr + 2 * i + 1);

int offset = (y - 1)*bmp.bmWidth + x - 1;//可能越界 添加越界检查

DWORD32 RGB = 0;

if (offset > bi.bmiHeader.biSizeImage >> 2 || offset < 0) {

RGB = 0;

OutputDebugString(L"Access Violation!");

}

else

RGB = *((PDWORD32)(pBits)+offset);

*(pRGB + i) = RGB;

}

free(pBits);

pBits = NULL;

return 1;

}

完成后就可以使用ctype调用了。准备工作完成,下面就直接用python调用,获取特定点位置上的颜色,非白色就发送点击指令。然后循环等待下一个黑色块的到来。同时设定定时时间,若长时间依旧是这个颜色,证明游戏结束,直接退出。代码如下:

WindowFunction = ctypes.windll.LoadLibrary("E:\\Python Hack\\DLL\\ScreenFunction.dll")

DllGetPixel = WindowFunction.GetWindowPixel

DllGetPixel.argtypes=[ctypes.wintypes.HWND,ctypes.wintypes.c_int,ctypes.wintypes.c_int]

DllGetPixel.restypes=[ctypes.wintypes.c_uint32]

DllGetMultiPixel = WindowFunction.GetWindowMultiPixel

DllGetMultiPixel.argtypes=[ctypes.wintypes.HWND,ctypes.wintypes.c_void_p,ctypes.wintypes.c_void_p]

DllGetMultiPixel.restypes=[ctypes.wintypes.c_int]

cMulti = (ctypes.wintypes.c_int * 17)(Pos0.x,Pos0.y,Pos1.x,Pos1.y,Pos2.x,Pos2.y,Pos3.x,Pos3.y,

Pos0.x,Pos0.y-5,Pos1.x,Pos1.y-5,Pos2.x,Pos2.y-5,Pos3.x,Pos3.y-5,

0)

dwLen = DllGetMultiPixel(wHWND,byref(cMulti),None)

RGB = (ctypes.wintypes.DWORD * dwLen)()

quit = False

while not quit:

DllGetMultiPixel(wHWND,byref(cMulti),byref(RGB))

flag = 0

if not RGB[0] == 0xfff5f5f5 or not RGB[4] == 0xfff5f5f5:

EmuCursorClick(rect.left+Pos0.x,rect.top+Pos0.y)

flag = 1

elif not RGB[1] == 0xfff5f5f5 or not RGB[5] == 0xfff5f5f5:

EmuCursorClick(rect.left+Pos1.x,rect.top+Pos1.y)

flag = 2

elif not RGB[2] == 0xfff5f5f5 or not RGB[6] == 0xfff5f5f5:

EmuCursorClick(rect.left+Pos2.x,rect.top+Pos2.y)

flag = 3

elif not RGB[3] == 0xfff5f5f5 or not RGB[7] == 0xfff5f5f5:

EmuCursorClick(rect.left+Pos3.x,rect.top+Pos3.y)

flag = 4

cot = 0

if flag == 0:

quit=True

elif flag == 1:

RGB0 = DllGetPixel(wHWND,Pos0.x,Pos0.y) & 0xffffffff

while not RGB0 == 0xfff5f5f5:

time.sleep(0.05)

cot += 1

if cot > 20:

quit=True

break

RGB0 = DllGetPixel(wHWND,Pos0.x,Pos0.y) & 0xffffffff

elif flag == 2:

RGB1 = DllGetPixel(wHWND,Pos1.x,Pos1.y) & 0xffffffff

while not RGB1 == 0xfff5f5f5:

time.sleep(0.05)

cot += 1

if cot > 20:

quit=True

break

RGB1 = DllGetPixel(wHWND,Pos1.x,Pos1.y) & 0xffffffff

elif flag == 3:

RGB2 = DllGetPixel(wHWND,Pos2.x,Pos2.y) & 0xffffffff

while not RGB2 == 0xfff5f5f5:

time.sleep(0.05)

cot += 1

if cot > 20:

quit=True

break

RGB2 = DllGetPixel(wHWND,Pos2.x,Pos2.y) & 0xffffffff

elif flag == 4:

RGB3 = DllGetPixel(wHWND,Pos3.x,Pos3.y) & 0xffffffff

while not RGB3 == 0xfff5f5f5:

time.sleep(0.05)

cot += 1

if cot > 20:

quit=True

break

RGB3 = DllGetPixel(wHWND,Pos3.x,Pos3.y) & 0xffffffff

print 'end'

OK 收工。虽然整个代码风格不像python,更像是脚本化的C语言。但是,谁管哪?好使就行。

python初学,在此留下笔记。

按键精灵python脚本_Python 假装自己是按键精灵相关推荐

  1. 手机编写python脚本_Python 脚本利用adb 进行手机控制

    一.  adb 相关命令: 1. 关闭adb服务:adb kill-server 2. 启动adb服务  adb start-server 3. 查询当前运行的所有设备  adb devices 4. ...

  2. 网页运行python脚本_python脚本和网页有何区别

    Python是一种计算机程序设计语言,一种面向对象的动态类型语言,一种脚本语言.最初被设计用于编写自动化脚本(shell)的,常用于各种服务器的维护和自动化运行.它具有丰富和强大的库.它常被昵称为胶水 ...

  3. python 脚本_python脚本如何同时运行多个

    当我们想一次运行多个py脚本的时候你想到了什么应用场景了吗?当你想同时并行的处理一些对象时你有什么好方法吗?下面我就简单的总结一些这方面的小技巧,方便大家根据情况灵活处理. 1 用一个py脚本运行多个 ...

  4. pycharm定时运行python脚本_Python脚本用于定时关闭网易云音乐PC客户端

    本文主要讲述如何使用Python在指定的秒数后关闭Windows上运行的程序(此程序以网易云音乐为例).本文的背景是昨晚发现网易云音乐的PC客户端没有定时关闭的功能,可以使用Python编写一个简单的 ...

  5. windows双击运行python脚本_Python脚本不能通过双击Windows来执行

    对于这样的项目结构:/tumblr /tumblr /module_foo __init__.py submodule_foo.py /module_bar __init__.py submodule ...

  6. 生物信息学python脚本_Python生物信息学数据管理

    内容简介 本书实例意在解决生物学问题,通过"编程技法"的形式,涵盖尽可能多的组织.分析.表现结果的策略.在每章结尾都会有为生物研究者设计的编程题目,适合教学和自学.本书由六部分组成 ...

  7. 自动化办公python脚本_Python自动化办公

    在公司购买的OA系统上,很多功能都是软件商开发好的,如果有什么自定义的需求,也很难实现.现实情况下需要将一个工单的各类信息汇总整理为一份Excel,看似简单的需求,却需要在OA系统上反复点击多次,人工 ...

  8. 自动打卡python脚本_python实现腾讯文档自动打卡教程

    ​明明365天都不得不待在家里面,居然还要天天去腾讯文档打卡,烦死人了. 天天都填一样的内容,重复无意义的事情,时间就是金钱,浪费人时间约等于谋财害命呀. 所以参考了网络上的代码,并做了改进,用pyt ...

  9. dnf自动刷图python脚本_python+selenium自动化登录dnf11周年活动界面领取奖励登录部分采坑总结[1]...

    背景: Dnf的周年庆活动之一,游戏在6月22日 06:00~6月23日 06:00之间登陆过游戏后可以于6月25日 16:00~7月04日 06:00领取奖励 目标:连续四天自动运行脚本,自动领取所 ...

  10. 一元二次方程python脚本_Python实现求解一元二次方程的方法示例

    本文实例讲述了Python实现求解一元二次方程的方法.分享给大家供大家参考,具体如下: 1. 引入math包 2. 定义返回的对象 3. 判断b*b-4ac的大小 具体计算代码如下: # -*- co ...

最新文章

  1. ubuntu18安装virtualbox
  2. 框架、文档、视图类之间的调用关系
  3. google nexus5x 刷机抓包逆向环境配置(三)
  4. 你必须知道的10个提高Canvas性能技巧
  5. mysql为什么使用b 树作为索引_为什么Mysql用B+树作为索引
  6. 史上最难高考数学,全国平均26分...
  7. bh1750采集流程图_重大更新:STM32空气监测仪,OneNET物联网平台实时查看(原理图、PCB源文件、程序源码等)...
  8. 贪婪洞窟2服务器维护,《贪婪洞窟2》11月30日更新维护多久 贪婪洞窟2更新维护公告...
  9. 正则表达式学习笔记,电话号码、电子邮件、汉字、数字、字母的筛选
  10. brackets ubuntu
  11. Delphi中Chrome Chromium、Cef3学习笔记(三)
  12. python面板数据分析代码_用python预处理面板数据(续)
  13. Linux篇:Shell脚本实现Gitlab双备份
  14. 让代码不运行的快捷键html5,使用 vscode 实现写代码双手不用离开键盘
  15. matlab 去除水印,初試 Matlab 之去除水印
  16. layui form.js select的扩展插件(转自Author:@贤心)
  17. proxy代理服务器,实现跨域
  18. 计算机凭证打印格式设置,打印凭证怎么设置纸张
  19. 我用Python实现自动化办公,美女同事投来羡慕的眼神,而后···
  20. Ubuntu20.4环境下,Android11(R)源码,下载,编译,Pixel4刷机

热门文章

  1. 微软商店下载显示错误,win11无法下载更新的解决方法
  2. android中TextView属性之autoText解析
  3. pix4d操作流程_Pix4d_mapper中文版操作手册.pdf
  4. LBS (基于位置服务)-- Location Based Service
  5. MATLAB信号处理——信号与系统的分析基础(4)
  6. Ubuntu16.04下安装NVIDIA显卡驱动
  7. ubuntu Nvidia 显卡驱动失效问题
  8. 织梦采集侠教程设置免费版本
  9. 软件测试方法和技术有哪些?
  10. 常见的软件测试方法有,常见的几种软件测试方法都有哪些