打开显示并终止屏幕保护程序
Posted
技术标签:
【中文标题】打开显示并终止屏幕保护程序【英文标题】:Turn Display On and Kill Screensaver 【发布时间】:2016-07-17 07:21:43 【问题描述】:假设一台具有以下电源选项的 Windows 8 (.1) 计算机:
屏幕保护程序配置为:
目标是以编程方式打开显示器并“关闭”屏幕保护程序(以便在空闲时间后重新激活)。 (请注意,根据设置,可能只有屏保开启,或者在屏保开启约一分钟后显示屏完全关闭)。
我试过的是:
SendMessage(HWND_Broadcast, WM_SysCommand, SC_MONITORPOWER, (LPARAM) - 1);
结合
// From Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
// "How to force a screen saver to close once started
// in Windows NT, Windows 2000, and Windows Server 2003"
public static void KillScreenSaver()
IntPtr hDesktop = OpenDesktop("Screen-saver", 0, false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
if (!EnumDesktopWindows(hDesktop, KillScreenSaverFunc, IntPtr.Zero) || !CloseDesktop(hDesktop))
throw new Win32Exception(Marshal.GetLastWin32Error());
else
TerminateWindow(GetForegroundWindow());
private static bool KillScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
if (IsWindowVisible(hWnd))
TerminateWindow(hWnd);
return true;
private static void TerminateWindow(IntPtr hWnd)
if (!PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
和
public static void ActivateScreensaver()
SetScreenSaverActive(TRUE);
private static void SetScreenSaverActive(uint active)
IntPtr nullVar = IntPtr.Zero;
// Ignoring error since ERROR_OPERATION_IN_PROGRESS is expected.
// Methode is called to reset timer and to prevent possible errors as mentioned in Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, active, ref nullVar, SPIF_SENDWININICHANGE);
部分有效。然而,显示器立即再次关闭(事实上,除了电源指示灯“稳定打开”而不是“闪烁关闭 = 电源安全”之外,大多数时候人们甚至看不到显示器再次打开" 短时间)。
所以我想我错过了图片的关键部分,例如可靠地重置系统空闲计时器以确保屏幕不会立即再次关闭。
编辑:根据我的调查,SetThreadExecutionState(ES_DISPLAY_REQUIRED)
似乎可以解决重置空闲计时器的问题。但是,我仍然不知道正确的调用顺序。等我弄明白了会自己回答的……
【问题讨论】:
你试过simulating a mouse move吗? @stuartd 感谢您的提示。我在我的回答中使用了它(尽管有点不同 - 使用更新的、非弃用的 API)。然而,单靠这两种情况是不够的 不错的答案。你不能只更改屏幕保护程序/显示设置吗? @stuartd 可以,但是我们的应用程序将在多台计算机上运行,有些是我们设置的,有些是由客户端设置和维护的。而不是处理“错误报告”和“客户反馈”和“安装手册”,我更喜欢编写“正常工作”的软件。您不想更改电源选项来运行视频播放器,对吗? ;-)(我们不是在开发视频播放器...) 啊,我以为这是某种信息亭模式。我的错。 【参考方案1】:以下代码将:
中断正在运行的屏幕保护程序 打开关闭的屏幕(电源选项中提到的“关闭”)private static readonly ILog Log = LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
public void TurnOnScreenAndInterruptScreensaver()
TryTurnOnScreenAndResetDisplayIdleTimer();
TryInterruptScreensaver();
/// <summary>
/// Moves the mouse which turns on a turned-off screen and also resets the
/// display idle timer, which is key, because otherwise the
/// screen would be turned off again immediately.
/// </summary>
private static void TryTurnOnScreenAndResetDisplayIdleTimer()
var input = new SendInputNativeMethods.Input
type = SendInputNativeMethods.SendInputEventType.InputMouse, ;
try
SendInputNativeMethods.SendInput(input);
catch (Win32Exception exception)
Log.Error("Could not send mouse move input to turn on display", exception);
private static void TryInterruptScreensaver()
try
if (ScreensaverNativeMethods.GetScreenSaverRunning())
ScreensaverNativeMethods.KillScreenSaver();
// activate screen saver again so that after idle-"timeout" it shows again
ScreensaverNativeMethods.ActivateScreensaver();
catch (Win32Exception exception)
Log.Error("Screensaver could not be deactivated", exception);
SendInputNativeMethods:
public static class SendInputNativeMethods
public static void SendInput(params Input[] inputs)
if (SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<Input>())
!= (uint)inputs.Length)
throw new Win32Exception(Marshal.GetLastWin32Error());
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern uint SendInput(
uint nInputs,
[MarshalAs(UnmanagedType.LPArray), In] Input[] pInputs,
int cbSize);
[StructLayout(LayoutKind.Sequential)]
public struct Input
public SendInputEventType type;
public MouseKeybdhardwareInputUnion mkhi;
[StructLayout(LayoutKind.Explicit)]
public struct MouseKeybdhardwareInputUnion
[FieldOffset(0)]
public MouseInputData mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
public int uMsg;
public short wParamL;
public short wParamH;
public struct MouseInputData
public int dx;
public int dy;
public uint mouseData;
public MouseEventFlags dwFlags;
public uint time;
public IntPtr dwExtraInfo;
[Flags]
public enum MouseEventFlags : uint
MOUSEEVENTF_MOVE = 0x0001,
MOUSEEVENTF_LEFTDOWN = 0x0002,
MOUSEEVENTF_LEFTUP = 0x0004,
MOUSEEVENTF_RIGHTDOWN = 0x0008,
MOUSEEVENTF_RIGHTUP = 0x0010,
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
MOUSEEVENTF_MIDDLEUP = 0x0040,
MOUSEEVENTF_XDOWN = 0x0080,
MOUSEEVENTF_XUP = 0x0100,
MOUSEEVENTF_WHEEL = 0x0800,
MOUSEEVENTF_VIRTUALDESK = 0x4000,
MOUSEEVENTF_ABSOLUTE = 0x8000
public enum SendInputEventType : int
InputMouse,
InputKeyboard,
InputHardware
ScreensaverNativeMethods:
internal static class ScreensaverNativeMethods
private const int SPI_GETSCREENSAVERRUNNING = 0x0072;
private const int SPI_SETSCREENSAVEACTIVE = 0x0011;
private const int SPIF_SENDWININICHANGE = 0x0002;
private const uint DESKTOP_WRITEOBJECTS = 0x0080;
private const uint DESKTOP_READOBJECTS = 0x0001;
private const int WM_CLOSE = 0x0010;
private const int TRUE = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SystemParametersInfo(
uint uiAction,
uint uiParam,
ref IntPtr pvParam,
uint fWinIni);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PostMessage(
IntPtr hWnd,
uint msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr OpenDesktop(
string lpszDesktop,
uint dwFlags,
[In, MarshalAs(UnmanagedType.Bool)]bool fInherit,
uint dwDesiredAccess);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumDesktopWindows(
IntPtr hDesktop,
EnumDesktopWindowsProc callback,
IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetForegroundWindow();
private delegate bool EnumDesktopWindowsProc(IntPtr hDesktop, IntPtr lParam);
public static bool GetScreenSaverRunning()
IntPtr isRunning = IntPtr.Zero;
if (!SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref isRunning, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
return isRunning != IntPtr.Zero;
public static void ActivateScreensaver()
SetScreenSaverActive(TRUE);
private static void SetScreenSaverActive(uint active)
IntPtr nullVar = IntPtr.Zero;
// Ignoring error since ERROR_OPERATION_IN_PROGRESS is expected.
// Methode is called to reset timer and to prevent possible errors
// as mentioned in Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
SystemParametersInfo(
SPI_SETSCREENSAVEACTIVE,
active,
ref nullVar,
SPIF_SENDWININICHANGE);
// From Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
// "How to force a screen saver to close once started
// in Windows NT, Windows 2000, and Windows Server 2003"
public static void KillScreenSaver()
IntPtr hDesktop = OpenDesktop(
"Screen-saver",
0,
false,
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
if (!EnumDesktopWindows(hDesktop, KillScreenSaverFunc, IntPtr.Zero)
|| !CloseDesktop(hDesktop))
throw new Win32Exception(Marshal.GetLastWin32Error());
else
TerminateWindow(GetForegroundWindow());
private static bool KillScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
if (IsWindowVisible(hWnd))
TerminateWindow(hWnd);
return true;
private static void TerminateWindow(IntPtr hWnd)
if (!PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
【讨论】:
以上是关于打开显示并终止屏幕保护程序的主要内容,如果未能解决你的问题,请参考以下文章
在软键盘打开/关闭之前,RecyclerView 不会更新并显示在屏幕上
每次从后台恢复应用程序时都会打开启动屏幕,如WhatsApp指纹屏幕
首次启动时卡在启动屏幕上,但如果我们不终止应用程序并再次启动应用程序,则应用程序可以正常工作。 (反应原生)Android
应用程序推送通知 - 收到并导航到屏幕 - 应用程序何时关闭?