Page 4 of 4
More like a screensaver
So far we have the basic functionality of a screensaver but it doesn’t look or behave like one. To make it look like one we have to resize the form to fill the screen and remove its border in the ShowScreenSaver function:
this.TopLevel = true;
this.Capture = true;
To make the screensaver close when the mouse moves or if its button is clicked we need a simple event handler:
private void OnMouseEvent(
if (MousePos != new Point(e.X, e.Y))
if (e.Clicks > 0)
MousePos = new Point(e.X, e.Y);
and another global variable to store the mouse position:
private Point MousePos;
This one-event handler can deal with MouseMove and MouseDown events. View the form in design mode and use the properties window to set both events to the OnMouseEvent function.
Now when you run the program it should fill the screen and close when the mouse moves or is clicked. You can improve the way that it works by adding code to close only if the mouse moves more than a set amount and you can trap keyboard events to close it.
The screensaver can also be run in /p – preview or /c – configuration mode. The configuration mode is fairly easy to implement and it’s described in the documentation. The /p mode is much more difficult and not described anywhere so this is the puzzle to solve next!
The screensaver in miniature
When the /p option is passed via the command line the windows handle (hwnd) of the preview window is passed as a sequence of decimal digits on the command line.
For example, if the command line is /p 8324238 then the handle to the preview window is 8324238. You could simply retrieve the hwnd and use it to construct a graphics object to draw on the preview window.
This works but there is a fundamental problem with doing things in this way. The system expects the screensaver to attach a child window to the preview window and to draw on the child, not the preview.
When the system needs to stop the screensaver, for any reason, it simply closes the child window. If you draw directly on the preview window then there is no child window to close and your screensaver will not be terminated when the user looks at another screensaver or views a full screen preview. The result it that you end up with lots of copies of your screensaver running at the same time and all trying to draw to the preview window – the system then crashes.
There seems to be no easy way of making a window a child of another window using the .NET framework and once again we have to resort to using the Windows API.
First we need to change the switch to deal with the hwnd passed on the command line case "/p":
if (m_args.Length > 2)
this.TopLevel = false;
m_hwnd = (IntPtr)Int32.Parse(m_args);
and we need to add the global variable:
private IntPtr m_hwnd;
If m_hwnd has a value then we need to display using the preview screen and the ShowScreenSaver function has to be modified to initialise the form either for full screen or preview display:
if (this.m_hwnd == IntPtr.Zero)
All of the code
already in ShowScreenSaver
The else part of the if handles the preview and uses a number of API calls, definitions of which are given later. We need to make the form’s window a child window of the preview window. To do this we need to retrieve and modify the form’s window style to make it a child window:
int style = GetWindowLong(
style = style | WS_CHILD;
Next we tell the system that the preview window is the parent of the form’s window and tell the form’s window that its parent is the preview window:
Now everything is set up for the form to display within the preview window except for the fact that it’s the wrong size and still has a border. To resize it we need to know the size of the preview window:
RECT r=new RECT();
GetClientRect(m_hwnd, out r);
To display the window a final API call is used do the resizing and place the form’s window in front of the preview window:
HWND_TOP, 0, 0,
SWP_NOACTIVATE | SWP_SHOWWINDOW);
For all of this to work we need to add:
at the start of the file and add the following declarations:
public struct RECT
public int Left;
public int Top;
public int Right;
public int Bottom;
static extern bool GetClientRect(
out RECT lpRect);
SetLastError = true)]
static extern int GetWindowLong(
const int GWL_STYLE = (-16);
const int WS_CHILD = 0x40000000;
static extern int SetWindowLong(
static extern IntPtr SetParent(
const int GWL_HWNDPARENT = (-8);
static extern bool SetWindowPos(
int X,int Y,
int cx, int cy,
static readonly IntPtr HWND_TOP
= new IntPtr(0);
const UInt32 SWP_NOZORDER = 0x0004;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_SHOWWINDOW = 0x0040;
To see the preview in action you have to install the screensaver. Notice that you don’t have to display the same graphics in the tiny preview window that you do full screen. You could, for example, show a few lines of explanation of the purpose of your screensaver.
Screensavers still have a lot of potential left in them as a way of delivery information and as a way of using otherwise idle processor time.
If you would like the code for this project then register and click on CodeBin.