Reputation: 16516
REASON: I'm working on an emergency alert application that needs to display information on a desktop. When the client receives an alert, it pops up a window. If a screensaver is active or the monitor is in standby the alert will not be visible. I'm wondering if it's possible to wake the computer up via some sort of programatic mouse move or system call so that the alert would be visible. I think the reason a mouse move or keypress wakes it up is because of a hardware interrupt so it may not be possible.
Currently, the project is being implemented in C#. I'm interested to hear about solutions for Windows, MAC, and Linux.
This is a customer request. I have considered the following:
I am NOT trying to:
Upvotes: 8
Views: 3403
Reputation: 10827
One way to interrupt a screen save programmatically is to move the mouse cursor. On Windows, however, that's not as simple as it might first seem.
The Windows operating system has a hierarchy of objects. At the top of the hierarchy is the "Window Station". Just below that is the "Desktop" (not to be confused with the desktop folder, or even the desktop window showing the icons of that folder). You can read more about this concept in the documentation.
I mention this because ordinarily only one Desktop can receive and process user input at any given time. And, when a screen saver is activated by Windows due to a timeout, Windows creates a new Desktop to run the screen saver.
This means any application associated with any other Desktop, including the running program you've written, will be unable to send input to the new Desktop without some extra work. The nature of that work depends on a few factors. Assuming the simplest case, a screen saver that's created without the "On resume, display logon screen", and no other Window Station has been created by a remote connection or local user login, then you can ask Windows for the active Desktop, attach the Python script to that Desktop, move the mouse, and revert back to the previous Desktop so the rest of the script works as expected.
Thankfully, the code to do this is easier than the explanation:
using System;
using System.Runtime.InteropServices;
namespace Example
{
class Program
{
const uint MAXIMUM_ALLOWED = 0x02000000;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetThreadDesktop(uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetThreadDesktop(IntPtr hDesktop);
[DllImport("user32.dll")]
static extern bool SetCursorPos(int x, int y);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
static void Main(string[] args)
{
// Get a handle to the current active Desktop
IntPtr hDesk = OpenInputDesktop(0, false, MAXIMUM_ALLOWED);
// Get a handle to the Desktop this process is associated with
IntPtr hDeskOld = GetThreadDesktop(GetCurrentThreadId());
// Set this process to handle messages and input on the active Desktop
SetThreadDesktop(hDesk);
// Move the mouse cursor some
for (int i = 0; i < 10; i ++)
{
SetCursorPos(i, i);
}
// Revert back to the old desktop association so the rest of this program works
SetThreadDesktop(hDeskOld);
}
}
}
However, if the screen saver is running on a separate Window Station because "On resume, display logon screen" is selected, or another user is connected either via the physical Console or has connected remotely, then connecting to and attaching to the active Desktop will require elevation of the program, and even then, depending on other factors, it may require special permissions.
And while this might work for a given use case, I will add the the core issue in the general case is perhaps more properly defined as asking "how do I notify the user of the state of something, without the screen saver blocking that notification?". The answer to that question isn't "cause the screen saver to end", but rather "Use something like SetThreadExecutionState()
with ES_DISPLAY_REQUIRED
to keep the screen saver from running. And show a full-screen top-most window that shows the current status, and when you want to alert the user, flash an eye-catching graphic and/or play a sound to get their attention".
While more work, doing that will solve issues around the secure desktop with "On resume, display logon screen" set, and also prevent the system from going to sleep if it's configured to do so. It just generally allows the application to more clearly communicate its intention.
And on top of this, the display of a screen saver and how to prevent it or interrupt it is inherently an operating specific endeavor. Linux and MacOS will require a different technique to interact with the system.
Upvotes: 0
Reputation: 5623
Building on what fbonnet said, to use the functions shown in the kb article in C# the pinvoke.net site is a great resource. They also have a article on the PostMessage
function here.
So the basic way of getting your code working could be searching the functions listed in the kb article on the pinvoke.net site. Or you can read this article has done that for you.
Upvotes: 4