打开显示并关闭屏幕保护程序

Turn Display On and Kill Screensaver

假设一台 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) 似乎可以解决重置空闲计时器的问题。但是,我仍然不知道正确的调用顺序。等我弄明白了我会自己回答...

以下代码将:

  • 中断 运行 屏保
  • 打开关闭的屏幕("turn off" 如电源选项中所述)
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());
        }
    }
}