Windows Compliant Screen Saver Using MFC


Writing Screen Savers

With the advent of modern display devices, including long-life phosphors, liquid crystals, and Energy StarTM power saving modes, there is less need for screen saver programs than ever before. Despite that, the number of screen saver programs is still exploding, especially those offered on a freebie or shareware price basis.

It might be due to the fact that writing a screen saver can be so much fun. And writing screen savers in MFC can be even more fun, since classes like CRect and CGdiObject really makes using the drawing functions a lot simpler than the equivalent C API code.

simpler than most "Hello, world." Windows applications. It doesn't have a WinMain(), for example.

If you dig through the Windows C API for the requirements to create a screen saver, you'll find they are very minimal indeed. That is, for a program written in C. A screen saver is, in many ways, Screen savers don't even call the DefWindowProc() function, and instead call DefScreenSaverProc(). If you export one of your functions (or optionally, three), you can have a full-featured screen saver program that supports everything that every other screen saver supports.

For all screen savers,

  • The name or description of the screen saver is in string 1.
  • The icon for the screen saver is ID_APP, defined in <scrnsave.h> to 100.
  • The WINAPI ScreenSaverProc() must be defined and exported.
    (The CScreenSaverWnd module here implements this API for you.)
  • The executable should be named with a .SCR file extension.

For configurable screen savers,

  • The dialog for configuring the screen saver is DLG_SCRNSAVECONFIGURE, defined in <scrnsave.h> to 2003.
  • The WINAPI ScreenSaverConfigureDialog() must be defined and exported.
  • The WINAPI RegisterDialogClasses() must be defined and exported.
    (The CScreenSaverDlg module here implements these APIs for you.)

(A quick note about password protection: WinNT handles it for you, and it's probably a waste of time to try to make security features for any other unsecure version of Windows.)


Some MFC Surgery

All this simplicity gets in the way of a program written in MFC, of course. The acronym AFX stands DLL lfor "Application Frameworks." And a screen saver really isn't an application, so much as it's aoaded by the operating system when you walk away from your keyboard. Without a WinMain(), an MFC program seems impossible; MFC writes WinMain() for you, and just about forces you to use it, doesn't it? If you are one of the many MFC programmers who have never written Windows C API programs, you might not even know that MFC is calling DefWindowProc() behind the scenes for you.

You can create a screen saver in MFC, it just takes a little work to ask MFC to live within the restrictions imposed on a screen saver. The two abstract classes offered here, CScreenSaverWnd and CScreenSaverDlg, will take these restrictions into account, and will let you make a fully Windows compliant screen saver in just a few minutes' time.

Make a Dialog-based MFC Application.
To create a new project for a screen saver, using Visual C++ 4.2 or 5.0, you can start with the closest AppWizard available, the "Win32 MFC Application". If you choose to link with MFC in a shared DLL, your screen saver will be quite small, indeed. A dialog-based application will avoid the CDocument/CView architecture infrastructure (see footnote).

Delete all mention of CWinApp.
It's counter-intuitive, but delete all declarations and definitions of your CWinApp-derived class, including that one global instance of the class that's created for you. Just chop it all out.

Create a CWnd-derived Class.
We started with a dialog-based application, but a screen saver is mostly about a simple CWnd that's made to be topmost and fills the screen completely. Use the ClassWizard to make something derived from the generic CWnd class.

It's Not Too Late to Choose Your Parents.
Grab your copy of CScreenSaverWnd and CScreenSaverDlg from the download links here, and include those in your project. (All the author asks is that you keep the copyright notices intact in the source code.) Search for CWnd in your own window class, and replace it with CScreenSaverWnd. Search for CDialog in your own dialog class, and replace it with CScreenSaverDlg. And include the supplied header files to make things compile again.

A Dialogue of Assumptions.
The Class Wizard makes CDialog-based classes, with non-default constructors which take arguments. Due to the rigid way in which a screen saver configuration dialog is specified to Windows, you won't need those arguments. Supply a public, argument-free constructor and destructor for your dialog.

Think Globally.
When you deleted the CWinApp-derived object, you also deleted a global instance of that application. For your CScreenSaverWnd-derived class to function properly, you need one global instance of it. Also, your CScreenSaverDlg-derived class needs a global instance to back it up.

Resources.
Above, the listed requirements for compliant screen savers includes some details for the resources. Add a description string of index one (1). It should probably be less than twenty characters, since it shows up in a small combo box when the user's choosing a screen saver. Change the icon's ID to be equal to 100. Change the dialog's ID to be equal to the wierd number 2003. (You can read mfcsaver.rc as a text file to learn a trick on how to include <scrnsave.h> and use ID_APP and DLG_SCRNSAVECONFIGURE instead of numbers.)

Lather, Rinse, Repeat.
You're pretty much done with the basic screen saver, and it's ready to compile, debug and develop. You can change the output filename setting for the project, so that it has a .SCR extension rather screen saver configuration dialog will appear. If you want to debug your screen saver window, then use the program argument "/save" when running the saver.

Dissect the Example.
Use the included MfcSaver.SCR project if in doubt. It was created in Visual C++ 5.0, and links to the shared MFC42.DLL. The code should be VC++4.2 compatible. Besides illustrating the use of CScreenSaverWnd and CScreenSaverDlg, the project also uses a CImageList to draw from a library of icons, animated to bounce around the screen. The source code is liberally commented.


The Code

Even though CScreenSaverWnd is not a CView-derived object, I felt it was useful to have the OnInitialUpdate() and OnDraw() overridables. I also added three time-saver features which can be used, or ignored, if you like.

  • The default behavior of CScreenSaverWnd is to black the whole screen in OnEraseBkgnd(); this can be disabled by a call to SetAutoBlack(FALSE) in your constructor or OnCreate() or OnInitialUpdate() override.
  • There is a member variable called m_pPalette which, if set to point to a CPalette, will be used to prepare the drawing display context before OnDraw() is called. The OnQueryNewPalette() and OnPaletteChanged() overrides do the correct handling for palettized applications. Leaving m_pPalette = NULL will mean that any and all palette management must be done by your code. (Note that if you draw in 16 colors, like the example does, you'll never need palette management.)
  • The configuration dialog and the screen saver itself rarely get a chance to be seen at the same time, but they do need to communicate. Two overridables, SaveOptions() and RestoreOptions() can be implemented to keep any parameters between runs. Where you save the options is up to you, but the system registry is the preferred choice.

This is a sketch of the declaration of the two classes included. The source code in the downloadable project is well-commented and complete.

// Implemented class CScreenSaverWnd
class CScreenSaverWnd : public CWnd
{
public:
	CScreenSaverWnd();
// Attributes
public:
	BOOL IsAutoBlack() const;
	void SetAutoBlack(BOOL bAutoBlack = TRUE);
	CPalette* GetPalette() const;
	void SetPalette(CPalette* pPalette);
// Overridables
public:
	virtual void OnDraw(CDC* pDC);
	virtual void OnInitialUpdate();
	virtual void SaveOptions();
	virtual void RestoreOptions();
//Implementation
public:
	virtual ~ScreenSaverWnd();
protected:
	virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
	virtual LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnPaint();
	afx_msg BOOL OnQueryNewPalette();
	afx_msg void OnPaletteChanged(CWnd* pFocusWnd);
};

// Implemented class CScreenSaverDlg
class CScreenSaverDlg : public CDialog
{
public:
	CScreenSaverDlg();
// Overrides
public:
	virtual BOOL OnInitDialog();
//Implementation
public:
	virtual ~ScreenSaverDlg();
protected:
	virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
	virtual LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
};

// Implemented APIs
LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT uMsg,
                               WPARAM wParam, LPARAM lParam);
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT uMsg,
                                       WPARAM wParam, LPARAM lParam);
BOOL WINAPI RegisterDialogClasses(HANDLE hInstance);

Footnote:Generally, I'm a big proponent of using CDocument/CView architecture. I once gave a lecture that showed that the Microsoft Excel office application and ID Software's DOOM action game were both good candidates for CViews and CDocuments; in fact, they had nearly identical data architectures. Honest!

Download source - 53KB



Comments

  • Memory Leak in Screen saver

    Posted by Legacy on 01/24/1999 12:00am

    Originally posted by: Andrew Bryan

    FYI:

    There is a memory leak in the sample provided.... I am usure as to where as I have no checker program.

    Andrew Bryan

    Reply
  • Excellent -- way to pick up the ball Microsoft dropped

    Posted by Legacy on 11/28/1998 12:00am

    Originally posted by: Dorothy Nelson

    It's amazing to me that all there is in Visual Studio is this lame-o
    1994 Scrnsvr example whose makefile has flags that nmake no longer
    accepts. Anyway, a huge chunk of thanks and a high five to Ed Halley
    for allowing me to avoid re-inventing the wheel.

    --Dorothy

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds