问题
Given a Windows 8 (.1) computer with the following power options:
And screensaver configured as:
The goal is to programmatically turn the display on and "kill" the screensaver (so that it will be re-activated after idle time). (Note, that according to the settings it's possible that only the screensaver is on, or, that the display is off completely after the screensaver was one for about one minute).
What i have tried is:
SendMessage(HWND_Broadcast, WM_SysCommand, SC_MONITORPOWER, (LPARAM) - 1);
in combination with
// 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());
}
}
And
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);
}
Which worked, partially. However, the display was turned off immediately again (as a matter of fact, most times one couldn't even see that the monitor was turned again apart from the power-led going "steady on" instead of "flashing on off = power safe" for a short time).
So i guess i'm missing a crucial part of the picture like reliably resetting system idle timer to make sure the screen is not immediately being turned off, again.
Edit: According to my investigations SetThreadExecutionState(ES_DISPLAY_REQUIRED)
seems to do the trick in regards to resetting the idle-timer. However, i still don't know the correct sequence of calls. I'll self-answer when i figure it out...
回答1:
The following code will:
- interrupt a running screensaver
- turn on a turned-off screen ("turn off" as mentioned in power options)
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());
}
}
}
来源:https://stackoverflow.com/questions/36289352/turn-display-on-and-kill-screensaver