编程中一种打断屏幕保护的方法是移动鼠标光标。然而,在Windows上,这并不像看起来那么简单。
Windows操作系统有一个对象层次结构。在层次结构的顶部是“窗口站”。其下方是“桌面”(不要与桌面文件夹或甚至显示该文件夹图标的桌面窗口混淆)。您可以在文档中了解更多关于此概念的信息。
我提到这一点是因为通常情况下,任何时候只有一个桌面可以接收和处理用户输入。当由于超时而由Windows激活屏幕保护程序时,Windows会创建一个新的桌面来运行屏幕保护程序。
这意味着与任何其他桌面相关联的应用程序,包括您编写的正在运行的程序,将无法发送输入到新的桌面,除非进行一些额外的工作。该工作的性质取决于几个因素。假设最简单的情况是创建了一个没有“恢复时显示登录屏幕”的屏幕保护程序,并且没有通过远程连接或本地用户登录创建其他窗口站,则可以向Windows请求活动桌面,将Python脚本附加到该桌面,移动鼠标,然后恢复到以前的桌面,以便脚本的其余部分按预期工作。
值得庆幸的是,执行此操作的代码比解释更容易:
using System;
using System.Runtime.InteropServices;
namespace Example
{
class Program
{
const uint MAXIMUM_ALLOWED = 0x02000000;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetThreadDesktop(uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetThreadDesktop(IntPtr hDesktop);
[DllImport("user32.dll")]
static extern bool SetCursorPos(int x, int y);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
static void Main(string[] args)
{
IntPtr hDesk = OpenInputDesktop(0, false, MAXIMUM_ALLOWED);
IntPtr hDeskOld = GetThreadDesktop(GetCurrentThreadId());
SetThreadDesktop(hDesk);
for (int i = 0; i < 10; i ++)
{
SetCursorPos(i, i);
}
SetThreadDesktop(hDeskOld);
}
}
}
然而,如果屏幕保护程序在单独的窗口站点上运行,因为选择了“恢复时显示登录屏幕”,或者另一个用户通过物理控制台连接或远程连接,则连接和附加到活动桌面将需要提升程序权限,即使如此,根据其他因素,它可能需要特殊权限。
虽然这对于特定用例可能有效,但我要补充的是,在一般情况下,核心问题更适合定义为“如何通知用户某些状态,而不被屏幕保护程序阻止?”答案不是“让屏幕保护程序结束”,而是“使用类似 SetThreadExecutionState()
和 ES_DISPLAY_REQUIRED
的东西来防止屏幕保护程序运行。并显示一个全屏顶部窗口,显示当前状态,并在想要警示用户时闪烁引人注目的图形和/或播放声音以引起他们的注意。”
虽然需要更多的工作,但这样做将解决在“恢复时显示登录屏幕”设置下安全桌面周围的问题,并防止系统进入睡眠状态(如果配置为这样)。它通常允许应用程序更清晰地传达其意图。
除此之外,屏幕保护程序的显示以及如何防止或中断它本质上是一个特定于操作的努力。Linux和MacOS将需要不同的技术来与系统交互。