以编程方式阻止Windows屏幕保护程序启动

Mic*_*ley 24 winapi windows-xp screensaver windows-vista

是否有推荐的方法来阻止Windows屏幕保护程序启动?我发现的最接近的是这篇文章,但我真正想做的就是告诉Windows计算机不是空闲而不是愚弄当前设置的屏幕保护程序值.

Eya*_*yal 10

为了测试,我将屏幕保护程序设置为1分钟并需要密码.

我尝试捕获SC_SCREENSAVE并在VB .Net中返回-1.如评论所述,它在没有屏幕保护程序密码时有效,但在屏幕保护程序密码处于活动状态时失败.(我在Windows XP中尝试过).我还将它放入Timer的tick事件中,每1000毫秒:

Static dir As Integer = 4
Cursor.Position = Cursor.Position + New Size(dir, dir)
dir = -dir
Run Code Online (Sandbox Code Playgroud)

它不起作用.光标前后摇晃,1分钟后屏幕保护程序闪烁一小段时间,然后关闭.屏幕保护程序只开启一段时间,不够长,不需要密码.但是,闪光灯仍然是丑陋的.

然后我尝试使用user32.dll的SetCursorPos和GetCursorPos.你可以在pinvoke上查找它们.与上述结果相同.

然后我偷看了这个问题中其他地方提到的"JiggleMouse"的代码.JiggleMouse使用SendInput. SendInput有效! 没有闪存的屏幕保护程序.我在Timer中调用了SendInput,每50秒触发一次(仅比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)))
Run Code Online (Sandbox Code Playgroud)

这来自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
Run Code Online (Sandbox Code Playgroud)

  • 这段代码有效吗?我试图将它转换为C#,但我无法让它工作(XP和Win7).我不确定问题是在我的代码移植中,还是代码不起作用.有人在C#中得到它吗? (2认同)
  • 移植到 C# 并成功用于防止 Win 8 平板电脑关闭显示(作为旧版应用程序运行)。此处移植代码:http://codejournal.blogspot.com/2014/01/mp3-player-hack-one-way-windows-8-on.html (2认同)

adz*_*dzm 9

SystemParametersInfo

具体来说,SPI_SETSCREENSAVEACTIVE参数.

这不起作用吗?我很惊讶我没有在这里看到它.请注意,SetThreadExecutionState根本不会影响屏幕保护程序,只会影响显示屏的睡眠状态.

  • 未经用户明确知情和同意,请不要更改用户全局设置. (11认同)

Dav*_*ske 9

PowerSetRequest()在 Windows 7+ 中,使用电源管理 APIPowerRequestDisplayRequired

https://msdn.microsoft.com/en-us/library/windows/desktop/dd405534(v=vs.85).aspx

在以前版本的 Windows 中,拦截 WM_SYSCOMMAND - SC_SCREENSAVE 消息,如 Eddie Parker 的回答中所述。


MSa*_*ers 7

微妙.告诉Windows系统不空闲的官方方法是SetThreadExecutionState.这会重置空闲计时器,(如果通过则将其关闭ES_CONTINUOUS).但是,即使SetThreadExecutionState重置了空闲计时器,它也不会停止屏幕保护程序!

  • 为什么这个答案有6个赞成?答案本身表明它没有回答这个问题. (4认同)

Edd*_*ker 6

这篇博文详细介绍了您需要在 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);
Run Code Online (Sandbox Code Playgroud)

}

  • 请注意,根据 MSDN,如果为屏幕保护程序配置了密码,则这不适用于 Windows Vista。 (3认同)
  • 啊,找到了:“Microsoft Windows Vista 及更高版本:如果通过策略启用了密码保护,则无论应用程序如何处理 SC_SCREENSAVE 通知,屏幕保护程序都会启动,即使无法将其传递给 DefWindowProc。” (2认同)

sys*_*USE 6

我使用Mouse Jiggler重置空闲状态.这绕过了一个组策略,它倾向于在不合时宜的时间启动我的屏幕保护程序(并锁定机器):当我正在阅读一个长文档,研究一大堆代码,或者在一个时间内说话/倾听/不经常打字会议.

由于让鼠标每秒对角跳1px可能会有点烦人,我打算使用AutoHotKey编写一个基本相同的脚本,但只有在配置的键盘/鼠标空闲超时之后,才可以使用Shift键(或Scroll Lock)而不是鼠标移动.


小智 5

无法相信没有人指出简单明了的解决方案:

#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);
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 这[建议](http://stackoverflow.com/a/1675793/1889329)超过一年之前.但是,不是实际移动鼠标光标(就像您的代码一样),原始解决方案不会(但仍然有效).这个答案是对现有答案的重新执行不力. (2认同)

Adr*_*thy 5

来自MSDN:

如果存在以下任何一种情况,Windows不会启动屏幕保护程序:

  • 活动应用程序不是基于Windows的应用程序.
  • 存在CBT窗口.
  • 活动应用程序接收WM_SYSCOMMAND消息,其中wParam参数设置为SC_SCREENSAVE值,但它不会将消息传递给DefWindowProc函数.

但有一点需要注意:

Windows Vista及更高版本:如果策略启用了密码保护,则无论应用程序使用SC_SCREENSAVE通知执行什么操作,都会启动屏幕保护程序.

即使您将SetThreadExecutionState与ES_CONTINUOUS一起使用,这似乎也适用.

所以,如果不是警告,你的选择将是:

  1. 具有ES_CONTINUOUS的SetThreadExecutionState(如其他答案中所述).
  2. 建立一个基于计算机的培训窗口(需要挂钩).
  3. 不要将带有SC_SCREENSAVE的WM_SYSCOMMAND传递给DefWindowProc.(假设您只关心您的应用程序是活动应用程序.)
  4. 安装模拟鼠标微动加密狗.

最后一个选项很好,因为它甚至可以使用密码保护策略.


yea*_*007 5

正如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);      
   }
}
Run Code Online (Sandbox Code Playgroud)

如果有人想在 C# 中使用它,必须PInvoke this :

[DllImport("kernel32.dll", CharSet = CharSet.Auto,SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
Run Code Online (Sandbox Code Playgroud)

用户定义的类型:

[FlagsAttribute]
public enum EXECUTION_STATE :uint
{
   ES_AWAYMODE_REQUIRED = 0x00000040,
   ES_CONTINUOUS = 0x80000000,
   ES_DISPLAY_REQUIRED = 0x00000002,
   ES_SYSTEM_REQUIRED = 0x00000001
}
Run Code Online (Sandbox Code Playgroud)

下面是调用过程:

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);      
    }
 }
Run Code Online (Sandbox Code Playgroud)

根据MSDN,这个 API 使用起来也是安全的。

系统维护已调用 SetThreadExecutionState 的应用程序的计数。系统跟踪每个调用 SetThreadExecutionState 的线程并相应地调整计数器。如果该计数器达到零并且没有任何用户输入,则系统进入睡眠状态。

如果应用程序在重置标志之前崩溃,系统将进行调整并自动重置。