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初学,在此留下笔记。

Measure

Measure

David_Li

我还没有学会写个人说明!

暂无评论

相关推荐