打开显示并终止屏幕保护程序

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

应用程序推送通知 - 收到并导航到屏幕 - 应用程序何时关闭?

iOS7如何以编程方式为iPhone多任务屏幕显示黑屏? [复制]

打开 Blender 时禁用开始屏幕