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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read