Introduction

Presented here is a C# class useful for monitoring and controlling the Windows screen saver.
The class provides methods for getting information about the screen saver, such as whether or
not it is active, it is running, and its time out setting. Additional methods are provided to
change these settings and to terminate the screen saver if it is running.

Background

Some things aren't always as straight forward as you might think going in. Such was the case
on a recent project where one of the requirements called for killing the screen saver on the occurrence
of a real time event. The project involved a radio dispatch console, and in this case the real time
event was a dispatch operator pressing the push-to-talk button on a radio handset. Also, the
screen saver time out needed to be reset if the screen saver wasn't actually running when the button
was pressed, similar to the action of a key press or mouse event.

The SystemParametersInfo API provided
by the user32.dll supports a set of functions for accessing screen saver settings, including getting
and setting activation, the time out value, and checking to see if the screen saver is running. However,
there doesn't seem to be a function for terminating the screen saver if it is running. The
SPI_SETSCREENSAVERRUNNING function oddly doesn't do the job as one might think, which instead
is suggested as an obscure technique to disable task switching
under Windows 95 and 98. Ultimately, the solution was found in another
knowledge base article discussing user32.dll functions which can be used to find the running screen
saver application and then force it to close. The main task now was to implement a class to call these unmanaged
functions using C#.

Using the code

To use the code, just copy the class ScreenSaver into your program and call the exposed methods, which for
the most part are just wrappers to the user32.dll functions:

The more interesting code here is the KillScreenSaver( ) method. Beginning with Microsoft Windows NT, you
cannot simply make a call to close the foreground window as you could under previous releases of Windows.
Windows NT introduces the concept of separate desktops. Applications can run on one desktop, and screen savers can run on
another. For example, under Windows XP, if you check the "On resume, display Welcome screen" option in the Screen Saver tab
of Display Properties, the screen saver will be running on a desktop other than the one for your application. In
this case, you need to find the screen saver desktop and close its foreground window to terminate the screen saver. If this
option is not checked, the screen saver is running on the same desktop as your application, and may be killed merely by
closing the foreground window in that desktop.

Another thing worth pointing out is in the KillScreenSaverFunc( ) callback function. Notice the call to
IsWindowVisible( hWnd ) just before closing what presumably is the screen saver window. Apparently it's possible
for the screen saver to be "running", yet not actually be seen as the foreground application on the desktop. As we'll see in
our Test Program, we need to keep this in mind for when the screen saver is running on the same desktop as our
application. Otherwise our application could end up being the one killed by the call to
PostMessage( GetForegroundWindow( ), WM_CLOSE, 0, 0 ), which might annoy the user.

The Test Program

The Screen Saver Test program demonstrates how to use the ScreenSaver class. It can also serve as a
convenient utility to edit the screen saver settings as you're testing your own application. Since it can modify the
screen saver values normally set with the Display Properties dialog, it saves off these values so
they may be restored on exit or manually using the Restore button. The form is set to be "TopMost" to make it easily
accessible when being used with full screen apps.

To change the screen saver timeout, enter the number of seconds in the NumericUpDown control on the right and click
the Write button. If the screen saver is "active", it should kick on that many seconds later. If any of the
settings are changed outside of the test program, for example by using the Display Properties dialog, click the Refresh
button to display the new values.

To facilitate testing the KillScreenSaver( ) method, the test program uses a periodic timer. The timeout
period can be set and the timer can be toggled using the Start/Stop Timer button. If the period is greater than the
screen saver timeout, a call to KillScreenSaver( ) will terminate the screen saver. If the period is less,
the screen saver timeout is reset by invoking SetScreenSaverActive( TRUE ), preventing the screen
saver from running until after a full timeout has expired.

As pointed out earlier, it's possible for the SPI_GETSCREENSAVERRUNNING call to return TRUE
before the screen saver actually becomes the foreground window. Perhaps this function should be renamed to SPI_GETSCREENSAVERSORTAKINDARUNNING. Anyways, make sure the foreground window isn't your own app before invoking
KillScreenSaver( ), otherwise it might get closed instead.

Share

About the Author

Kurt's programming career began in 1978, developing firmware for the Zilog Z80-A microprocessor on a Mostek development workstation. For 23 years, Kurt led the development of Human Machine Interface (HMI) programs used in factory automation, marketed and sold worldwide by CTC and Parker Hannifin as ScreenWare, Interact and InteractX. For the last 4 years, Kurt has been consulting on agile development practices and working as an independent software contractor focusing on graphical and user interface development.

Other passions include spending time with his wife Janice, daughters Erica and Laura, scuba diving, target shooting, guitar, travel, and digital photo imaging and restoration. Currently residing in southwestern Ohio.

If the screen saver is running on a dual screen system and you call the KillScreenSaver function, it only kills the screensaver on the main screen, not the second one. Since the screensaver is actually still active that can cause unusual behaviors as well.

Hi Kurt,
I am using your app in a program i am working on and I am checking the status of my screensaver on start-up. This works fine. What I would like to know is, "Does your app detect if there is no screensaver activated...Control Panel Screensaver applet set to ( 'NONE' )"?

I believe the most reliable way to see if a screensaver is active is to look at the registry key HKEY_CURRENT_USER\Control Panel\Desktop and examine the SCRNSAVE.EXE value. If the Control Panel Screensaver applet is set to NONE, this member will not be present. If a screen saver is specified (active), this member's value will contain the path and filename of the assigned screen saver.

I am new member to The Code Forum and a novice at C programming and controlling Windows functionality. The test program provided works on my system but I would like to incorporate it into Windows XP Pro to shut off the screensaver so the PC can enter hibernate mode. Can someone tell me what file need modified? Thanks.

Take a look at the ScreenSaverTest code. The ScreenSaver class has been copied into ScreenSaverTest.cs at the bottom. You can copy this class as is into your own (C#) project, and call the functions as needed. In particular, look at the KillTimer_Elapsed( ) function to get an idea of the call sequence to kill the screen saver. Tested under WinXP Pro.

How about notifying the system that the screensaver is running without actually running it? In other words, I need to enable SCREENSAVERRUNNING flag, so that SPI_GETSCREENSAVERRUNNING will return TRUE. Unfortunately SPI_SETSCREENSAVERRUNNING does not do this, as one would think. SPI_SETSCREENSAVEACTIVE succesfully sets the screensaver active (as in enabled) without even a screensaver present. The method someone wrote here:SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0); requires that the screensaver is set in the Screen properties dialog box as a system screensaver. However, when system runs screensaver it sets the ..RUNNING flags, because then SPI_GETSCREENSAVERRUNNING returns true, but I don't know how it does that.

Application not generating any of the keyboard or any user events to disturb the system. If I exit the application every thing works normal(Means password protected screen saver gets activates after screen saver time out period.)

Is it possible to programmatically enable/disable the "On resume, display logon screen" option for the screensaver? I'm writing a utility to manually start the screensaver (easy enough) as a locking mechanism, but want to ensure that the password option is enabled. So the idea is that when the utility is run, it automatically enables the password option and then starts the screensaver.

You may want to look at the ScreenSaverIsSecure setting in the system registry. Under WinXP, it's under HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaverIsSecure (you may also need to look under HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Control Panel\Desktop\ScreenSaverIsSecure).

Setting this to a 1 will enable (check) the "On resume, show Welcome screen" option, and setting to a 0 will disable (uncheck) it. I have not tried it programmatically, but it appears this should work.

I just downloaded this example program and tried it and it did NOT work at all. I set break points at each of the functions, and they seem to be getting called fine. I'm using XP-Pro on a corporate network.

I took a look at the registry, and it was not changing either. I know when I change the registry myself, the change doesn't take effect until I logout. Is this code supposed to resolve this problem also?

For my code to function correctly, I need to recieve an event when the screen saver is started/finished. I also need to be able to reset the screen saver time-out value (the same as if the user had typed a key or moved the mouse.)

Has anyone else had this program fail? How can I debug it? What's the next step?

Jim - the examples were created using VS2005 under XP-Pro. Are you logged in as Admin? Not sure if that will make a difference - I've only tested it under Admin accounts, mostly using the set of screen savers that come with XP-Pro. I would not expect to see any changes to the registry.

>>...not expect to see any changes to the registry.
- The registry location for the Screen Saver:
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Control Panel\Desktop
- My Values:
"ScreenSaveActive"="1"
"ScreenSaverIsSecure"="1"
"ScreenSaveTimeOut"="120"
- These values are correctly being read from the registry and displayed in your appication. If I change these values manually, they are not used by Windows until after logging out. Your application does not change any of these values, and I would expect them to be changed by your appication.

Jim - "3D Text" was one of the screen savers not recognized as the foreground window, and therefore did not close using the method suggested by Microsoft (whereas seems "Windows XP" is always recognized). Try the method described by Kurt Callebaut posted below. I tested this successfully using the "3D Text" screen saver. Here's the code:

Thanks for this additional information. I have been playing around with things, and finally got it working correctly. The reason I knew it was not working was because when the code asked for the current time-out value [ScreenSaver.GetScreenSaverTimeout( ).ToString( );], it always returned the same value, not the new value that should have just been set.

The clue provided that gives the problem is the registry key of:[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Control Panel\Desktop
which is NOT the location of where the real values are stored, but the location of where the corporate policy values are stored. When these policy values exist, items in the Screen Saver options dialog are grayed-out, and it seems they are also not available through these Win32 command sets either.