屏幕保护程序制作基础
Windows控制面板可以帮助用户选择,设置和预览一系列屏幕保护程序。屏幕保护程序在Windows启动或者用户从控制面板中启动的时候被加载。
一旦一个屏幕保护程序被加载,Windows将会监视鼠标和键盘活动,但是Windows不会启动屏幕保护程序,如果以下三条件不满足:
1 如果当前窗口不是一个windows窗口。
2 如果一个windows窗口在工作。
3 如果当前程序收到带有wParam参数( 设置SC_SCREENSAVE 值), 但是没有把消息传递给 DefWindowProc() 函数的WM_SYSCOMMAND 消息。
屏幕保护程序包含着特定的导出函数,资源定义和变量声明。静态连接库SCRNSAVE.LIB包含main和其他一些屏幕保护程序启动所需要的启动代码。当一个屏幕保护程序启动后,这些包含在SCRNSAVE.LIB中的启动代码创建一个全屏窗口,这个窗口的声明如下:
WNDCLASS cls;
cls.hCursor = NULL;
cls.hIcon = LoadIcon(hInst, MAKEINTATOM(ID_APP));
cls.lpszMenuName = NULL;
cls.lpszClassName = "WindowsScreenSaverClass";
cls.hbrBackground = GetStockObject(BLACK_BRUSH);
cls.hInstance = hInst;
cls.style = CS_VREDRAW | CS_HREDRAW |
CS_SAVEBITS | CS_DBLCLKS;
cls.lpfnWndProc = (WNDPROC) ScreenSaverProc;
cls.cbWndExtra = 0;
cls.cbClsExtra = 0;
大部分开发者开发一个包含三个必需的函数的模块,并且和SCRNSAVE.LIB相连接,这个模块负责设置屏幕保护程序并且负责提供视觉效果。
三个必需函数之一是ScreenSaverProc(),这个函数处理特定的消息并将任何没有处理的消息传递给SCRNSAVE.LIB,下面是一些ScreenSaverProc()处理的典型的消息:
消 息 含 义
WM_CREATE 从REGEDIT.INI文件中取出所有的初始化参数. 并且为屏幕保护窗口设置一个timer(). 然后做一些其他的初始化工作。WM_ERASEBKGND 檫除屏幕保护窗口为下一步绘图作准备。
WM_TIMER 画图的代码包含在这里。
WM_DESTROY 销毁程序在处理WM_CREATE消息时创建的timer(s),做其他一些收尾的清除工作。
ScreenSaverProc()通过调用 DefScreenSaverProc()把没有处理的消息传递给 SCRNSAVE.LIB . 下面是怎样处理这些消息的表格:.
消 息 动 作
WM_SETCURSOR 把鼠标指针设置为空,并且从屏幕上清除.
WM_PAINT 绘制屏幕背景。
WM_LBUTTONDOWN 结束屏幕保护程序.
WM_MBUTTONDOWN 结束屏幕保护程序.
WM_RBUTTONDOWN 结束屏幕保护程序.
WM_KEYDOWN 结束屏幕保护程序.
WM_MOUSEMOVE 结束屏幕保护程序.
WM_ACTIVATE 如果 wParam 是 FALSE,结束屏幕保护程序.
第二个屏幕保护程序模块必须的函数是 ScreenSaverConfigureDialog()。这个函数显示一个用来设置屏幕保护程序的对话框。(所以,一个应用程序必须提供一个相应的对话框摸板)。当用户在控制面板里边选择屏幕保护程序的设置按扭,windows就显示设置对话框。用户在这里输入的设置参数都将存入REGEDIT.INI文件
第三个屏幕保护程序模块必须的函数是RegisterDialogClasses()。这个函数是所有屏幕保护程序都需要调用的函数。然而,一个程序如果不需要特殊的窗口或者对设置对话框的特殊控制,那么只要简单的返回TRUE。需要特殊窗口和控制的程序通过这个函数注册相应的窗口类。
其次,要创建一个支持刚才说过的三个函数的屏幕保护程序模块,你还必须提供一个图标。这个图标只有在屏幕保护程序作为一个单独的应用程序运行的时候是需要的。(从控制面板里边运行一个屏幕保护程序,则这个程序必须是 .SCR 文件; 作为一个单独的文件运行,则这个文件必须是.exe文件)这个图标在屏幕保护程序的资源里边必须被标识为constant ID_APP,这个变量是SCRNSAVE.H头文件里边定义的。
最后一个要求是屏幕保护程序的描述字符串。屏幕保护程序的源文件必须包含一个描述性的字符串,用来在控制面板里边显示屏幕保护程序的名字。这个字符串必须是资源文件字符串表里边的第一项(序号为一)。
设置对话框的实现
大部分屏幕保护程序都提供设置对话框,使用户可以设置颜色,速度,线条样式,字体等。为了支持设置对话框,应用程序必须提供一个对话框摸板并支持ScreenSaverConfigureDialog(),下边是一个例子程序:
DLG_SCRNSAVECONFIGURE DIALOG 6, 18, 160, 63
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Sample Screen-Saver Setup"
FONT 8, "MS Shell Dlg"
BEGIN
GROUPBOX "Redraw Speed", 101, 0, 6, 98, 40
SCROLLBAR ID_SPEED, 5, 31, 89, 10
LTEXT "Fast", 103, 6, 21, 20, 8
LTEXT "Slow", 104, 75, 21, 20, 8
PUSHBUTTON "OK", ID_OK, 117, 10, 40, 14
PUSHBUTTON "Cancel", ID_CANCEL, 117, 32, 40, 14
END
你必须用decimal 2003来定义这个标识对话框摸板的常量。定义如下:
#define DLG_SCRNSAVECONFIGURE 2003
下边是 ScreenSaverConfigureDialog () 的示例:
#define MINVEL 1 /* minimum redraw-speed value */
#define MAXVEL 10 /* maximum redraw-speed value */
#define DEFVEL 5 /* default redraw-speed value */
LONG lSpeed = DEFVEL; /* redraw-speed variable */
extern HINSTANCE hMainInstance; /* screen saver instance handle */
CHAR szAppName[80]; /* .INI section name */
CHAR szTemp[20]; /* temporary array of characters */
CHAR szRedrawSpeed[] = "Redraw Speed"; /* .INI speed entry */
BOOL WINAPI ScreenSaverConfigureDialog(hDlg, message, wParam, lParam)
HWND hDlg;
UINT message;
DWORD wParam;
LONG lParam;
{
static HWND hSpeed; /* handle of speed scroll bar */
static HWND hOK; /* handle of OK push button */
switch(message)
{
case WM_INITDIALOG:
/* 从.rc文件中获得应用程序名字 */
LoadString(hMainInstance, idsAppName, szAppName, 40);
/* 获得ini文件名. */
LoadString(hMainInstance, idsIniFile, szIniFile,
MAXFILELEN);
/* 从注册表里边取出所有重画速度数据. */
lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,
DEFVEL, szIniFile);
/*
如果初始化文件不包含任何屏幕保护程序的入口,则使用默认的入口
*/
if(lSpeed > MAXVEL || lSpeed < MINVEL)
lSpeed = DEFVEL;
/* Initialize the redraw-speed scroll bar control. */
hSpeed = GetDlgItem(hDlg, ID_SPEED);
SetScrollRange(hSpeed, SB_CTL, MINVEL, MAXVEL, FALSE);
SetScrollPos(hSpeed, SB_CTL, lSpeed, TRUE);
/* Retrieve a handle of the OK push button control. */
hOK = GetDlgItem(hDlg, ID_OK);
return TRUE;
case WM_HSCROLL:
/*
* Process scroll bar input, adjusting the lSpeed
* value as appropriate.
*/
switch (LOWORD(wParam))
{
case SB_PAGEUP:
--lSpeed;
break;
case SB_LINEUP:
--lSpeed;
break;
case SB_PAGEDOWN:
++lSpeed;
break;
case SB_LINEDOWN:
++lSpeed;
break;
case SB_THUMBPOSITION:
lSpeed = HIWORD(wParam);
break;
case SB_BOTTOM:
lSpeed = MINVEL;
break;
case SB_TOP:
lSpeed = MAXVEL;
break;
case SB_THUMBTRACK:
case SB_ENDSCROLL:
return TRUE;
break;
}
if ((int) lSpeed <= MINVEL)
lSpeed = MINVEL;
if ((int) lSpeed >= MAXVEL)
lSpeed = MAXVEL;
SetScrollPos((HWND) lParam, SB_CTL, lSpeed, TRUE);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_OK:
/*
将当前的重画速度写入ini文件;
*/
wsprintf(szTemp, "%ld", lSpeed);
WritePrivateProfileString(szAppName, szRedrawSpeed,
szTemp, szIniFile);
case ID_CANCEL:
EndDialog(hDlg, LOWORD(wParam) == ID_OK);
return TRUE;
}
}
return FALSE;
}
为了支持ScreenSaverConfigureDialog()和设置对话框,一个程序还必须提供RegisterDialogClasses(),这个函数注册任何屏幕保护程序需要的其他window类。
BOOL WINAPI RegisterDialogClasses(hInst)
HANDLE hInst;
{
return TRUE;
}
屏幕保护程序窗口过程的实现
每一个屏幕保护程序都要实现一个名叫ScreenSaverProc()的函数。和所有其他的窗口函数一样,ScreenSaverProc()处理一系列特定的消息并且把没有处理的传递给默认的过程。但是,不是把消息传递给DefWindowProc(),ScreenSaverProc()把没有处理的消息传递给DefScreenSaverProc(),ScreenSaverProc()和其他一般窗口过程另外一个不同是传递给ScreenSaverProc()的句柄标识整个桌面而不是一个用户窗口。下边是一个例子:
LONG WINAPI ScreenSaverProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
DWORD wParam;
LONG lParam;
{
static HDC hdc; /* device-context handle */
static RECT rc; /* RECT structure */
static UINT uTimer; /* timer identifier */
switch(message)
{
case WM_CREATE:
/* Retrieve the application name from the .RC file. */
LoadString(hMainInstance, idsAppName, szAppName, 40);
/* Retrieve the .INI (or registry) filename. */
LoadString(hMainInstance, idsIniFile, szIniFile,
MAXFILELEN);
/* Retrieve any redraw-speed data from the registry. */
lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,
DEFVEL, szIniFile);
/*
* Set a timer for the screen saver window using the
* redraw-rate stored in REGEDIT.INI.
*/
uTimer = SetTimer(hwnd, 1, lSpeed * 1000, NULL);
break;
case WM_ERASEBKGND:
/*
* The WM_ERASEBKGND message is issued before the
* WM_TIMER message, allowing the screen saver to
* paint the background as appropriate.
*/
hdc = GetDC(hwnd);
GetClientRect (hwnd, &rc);
FillRect (hdc, &rc, GetStockObject(BLACK_BRUSH));
ReleaseDC(hwnd,hdc);
break;
case WM_TIMER:
/*
* The WM_TIMER message is issued at (lSpeed * 1000)
* intervals, where lSpeed == .001 seconds. This
* code repaints the entire desktop with a white,
* light gray, dark gray, or black brush each
* time a WM_TIMER message is issued.
*/
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rc);
if (i++ <= 4)
FillRect(hdc, &rc, GetStockObject(i));
else
(i = 0);
ReleaseDC(hwnd,hdc);
break;
case WM_DESTROY:
/*
* When the WM_DESTROY message is issued, the screen saver
* must destroy any of the timers that were set at WM_CREATE
* time.
*/
if (uTimer)
KillTimer(hwnd, uTimer);
break;
}
/*
* DefScreenSaverProc processes any messages
* ignored by ScreenSaverProc.
*/
return DefScreenSaverProc(hwnd, message, wParam, lParam);
}
创建模块定义文件
ScreenSaverProc()和 ScreenSaverConfigureDialog()必须被导出到模块定义文件中去,而RegisterDialogClasses()不需要,模块定义文件如下:
NAME SSTEST.SCR
DESCRIPTION 'SCRNSAVE : Test'
STUB 'WINSTUB.EXE'
EXETYPE WINDOWS
CODE MOVEABLE
DATA MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 4096
EXPORTS
ScreenSaverProc
ScreenSaverConfigureDialog