赞
踩
这一篇我们来探索如何实现后台键鼠操作。
键鼠操作就是模拟人工操作了,一旦实现了后台键鼠操作,我们就可以让游戏窗口在后台按照我们的脚本运行,同时处理别的工作了,也能够实现利用脚本同时操作多个游戏窗口。
我首先查阅到文档是Simulating Input,但是里面使用的SendInput函数,根据原理只能对前台窗口生效,但我们要实现后台操作,所以我继续翻阅文档,得到了更多信息:关于窗口消息的文档_About Messages and Message Queues_中给出了游戏窗口如何处理键鼠操作的解释。键鼠操作其实就是向当前窗口发送对应的消息,我们可以通过PostMessageW或者SendMessage来想特定窗口发送消息,不同的是,PostMessageW只是通知窗口有个消息要处理,发送消息后不会等待消息处理结果直接返回,而SendMessage是通知窗口有个消息要立即处理,并等待处理结果再返回,二者在发送消息时没有区别,下面仅以PostMessageW为例,讲解如何发送消息,PostMessageW的声明如下:
BOOL PostMessageW(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
其中hWnd是窗口句柄,Msg是消息的ID,wParam和lParam都需要按照消息的要求进行设置。每个消息的要求都不一样,具体如何设置,需要查看对应消息的文档。
对于键盘消息我们主要关注WM_KEYDOWN、WM_KEYUP,其中WM_KEYDOWN和WM_KEYUP分别对应按键的按下和放开。
from ctypes import windll``from ctypes.wintypes import HWND``import string``import time`` ``PostMessageW = windll.user32.PostMessageW``MapVirtualKeyW = windll.user32.MapVirtualKeyW``VkKeyScanA = windll.user32.VkKeyScanA`` ``WM_KEYDOWN = 0x100``WM_KEYUP = 0x101`` ``# https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes``VkCode = {` `"back": 0x08,` `"tab": 0x09,` `"return": 0x0D,` `"shift": 0x10,` `"control": 0x11,` `"menu": 0x12,` `"pause": 0x13,` `"capital": 0x14,` `"escape": 0x1B,` `"space": 0x20,` `"end": 0x23,` `"home": 0x24,` `"left": 0x25,` `"up": 0x26,` `"right": 0x27,` `"down": 0x28,` `"print": 0x2A,` `"snapshot": 0x2C,` `"insert": 0x2D,` `"delete": 0x2E,` `"lwin": 0x5B,` `"rwin": 0x5C,` `"numpad0": 0x60,` `"numpad1": 0x61,` `"numpad2": 0x62,` `"numpad3": 0x63,` `"numpad4": 0x64,` `"numpad5": 0x65,` `"numpad6": 0x66,` `"numpad7": 0x67,` `"numpad8": 0x68,` `"numpad9": 0x69,` `"multiply": 0x6A,` `"add": 0x6B,` `"separator": 0x6C,` `"subtract": 0x6D,` `"decimal": 0x6E,` `"divide": 0x6F,` `"f1": 0x70,` `"f2": 0x71,` `"f3": 0x72,` `"f4": 0x73,` `"f5": 0x74,` `"f6": 0x75,` `"f7": 0x76,` `"f8": 0x77,` `"f9": 0x78,` `"f10": 0x79,` `"f11": 0x7A,` `"f12": 0x7B,` `"numlock": 0x90,` `"scroll": 0x91,` `"lshift": 0xA0,` `"rshift": 0xA1,` `"lcontrol": 0xA2,` `"rcontrol": 0xA3,` `"lmenu": 0xA4,` `"rmenu": 0XA5``}`` `` ``def get_virtual_keycode(key: str):` `"""根据按键名获取虚拟按键码`` ` `Args:` `key (str): 按键名`` ` `Returns:` `int: 虚拟按键码` `"""` `if len(key) == 1 and key in string.printable:` `# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscana` `return VkKeyScanA(ord(key)) & 0xff` `else:` `return VkCode[key]`` `` ``def key_down(handle: HWND, key: str):` `"""按下指定按键`` ` `Args:` `handle (HWND): 窗口句柄` `key (str): 按键名` `"""` `vk_code = get_virtual_keycode(key)` `scan_code = MapVirtualKeyW(vk_code, 0)` `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown` `wparam = vk_code` `lparam = (scan_code << 16) | 1` `PostMessageW(handle, WM_KEYDOWN, wparam, lparam)`` `` ``def key_up(handle: HWND, key: str):` `"""放开指定按键`` ` `Args:` `handle (HWND): 窗口句柄` `key (str): 按键名` `"""` `vk_code = get_virtual_keycode(key)` `scan_code = MapVirtualKeyW(vk_code, 0)` `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup` `wparam = vk_code` `lparam = (scan_code << 16) | 0XC0000001` `PostMessageW(handle, WM_KEYUP, wparam, lparam)`` `` ``if __name__ == "__main__":` `# 需要和目标窗口同一权限,游戏窗口通常是管理员权限` `import sys` `if not windll.shell32.IsUserAnAdmin():` `# 不是管理员就提权` `windll.shell32.ShellExecuteW(` `None, "runas", sys.executable, __file__, None, 1)`` ` `import win32gui` `handle = windll.user32.FindWindowW(None, "雷电模拟器")` `#handle = win32gui.FindWindowEx(0, 0, None, "雷电模拟器")` `print(handle)` `classname = win32gui.GetClassName(handle)` `# print(classname)` `if classname == 'LDPlayerMainFrame':` `mainhandle = win32gui.FindWindowEx(handle, 0, "RenderWindow", "TheRender")` `print(mainhandle)` `renderhandle = win32gui.FindWindowEx(mainhandle, 0, "subWin", "sub")` `print(renderhandle)` `clickhandle = renderhandle` `else:` `clickhandle = handle`` ` `print(clickhandle)` `# 控制角色向前移动两秒` `key_down(mainhandle, 'w')` `time.sleep(2)` `key_up(mainhandle, 'w')
后台发送按键消息“w”,因按下间隔2秒后松开,效果如上图
对于鼠标消息我们主要关注WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEWHEEL,这些消息分别对应鼠标移动、鼠标左键按下和放开,以及滚轮操作。
from ctypes import windll, byref``from ctypes.wintypes import HWND, POINT``import time`` ``PostMessageW = windll.user32.PostMessageW``ClientToScreen = windll.user32.ClientToScreen`` ``WM_MOUSEMOVE = 0x0200``WM_LBUTTONDOWN = 0x0201``WM_LBUTTONUP = 0x202``WM_MOUSEWHEEL = 0x020A``WHEEL_DELTA = 120`` `` ``def move_to(handle: HWND, x: int, y: int):` `"""移动鼠标到坐标(x, y)`` ` `Args:` `handle (HWND): 窗口句柄` `x (int): 横坐标` `y (int): 纵坐标` `"""` `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousemove` `wparam = 0` `lparam = y << 16 | x` `PostMessageW(handle, WM_MOUSEMOVE, wparam, lparam)`` `` ``def left_down(handle: HWND, x: int, y: int):` `"""在坐标(x, y)按下鼠标左键`` ` `Args:` `handle (HWND): 窗口句柄` `x (int): 横坐标` `y (int): 纵坐标` `"""` `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttondown` `wparam = 0` `lparam = y << 16 | x` `PostMessageW(handle, WM_LBUTTONDOWN, wparam, lparam)`` `` ``def left_up(handle: HWND, x: int, y: int):` `"""在坐标(x, y)放开鼠标左键`` ` `Args:` `handle (HWND): 窗口句柄` `x (int): 横坐标` `y (int): 纵坐标` `"""` `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttonup` `wparam = 0` `lparam = y << 16 | x` `PostMessageW(handle, WM_LBUTTONUP, wparam, lparam)`` `` ``def scroll(handle: HWND, delta: int, x: int, y: int):` `"""在坐标(x, y)滚动鼠标滚轮`` ` `Args:` `handle (HWND): 窗口句柄` `delta (int): 为正向上滚动,为负向下滚动` `x (int): 横坐标` `y (int): 纵坐标` `"""` `move_to(handle, x, y)` `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel` `wparam = delta << 16` `p = POINT(x, y)` `ClientToScreen(handle, byref(p))` `lparam = p.y << 16 | p.x` `PostMessageW(handle, WM_MOUSEWHEEL, wparam, lparam)`` `` ``def scroll_up(handle: HWND, x: int, y: int):` `"""在坐标(x, y)向上滚动鼠标滚轮`` ` `Args:` `handle (HWND): 窗口句柄` `x (int): 横坐标` `y (int): 纵坐标` `"""` `scroll(handle, WHEEL_DELTA, x, y)`` `` ``def scroll_down(handle: HWND, x: int, y: int):` `"""在坐标(x, y)向下滚动鼠标滚轮`` ` `Args:` `handle (HWND): 窗口句柄` `x (int): 横坐标` `y (int): 纵坐标` `"""` `scroll(handle, -WHEEL_DELTA, x, y)`` `` ``if __name__ == "__main__":` `# 需要和目标窗口同一权限,游戏窗口通常是管理员权限` `import sys` `if not windll.shell32.IsUserAnAdmin():` `# 不是管理员就提权` `windll.shell32.ShellExecuteW(` `None, "runas", sys.executable, __file__, None, 1)`` ` `import cv2` `handle = windll.user32.FindWindowW(None, "雷电模拟器")` `# 点击线路` `left_down(handle, 1234, 20)` `time.sleep(0.1)` `left_up(handle, 1234, 20)` `time.sleep(1)` `# 滚动线路列表` `scroll_down(handle, 1000, 200)
由于部分游戏技术干预,玩家无法使用后台键鼠操作,可以考虑使用SendInput进行前台操作,详细操作见前面的文章。
以上就是本篇的所有内容,下一篇我们将探索如何实现窗口操作。
本文转自网络,如有侵权,请联系删除。
除了上述分享,如果你也喜欢编程,想通过学习Python获取更高薪资,这里给大家分享一份Python学习资料。
这里给大家展示一下我进的兼职群和最近接单的截图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。