python可以代替按键精灵吗_Python 假装自己是按键精灵

按键精灵作为一款模拟鼠标以及键盘操作的软件来说,其有着相当强大的功能。然而可惜的是,按键精灵使用相当过时的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初学,在此留下笔记。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值