Home > Articles > Programming > General Programming/Other Languages

Tour the Screensavers Library

My screensavers library is fully described by the files slib.h and slib.lib. The former C-style header file presents useful constants and function prototypes for screensaver functions, and is included in a screensaver’s source file. The latter C-style static library file contains code that manages screensavers and is included in Win32 projects whose screensaver object files access the screensavers library. Listing 3 presents the contents of slib.h.

Listing 3 slib.h

// slib.h

// resource constants

#define DLG_SCRNSAVECONFIGURE 2003

#define ID_APP        100

#define IDS_DESCRIPTION    1

// function prototypes

LRESULT WINAPI DefScreenSaverProc     (HWND hwnd, UINT iMsg,
                      WPARAM wparam, LPARAM lparam);
BOOL  WINAPI RegisterDialogClasses   (HANDLE hinstance);
BOOL  WINAPI ScreenSaverConfigureDialog (HWND hwnd, UINT iMsg,
                      WPARAM wparam, LPARAM lparam);
LRESULT WINAPI ScreenSaverProc      (HWND hwnd, UINT iMsg,
                      WPARAM wparam, LPARAM lparam);

The slib.h header file presents three resource constants: DLG_SCRNSAVECONFIGURE is the resource ID of the screensaver configuration dialog box, ID_APP is the resource ID of the icon to specify for the screensaver, and IDS_DESCRIPTION is the resource ID of a string describing the screensaver. This string, which must be fewer than 25 characters in length, appears in the Control Panel’s Desktop applet screensaver listbox for early versions of Windows NT. For Windows 98 SE and Windows ME, it is the filename of the screensaver—not this string—that appears in the Screen Saver drop-down listbox on the Screen Saver tab of the Display Properties dialog box.

The slib.h header file also presents four screensaver function prototypes:

  • LRESULT WINAPI DefScreenSaverProc (HWND hwnd, UINT iMsg, WPARAM wparam, LPARAM lparam); provides default message processing; it must be invoked from a screensaver’s ScreenSaverProc() function for messages not handled by that function.
  • BOOL WINAPI RegisterDialogClasses (HANDLE hinstance); lets a programmer register any child control windows needed by the screensaver’s configuration dialog box. If no child control windows need to be registered, or if registration is successful, TRUE returns. Otherwise, this function returns FALSE. This function is defined by a screensaver and invoked from the screensavers library.
  • BOOL WINAPI ScreenSaverConfigureDialog (HWND hwnd, UINT iMsg, WPARAM wparam, LPARAM lparam); processes messages on behalf of the configuration dialog box. This function is defined by a screensaver and invoked from the screensavers library.
  • LRESULT WINAPI ScreenSaverProc (HWND hwnd, UINT iMsg, WPARAM wparam, LPARAM lparam); provides screensaver-specific message processing and must invoke DefScreenSaverProc() if it does not respond to a given message. This function is defined by a screensaver and invoked from the screensavers library.

If you’ve worked with Microsoft’s screensavers library, you’ll probably notice a similarity to what appears in Listing 3. The constants and function prototypes are practically identical to their Microsoft counterparts, because I wanted to make it as easy as possible to compile existing screensaver source code and link its object code with my screensavers library.

The screensavers library’s slib.lib static library file is created from slib.c. That C source code is presented in Listing 4.

Listing 4 slib.c

// slib.c

// Screensavers library.

#include <windows.h>

#include "slib.h"

// minimum number of pixels that mouse must move (horizontally plus vertically)
// before screensaver will close

#define WAKE_THRESHOLD 4

// type definitions for change password and verify password function prototypes

typedef VOID (WINAPI *PWDCHANGEPASSWORD)
 (LPCSTR lpcRegkeyname, HWND hwnd, UINT uiReserved1, UINT uiReserved2);

typedef BOOL (WINAPI *VERIFYSCREENSAVEPWD)(HWND hwnd);

// enumeration of screensaver modes. g_scrmode holds the mode in which the
// screensaver should be running. the default mode is smNone (0).

enum {smNone, smConfig, smPassword, smPreview, smSaver} g_scrmode;

// checking password flag. this flag is set to TRUE prior to calling the
// VerifyScreenSavePwd() function and reset to FALSE after that function
// returns. if set, we show the arrow pointer mouse cursor. if reset, we hide
// the mouse cursor. after all, we want the user to see the mouse cursor as
// they move it around the screen while the verification dialog box is visible.
// otherwise, they might have trouble moving the mouse into that dialog box, if
// they cannot see the mouse cursor over much of the screen.

static BOOL g_fCheckingPassword = FALSE;

// screensaver must close flag. this flag is set to TRUE if the user enters the
// correct password in the password verification dialog box or if there is no
// VerifyScreenSavePwd() function.

static BOOL g_fClosing = FALSE;

// running under Windows 95/98/ME flag. this flag is set to TRUE if running
// under Windows 95/98/ME. it is used to prevent access to the password
// change/verification dialog boxes under Windows NT and other Win32 operating
// systems. it is also used to prevent disabling/renabling the Alt+Tab,
// Ctrl+Alt+Delete, and Windows keys under NT and other Win32 operating systems.
// these system keys are automatically disabled under NT.

static BOOL g_fOnWin95 = FALSE;

// screensaver class name

static char g_szClassName [] = "WindowsScreenSaverClass";

// password.cpl instance handle

static HINSTANCE g_hinstPwdDLL;

// program instance handle

static HINSTANCE g_hinstance;

// last recorded mouse position. this position is initialized in response to
// DefScreenSaverProc()’s WM_CLOSE and WM_MOUSEMOVE messages. this position is
// also initialized in response to RealScreenSaverProc()’s WM_CREATE message.

static POINT g_ptMouse;

// message ID associated with "QueryCancelAutoPlay" message string

static UINT uShellMessage;

// VerifyScreenSavePwd() entry point address

static VERIFYSCREENSAVEPWD g_verifyScreenSavePwd;

// local function prototypes

void doChangePwd   (HWND hwnd);
void doConfig    (HWND hwnd);
void doSaver     (HWND hwndParent);
void hogMachine   (BOOL fDisable);
void loadPwdDLL   (void);
void unloadPwdDLL  (void);

LRESULT CALLBACK RealScreenSaverProc (HWND hwnd, int iMsg, WPARAM wparam,
                   LPARAM lparam);

// ==========================================================================
// int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hinstancePrev,
//           LPSTR lpszCmdLine, int iCmdShow)
//
// Program entry point.
//
// Arguments:
//
// hinstance   - program’s instance handle (which uniquely identifies the
//         program when running under Windows)
//
// hinstancePrev - obsolete (leftover from Windows 3.1)
//
// lpszCmdLine  - address of \0-terminated string that contains command-line
//         parameters (not used in this program)
//
// iCmdShow   - number that indicates how the window is to be initially
//         displayed (not used in this program)
//
// Return    - 0
// ==========================================================================

#pragma argsused
int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hinstancePrev,
          LPSTR lpszCmdLine, int iCmdShow)
{
  char     *pch;
  HWND     hwnd;
  OSVERSIONINFO osvi;

  // Obtain version number and determine if running Windows operating system
  // belongs to the Windows 95/98/ME family. Set the g_fOnWin95 flag to true
  // for Windows 95/98/ME family.
  
  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (GetVersionEx (&osvi) || osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
    g_fOnWin95 = TRUE;

  // If an exception occurs within the body of the following try statement, we
  // must handle that exception in the __except handler. That handler invokes
  // hogMachine() with a FALSE argument to re-enable the Alt+Tab, Windows, and
  // Ctrl+Alt+Delete keys.

  __try
  {
    // Save instance handle for later access in doConfig() and doSaver().

    g_hinstance = hinstance;

    // Obtain the command line.

    pch = GetCommandLine ();

    // Process the command line. Example: "c:\bc45\projects\pscl\pscl.scr" /S
    // In some cases, there may not be double quotes.

    if (*pch == ’\"’)
    {
      // Skip open double quote.

      pch++;

      // Skip all characters until end of command line or close double
      // quote encountered.

      while (*pch != ’\0’ && *pch != ’\"’)
       pch++;
    }
    else
    {
      // Skip all characters until end of command line or space
      // encountered.

      while (*pch != ’\0’ && *pch != ’ ’)
       pch++;
    }

    // If *pch is not \0 (end of command line), it is either a double quote
    // or a space. Skip past this character.

    if (*pch != ’\0’)
      pch++;

    // Consume all intervening spaces.

    while (*pch == ’ ’)
     pch++;

    // If *pch is \0, no arguments were passed to the screensaver. Establish
    // smConfig as the screensaver mode, with the foreground window as the
    // parent window handle.

    if (*pch == ’\0’)
    {
      g_scrmode = smConfig;
      hwnd = GetForegroundWindow ();
    }
    else
    {
      // If we found an option, skip over option indicator.

      if (*pch == ’-’ || *pch == ’/’)
        pch++;

      // Detect valid option characters.

      if (*pch == ’a’ || *pch == ’A’)
      {
        // Establish smPassword as the screensaver mode.

        g_scrmode = smPassword;

        // Skip over option character.

        pch++;

        // Skip intervening spaces or colons.

        while (*pch == ’ ’ || *pch == ’:’)
         pch++;

        // Save handle argument as the parent window handle.

        hwnd = (HWND) atoi (pch);
      }
      else
      if (*pch == ’c’ || *pch == ’C’)
      {
        // Establish smConfig as the screensaver mode.

        g_scrmode = smConfig;

        // Skip over option character.

        pch++;

        // Skip intervening spaces or colons.

        while (*pch == ’ ’ || *pch ==’:’)
         pch++;

        // If nothing follows the option character (except for spaces and
        // colons), use the foreground window’s handle as the parent
        // window handle. Otherwise, save the handle argument as the
        // parent window handle.

        if (*pch == ’\0’)
          hwnd = GetForegroundWindow ();
        else
          hwnd = (HWND) atoi (pch);
      }
      else
      if (*pch == ’p’ || *pch == ’P’ || *pch == ’l’ || *pch == ’L’)
      {
        // Establish smPreview as the screensaver mode.

        g_scrmode = smPreview;

        // Skip over option character.

        pch++;

        // Skip intervening spaces or colons.

        while (*pch == ’ ’ || *pch == ’:’)
         pch++;

        // Save handle argument as the parent window handle.

        hwnd = (HWND) atoi (pch);
      }
      else
      if (*pch == ’s’ || *pch == ’S’)
      {
        // Establish smSaver as the screensaver mode.

        g_scrmode = smSaver;
      }
    }

    // Invoke appropriate screensaver entry point based on screensaver mode.

    if (g_scrmode == smConfig)
      doConfig (hwnd);
    else
    if (g_scrmode == smPassword)
      doChangePwd (hwnd);
    else
    if (g_scrmode == smPreview)
      doSaver (hwnd);
    else
    if (g_scrmode == smSaver)
      doSaver (NULL);
  }
  __except (EXCEPTION_EXECUTE_HANDLER)
  {
    hogMachine (FALSE);
  }

  return 0;
}

// ============================================================================
// LRESULT WINAPI DefScreenSaverProc (HWND hwnd, int iMsg, WPARAM wparam,
//                  LPARAM lparam)
//
// Default screensaver procedure for processing messages.
//
// Arguments:
//
// hwnd  - handle of window associated with this screensaver procedure
//
// iMsg  - message identifier
//
// wparam - 32-bit word parameter with message data (if applicable)
//
// lparam - 32-bit long word parameter with message data (if applicable)
//
// Return:
//
// 0, 1, or -1 if message processed, otherwise DefWindowProc() result
// ============================================================================

LRESULT WINAPI DefScreenSaverProc (HWND hwnd, UINT iMsg, WPARAM wparam,
                  LPARAM lparam)
{
  POINT ptCheck;
  int deltax, deltay;

  // The doSaver() function invokes function RegisterWindowMessage () with the
  // "QueryCancelAutoPlay" string argument. That function attempts to register
  // this message with Windows. If registration succeeds, a nonzero message ID
  // returns. Windows sends that message ID to the foreground window’s window
  // procedure whenever the user inserts a CD into the CD-ROM/DVD-ROM drive,
  // and the CD contains an autorun.inf file identifying a program to run.

  // The message handler returns 1 if it does not want that program to run. It
  // returns 0 otherwise. We don’t want to run a CD-based program while a
  // password-protected screensaver is running. After all, the user should not
  // be able to execute programs prior to entering the correct password. If we
  // permitted these CD-based programs to execute, it is possible that a
  // program would be executed that could violate the security of the password,
  // and then there would be no point in assigning a password to a screensaver.

  // RegisterWindowMessage() returns 0 if it fails to register the message.

  if (iMsg == uShellMessage)
  {
    if (uShellMessage == 0)
      return DefWindowProc (hwnd, iMsg, wparam, lparam);

    PostMessage (hwnd, WM_CLOSE, 0, 0);

    return (g_verifyScreenSavePwd) ? 1 : 0;
  }

  // The following switch statement is only entered if we are not running in a
  // preview window (the screensaver occupies the entire desktop) and we are
  // not closing the screensaver.

  if (g_scrmode != smPreview && !g_fClosing)
    switch (iMsg)
    {
     case WM_ACTIVATEAPP:
        // Close the screensaver window if another window is being
        // activated. For example, suppose the screensaver-specific
        // window procedure launches a copy of Windows Notepad. In
        // preview mode, we can see Notepad’s main window. We cannot see
        // that window when the screensaver occupies the entire desktop.
        // By posting the close message, we shut down the desktop
        // screensaver.

        if (wparam == FALSE)
          PostMessage (hwnd, WM_CLOSE, 0, 0);

        break;

     case WM_CLOSE:
        // If we are not running under the Windows 95/98/ME family, we
        // must forward WM_CLOSE to default window procedure.

        if (!g_fOnWin95)
          break;

        // Prepare to verify the password. If g_verifyScreenSavePwd is
        // NULL, we cannot verify the password, so forward the WM_CLOSE
        // message to DefWindowProc() to cause the screensaver to close.

        if (!g_verifyScreenSavePwd)
        {
          g_fClosing = TRUE;
          break;
        }

        // Verify the password. If the user enters the correct password,
        // forward WM_CLOSE to DefWindowProc(), to cause the screensaver
        // to close.

        g_fCheckingPassword = TRUE;

        g_fClosing = g_verifyScreenSavePwd (hwnd);

        g_fCheckingPassword = FALSE;

        if (g_fClosing)
          break;

        // Get current mouse cursor coordinates. We do this because we
        // are not yet closing the screensaver (which is occupying the
        // entire desktop) and the mouse cursor will have moved while
        // clicking a button in the verification dialog box. These mouse
        // coordinates are compared with mouse coordinates obtained
        // during WM_MOUSEMOVE message processing (see below). If they
        // indicate a combined movement more than WAKE_THRESHOLD pixels,
        // we want to close the screensaver.

        GetCursorPos (&g_ptMouse);

        return 0;

     case WM_KEYDOWN:
     case WM_LBUTTONDOWN:
     case WM_MBUTTONDOWN:
     case WM_POWERBROADCAST:
     case WM_RBUTTONDOWN:
     case WM_SYSKEYDOWN:
        // Close the screensaver.

        PostMessage (hwnd, WM_CLOSE, 0, 0);

        break;

     case WM_MOUSEMOVE:
        // Get current mouse cursor coordinates.

        GetCursorPos (&ptCheck);

        // Compute distance between current horizontal coordinate and
        // last saved horizontal coordinate.

        deltax = ptCheck.x-g_ptMouse.x;
        if (deltax < 0)
          deltax = -deltax;

        // Compute distance between current vertical coordinate and last
        // saved vertical coordinate.

        deltay = ptCheck.y-g_ptMouse.y;
        if (deltay < 0)
          deltay = -deltay;

        // Compute total distance change.

        deltay += deltax;

        // If total distance change exceeds a pixels threshold, save the
        // current mouse cursor coordinates (for next time) and close
        // the screensaver.

        if (deltay > WAKE_THRESHOLD)
        {
          g_ptMouse.x = ptCheck.x;
          g_ptMouse.y = ptCheck.y;
          PostMessage (hwnd, WM_CLOSE, 0, 0);
        }

        break;

     case WM_POWER:
        // If the system is resuming operation after entering suspended
        // mode, close the screensaver.

        if (wparam == PWR_CRITICALRESUME)
          PostMessage (hwnd, WM_CLOSE, 0, 0);

        break;

     case WM_SETCURSOR:
        // Show or hide the cursor.

        SetCursor (g_fCheckingPassword ? LoadCursor (NULL, IDC_ARROW)
                       : NULL);

        return -1;
    }

  return DefWindowProc (hwnd, iMsg, wparam, lparam);
}

// ==========================================================
// void doChangePwd (HWND hwnd)
//
// Screensaver entry point for changing screensaver password.
//
// Arguments:
//
// hwnd - parent window handle
//
// Return:
//
// none
// ==========================================================

static void doChangePwd (HWND hwnd)
{
  HINSTANCE hinstMPRDLL;
  PWDCHANGEPASSWORD pwdChangePassword;

  // Return if not running under Windows 95/98/ME family. This function will
  // most likely never be invoked under Windows NT and above (which handle
  // passwords themselves and do not invoke screensavers with argument /A).
  // Still, it is better to be safe than sorry -- hence "if (!g_fOnWin95)".

  if (!g_fOnWin95)
    return;

  // Load multiple provider router DLL.

  hinstMPRDLL = LoadLibrary ("mpr.dll");
  if (hinstMPRDLL == NULL)
    return;

  // Obtain PwdChangePasswordA() function entry point.

  pwdChangePassword = (PWDCHANGEPASSWORD) GetProcAddress (hinstMPRDLL,
                              "PwdChangePasswordA");

  // Invoke PwdChangePasswordA() function to change screensaver password.

  if (pwdChangePassword != NULL)
    (*pwdChangePassword) ("SCRSAVE", hwnd, 0, 0);

  // Unload multiple provider router DLL.

  FreeLibrary (hinstMPRDLL);
}

// ====================================================
// void doConfig (HWND hwnd)
//
// Screensaver entry point for configuring screensaver.
//
// Arguments:
//
// hwnd - parent window handle
//
// Return:
//
// none
// ====================================================

static void doConfig (HWND hwnd)
{
  // RegisterDialogClasses() lets the screensaver register any child control
  // windows needed by the configuration dialog box. If successful, that
  // function returns TRUE.

  if (RegisterDialogClasses (g_hinstance))
  {
    // Display the configuration dialog box, allowing the user to configure
    // the screensaver.

    DialogBoxParam (g_hinstance, MAKEINTRESOURCE(DLG_SCRNSAVECONFIGURE),
            hwnd, (DLGPROC) ScreenSaverConfigureDialog, 0);
  }
}

// ================================================
// void doSaver (HWND hwndParent)
//
// Screensaver entry point for running screensaver.
//
// Arguments:
//
// hwndParent - parent window handle
//
// Return:
//
// none
// ================================================

static void doSaver (HWND hwndParent)
{
  HDC hdc;
  MSG msg;
  HWND hwnd;
  WNDCLASS cls;
  HANDLE hOther;
  RECT rcParent;
  int nx, ny, cx, cy;
  PSTR pszWindowTitle;
  UINT uExStyle, uStyle;

  // Define screensaver’s window class.

  cls.style = CS_OWNDC | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
  cls.lpfnWndProc = (WNDPROC) RealScreenSaverProc;
  cls.cbClsExtra = 0;
  cls.cbWndExtra = 0;
  cls.hInstance = g_hinstance;
  cls.hIcon = LoadIcon (g_hinstance, MAKEINTRESOURCE(ID_APP));
  cls.hCursor = NULL;
  cls.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
  cls.lpszMenuName = NULL;
  cls.lpszClassName = g_szClassName;

  // If hwndParent is not NULL, we are running in preview mode. Otherwise, we
  // are running in saver mode.

  if (hwndParent != NULL)
  {
    // Specify appropriate title for preview mode screensaver window.

    pszWindowTitle = "Preview";

    // Get the parent window rectangle.

    GetClientRect (hwndParent, &rcParent);

    // Compute width and position information for preview window, relative to
    // the parent window.

    cx = rcParent.right;
    cy = rcParent.bottom;
    nx = 0;
    ny = 0;

    // No extended style required for preview window, which is a child of its
    // non-desktop parent window.

    uExStyle = 0;

    // Set the preview window style -- child window, visible, exclude the
    // area occupied by child windows when drawing in parent window.

    uStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN;
  }
  else
  {
    // Specify appropriate title for saver mode screensaver window.

    pszWindowTitle = "Screen Saver";

    // Search for existing screensaver window.

    hOther = FindWindow (g_szClassName, pszWindowTitle);

    // If this window exists, make it the foreground window and return. We
    // don’t execute more than one screensaver at the same time. What would
    // be the point of doing that?
	  
    if (hOther != NULL && IsWindow ((HWND) hOther))
    {
      SetForegroundWindow (hOther);
      return;
    }

    // Get the desktop window rectangle.

    hdc = GetDC (HWND_DESKTOP);
    GetClipBox (hdc, &rcParent);
    ReleaseDC (HWND_DESKTOP, hdc);

    // Compute width and position information for screensaver window,
    // relative to the desktop window -- the entire screen.

    cx = rcParent.right - rcParent.left;
    cy = rcParent.bottom - rcParent.top;
    nx = rcParent.left;
    ny = rcParent.top;

    // The screensaver window occupies the entire screen and must be the
    // topmost window -- no other window can be located on top of the
    // screensaver window.

    uExStyle = WS_EX_TOPMOST;

    // Set the screensaver window style -- popup, visible, exclude the area
    // occupied by child windows when drawing in parent window, clip child
    // windows relative to each other.

    uStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  }

  // Attempt to register the screensaver’s window class.

  if (!RegisterClass (&cls))
    return;

  if (g_fOnWin95)
  {
    // Load the password DLL (found in password.cpl -- a DLL with a .cpl
    // file extension.

    loadPwdDLL ();

    // Register the QueryCancelAutoPlay message with Windows.

    uShellMessage = RegisterWindowMessage ("QueryCancelAutoPlay");
  }

  // Disable the Alt+Tab, Ctrl+Alt+Delete, and Windows keys.

  hogMachine (TRUE);

  // Create either preview mode or saver mode screensaver window.

  hwnd = CreateWindowEx (uExStyle, g_szClassName, pszWindowTitle, uStyle,
             nx, ny, cx, cy, hwndParent, NULL, g_hinstance, NULL);

  // If we were successful ...

  if (hwnd)
  {
    // Either a preview window or a window occupying the entire desktop has
    // been created. In the latter case, make that window the foreground
    // window. Nothing can supersede a window occuping the entire desktop.

    if (g_scrmode != smPreview)
      SetForegroundWindow (hwnd);

    // Enter the screensaver’s message loop. Loop ends when
    // RealScreenSaverProc() responds to the WM_DESTROY message by calling
    // PostQuitMessage (0).

    while (GetMessage (&msg, NULL, 0, 0))
    {
     TranslateMessage (&msg);
     DispatchMessage (&msg);
    }
  }

  // Enable the Alt+Tab, Ctrl+Alt+Delete, and Windows keys.

  hogMachine (FALSE);

  if (g_fOnWin95)
  {
    // Unload the password DLL.

    unloadPwdDLL ();
  }
}

// ==========================================================================
// void hogMachine (BOOL fDisable)
//
// Disable or enable special keys when running under Windows 95/98/ME family.
//
// Arguments:
//
// fDisable - disable/enable flag (true - disable, false - enable)
//
// Return:
//
// none
// ==========================================================================

static void hogMachine (BOOL fDisable)
{
  UINT uOldValue;

  if (!g_fOnWin95)
    return;

  // Disable or enable CTRL-ALT-DELETE, ALT-TAB, and Windows keys when running
  // under the Windows 95/98/ME family.

  SystemParametersInfo (SPI_SCREENSAVERRUNNING, fDisable, &uOldValue, 0);
}

// ======================
// void loadPwdDLL (void)
//
// Load the password DLL.
//
// Arguments:
//
// none
//
// Return:
//
// none
// ======================

static void loadPwdDLL (void)
{
  HKEY hkey;
  DWORD dwSize, dwVal;

  // Open the specified key.

  if (RegOpenKey (HKEY_CURRENT_USER, "Control Panel\\Desktop", &hkey))
    return;

  // Fetch password value from the ScreenSaveUsePassword field of the open
  // key.

  dwSize = sizeof(DWORD);
  if (RegQueryValueEx (hkey, "ScreenSaveUsePassword", NULL, NULL,
    (LPBYTE) &dwVal, &dwSize))
  {
    RegCloseKey (hkey);
    return;
  }

  // Close the open key.

  RegCloseKey (hkey);

  // If a password exists ...

  if (dwVal != 0)
  {
    // Load the password.cpl DLL. This DLL contains the VerifyScreenSavePwd()
    // function.

    g_hinstPwdDLL = LoadLibrary ("password.cpl");
    if (!g_hinstPwdDLL)
      return;

    // Obtain entry point for VerifyScreenSavePwd.

    g_verifyScreenSavePwd =
     (VERIFYSCREENSAVEPWD) GetProcAddress (g_hinstPwdDLL,
                        "VerifyScreenSavePwd");
    if (g_verifyScreenSavePwd != NULL)
      return;

    // Unload password.cpl DLL.

    unloadPwdDLL ();
  }
}

// ========================
// void unloadPwdDLL (void)
//
// Unload the password DLL.
//
// Arguments:
//
// none
//
// Return:
//
// none
// ========================

static void unloadPwdDLL (void)
{
  // Return if password.cpl DLL not loaded.

  if (!g_hinstPwdDLL)
    return;

  // Remove the passworld.cpl DLL from memory.

  FreeLibrary (g_hinstPwdDLL);
  g_hinstPwdDLL = NULL;
  g_verifyScreenSavePwd = NULL;
}

// ===========================================================================
// LRESULT WINAPI RealScreenSaverProc (HWND hwnd, int iMsg, WPARAM wparam,
//                   LPARAM lparam)
//
// Real screensaver procedure for processing messages.
//
// Arguments:
//
// hwnd  - handle of window associated with this real screensaver procedure
//
// iMsg  - message identifier
//
// wparam - 32-bit word parameter with message data (if applicable)
//
// lparam - 32-bit long word parameter with message data (if applicable)
//
// Return:
//
// 0 or 1 if message processed, otherwise DefWindowProc() or ScreenSaverProc()
// result
// ===========================================================================

static LRESULT CALLBACK RealScreenSaverProc (HWND hwnd, int iMsg, WPARAM wparam,
                       LPARAM lparam)
{
  switch (iMsg)
  {
   // If preview window is being displayed, user clicks the right mouse
   // button in preview window, or user clicks ? button beside X button in
   // caption area of Display Properties window followed by right mouse
   // button in preview window, Windows sends a WM_CONTEXTMENU message. But
   // if user clicks ? button followed by left mouse button in preview
   // window, and then moves the mouse pointer out of preview window, Windows
   // sends a WM_HELP message. In either case, message is forwarded to valid
   // parent window.

   case WM_CONTEXTMENU:
   case WM_HELP:
      if (g_scrmode == smPreview)
      {
        HWND hwndParent = GetParent (hwnd);
        if (hwndParent == NULL || !IsWindow (hwndParent))
          return 1;
        PostMessage (hwndParent, iMsg, (WPARAM) hwndParent, lparam);
        return 1;
      }

      break;

   // WM_CREATE message sent in response to CreateWindowEx() function call in
   // the doSaver() function.

   case WM_CREATE:
      // Capture the mouse’s current position. This position is used in
      // DefScreenSaverProc() to determine if the mouse has moved
      // sufficiently to close the screensaver.

      GetCursorPos (&g_ptMouse);

      // If preview window is not being displayed (we are covering the
      // entire desktop with a screensaver), hide the mouse cursor.

      if (g_scrmode != smPreview)
        SetCursor (NULL);

      break;

   // WM_DESTROY message sent in response to window being destroyed, after
   // the window has been removed from the screen.

   case WM_DESTROY:
      // Terminate the screensaver. PostQuitMessage() posts a WM_QUIT
      // message to the thread’s message queue and immediately returns. 

      PostQuitMessage (0);
  }

  return ScreenSaverProc (hwnd, iMsg, wparam, lparam);
}

Listing 4 reveals lots of stuff happening in the screensavers library, which is nothing more than a Windows program without implementations for the RegisterDialogClasses(), ScreenSaverConfigureDialog(), and ScreenSaverProc() callback functions. You must implement these three functions for each screensaver that you write. Perhaps the most important task performed by the library is to parse the command line (from within WinMain()) to determine in which mode the screensaver will run: authentication, configuration, preview, or saver. The following introductions to these modes assume Windows 98 SE for GUI descriptions:

  • Authentication mode, which appears to be unique to Windows 95/98/ME, allows the user to specify a password for a screensaver. When the screensaver runs and the user wants to close the screensaver, the user must specify, via the password verification dialog box, the exact password. Otherwise, the screensaver continues to run.

    Access this mode by choosing the Password protected checkbox and then clicking the Change button on the Screen Saver tab of the Display Properties dialog box. In response, Windows passes either /a or /A, followed by a decimal integer argument representing the parent window handle, to the screensaver at startup.

  • Configuration mode allows the user to configure the screensaver (change colors, for example).

    Access this mode by clicking the Settings button on the Screen Saver tab of the Display Properties dialog box or by right-clicking the screensaver filename in Windows Explorer and selecting the Configure menu item from the resulting popup menu. In response, Windows passes either /c or /C, optionally followed by a decimal integer argument representing the parent window handle, to the screensaver at startup.

    Although I have not confirmed this behavior, I understand that circumstances exist in which Windows does not pass a decimal integer argument after /c or /C. Under those circumstances, GetForegroundWindow() must be called to obtain the handle of the foreground window, which is then used as the parent window handle.

  • Preview mode allows the user to observe the screensaver in a small preview window on the Screen Saver tab of the Display Properties dialog box.

    This mode is accessed automatically whenever the screensaver name is chosen from that tab’s Screen Saver dropdown listbox. In response, Windows passes either /p or /P, followed by a decimal integer argument representing the parent window handle, to the screensaver at startup.

  • Saver mode allows the user to observe the screensaver occupying the full screen.

    Access this mode by clicking the Preview button on the Screen Saver tab of the Display Properties dialog box, or by right-clicking the screensaver filename in Windows Explorer and selecting the Test menu item from the resulting popup menu. In response, Windows passes either /s or /S to the screensaver at startup.

Also, with respect to Windows 95/98/ME, the screensavers library interacts with two DLLs for password verification and change: mpr.dll and password.cpl. The former multiple provider router DLL contains a function that, when called, presents a password verification dialog box to the user. That VerifyScreenSavePwd() function has the following signature:

BOOL WINAPI VerifyScreenSavePwd (HWND hwnd)

Parameter hwnd identifies the parent window handle for the child dialog box that is presented by this function. If verification succeeds (the user enters the correct password), this function returns TRUE. Otherwise, the function returns FALSE.

The latter control panel DLL contains a function that, when called, presents a password change dialog box to the user. That PwdChangePassword() function has the following signature:

VOID WINAPI PwdChangePassword (LPCSTR lpcRegkeyname, HWND hwnd, UINT uiReserved1, UINT uiReserved2)

Parameter lpcRegkeyname identifies the registry key name in which the new password is stored. Parameter hwnd identifies the parent window handle for the child dialog box that is presented by this function. Parameters uiReserved1 and uiReserved2 are reserved and should be passed 0 arguments.

Build the Library

Before we can link the screensavers library with a screensaver object file, we need to build that library. To simplify this task, I created a makelib.bat batch file, whose contents appear in Listing 5.

Listing 5 makelib.bat

erase slib.lib
bcc32 -tW -c -Ic:\borland\bcc55\include -Lc:\borland\bcc55\lib slib.c
tlib slib.lib +slib.obj

The batch file first erases slib.lib because Borland’s tlib.exe librarian program will not add an object file to a library if that object file has already been added. The batch file next compiles slib.c into slib.obj, thanks to bcc32.exe’s -c command-line option. Finally, the batch file adds slib.obj to a newly created slib.lib.

Make sure that the current directory contains makelib.bat, slib.c, and slib.h. Also, make sure that Borland C++’s Bin directory has been added to your PATH environment variable. From the command line, invoke makelib. If there are no problems, you should discover an slib.lib file with a file size of 7,680 bytes. Congratulations! Let’s create a screensaver.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020