Windows操作系统有一个对象层次结构。在层次结构的顶部是"Window Station"(窗口站点)。紧接着是"Desktop"(不要与桌面文件夹或桌面窗口混淆,后者显示该文件夹的图标)。您可以在
文档中了解更多关于这个概念的信息。
我提到这一点是因为通常只有一个桌面可以接收和处理用户输入。当由于超时而被Windows激活的屏幕保护程序时,Windows会创建一个新的桌面来运行屏幕保护程序。
这意味着任何与任何其他桌面相关联的应用程序,包括你的Python脚本,都将无法向新的桌面发送输入,除非进行一些额外的工作。这项工作的性质取决于几个因素。假设最简单的情况是创建了没有“恢复时显示登录屏幕”的屏幕保护程序,并且没有通过远程连接或本地用户登录创建其他Window Station,则可以请求Windows获取活动桌面,将Python脚本附加到该桌面,移动鼠标,然后恢复以前的桌面,以便其余脚本按预期工作。
幸运的是,执行此操作的代码比解释要简单:
import win32con, win32api, win32service
import random
hdesk = win32service.OpenInputDesktop(0, False, win32con.MAXIMUM_ALLOWED);
hdeskOld = win32service.GetThreadDesktop(win32api.GetCurrentThreadId())
hdesk.SetThreadDesktop()
for _ in range(4):
win32api.SetCursorPos((random.randint(0, 100), random.randint(0, 100)))
hdeskOld.SetThreadDesktop()
然而,如果屏幕保护程序在单独的窗口工作站上运行(因为选择了“在恢复时显示登录屏幕”),或者另一个用户通过物理控制台连接或远程连接,则连接和附加到活动桌面将需要提升Python脚本的权限,即使如此,根据其他因素,它可能需要特殊权限。
虽然这可能有助于您的特定情况,但我要补充一下,在一般情况下,核心问题也许更适当地定义为“如何通知用户某个状态,而不会被屏幕保护程序阻止?”这个问题的答案不是“结束屏幕保护程序”,而是“使用像
SetThreadExecutionState()
中的
ES_DISPLAY_REQUIRED
来防止屏幕保护程序运行。并显示一个全屏顶部窗口,显示当前状态,当您想要提醒用户时,闪烁一个引人注目的图形和/或播放声音来引起他们的注意”。
以下是使用tkinter显示窗口的示例:
from datetime import datetime, timedelta
import ctypes
import tkinter as tk
ES_CONTINUOUS = 0x80000000
ES_SYSTEM_REQUIRED = 0x00000001
ES_DISPLAY_REQUIRED= 0x00000002
ALERT_AT = datetime.utcnow() + timedelta(minutes=2)
def timer(root):
if datetime.utcnow() >= ALERT_AT:
root.configure(bg='red')
else:
root.after(1000, timer, root)
root = tk.Tk()
root.bind("<Escape>", lambda e: e.widget.destroy())
root.wm_attributes("-fullscreen", 1)
root.wm_attributes("-topmost", 1)
root.configure(bg='black')
root.config(cursor="none")
root.after(1000, timer, root)
ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED)
root.mainloop()
ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS)
虽然需要更多的工作,但这样做可以解决安全桌面设置为“恢复后显示登录屏幕”时出现的问题,还可以防止系统在配置为休眠时进入休眠状态。它通常允许应用程序更清晰地传达其意图。