You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

自动点击器模拟点击干扰硬件鼠标状态检测问题排查

问题根源:pynput的click方法干扰了物理鼠标状态检测

你的推断完全正确——问题出在pynput.mouse.Controllerclick()方法上。当你调用rat.click(mouse.Button.left, 1)时,这个方法会执行以下隐含操作:

  1. 如果当前鼠标左键处于按下状态(也就是你物理按住左键的时候),它会先发送一个左键释放事件
  2. 然后发送左键按下事件
  3. 最后发送左键释放事件

这就导致在模拟点击的瞬间,系统会认为左键被短暂释放了,而GetAsyncKeyState(0x01)会读取到这个临时的释放状态,让你的clicker循环中的条件不满足,进而暂停点击。当物理左键还保持按住时,下一次循环又会检测到按下状态,重新开始点击,最终造成断断续续的卡顿甚至看起来像是停止运行的问题。

解决方案:跳过pynput的鼠标模拟,改用ctypes直接发送输入事件

既然我们已经用ctypes读取硬件鼠标状态,不如直接用它来模拟鼠标点击,这样可以完全控制输入事件,避免pynput的自动状态处理干扰。我们可以使用Windows API的SendInput函数来发送鼠标点击事件,这样不会影响物理按键的状态检测。

下面是修改后的完整代码:

import time
import threading
import ctypes
from pynput import keyboard

# 定义Windows API相关结构和函数
PUL = ctypes.POINTER(ctypes.c_ulong)

class KeyBdInput(ctypes.Structure):
    _fields_ = [("wVk", ctypes.c_ushort),
                ("wScan", ctypes.c_ushort),
                ("dwFlags", ctypes.c_ulong),
                ("time", ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class HardwareInput(ctypes.Structure):
    _fields_ = [("uMsg", ctypes.c_ulong),
                ("wParamL", ctypes.c_short),
                ("wParamH", ctypes.c_ushort)]

class MouseInput(ctypes.Structure):
    _fields_ = [("dx", ctypes.c_long),
                ("dy", ctypes.c_long),
                ("mouseData", ctypes.c_ulong),
                ("dwFlags", ctypes.c_ulong),
                ("time",ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class Input_I(ctypes.Union):
    _fields_ = [("ki", KeyBdInput),
                ("mi", MouseInput),
                ("hi", HardwareInput)]

class Input(ctypes.Structure):
    _fields_ = [("type", ctypes.c_ulong),
                ("ii", Input_I)]

def simulate_left_click():
    # 发送左键按下事件
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.mi = MouseInput(0, 0, 0, 0x0002, 0, ctypes.pointer(extra))
    x = Input(ctypes.c_ulong(0), ii_)
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
    
    # 发送左键释放事件
    ii_.mi = MouseInput(0, 0, 0, 0x0004, 0, ctypes.pointer(extra))
    x = Input(ctypes.c_ulong(0), ii_)
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

toggle = keyboard.Key.f6
clicking = False

def clicker():
    while True:
        if clicking and (ctypes.windll.user32.GetAsyncKeyState(0x01) & 0x8000 != 0):
            simulate_left_click()
            time.sleep(0.01)
        else:
            time.sleep(0.005)

def toggled(key):
    global clicking
    if key == toggle:
        clicking = not clicking
        print("Autoclicker:", clicking)

clonk = threading.Thread(target=clicker, daemon=True)
clonk.start()

with keyboard.Listener(on_press=toggled) as key_listener:
    key_listener.join()

为什么这个方案有效?

SendInput直接向Windows输入队列发送原始输入事件,不会像pynput的click方法那样先检查当前鼠标状态再调整行为。我们手动发送按下和释放事件,而物理上按住的左键状态会被GetAsyncKeyState持续检测到——因为这个函数读取的是硬件层面的按键状态,不受模拟输入事件的影响。

这样一来,只要你物理按住左键,点击器就会持续发送模拟点击,不会因为模拟操作导致状态检测中断。

内容的提问来源于stack exchange,提问作者ToeSpreader

火山引擎 最新活动