有没有推荐的方法来阻止 Windows 屏幕保护程序启动?我找到的最接近的是这篇文章,但我真正想做的只是告诉 Windows 计算机没有空闲,而不是使用当前设置的屏幕保护程序值。
14 回答
为了测试,我将屏幕保护程序设置为 1 分钟并要求输入密码。
我尝试在 VB .Net 中捕获 SC_SCREENSAVE 并返回 -1。如评论所述,它在没有屏幕保护程序密码时有效,但如果屏幕保护程序密码处于活动状态则失败。(我在 Windows XP 中尝试过)。我还将它放入一个定时器的滴答事件中,每 1000 毫秒:
Static dir As Integer = 4
Cursor.Position = Cursor.Position + New Size(dir, dir)
dir = -dir
它不起作用。光标来回晃动,1 分钟后屏幕保护程序会闪烁一小段时间,然后关闭。屏幕保护程序只打开片刻,时间不足以要求输入密码。但是,闪光灯还是丑陋的。
然后我尝试使用 user32.dll 的 SetCursorPos 和 GetCursorPos。你可以在 pinvoke 上查看它们。结果与上述相同。
然后我偷看了这个问题其他地方提到的“JiggleMouse”的代码。JiggleMouse 使用 SendInput。 发送输入有效! 屏幕保护程序没有闪烁。我在每 50 秒触发一次的 Timer 内调用 SendInput(仅小于 60 秒的最小屏幕保护程序超时)。将鼠标移动 0,0 的增量就足够了,没有真正的移动。这确实有效。放入 Tick 事件的代码:
Dim i(0) As INPUT
i(0).dwType = INPUT.InputType.INPUT_MOUSE
i(0).mkhi = New MOUSEKEYBDHARDWAREINPUT
i(0).mkhi.mi = New MOUSEINPUT
i(0).mkhi.mi.dx = 0
i(0).mkhi.mi.dy = 0
i(0).mkhi.mi.mouseData = 0
i(0).mkhi.mi.dwFlags = MOUSEINPUT.MouseEventFlags.MOUSEEVENTF_MOVE
i(0).mkhi.mi.time = 0
i(0).mkhi.mi.dwExtraInfo = IntPtr.Zero
SendInput(1, i(0), Marshal.SizeOf(i(0)))
这来自 pinvoke.com:
Public Declare Function SendInput Lib "user32" (ByVal nInputs As Integer, ByRef pInputs As INPUT, ByVal cbSize As Integer) As Integer
Public Structure INPUT
Enum InputType As Integer
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2
End Enum
Dim dwType As InputType
Dim mkhi As MOUSEKEYBDHARDWAREINPUT
End Structure
Public Structure MOUSEINPUT
Enum MouseEventFlags As Integer
MOUSEEVENTF_MOVE = &H1
MOUSEEVENTF_LEFTDOWN = &H2
MOUSEEVENTF_LEFTUP = &H4
MOUSEEVENTF_RIGHTDOWN = &H8
MOUSEEVENTF_RIGHTUP = &H10
MOUSEEVENTF_MIDDLEDOWN = &H20
MOUSEEVENTF_MIDDLEUP = &H40
MOUSEEVENTF_XDOWN = &H80
MOUSEEVENTF_XUP = &H100
MOUSEEVENTF_WHEEL = &H800
MOUSEEVENTF_VIRTUALDESK = &H4000
MOUSEEVENTF_ABSOLUTE = &H8000
End Enum
Dim dx As Integer
Dim dy As Integer
Dim mouseData As Integer
Dim dwFlags As MouseEventFlags
Dim time As Integer
Dim dwExtraInfo As IntPtr
End Structure
Public Structure KEYBDINPUT
Public wVk As Short
Public wScan As Short
Public dwFlags As Integer
Public time As Integer
Public dwExtraInfo As IntPtr
End Structure
Public Structure HARDWAREINPUT
Public uMsg As Integer
Public wParamL As Short
Public wParamH As Short
End Structure
Const KEYEVENTF_EXTENDEDKEY As UInt32 = &H1
Const KEYEVENTF_KEYUP As UInt32 = &H2
Const KEYEVENTF_UNICODE As UInt32 = &H4
Const KEYEVENTF_SCANCODE As UInt32 = &H8
Const XBUTTON1 As UInt32 = &H1
Const XBUTTON2 As UInt32 = &H2
<StructLayout(LayoutKind.Explicit)> Public Structure MOUSEKEYBDHARDWAREINPUT
<FieldOffset(0)> Public mi As MOUSEINPUT
<FieldOffset(0)> Public ki As KEYBDINPUT
<FieldOffset(0)> Public hi As HARDWAREINPUT
End Structure
Subtle. The official way to tell Windows that the system is not idle is SetThreadExecutionState. This resets the idle timer, (or turns it off, if you pass ES_CONTINUOUS
). However, even though SetThreadExecutionState resets the idle timer, it does not stop the screensaver!
具体来说,SPI_SETSCREENSAVEACTIVE
参数。
这不起作用吗?我很惊讶我没有在这里看到它。请注意,SetThreadExecutionState 根本不会影响屏幕保护程序,只会影响显示器的休眠。
我使用Mouse Jiggler来重置空闲状态。这绕过了一个组策略,该策略往往会在不合时宜的时候启动我的屏幕保护程序(并锁定机器):当我阅读长文档、研究复杂的代码块或说话/听/不经常打字时会议。
由于鼠标每秒对角跳跃 1px 可能会有点烦人,我打算使用AutoHotKey编写一个基本相同的脚本,但仅在配置的键盘/鼠标空闲超时之后,并且可能使用 Shift 键(或滚动锁定)而不是鼠标移动。
来自MSDN:
如果存在以下任何一种情况,Windows 不会启动屏幕保护程序:
- 活动应用程序不是基于 Windows 的应用程序。
- 存在 CBT 窗口。
- 活动应用程序接收到 wParam 参数设置为 SC_SCREENSAVE 值的 WM_SYSCOMMAND 消息,但它不会将消息传递给 DefWindowProc 函数。
不过有一个警告:
Windows Vista 和更高版本:如果通过策略启用密码保护,则无论应用程序如何处理 SC_SCREENSAVE 通知,都会启动屏幕保护程序。
即使您将 SetThreadExecutionState 与 ES_CONTINUOUS 一起使用,这似乎也适用。
因此,如果不是出于警告,您的选择将是:
- SetThreadExecutionState 与 ES_CONTINUOUS (如其他答案中所述)。
- 建立一个基于计算机的培训窗口(需要挂钩)。
- 不要让带有 SC_SCREENSAVE 的 WM_SYSCOMMAND 传递到 DefWindowProc。(假设您仅在您的应用程序是活动应用程序时才关心。)
- 安装一个模拟鼠标抖动的加密狗。
最后一个选项很好,因为它甚至可以使用密码保护策略。
在 Windows 7+ 中,将电源管理 APIPowerSetRequest()
与PowerRequestDisplayRequired
https://msdn.microsoft.com/en-us/library/windows/desktop/dd405534(v=vs.85).aspx
在以前的 Windows 版本中,拦截 WM_SYSCOMMAND - SC_SCREENSAVE 消息,如 Eddie Parker 的回答中所述。
Can't believe no one has pointed out the easy and obvious solution:
#include <windows.h>
void main()
{
while(1){
INPUT input;
input.type = INPUT_MOUSE;
input.mi.dx = 1;
input.mi.dy = 1;
input.mi.mouseData = 0;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
input.mi.time = 0;
input.mi.dwExtraInfo = 0;
SendInput( 1, &input, sizeof(input) );
sleep(60000);
}
}
这篇博文详细介绍了您需要在 C++ 中执行的操作。
来自网站的实际代码片段:
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SYSCOMMAND:
{
switch (wParam)
{
case SC_SCREENSAVE:
return 0;
case SC_MONITORPOWER:
return 0;
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
正如Adrian McCarthy从MSDN中提到的那样:
如果通过策略启用密码保护,则无论应用程序如何处理 SC_SCREENSAVE 通知,都会启动屏幕保护程序。
因此,使用 UINT SC_SCREENSAVE 从 WM_SYSCOMMAND 捕获事件并通过返回 0 或创建假鼠标移动("mouse_event(MOUSEEVENTF_MOVE, 0, 1, 0, 0)")将其丢弃,如果用户启用了受密码保护的屏幕,将无法正常工作保护选项。
使用SetThreadExecutionState winAPI 告诉操作系统线程正在使用中,即使用户没有与计算机交互。这些将防止出现屏幕保护程序并阻止机器自动挂起。
有一系列标志来指定当前线程的新状态:
- ES_AWAYMODE_REQUIRED (0x00000040) :启用离开模式。
- ES_DISPLAY_REQUIRED (0x00000002) :通过重置显示空闲计时器强制显示打开。
- ES_SYSTEM_REQUIRED (0x00000001) :通过重置系统空闲计时器来强制系统处于工作状态。
- ES_CONTINUOUS (0x80000000) :通知系统正在设置的状态应该保持有效,直到下一次使用 ES_CONTINUOUS 的调用和其他状态标志之一被清除。
因为它是一个winAPI,你可以直接在win32或mfc应用程序中调用它
//To stop/start screen saver and monitor power off event
void SetKeepScreenOn(BOOL isKeepScreenOn)
{
if (isKeepScreenOn == TRUE)
{
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED /*| ES_AWAYMODE_REQUIRED*/);
}
else
{
SetThreadExecutionState(ES_CONTINUOUS);
}
}
如果有人想在 C# 中使用这个,必须PInvoke这个:
[DllImport("kernel32.dll", CharSet = CharSet.Auto,SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
用户定义的类型:
[FlagsAttribute]
public enum EXECUTION_STATE :uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001
}
下面是调用过程:
void SetKeepScreenOn(bool isKeepScreenOn)
{
if (isKeepScreenOn == true)
{
//You can combine several flags and specify multiple behaviors with a single call
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED /*| EXECUTION_STATE.ES_AWAYMODE_REQUIRED*/);
}
else
{
//To reset or allow those event again you have to call this API with only ES_CONTINUOUS
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}
}
根据MSDN ,此 API 也可以安全使用。
系统维护调用 SetThreadExecutionState 的应用程序的计数。系统跟踪调用 SetThreadExecutionState 的每个线程并相应地调整计数器。如果此计数器达到零并且没有任何用户输入,则系统进入睡眠状态。
如果应用程序在重置标志之前崩溃,系统将调整并自动重置。
您可以使用SystemParametersInfo
获取SCREENSAVETIMEOUT
,然后立即将超时设置回相同的值。只要您想阻止屏幕保护程序继续,请定期在计时器上执行此操作。
这具有在不实际更改系统设置的情况下重置当前倒数计时器的效果。
正如其他答案所提到的,您可能还想打电话SetThreadExecutionState
来影响权力。
只需重置超时计数器
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, nil, SPIF_SENDWININICHANGE);
来自JD Design Freeware - Flipss.exe(下载 12kb)是一个命令行实用程序,它将为您设置 SPI_SETSCREENSAVEACTIVE。
"FlipSS.exe -h" to see the current state.
"FlipSS.exe /on" to set the screensaver on.
"FlipSS.exe /off" to set the screensaver off.
AutoHotkey 可以在脚本中使用 1-liner DllCall 设置 SystemParametersInfo(SPI_SETSCREENSAVEACTIVE),以便使用 .ahk 脚本轻松完成此操作。
禁用屏幕保护程序的 AutoHotkey 代码:
DllCall("SystemParametersInfo", Int, 17, Int, 0, UInt, NULL, Int, 2)
启用屏幕保护程序的 AutoHotkey 代码:
DllCall("SystemParametersInfo", Int, 17, Int, 1, UInt, NULL, Int, 2)
参考论坛主题:
F13Key -使用 SystemParametersInfo 切换屏幕保护程序
SKAN -如何临时禁用屏幕保护程序
我意识到这是一个旧线程,但我第一次遇到这个问题(工作机器完全锁定,至于更改超短睡眠时间、屏幕保护程序等 - 我什至无法更改我的桌面背景)。我环顾四周寻找解决方案,有些看似过于复杂,有些......不是那么多。
我的一些同事正在使用咖啡因。但这肯定是某种间谍软件等,因为如果没有开放的互联网连接,它就会拒绝运行。
所以我找到了这个(并稍微修改了它),这正是 Caffeine 所做的(除了 Caffeine 每 59 秒做一次),没有所有......充其量,英国媒体报道。
在 PowerShell 中,执行以下 2 个命令行:
$WShell = New-Object -Com "Wscript.Shell"
while(1) {$WShell.SendKeys("{F15}"); sleep 200}
或者,如果您愿意,也可以将其设为单线:
while(1) {(New-Object -Com "Wscript.Shell").SendKeys("{F15}"); sleep 200}
(后者似乎会泄漏内存,但似乎根本不会)
一旦您运行其中任何一个,您的屏幕将不会锁定,直到您执行 ctrl-c 或关闭 Powershell 窗口(似乎仅在后一个版本中, ctrl-c 可能不会发生,直到睡眠间隔过去)。
请注意,至少在我见过的任何键盘上都没有 F15 键(但它是合法的 Windows 击键),因此没有副作用。现在,如果你的 IT 部门。非常偏执,他们可能会标记 F15 击键(我是超级偏执,但他们几个月都没有注意到任何事情)。如果是这样,请改用滚动锁定之类的东西。
这两个 100% 在我的 win10 机器上工作。简单就是好!