2026年1月19日晚上,我帮朋友跑一个视频转码,顺手开了几路下载任务,然后合上笔记本就去睡了。第二天起来,转码进度停在 37%,下载任务全部报错,原因都是同一个——凌晨某个时刻,系统认为我已经离开,触发了“空闲超时关机”策略。系统设置里写得很清楚:电脑100分钟无操作后关机。那台机器上还挂着十几个后台任务,我就是被这个“100分钟无操作自动关机”坑掉的。
很多人遇到这种情况的第一反应是去改电源计划,把睡眠和关机都设成“从不”。但有些电脑是公司策略锁死的,有些是任务计划里加了定时关机,还有的场景根本不允许你改设置。于是就有了另一个思路:模拟鼠标操作,让系统始终认为“有人还在用电脑”。这篇文章就从这个需求说起,适合需要长期挂机跑任务的个人用户,也适合需要在演示、监控、自动化测试时保持会话不掉的开发者。我会把原理、方案选型、可复制的脚本和踩坑记录都放出来,尽量一次说透。
1. 为什么电脑会“以为你离开”了——系统空闲判定与关机/锁屏的底层逻辑
1.1 系统不是按照“人”,而是按照“输入事件”判断
无论是 Windows、macOS 还是 Linux 桌面,判断用户是否在场的方式都不是摄像头,而是输入事件。鼠标移动、滚轮滚动、键盘按键、触摸板点击,都会被系统记录成一条带时间戳的“最近输入事件”。Windows 上对应的 API 是 GetLastInputInfo,它返回系统最后一次输入发生的时间,系统拿这个时间和当前时间一减,就得出了“空闲时间”。
屏幕保护、锁屏、电源管理、甚至某些软件的“自动离线”定时器,全都基于这个空闲时间做判断。所以你坐在电脑前看视频不动鼠标,系统依然会认为你离开了——因为在它眼里,长时间没有输入事件就等于人不在。这个设计本身是为了省电和安全,但对挂机跑任务的人来说,就成了噩梦。
模拟鼠标操作的本质,就是伪造这条“最近输入事件”。只要定期给系统注入一次真实的鼠标移动事件,空闲时间就会被清零,屏幕保护、睡眠、锁屏那些基于空闲计时的策略就始终不会触发。注意关键词是“真实”。单纯调用 SetCursorPos 把光标挪个位置,在很多版本的 Windows 上并不会生成输入事件,也不会刷新空闲计时器,必须用 mouse_event 或 SendInput 这类能注入系统输入队列的 API。这个细节是很多号称“鼠标精灵”却无效的根本原因。
1.2 三种把你“踢下线”的机制:屏幕关闭、睡眠休眠、定时关机
实际动手以前,先要搞清楚系统到底是通过哪种机制把你踢下线的。我遇到过的情况大概可以分成三类:
| 机制 | 现象 | 对任务的影响 | 模拟鼠标是否有效 |
|---|---|---|---|
| 屏幕关闭 | 显示器黑屏,主机还在运行 | 基本没影响,只是看不见画面 | 有效,但没必要 |
| 睡眠/休眠 | 整机进入低功耗,风扇停转,网络断开 | 任务中断,等待状态丢失 | 有效,是最常见的用途 |
| 定时关机 | 系统在空闲 N 分钟后执行关机或重启 | 任务中断,进程全部结束 | 不一定有效,要看触发方式 |
屏幕关闭是最温和的,它只是把显示输出关掉,后台任务照常跑。这种场景不需要任何模拟输入,去电源设置里把“关闭显示器”改成“从不”或者延长就行。睡眠和休眠就比较麻烦,一旦进入,CPU 暂停、内存进入自刷新,视频渲染、下载任务、脚本进程全部停摆。模拟鼠标在这里是真正管用的。
定时关机要单独说。它可能是电源计划里的“休眠超时”,也可能是任务计划程序里添加的“系统空闲 N 分钟后运行 shutdown.exe”。如果是前者,模拟鼠标能防住;如果是后者,就要看任务是怎么写的——有些计划任务会忽略输入事件,只认“系统空闲时间”这个计数器,你就算每 50 分钟动一下鼠标,某些版本的 Windows 依旧会在累计空闲满 100 分钟后执行关机。这种情况需要的是找到并调整那个计划任务,而不是靠模拟输入硬扛。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模拟鼠标操作的方案选型:硬件、脚本、系统原生,哪种最适合你
2.1 硬件方案:USB鼠标振荡器/自动移动器
最省心的办法其实是买一个“鼠标自动移动器”。它是一个 USB 小设备,插上以后能让实际鼠标指针周期性微动,有些还带开关和间隔调节。优点非常明显:不依赖操作系统、没有后台进程、不会被软件误杀、即插即用。对于一台长期放在机房或角落里无人值守的电脑,这种物理方案很稳定。
缺点也同样明显。第一是花钱,虽然不贵但也不是零成本;第二是机械结构容易坏,尤其是那种靠马达带动摇杆的型号,连续跑几个月后故障率不低;第三是有时会被某些安全软件识别为可疑外设,在管控严格的办公电脑上甚至会被直接拒绝驱动。我见过有人把这种设备插在监控电脑上,结果被 IT 告警“发现未知 USB 设备”,后续一堆麻烦。所以我个人建议:公司电脑、有严格安全策略的环境不要用这种旁门左道,老老实实走正规审批流程。
2.2 软件方案:AutoHotkey、Python、PowerShell
软件方案的自由度就高很多了。Windows 下最常用的是 AutoHotkey,它的脚本语言非常贴近“模拟输入”这个场景,几行代码就能做一个定时移动鼠标的小工具,还能编译成 exe 放到任意机器上跑。Python 的优势是通用性和可调试性,可以在脚本里加日志、随机延迟、判断鼠标空闲状态,而且代码逻辑比 AHK 更容易维护。PowerShell 和批处理则完全不需要额外安装环境,适合在“不能装任何第三方软件”的机器上用,但写起来比较绕,可读性差一些。
我列一个简单的选型建议:临时用一次、机器上没有 Python 环境、也不想装东西,用 PowerShell 或 VBS 就好;长期挂机、要控制逻辑、想记录日志,用 Python;只想要一个轻量小工具、开机自启、不占资源,用 AutoHotkey 编译出来的 exe 最舒服。这三种方案都能做到“每 50 分钟模拟动一下鼠标”,差别在于后期的可维护性和灵活性。
2.3 我的选择:Python + Windows计划任务
我自己的最终选择是 Python 脚本加 Windows 计划任务。原因有三个:第一,脚本逻辑可以随时改,比如我把间隔从固定值改成根据系统空闲时间动态判断,只需改几行代码;第二,Python 脚本里有完整的日志输出,哪天出了“看似动了鼠标却还是被锁”的玄学问题,我能翻日志去排查;第三,计划任务可以设置开机自启、失败重试,比把脚本丢到启动文件夹里要可靠得多。
我不太推荐在这个需求上一上来就写高大上的框架。模拟鼠标操作说到底就是一个“定时注入输入事件”的活,核心逻辑可能不到二十行代码。写得越简单,后面越不容易出问题。接下来我就把完整的实现步骤和代码放出来,你直接照着抄就行。
3. 动手实现“每50分钟动一下鼠标”的防休眠脚本
3.1 先看电源计划,再决定模拟间隔
有人直接写了个每 50 分钟动一次鼠标的脚本,结果第二天发现系统还在凌晨睡过去了。为什么?因为那台电脑的电源计划里,睡眠超时是 30 分钟,50 分钟的模拟间隔比系统的空闲阈值还要长,脚本还没触发,系统就已经先进入睡眠了。这个逻辑一定要记住:模拟间隔必须比系统里最短的空闲阈值还要短,最好预留一半的安全余量。
在 Windows 上查询当前电源计划,最简单的是命令行:
bash复制powercfg /query SCHEME_CURRENT
输出内容很长,重点关注“硬盘关闭”“睡眠”“休眠”这几项的 TimeoutValue,单位是秒。如果你看到“睡眠”后面写着 1800,那就是 30 分钟;写着 6000,那就是 100 分钟。假设系统设置的是 100 分钟无操作关机,你的模拟间隔至少得小于 100 分钟,通常我会设成它的三分之一到二分之一,也就是 30 到 50 分钟都合理。如果系统睡眠超时只有 10 分钟,那你每 50 分钟动一次就必死无疑,必须改成 3 到 5 分钟。
有些机器还开着“快速启动”和“混合睡眠”,这两个功能会改变电源策略的实际行为,单纯看控制面板里的数字并不可靠。最稳妥的做法是:改完脚本后先实测一次,把间隔设成 2 分钟跑一晚上,如果没问题再逐步调大。不要一上来就追求极限,挂机任务丢一次成本太高了。
3.2 用Python写一个模拟鼠标移动的脚本
下面这个脚本是我现在一直在用的版本,基于 Windows API,不依赖任何第三方库,装好 Python 就能直接跑。它做的事情很简单:每隔 N 分钟,向系统注入一次真实的鼠标相对移动事件,让空闲计时器归零。
python复制import ctypes
import random
import sys
import time
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
def simulate_mouse_move():
# MOUSEEVENTF_MOVE = 0x0001,这是相对位移,会生成真实的鼠标输入事件
# 在当前位置周围随机偏移 3-8 个像素,避免每次都走完全相同的路径
dx = random.randint(3, 8)
dy = random.randint(3, 8)
user32.mouse_event(0x0001, dx, dy, 0, 0)
time.sleep(0.1)
# 移回来,尽量不改变鼠标实际位置
user32.mouse_event(0x0001, -dx, -dy, 0, 0)
def main():
interval = 50 # 默认间隔,单位:分钟
if len(sys.argv) > 1:
interval = int(sys.argv[1])
print(f"模拟鼠标操作已启动,间隔 {interval} 分钟")
while True:
time.sleep(interval * 60)
simulate_mouse_move()
print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} 已模拟鼠标移动")
if __name__ == "__main__":
main()
保存成 keep_awake.py,运行 python keep_awake.py 30 就能每隔 30 分钟模拟一次鼠标移动。如果你只想防休眠、又不想让鼠标指针在屏幕上乱跳,可以把 simulate_mouse_move 换成模拟 ScrollLock 键:
python复制def simulate_scrolllock():
# VK_SCROLL = 0x91
user32.keybd_event(0x91, 0, 0, 0) # 按下
time.sleep(0.05)
user32.keybd_event(0x91, 0, 2, 0) # 释放,KEYEVENTF_KEYUP = 0x0002
ScrollLock 平时几乎不会有副作用,但能刷新系统空闲计时器。用这种方式,即使你正在用电脑做别的事情,也不会觉得鼠标被“鬼手”控制了。脚本本身要不要加随机延迟?我个人经验是:加一个 ±20% 的随机延时会更好,既不会真的影响什么,也能让节奏更接近真人行为。代码改成 time.sleep(interval * 60 * random.uniform(0.8, 1.2)) 即可。
3.3 AutoHotkey的极简版与无进程残留运行
如果你不想装 Python,或者想要一个更轻量的方案,AutoHotkey 脚本是很好的选择。把下面这段保存为 keep_awake.ahk,安装 AutoHotkey 后双击运行即可。
autohotkey复制#Persistent
global interval := 50 * 60 * 1000 ; 50分钟,单位毫秒
SetTimer, FakeMove, %interval%
return
FakeMove:
MouseGetPos, x, y
MouseMove, x+2, y+2, 0
MouseMove, x, y, 0
return
注意这里我用的是 MouseMove,这是 AutoHotkey 生成的真实鼠标事件,不是单纯设置光标位置,所以可以刷新空闲计时器。和 Python 版本相比,AHK 版本更省心,内存占用也小,但缺少日志和动态间隔逻辑。想实现动态间隔,需要在计时器回调里用 SetTimer 重新设置周期。AHK 还能用 Ahk2Exe 编译成独立的 exe,放到目标机器上不需要安装任何环境,这对“给不太懂电脑的人部署”来说是最方便的。
这里有一个很多人不知道的小技巧:如果脚本在托盘区显示图标很烦,可以在脚本开头加一行 #NoTrayIcon,这样就不会留下托盘图标。不过我建议调试阶段先留着托盘图标,确认脚本在跑以后再去掉。
3.4 用Windows计划任务实现开机自启/无人值守
脚本写好了,但每次开机手动运行也太蠢了。用 Windows 任务计划程序注册一个开机自启任务,才是真正意义上的无人值守。操作步骤如下:
- 按 Win+R,输入 taskschd.msc,打开任务计划程序。
- 右侧点击“创建任务”,名称填 keep_awake。
- “常规”选项卡里勾选“不管用户是否登录都要运行”。但这里要特别注意:如果你勾选了这一项,脚本运行在 Session 0 里,有些版本的系统对 Session 0 的输入注入支持有限,反而可能失效。我实际测试下来,更稳的是选“只在用户登录时运行”。
- “触发器”里新建一个“登录时”的触发器。
- “操作”里新建操作,程序填 python.exe 的完整路径,参数填脚本路径和间隔,比如
D:\scripts\keep_awake.py 30。 - “条件”选项卡把“只有当计算机空闲时启动”取消勾选,“电源”里取消“使用电池时停止”,如果是笔记本插电运行,建议勾上“唤醒计算机以运行此任务”旁边的相关项。
- 确认保存,重启一次看看任务是否自动拉起。
如果目标机器没有 Python,又不想装环境,更轻的办法是直接在计划任务里调用 PowerShell。在“操作”里填入 powershell.exe,参数写成一段调用 .NET SendInput 的命令。不过那段代码太长了,可读性也差,除非是强制要求不能装软件的场景,否则我不推荐。
4. 常见问题与排查技巧实录
4.1 脚本明明在动,电脑还是睡眠/锁屏了
这是被问得最多的问题。脚本日志里清楚记录着“每 30 分钟已模拟鼠标移动”,但电脑还是凌晨锁屏甚至关机了。通常原因有四个。
第一,间隔大于系统的空闲阈值,这是我前面反复强调的,先查电源计划里的睡眠超时,再看自己的脚本间隔,确保至少有 20% 的余量。第二,系统“空闲关机”不是电源计划,而是任务计划程序里的某个任务,模拟鼠标只能阻止基于空闲计时的睡眠/锁屏,对任务计划里显式触发的 shutdown.exe 不一定有效。第三,屏幕保护程序设置了“恢复时显示登录界面”,屏幕保护一启动,会话就被锁定,模拟鼠标不会自动帮你解除锁定,需要手动输密码。第四,显卡驱动或某类 OEM 电源管理工具会在更底层接管电源策略,脚本层面无能为力。
排查思路是先对号入座:看事件查看器里系统日志的 Kernel-Power 事件,能知道是哪个 ID、哪个阶段进入了睡眠或关机。如果日志显示的是“按电源按钮”或“系统睡眠”,基本就是电源计划;如果是计划任务触发的,事件里会有对应的进程是 shutdown.exe。定位到真实原因以后再动手,别一上来就怀疑脚本有问题。
4.2 模拟鼠标影响我正在用的电脑怎么办
如果你并不是要全程无人值守,而是人偶尔会用这台电脑,同时又想让它挂着不睡眠,最烦的就是脚本每隔一段时间让鼠标跳一下。我的解决方案是:让脚本先判断系统真正的空闲时间,只有空闲超过一定分钟数才模拟输入。
python复制class LASTINPUTINFO(ctypes.Structure):
_fields_ = [
("cbSize", ctypes.c_uint),
("dwTime", ctypes.c_uint)
]
def idle_seconds():
info = LASTINPUTINFO()
info.cbSize = ctypes.sizeof(LASTINPUTINFO)
if user32.GetLastInputInfo(ctypes.byref(info)):
millis = kernel32.GetTickCount() - info.dwTime
return millis / 1000.0
return 0
然后在主循环里,每次先取 idle_seconds,如果这个值已经大于某个阈值(比如 5 分钟),才执行模拟鼠标移动;如果系统空闲时间还很小,说明真人正在操作,脚本就继续睡觉。这样既不影响正常使用,又能在人离开后兜底。把这段逻辑加到前面 keep_awake.py 里,几行代码的事,实用性提升很大。
另一种方案是改用 ScrollLock 键模拟,或者用音量静音键那种不干扰光标的按键。但注意,某些程序在全屏模式下会拦截组合键,ScrollLock 也未必能刷新所有场景的空闲计时器。最稳妥的还是“空闲超过 N 分钟才动鼠标”。
4.3 有些程序会“识别”模拟输入,怎么办
某些视频会议软件、远程桌面客户端、或者严格的拖机监控程序,会检测输入事件是不是来自真实物理设备。大部分普通场景里,mouse_event 和 keybd_event 足够用,因为这些程序拿到的就是系统输入队列里的合法输入。但如果对方用钩子程序或驱动级检测,脚本层面的伪造就会露馅。
我的态度很明确:不要为了绕过检测去搞更底层的驱动、内核回调之类的东西。模拟鼠标操作的合理用途是防止自己电脑空闲锁机、防止睡眠丢任务,不是用来对抗公司监控或者游戏反作弊。如果一台电脑被管理员策略强制锁屏、强制关机,正确做法是联系 IT 或管理员调整策略,而不是自己写工具绕过去。合规边界守住了,这个技能才能真正帮到你。
如果只是想让自己的脚本更稳定,可以把模拟方式从 mouse_event 升级为 SendInput,它在 Windows API 层面更标准,也更容易被系统识别为真实输入。需要完整 SendInput 示例的话,网上有很多现成的 Python 封装,我这里就不贴长代码了,原则是先保证系统空闲计时器被刷新,再去追求“不被识别”。
最后分享一点我自己踩过的坑
模拟鼠标操作这件事,看起来简单,但真正用起来还是有不少细节。我个人现在的工作习惯是:先改电源计划,能通过 powercfg /change standby-timeout-ac 0 这类命令解决的就不要写脚本;只有遇到策略锁死或必须保持会话活跃的场景,才用模拟输入兜底。脚本的间隔永远不要设置成和系统阈值一样,宁可多动几次,也不要临界触发。日志一定要留着,哪天出了问题能快速判断是脚本没跑、还是系统策略太强。这个“20260119 模拟鼠标操作”的项目折腾了我一个晚上,希望这篇记录能帮你少走一次弯路。
