文章目录[隐藏]
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初学,在此留下笔记。
Measure
Measure
暂无评论
要发表评论,您必须先 登录