ScrHots - Redistribution

ScrHots adds itself to the Display Properties property sheet, and sits in the system tray. It turns corners of your screen into 'hot corners' that can start the saver immediately, or prevent it from running. If only allows for four corners. If you have multiple monitors, then those corners are taken to be the furthest ones.

You may redistribute ScrHots3 in the following ways:

There's no charge for redistributing Scrhots. But the author would appreciate it if you send him an email!

Installing Scrhots

The neatest way to install ScrHots is with the program ScrHots3-setup.exe. This installer has the ability to installer a more recent version of Scrplus, even if the old one is currently running! You may re-distribute this program.

A common way to install ScrHots is to write an installer program which installs both your screen saver and ScrHots. If you do this you will need to do the same version-checking and replacing-existing-version stuff as HotsInst. Example source code is given below.

Otherwise, you can copy it into the Windows directory yourself. It must go in the windows directory, not the system directory. It must have the name scrhots.dll. You must be careful: if there is a more up-to-date version of scrhots.dll there, then you should not overwrite it.

If you choose this second route you must also ensure that the registry and things are set up correctly. There are three ways of doing this:

[HKEY_CLASSES_ROOT\CLSID\{9CB48109-E0FC-11D0-8E0C-C52523886C3B}]
  @="ScrHots"
[HKEY_CLASSES_ROOT\CLSID\{9CB48109-E0FC-11D0-8E0C-C52523886C3B}\InProcServer32]
  @="C:\\WINDOWS\\SCRHOTS.DLL"
  "ThreadingModel"="Apartment"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Controls Folder\Display\shellex\PropertySheetHandlers\ScrHots Properties]
  @="{9CB48109-E0FC-11D0-8E0C-C52523886C3B}"
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Screen Savers]
  "Mouse Corners"="YNN-"
  "Corner Popups"=dword:00000001
  "Hot corners tray"=dword:00000001

More ways of controlling Scrhots

void __fastcall NotifyHots()
{ typedef VOID (WINAPI *SCREENSAVERCHANGED)();
  HINSTANCE sagedll=LoadLibrary("Sage.DLL");
  if (sagedll!=NULL)
  { SCREENSAVERCHANGED changedproc= (SCREENSAVERCHANGED)GetProcAddress(sagedll,"Screen_Saver_Changed");
    if (changedproc!=NULL) changedproc();
    FreeLibrary(sagedll);
  }
  HINSTANCE hotsdll=LoadLibrary("ScrHots.dll");
  if (hotsdll!=NULL)
  { SCREENSAVERCHANGED changedproc= (SCREENSAVERCHANGED)GetProcAddress(hotsdll,"Screen_Saver_Changed");
    if (changedproc!=NULL) changedproc();
    FreeLibrary(hotsdll);
  }
}

How to write your own installer for Scrhots

It's realy neat to have a single 'setup.exe' executable, which has both your saver.scr and scrhots.dll embedded as resources. The user runs this setup.exe. It replaces scrhots.dll if necessary. And it installs the saver. And it displays a dialog showing that it is installed. Complete source code for such an installer is given below.

If you only want to install Scrhots itself, and not your own saver, then you can just use the InstallScrhots function defined below.

// INSTALLER
// (c) 1998-2000 Lucian Wischik
// You may use this source code however you wish, with no restrictions at all.
// The program intalls Scrhots, and also installs your saver,
// and then pops up a dialog box to show that it's finished.
// It uses certain resources, which you should also include:
//
// RESOURCE_SCRHOTS  RCDATA DISCARDABLE  "scrhots.dll"
// RESOURCE_SAVER    RCDATA DISCARDABLE  "yoursaver.scr"
// STRINGTABLE DISCARDABLE 
// BEGIN
//   1 "the Massage saver"
//   2 "Massage.scr"
// END
// the first string is used in the sentence: "this program will install <1>"
// the second string is the filename for the saver. It gets installed into the windows directory.
//
//
// Also, if there is a dialog called "DIALOG_PRELIM", it will show this dialog
// before proceeding with the installation. Here's the dialog I use:
//
// DIALOG_PRELIM DIALOG DISCARDABLE  0, 0, 199, 146
// STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
// CAPTION "Massage Puzzle Saver"
// FONT 8, "MS Sans Serif"
// BEGIN
//     DEFPUSHBUTTON   "&Install",IDOK,85,125,50,14
//     PUSHBUTTON      "Cancel",IDCANCEL,142,125,50,14
//     ICON            ICON_MASSAGE,IDC_STATIC,9,17,21,20
//     ICON            ICON_LU,IDC_STATIC,9,44,21,20
//     ICON            ICON_SCRHOTS,IDC_STATIC,9,78,21,20
//     LTEXT           "MASSAGE PUZZLE SAVER",IDC_STATIC,36,7,156,9
//     CONTROL         "Screensaver code by Lucian Wischik\r\nusing the ScrPlus screensaver development kit\r\nwww.wischik.com/scr",
//                     IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,36,44,
//                     156,28
//     LTEXT           "Also in this package is ScrHots. This turns the corners of your screen into mouse 'hot corners' that can start the saver immediately, or prevent it from running. Configure it from its tab in the Display Properties dialog.",
//                     IDC_STATIC,36,78,156,45
//     LTEXT           "An interractive tile puzzle to help with anatomy revision.",
//                     IDC_STATIC,36,19,156,19
// END





#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#pragma hdrstop
#include "resource.h"


HINSTANCE hInstance;
BOOL CALLBACK FirstDialogProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK InstallWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
BOOL InstallFile(HWND hwnd,char *fn,char *res,char *name);
BOOL InstallScrhots(HWND hwnd,char *res,BOOL ShowDialog);
void RegisterClasses();
BOOL SaveResourceToFile(char *fn,char *res);
BOOL FileExists(char *fn);
void SelectAsDefault(char *scr);
int ExecutePreviewDialog(HWND hwnd,char *scr);
int ExecuteConfigDialog(HWND hwnd,char *scr);
int ExecuteInstallDialog(HWND hwnd,char *scr);
BOOL CheckDeskCpl();
int GetFileVersionField(char *fn,char *info,char *ret,int len,DWORD *dwver);



// WINMAIN: all the work gets done here
int WINAPI WinMain(HINSTANCE h,HINSTANCE,LPSTR,int)
{ hInstance=h;
  RegisterClasses();
  RECT rc;
  SystemParametersInfo(SPI_GETWORKAREA,0,&rc,FALSE); // WorkArea excludes the taskbar
  HWND hwnd=CreateWindow("InstallClass","Install Saver",WS_POPUP|WS_VISIBLE,
    rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,
    NULL,NULL,hInstance,NULL);
  if (hwnd==NULL) return IDCANCEL;
  int res=DialogBox(hInstance,"DIALOG_PRELIM",hwnd,FirstDialogProc);
  if (res==IDCANCEL) return res;
  HCURSOR hOld=SetCursor(LoadCursor(NULL,IDC_WAIT));
  InvalidateRect(hwnd,NULL,FALSE); UpdateWindow(hwnd); // gets rid of the dialog
  //
  // 1, ScrHots
  // We're going to terminate services, if they're running, so that the DLL is unloaded,
  // and then try to use its InstallSilently thing to get it running.
  InstallScrhots(hwnd,"RESOURCE_SCRHOTS",FALSE);
  //
  // 2. Screensaver
  // We're going to copy it into the windows directory and then use the very same 'install'
  // procedure that you'd get if you right-clicked on it in the explorer. Or at least we would,
  // but if the user installed the '98 preview or IE4 onto Win'95 then this is broken. So, we'll
  // roll our own
  char c[50]; res=LoadString(hInstance,2,c,50); if (res==0) strcpy(c,"Puzzle.scr");
  char savpath[MAX_PATH]; GetWindowsDirectory(savpath,MAX_PATH); strcat(savpath,"\\"); strcat(savpath,c);
  res=LoadString(hInstance,1,c,50); if (res==0) strcpy(c,"the Puzzle saver");
  InstallFile(hwnd,savpath,"RESOURCE_SAVER",c);
  SelectAsDefault(savpath);
  ExecutePreviewDialog(hwnd,savpath);
  //
  SetCursor(hOld);
  return IDOK;
}





// INSTALLSCRHOTS
// Assume that scrhots.dll is embedded inside this installer as an
// RT_RCDATA resource called 'res'
// We first stop Scrhots if it is running. Then we install our new version over
// the top of the existing version of Scrhots. (If actually the user had a more
// up-to-date version of Scrhots already installer, then we don't actually do that.)
// Any messageboxes that pop up are children of 'hwnd'
// Return true of false, for whether it was succesfull.
// We can either do this showing the install dialog, or not.
BOOL InstallScrhots(HWND hwnd,char *res,BOOL ShowDialog)
{ char hotspath[MAX_PATH]; GetWindowsDirectory(hotspath,MAX_PATH); strcat(hotspath,"\\scrhots.dll");
  HINSTANCE hScrHots=LoadLibrary(hotspath);
  typedef void (WINAPI *RUNDLLPROC) (HWND,HINSTANCE,LPSTR,int);
  if (hScrHots!=NULL)
  { RUNDLLPROC pTerminateService=(RUNDLLPROC)GetProcAddress(hScrHots,"TerminateService");
    if (pTerminateService!=NULL) pTerminateService(hwnd,hInstance,"/notitle",SW_SHOWNORMAL);
    FreeLibrary(hScrHots);
  }
  InstallFile(hwnd,hotspath,res,"ScrHots");  
  hScrHots=LoadLibrary(hotspath);
  if (hScrHots==NULL) return FALSE;
  RUNDLLPROC pInstallSilently=(RUNDLLPROC)GetProcAddress(hScrHots,"InstallSilently");
  BOOL b; if (pInstallSilently==NULL) b=FALSE; else b=TRUE;
  if (pInstallSilently!=NULL) pInstallSilently(hwnd,hInstance,"/notitle",SW_SHOWNORMAL);
  if (ShowDialog)
  { RUNDLLPROC pRunDialog=(RUNDLLPROC)GetProcAddress(hScrHots,"RunDialog");
    if (pRunDialog!=NULL) pRunDialog(hwnd,hInstance,"/notitle",SW_SHOWNORMAL);
  }
  FreeLibrary(hScrHots);
  return b;
}


// INSTALLFILE
// We want to install a file.
// This installer program has embedded this file as a RT_RCDATA resource - res
// We wish to install it to the destination filename - fn
// We might display a messagebox referring to this file by its human-readable description - name
// If we do, this messagebox will be a child of - hwnd
// We compare version numbers. Note: the version number comparison only works if all four digits in the
// program are less than 255. (This is true for Scrhots)
// - If the new file is the same version as the existing, it *DOES NOT* install
// - If the new file is older version that the existing, it does not install
// - If neither new nor existing are versioned, then it *DOES* install.
BOOL InstallFile(HWND hwnd,char *fn,char *res,char *name)
{ // return TRUE if we installed it; FALSE if there was an error or if it already existed
  if (!FileExists(fn))
  { SaveResourceToFile(fn,res);
    return TRUE;
  }
  // Otherwise, we'll check versions first
  char tpath[MAX_PATH]; char tfn[MAX_PATH]; GetTempPath(MAX_PATH,tpath); GetTempFileName(tpath,"sci",0,tfn);
  SaveResourceToFile(tfn,res);
  DWORD origver; GetFileVersionField(fn,"FileVersion",NULL,0,&origver);
  DWORD newver; GetFileVersionField(tfn,"FileVersion",NULL,0,&newver);
  DeleteFile(tfn);
  if (origver>=newver && !(origver==0 && newver==0)) return FALSE; // if there's an already up to date version.
  char c[1000];
  if (origver==0 && newver==0)
  { // just overwrite it. Don't ask the user.
  }
  else
  { 
    wsprintf(c,"You have an out-of-date version of %s on your system. May I try to upgrade, please?",name);
    int r=MessageBox(hwnd,c,"Install",MB_YESNOCANCEL);
    if (r!=IDYES) return FALSE;
  }
  BOOL br=DeleteFile(fn);
  if (!br)
  { wsprintf(c,"Error. Unable to delete old version of %s. Make sure it is not in use, and then try again.",name);
    MessageBox(hwnd,c,"Install Error",MB_ICONEXCLAMATION|MB_OK);
    return FALSE;
  }
  SaveResourceToFile(fn,res);
  return TRUE;
}







// INSTALLER ROUTINES FOR SCREENSAVERS
// (c) 1998 Lucian Wischik. May be used freely, without restriction.
// 
// Summary: to install a saver and pop up its preview, use this code:
//   SelectAsDefault("c:\\windows\\mysaver.scr");
//   ExecutePreviewDialog(hwnd,"c:\\windows\\mysaver.scr");
//
// 1. ExecuteInstallDialog(hwnd,"c:\\windows\\mysaver.scr");
// This is the way you'd normally install a screensaver. It calls
// the routine ShellExecute(hwnd,"install",scr,...);
// This displays the Screensaver page of the Display Properties
// control panel.
// However, if the user has tried to install the Win'98 preview
// on top of '95 (or possibly just IE4 onto Win'95) then this
// ShellExecute is broken and the user can't even right-click
// on a saver and choose 'install'. The routine CheckDeskCpl()
// returns FALSE if it looks as though the system is broken in this way.
//
// 2. ExecuteConfigDialog(hwnd,"c:\\windows\\mysaver.scr");
// 3. ExecutePreviewDialog(hwnd,"c:\\windows\\mysaver.scr");
// Because the regular install method is broken, you'll have
// to use something else. First, call the routine
// SelectAsDefault("c:\\windows\\mysaver.scr") to set your
// saver as the default saver. Next, you might like to call
// one of the above routines. They pop up the saver's config
// dialog, and a window with a preview of the saver, respectively.
//
// Note: you should have written your saver so that, when started
// with the /c argument, it creates its config dialog as a child
// of GetForegroundWindow(). If you failed to do this, then
// ExecuteConfigDialog will behave kind of oddly.
// Note: you should have written your saver so that, when
// started with /p ####, it runs a preview window. If you failed
// to do this, then ExecutePreviewDialog will look kind of odd.
// To compile: you'll have to #include <shellapi.h>. If you're
// compiling with Visual C++, you'll have to go to
// Build|Settings|Link and add 'version.lib' in the window at
// the bottom.
// 
//
LRESULT CALLBACK BlackWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ LONG oldproc=GetWindowLong(hwnd,GWL_USERDATA);
  if (msg==WM_DESTROY) SetWindowLong(hwnd,GWL_WNDPROC,oldproc);
  if (msg==WM_PAINT) {PAINTSTRUCT ps;BeginPaint(hwnd,&ps);FillRect(ps.hdc,&ps.rcPaint,(HBRUSH)GetStockObject(BLACK_BRUSH));EndPaint(hwnd,&ps);return 0;}
  return CallWindowProc((WNDPROC)oldproc,hwnd,msg,wParam,lParam);
}
BOOL CALLBACK ExecutePreviewDialogProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ switch (msg)
  { case WM_INITDIALOG:
    { SetDlgItemText(hwnd,IDOK,"OK");
      SetDlgItemText(hwnd,2,"Screen saver succesfully installed!");
      HWND hPrev=GetDlgItem(hwnd,3);
      LONG oldproc=GetWindowLong(hPrev,GWL_WNDPROC);
      SetWindowLong(hPrev,GWL_USERDATA,oldproc);
      SetWindowLong(hPrev,GWL_WNDPROC,(LONG)BlackWindowProc);
      STARTUPINFO si; PROCESS_INFORMATION pi;
      ZeroMemory(&si,sizeof(si)); ZeroMemory(&pi,sizeof(pi));
      si.cb=sizeof(si); char *scr=(char *)lParam;
      char c[MAX_PATH]; wsprintf(c,"\"%s\" /p %i",scr,(int)hPrev);
      CreateProcess(scr,c,NULL,NULL,TRUE,IDLE_PRIORITY_CLASS,NULL,NULL,&si,&pi);
      return TRUE;
	  }
    case WM_COMMAND:
    { int id=LOWORD(wParam); switch (id)
      { case IDOK: case IDCANCEL:
        { HWND hPrev=GetDlgItem(hwnd,3); HWND hChild=GetWindow(hPrev,GW_CHILD);
          if (hChild!=NULL) SendMessage(hChild,WM_CLOSE,0,0);
          hChild=GetWindow(hPrev,GW_CHILD); if (hChild!=NULL) DestroyWindow(hChild);
          EndDialog(hwnd,id); return TRUE;
        }
      }
    }
  }
  return FALSE;
}
int ExecutePreviewDialog(HWND hwnd,char *scr)
{ typedef struct {DLGITEMTEMPLATE dli; WORD ch; WORD c; WORD t; WORD dummy; WORD cd;} TDlgItem;
  typedef struct {DLGTEMPLATE dlt; WORD m; WORD c; WCHAR t[8]; WORD pt; WCHAR f[14];
                  TDlgItem ia; TDlgItem ib; TDlgItem ic;} TDlgData;
  TDlgData dtp={{DS_MODALFRAME|DS_3DLOOK|DS_SETFONT|DS_CENTER|WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_VISIBLE,0,3,0,0,278,196},
                0,0,L"Preview",8,L"MS Sans Serif",
                {{BS_DEFPUSHBUTTON|WS_CHILD|WS_VISIBLE,0,113,175,50,14,IDOK},0xFFFF,0x0080,0,0,0},
                {{SS_BLACKRECT|WS_CHILD|WS_VISIBLE,0,7,7,264,152,3},0xFFFF,0x0082,0,0,0},
                {{SS_CENTER|WS_CHILD|WS_VISIBLE,0,7,162,264,8,2},0xFFFF,0x0082,0,0,0}};
  return DialogBoxIndirectParam(hInstance,(DLGTEMPLATE*)&dtp,hwnd,ExecutePreviewDialogProc,(LONG)scr);
}
int ExecuteConfigDialog(HWND hwnd,char *scr)
{ STARTUPINFO si; PROCESS_INFORMATION pi;
  ZeroMemory(&si,sizeof(si)); ZeroMemory(&pi,sizeof(pi));
  si.cb=sizeof(si);
  char c[MAX_PATH]; wsprintf(c,"\"%s\" /c",scr);
  BOOL res=CreateProcess(scr,c,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
  if (res) return IDOK; else return IDCANCEL;
}
void SelectAsDefault(char *scr)
{ char sscr[MAX_PATH];
  GetShortPathName(scr,sscr,MAX_PATH);
  WritePrivateProfileString("boot","SCRNSAVE.EXE",sscr,"system.ini");
  SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,TRUE,NULL,TRUE);
  // that sends a WM_WININICHANGE so that DisplayProperties dialog knows we've changed
}
int ExecuteInstallDialog(HWND hwnd,char *scr)
{ HINSTANCE hInst=ShellExecute(hwnd,"install",scr,NULL,NULL,SW_SHOWNORMAL);
  if ((int)hInst<=32) return IDCANCEL; else return IDOK;
}
BOOL CheckDeskCpl()
{ HINSTANCE hDesk=LoadLibrary("desk.cpl");
  if (hDesk==NULL) return FALSE;
  char cDesk[MAX_PATH]; GetModuleFileName(hDesk,cDesk,MAX_PATH);
  FreeLibrary(hDesk);
  char c[MAX_PATH];
  BOOL GetFileVersionField(char *fn,char *info,char *ret,int len,DWORD *ver);
  DWORD dwver; GetFileVersionField(cDesk,"FileVersion",c,MAX_PATH,&dwver);
  DWORD osver=GetVersion();
  BOOL is95=FALSE; if ((osver&0x80000000)==0x80000000 && (osver&0x000000FF)==4) is95=TRUE; 
  if (dwver==0x04483a01 && is95) return FALSE; else return TRUE;
}

// GETFILEVERSIONFIELD: returns version info.
// fn: filename
// info: information to get back. Things like "FileVersion"
// ret: a buffer in which to return the thing. len: length of this buffer.
// ver: if non-null, then we'll try to parse a string like "1,7,5,0" into 0x01070500
// NOTE: This function only works with smallish version numbers!
// If any digit is larger than 255, then it won't work!
// This doesn't matter for Scrhots, because Scrhots has a small version number.
//
int GetFileVersionField(char *fn,char *info,char *ret,int len,DWORD *dwver)
{ DWORD hVersion;
  if (dwver!=NULL) *dwver=0;
  DWORD vis=GetFileVersionInfoSize(fn,&hVersion);
  if (vis==0) return 0;
  void *vData;
  vData=(void *)new char[(UINT)vis];
  if (!GetFileVersionInfo(fn,hVersion,vis,vData)) {delete vData;return 0;}
  char vn[100];
  strcpy(vn,"\\VarFileInfo\\Translation");
  LPVOID transblock; UINT vsize;
  BOOL res=VerQueryValue(vData,vn,&transblock,&vsize);
  if (!res) {delete vData;return 0;}
  // Swap the words so wsprintf will print the lang-charset in the correct format.
  *(DWORD *)transblock = MAKELONG(HIWORD(*(DWORD *)transblock), LOWORD(*(DWORD *)transblock));
  wsprintf(vn,"\\StringFileInfo\\%08lx\\%s",*(DWORD *)transblock,info);
  char *ver;
  res=VerQueryValue(vData,vn,(LPVOID*)&ver,&vsize);
  if (!res) {delete vData; return 0;}
  int vlen=strlen(ver);
  if (ret!=NULL)
  { int clen=vlen+1; if (clen>=len-1) clen=len-1;
    // ie. we'll try to copy the \0 in vData, but we'll leave space
    // for the thing.
    for (int i=0; i<clen; i++) ret[i]=ver[i];
    ret[len-1]=0;
  }
  // The following code converts a string like 1.0 or 1.1.5 into
  // a DWORD where the first digit is the leading byte, and so on.
  // It discards version numbers greater than 255, and discards
  // anything after the fourth version sub-part.
  if (dwver!=NULL)
  { DWORD v=0;
    BOOL atend=FALSE;
    char *first=ver;
    char *next=first;
    int places=0;
    while (!atend)
    { while (*next!=0 && *next!='.' && *next!=',') next++;
      if (*next==0) atend=TRUE; *next=0;
      int i; int res=sscanf(first,"%i",&i);
      if (res==0) atend=TRUE; else {v=(v<<8)|(i&255); places++;}
      next++; first=next;
      if (places==4) atend=TRUE;
    }
    for (int i=places; i<4; i++) v=v<<8;
    *dwver=v;
  }
  delete vData;
  //
  return vlen;
}


// SAVERESOURCETOFILE: if 'res' is the name of an embedded resource, this routine
// saves it to the file 'fn'. With no checking or anything.
BOOL SaveResourceToFile(char *fn,char *res)
{ HRSRC hrsrc=FindResource(hInstance,res,RT_RCDATA);
  if (hrsrc==NULL) return FALSE;
  DWORD size=SizeofResource(hInstance,hrsrc);
  HGLOBAL hglob=LoadResource(hInstance,hrsrc);
  LPVOID rdata=LockResource(hglob);
  HANDLE hFile=CreateFile(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  DWORD writ; WriteFile(hFile,rdata,size,&writ,NULL);
  CloseHandle(hFile);
  return TRUE;
}


BOOL FileExists(char *fn)
{ HANDLE hFile=CreateFile(fn,0,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
  if (hFile==INVALID_HANDLE_VALUE) return FALSE;
  CloseHandle(hFile); return TRUE;
}



void RegisterClasses()
{ WNDCLASSEX wcs;
  wcs.cbSize=sizeof(wcs);
  wcs.style=CS_VREDRAW | CS_HREDRAW;
  wcs.lpfnWndProc=InstallWindowProc;
  wcs.cbClsExtra=0;
  wcs.cbWndExtra=0;
  wcs.hInstance=hInstance;
  wcs.hIcon=NULL;
  wcs.hCursor=NULL;
  wcs.hbrBackground=NULL;
  wcs.lpszMenuName=NULL;
  wcs.lpszClassName="InstallClass";
  wcs.hIconSm=NULL;
  RegisterClassEx(&wcs);
}



BOOL CALLBACK FirstDialogProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ switch (msg)
  { case WM_INITDIALOG: return TRUE;
    case WM_COMMAND:
    { int id=LOWORD(wParam);
      if (id==IDOK || id==IDCANCEL) EndDialog(hwnd,id);
      return TRUE;
    }
  }
  return FALSE;
}


LRESULT CALLBACK InstallWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ switch (msg)
  { case WM_CREATE:
    { 
    } break;
    case WM_PAINT:
    { PAINTSTRUCT ps; BeginPaint(hwnd,&ps);
      RECT rectFill;
      RECT rectClient;
      GetClientRect(hwnd, &rectClient);
      float fStep = (float)rectClient.bottom / 256.0f;
      for (int iOnBand = 0; iOnBand < 256; iOnBand++)
      { SetRect(&rectFill,0,(int)(iOnBand * fStep),rectClient.right+1,(int)((iOnBand+1) * fStep));
        HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, (255 - iOnBand)));
        FillRect(ps.hdc, &rectFill, hBrush);
        DeleteObject(hBrush);
      };
      EndPaint(hwnd,&ps); return 0;
    }
    case WM_DESTROY: PostQuitMessage(0); break;
  }
  return DefWindowProc(hwnd,msg,wParam,lParam);
}

These are the resources I use:

RESOURCE_SCRHOTS  RCDATA DISCARDABLE  "c:/windows/scrhots.dll"
RESOURCE_SAVER    RCDATA DISCARDABLE  "saver.scr"

1                       ICON    DISCARDABLE     "install.ico"
2                       ICON    DISCARDABLE     "saver.ico"
3                       ICON    DISCARDABLE     "lu.ico"
4                       ICON    DISCARDABLE     "scrhots.ico"

STRINGTABLE DISCARDABLE 
BEGIN
    1                       "the screen saver"
    2                       "saver.scr"
END

DIALOG_PRELIM DIALOG DISCARDABLE  0, 0, 199, 146
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "A Screen Saver"
FONT 8, "MS Sans Serif"
BEGIN
    DEFPUSHBUTTON   "&Install",IDOK,85,125,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,142,125,50,14
    ICON            2,IDC_STATIC,9,17,18,20
    ICON            3,IDC_STATIC,9,44,18,20
    ICON            4,IDC_STATIC,9,78,18,20
    LTEXT           "A SCREEN SAVER",IDC_STATIC,36,7,156,9
    CONTROL         "Screensaver code by Lucian Wischik\r\nusing the ScrPlus screensaver development kit\r\nwww.wischik.com/scr",
                    IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,36,44,
                    156,28
    LTEXT           "Also in this package is ScrHots. This turns the corners of your screen into mouse 'hot corners' that can start the saver immediately, or prevent it from running. Configure it from its tab in the Display Properties dialog.",
                    IDC_STATIC,36,78,156,45
    LTEXT           "This is a screen saver.",
                    IDC_STATIC,36,19,156,19
END

By Lucian Wischik