打开显示并杀死屏幕保护程序

时间:2016-03-29 16:00:37

标签: c# windows windows-8

给定具有以下电源选项的Windows 8(.1)计算机: Power Options

屏幕保护程序配置为: Screen Saver Settings

目标是以编程方式打开显示屏并且" kill"屏幕保护程序(以便在空闲时间后重新激活)。 (注意,根据设置,可能只有屏幕保护程序打开,或者,在屏幕保护程序为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)似乎在重置空闲计时器方面做了诀窍。但是,我仍然不知道正确的呼叫顺序。当我弄清楚时,我会自我回答......

1 个答案:

答案 0 :(得分: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());
        }
    }
}