keep_awake.py 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. # -*- coding: utf-8 -*-
  2. # Author : Charley
  3. # Python : 3.12.10
  4. # Date : 2026/08/08
  5. """防止 Windows 自动锁屏 / 息屏,保持机器处于唤醒可用状态。
  6. 原理说明:
  7. Windows 锁屏界面运行在独立的 Secure Desktop(Winlogon)上,普通用户态脚本
  8. 无法向其注入按键,因此「锁了再自动输密码解锁」在常规权限下无法实现。本脚本
  9. 改为从源头阻止自动锁屏——通过两条手段叠加保证覆盖面:
  10. 1. SetThreadExecutionState 持续声明「系统 + 显示器」处于使用中,阻止息屏与
  11. 随之而来的空闲锁屏;
  12. 2. 每隔一段时间模拟一次无副作用的按键(F15),重置系统空闲计时器,覆盖
  13. 「计算机不活动限制」这类基于空闲时长的锁屏策略。
  14. 注意:本脚本无法阻止手动 Win+L、动态锁、或域策略强制锁屏——那类锁屏一旦发生
  15. 只能人工解锁。
  16. """
  17. import sys
  18. import time
  19. import ctypes
  20. from ctypes import wintypes
  21. # ---- 常量:SetThreadExecutionState 标志位 ----
  22. ES_CONTINUOUS = 0x80000000 # 持续生效,直到下次调用清除
  23. ES_SYSTEM_REQUIRED = 0x00000001 # 阻止系统进入睡眠
  24. ES_DISPLAY_REQUIRED = 0x00000002 # 阻止显示器关闭(连带阻止空闲锁屏)
  25. # ---- 常量:模拟按键 ----
  26. VK_F15 = 0x7E # F15 键,绝大多数程序不响应,用作无副作用「保活」键
  27. KEYEVENTF_KEYUP = 0x0002 # 按键抬起事件标志
  28. # ---- 常量:轮询与保活间隔(秒)----
  29. NUDGE_INTERVAL = 60 # 每 60 秒模拟一次按键,重置空闲计时器
  30. def prevent_sleep_lock() -> bool:
  31. """声明系统与显示器处于使用中,阻止息屏与空闲锁屏。
  32. Returns:
  33. bool: True 表示调用成功,False 表示 SetThreadExecutionState 返回 0(失败)。
  34. """
  35. # 返回值为 0 表示失败;非 0 为上一次的执行状态标志
  36. result = ctypes.windll.kernel32.SetThreadExecutionState(
  37. ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED
  38. )
  39. return result != 0
  40. def allow_sleep_lock() -> None:
  41. """清除保活声明,恢复系统默认的息屏 / 锁屏行为。"""
  42. # 只传 ES_CONTINUOUS 表示撤销之前的 SYSTEM/DISPLAY 声明
  43. ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS)
  44. def nudge_input() -> None:
  45. """模拟一次 F15 按下并抬起,重置系统空闲计时器。"""
  46. user32 = ctypes.windll.user32
  47. # 按下再抬起,模拟一次完整敲击;F15 不影响正常输入与前台程序
  48. user32.keybd_event(VK_F15, 0, 0, 0)
  49. user32.keybd_event(VK_F15, 0, KEYEVENTF_KEYUP, 0)
  50. def keep_awake() -> None:
  51. """主循环:持续保持机器唤醒,直到用户 Ctrl+C 退出。
  52. 循环内每 NUDGE_INTERVAL 秒重申一次保活声明并模拟一次按键,
  53. 退出时恢复系统默认锁屏行为。
  54. """
  55. print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 防锁屏已启动,每 {NUDGE_INTERVAL}s 保活一次,按 Ctrl+C 退出")
  56. try:
  57. while True:
  58. ok = prevent_sleep_lock() # 重申一次,防止声明被其他程序清除
  59. nudge_input() # 敲一下 F15,重置空闲计时器
  60. status = "ok" if ok else "FAILED"
  61. print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 保活心跳 -> {status}")
  62. time.sleep(NUDGE_INTERVAL)
  63. except KeyboardInterrupt:
  64. print(f"\n[{time.strftime('%Y-%m-%d %H:%M:%S')}] 收到退出信号,恢复默认锁屏行为")
  65. finally:
  66. allow_sleep_lock() # 无论如何都恢复系统默认,避免留下副作用
  67. if __name__ == "__main__":
  68. # 仅支持 Windows;其他平台上 windll 不存在,直接提示退出
  69. if not sys.platform.startswith("win"):
  70. print("本脚本仅适用于 Windows")
  71. sys.exit(1)
  72. keep_awake()