python - pymouse.click 不与其他软件交互

标签 python python-3.x input mouse

我过去曾使用 pymouse 来帮助自动化重复游戏并取得成功。

但是,当玩在 BlueStacks 上下载的游戏时, pymouse 似乎会移动到屏幕上的正确位置,但随后不会“注册”任何点击。

如果我将操作系统 native 的其他内容放在同一位置,它将被点击。我不明白为什么在这种情况下,当我将鼠标移动到 Bluestacks 中正在玩的游戏上方的某个位置时,点击不起作用。

代码如下:

from pymouse import PyMouse
import time

m = PyMouse()

i=1
for i in range(1,1000):
        time.sleep(2)
        x, y = m.position()
        print(x, y)
        m.click(x,y,1)
        i+=1

即使鼠标悬停在 Bluestacks 的窗口上,这(如下)也会返回值。

print(m.position())

最佳答案

有一个选项可供您选择,无需求助于虚拟机或编写自定义驱动程序。如果您为鼠标命令安装一个 Hook ,您可以在将它们传递给下一个 Hook 之前更改该 Hook 中的标志。

由于 Windows 如何对 Hook 进行排队,您需要 Hook 将标志更改为最后注册的 Hook ,因此在此示例中,您需要在安装 Hook 之前启动游戏。

下面是执行此操作的一些示例代码,我使用 pyHook 对此进行了测试以检查结果。出于您的目的,您可能希望将其包装在一个线程中,以便它在后台发生。

import atexit
import ctypes
import time
from ctypes import c_short, c_char, c_uint8, c_int32, c_int, c_uint, c_uint32, c_long, byref, Structure, CFUNCTYPE, POINTER
from ctypes.wintypes import DWORD, BOOL, HHOOK, MSG, LPWSTR, WCHAR, WPARAM, LPARAM
from collections import namedtuple
LPMSG = POINTER(MSG)

user32 = ctypes.WinDLL('user32', use_last_error = True)

class MSLLHOOKSTRUCT(Structure):
    _fields_ = [("x", c_long),
                ("y", c_long),
                ('data', c_int32),
                ("flags", DWORD),
                ("time", c_int),
                ('extrainfo', c_int32),
                ]

LowLevelMouseProc = CFUNCTYPE(c_int, WPARAM, LPARAM, POINTER(MSLLHOOKSTRUCT))

SetWindowsHookEx = user32.SetWindowsHookExA
#SetWindowsHookEx.argtypes = [c_int, LowLevelMouseProc, c_int, c_int]
SetWindowsHookEx.restype = HHOOK

CallNextHookEx = user32.CallNextHookEx
#CallNextHookEx.argtypes = [c_int , c_int, c_int, POINTER(MSLLHOOKSTRUCT)]
CallNextHookEx.restype = c_int

UnhookWindowsHookEx = user32.UnhookWindowsHookEx
UnhookWindowsHookEx.argtypes = [HHOOK]
UnhookWindowsHookEx.restype = BOOL

GetMessage = user32.GetMessageW
GetMessage.argtypes = [LPMSG, c_int, c_int, c_int]
GetMessage.restype = BOOL

TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = [LPMSG]
TranslateMessage.restype = BOOL

DispatchMessage = user32.DispatchMessageA
DispatchMessage.argtypes = [LPMSG]

# Beware, as of 2016-01-30 the official docs have a very incomplete list.
# This one was compiled from experience and may be incomplete.
WM_MOUSEMOVE = 0x200
WM_LBUTTONDOWN = 0x201
WM_LBUTTONUP = 0x202
WM_LBUTTONDBLCLK = 0x203
WM_RBUTTONDOWN = 0x204
WM_RBUTTONUP = 0x205
WM_RBUTTONDBLCLK = 0x206
WM_MBUTTONDOWN = 0x207
WM_MBUTTONUP = 0x208
WM_MBUTTONDBLCLK = 0x209
WM_MOUSEWHEEL = 0x20A
WM_XBUTTONDOWN = 0x20B
WM_XBUTTONUP = 0x20C
WM_XBUTTONDBLCLK = 0x20D
WM_NCXBUTTONDOWN = 0x00AB
WM_NCXBUTTONUP = 0x00AC
WM_NCXBUTTONDBLCLK = 0x00AD
WM_MOUSEHWHEEL = 0x20E
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_MOUSEMOVE = 0x0200
WM_MOUSEWHEEL = 0x020A
WM_MOUSEHWHEEL = 0x020E
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205

LEFT = 'left'
RIGHT = 'right'
MIDDLE = 'middle'
X = 'x'

UP = 'up'
DOWN = 'down'
DOUBLE = 'double'

buttons_by_wm_code = {
    WM_LBUTTONDOWN: (DOWN, LEFT),
    WM_LBUTTONUP: (UP, LEFT),
    WM_LBUTTONDBLCLK: (DOUBLE, LEFT),

    WM_RBUTTONDOWN: (DOWN, RIGHT),
    WM_RBUTTONUP: (UP, RIGHT),
    WM_RBUTTONDBLCLK: (DOUBLE, RIGHT),

    WM_MBUTTONDOWN: (DOWN, MIDDLE),
    WM_MBUTTONUP: (UP, MIDDLE),
    WM_MBUTTONDBLCLK: (DOUBLE, MIDDLE),

    WM_XBUTTONDOWN: (DOWN, X),
    WM_XBUTTONUP: (UP, X),
    WM_XBUTTONDBLCLK: (DOUBLE, X),
}

NULL = c_int(0)

def translate_injected_mouse():
    def low_level_mouse_handler(nCode, wParam, lParam):
        struct = lParam.contents

        if wParam in buttons_by_wm_code:
            struct.flags &= 0x11111100 # clear the injected flags
        return CallNextHookEx(NULL, nCode, wParam, lParam)

    WH_MOUSE_LL = c_int(14)
    mouse_callback = LowLevelMouseProc(low_level_mouse_handler)
    mouse_hook = SetWindowsHookEx(WH_MOUSE_LL, mouse_callback, user32._handle, NULL)

    # Register to remove the hook when the interpreter exits. Unfortunately a
    # try/finally block doesn't seem to work here.
    atexit.register(UnhookWindowsHookEx, mouse_hook)

    msg = LPMSG()
    while not GetMessage(msg, NULL, NULL, NULL):
        TranslateMessage(msg)
        DispatchMessage(msg)

if __name__ == '__main__':
    translate_injected_mouse()

将功能包装到线程类中的精简示例:

import atexit
import ctypes
from ctypes import c_int, c_uint, c_uint32, c_long, Structure, CFUNCTYPE, POINTER
from ctypes.wintypes import DWORD, BOOL, HWND, HHOOK, MSG, WPARAM, LPARAM
import threading

LPMSG = POINTER(MSG)

user32 = ctypes.WinDLL('user32', use_last_error = True)

class MSLLHOOKSTRUCT(Structure):
    _fields_ = [("x", c_long), ("y", c_long),
                ('data', c_uint32), ("flags", DWORD),
                ("time", c_int), ('extrainfo', c_uint32), ]

LowLevelMouseProc = CFUNCTYPE(c_int, WPARAM, LPARAM, POINTER(MSLLHOOKSTRUCT))

SetWindowsHookEx = user32.SetWindowsHookExA
#SetWindowsHookEx.argtypes = [c_int, LowLevelMouseProc, c_int, c_int]
SetWindowsHookEx.restype = HHOOK

CallNextHookEx = user32.CallNextHookEx
#CallNextHookEx.argtypes = [c_int , c_int, c_int, POINTER(MSLLHOOKSTRUCT)]
CallNextHookEx.restype = c_int

UnhookWindowsHookEx = user32.UnhookWindowsHookEx
UnhookWindowsHookEx.argtypes = [HHOOK]
UnhookWindowsHookEx.restype = BOOL

GetMessage = user32.GetMessageW
GetMessage.argtypes = [LPMSG, c_int, c_int, c_int]
GetMessage.restype = BOOL

TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = [LPMSG]
TranslateMessage.restype = BOOL

DispatchMessage = user32.DispatchMessageA
DispatchMessage.argtypes = [LPMSG]

NULL = c_int(0)

class TranslateInjectedMouse(threading.Thread):
    daemon=True
    def run(self):
        def low_level_mouse_handler(nCode, wParam, lParam):
            print("handler")
            lParam.contents.flags &= 0x11111100
            return CallNextHookEx(NULL, nCode, wParam, lParam)

        WH_MOUSE_LL = c_int(14)
        mouse_callback = LowLevelMouseProc(low_level_mouse_handler)
        self.mouse_hook = SetWindowsHookEx(WH_MOUSE_LL, mouse_callback, user32._handle, NULL)

        # Register to remove the hook when the interpreter exits. Unfortunately a
        # try/finally block doesn't seem to work here.
        atexit.register(UnhookWindowsHookEx, self.mouse_hook)

        msg = LPMSG()
        while not GetMessage(msg, NULL, NULL, NULL):
            TranslateMessage(msg)
            DispatchMessage(msg)

    def stop(self):
        UnhookWindowsHookEx(self.mouse_hook)

if __name__ == '__main__':
    # this is all you need to translate in background
    t = TranslateInjectedMouse()
    t.start()

    # below this is test code to create clicks
    import time
    mouse_event = user32.mouse_event
    MOUSEEVENTF_LEFTDOWN = 0x0002
    MOUSEEVENTF_LEFTUP = 0x0004

    while True:
        try:
            time.sleep(1)
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)

            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
        except KeyboardInterrupt:
            if t.is_alive():
                t.stop()
            else:
                break

请注意,目前我找不到根据命令终止此线程的方法。线程执行 block 在 GetMessage 等待消息处理,例如 WM_QUIT,因为这发生在 C 中而不是在 python 中,在线程中引发异常不会导致它退出,我尝试了一个大SendMessage、PostMessage 和 PostThreadMessage 等组合的数量,用于尝试发送 WM_QUIT 消息但没有成功。取而代之的是设置守护进程标志,当主线程退出时强制它退出。

关于python - pymouse.click 不与其他软件交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40312557/

相关文章:

python - 是否可以自动合并本体,以便将精确匹配合并到一个首选术语下?

python - Bokeh 在 Raspberry Pi 上找不到条形图模块

python-3.x - 定义并使用元组作为默认参数

python - 如何将列表与数据框的列进行比较,并在数据框列和列表之间的元素匹配时从列表中删除元素?

javascript - Javascript 中的初始输入值

php - 从输入(datetime_local)字段进行MYSQL查询

python - TensorFlow:以随机角度旋转图像和点时的偏移

python - 遍历字符串列表并组合字符串值 Python

python - 使用 python 模拟库测试与 Django Rest Framework API 交互的函数

java - 查找大于平均值的数字 - 为什么我的 IF 语句不能正常工作?