处理屏幕保护程序
Microsoft® Win32® 应用编程接口(API)支持称作屏幕保护程序的特殊程序。屏幕保护程序在鼠标和键盘空闲指定的一段时间后开始运行。它们用于这两个原因:
防止屏幕上的荧光粉被表态图像烧坏。隐藏屏幕上的敏感信息。本文被分成下面两个小节。
关于屏幕保护程序使用屏幕保护程序函数并于屏幕保护程序
Microsoft® Windows®控制面板中的显示程序让用户从一个列表中选择屏幕保护程序,指定屏幕保护程序应该在系统空闲多久之后启动,配置,以及预览屏幕保护程序。屏幕保护程序可以由Windows自动启动或者由用户通过控制面板来启动。
一旦选择了屏幕保护程序,Windows便监视键盘和鼠标动作并在其静止一段时间后启动屏保。然而,Windows在下列任意一种情况下不会启动屏保:
活动的应用程序不是一个基于Windows的程序。一个computer-based training(CBT)窗口在显示。活动的应用程序收到一个wParam参数为SC_SCREENSAVE的WM_SYSCOMMAND消息,但是它没有把消息传给DefWindowProc函数。屏保程序包含一些特定的导出函数,资源定义,和变量声明。屏保程序包含main函数和其它对屏保程序必要的启动代码。当一个屏保程序启动时,该屏保程序库中的启动代码会创建一个全屏窗口。这一窗口的窗口类声明如下:
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;
为了创建一个屏保程序,多数开发者会创建一个包含三个必需函数的源码模块并将它们与屏保程序库链接。屏保模块仅负责自身的配置及提供可视化效果。
屏保程序模块所需的三个函数中的一个是ScreenSaverProc。这个函数处理特定的消息并将任何不处理的消息传回到屏保程序库。下面是ScreenSaverProc处理的一些典型消息。
消息
含义
WM_CREATE
从Regedit.ini文件获取初始化数据。为屏保窗口设置一个窗口定时器。执行其它必要操作。
WM_ERASEBKGND
擦除屏保窗口,为紧接着的绘制操作作准备。
WM_TIMER
执行绘制操作。
WM_DESTROY
销毁应用程序处理WM_CREATE消息时创建的定时器。执行其它必要的清理。
ScreenSaverProc通过调用DefScreenSaverProc函数将不处理的消息传给屏保程序库。下表说明了这个函数如何处理各种消息。
消息
动作
WM_SETCURSOR
将鼠标指针设为空指针,以从屏幕上移除。
WM_PAINT
绘制窗口背景。
WM_LBUTTONDOWN
终止屏保程序。
WM_MBUTTONDOWN
终止屏保程序。
WM_RBUTTONDOWN
终止屏保程序。
WM_KEYDOWN
终止屏保程序。
WM_MOUSEMOVE
终止屏保程序。
WM_ACTIVATE
如果wParam被设置为FALSE则终止屏保程序。
屏保程序模块必需的第二个函数是ScreenSaverConfigureDialog。这个函数显示一个对话框以允许用户对屏保程序进行配置(应用程序必须提供相应的对话框模板)。Windows在用户点击控制面板中屏保程序对话框中的设置按钮时显示这个配置对话框。
屏保程序模块必需的第三个函数是函数是RegisterDialogClasses。这个函数必须被所有的屏保程序调用。然而,配置对话框中不需要特殊窗口或者自定义控件的程序可以简单地返回TRUE。需要特殊窗口或者自定义控件的程序应该用这个函数来注册相应的窗口类。
除了创建一个支持刚才所描述的这三个函数的模块之外,屏保程序还应该提供一个图标。这个图标只在屏保程序作为一个独立的程序运行时才可见。(为了通过控制面板来运行,屏保程序必须以.scr作为文件的扩展名。)图标必须在屏保程序的资源文件中通过ID_APP来标识,该值的定义在Scrnsave.h头文件中。
最后一个必要条件是一个屏保程序描述字符串。屏保程序的资源文件中必须包含一个被控制面板用来显示作为其名字的字符串。描述字符串必须是其资源文件的字符串表中的第一个字符串(用常数1来标识)。
使用屏保程序函数
本节通过从屏保程序中截取下来的示例代码来说明如下任务:
创建一个屏保程序安装新屏保程序为屏保程序配置对话框添加帮助创建一个屏保程序
该程序以1到10秒范围内的时间间隔以这四中颜色中的一个来重绘屏幕:白色,淡灰,深灰,和黑色。程序在每收到一个WM_TIMER消息时绘制窗口。用户可以通过调节程序的配置对话框中的滚动条来调整这个消息发送的间隔时间。
屏保程序库
静态屏保程序函数包含在屏保程序库中。该库有两个可用版本,Scrnsave.lib和Scrnsavw.lib。你必须将你的工程链接到其中的一个。Scrnsave.lib用于使用ANSI字符集的屏保程序,Scrnsavw.lib则用于使用UNICODE字符集的程序。用Scrnsavw.lib链接的屏保程序只能在支持UNICODE的Windows平台上运行,而用链接的屏保程序则可以在任何Windows平台上运行。
对配置对话框的支持
多数屏保程序提供了一个配置对话框以便用户指定一些自定义数据,如唯一颜色,绘制速度,线宽,字体等待。为了支持配置对话框,程序必须提供一个对话框模板并且必须支持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
你必须将标识对话框模板的的常数定义为十进制数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 to speed scroll bar */
static HWND hOK; /* handle to OK push button */
switch(message)
{
case WM_INITDIALOG:
/* Retrieve the application name from the .rc file. */
LoadString(hMainInstance, idsAppName, szAppName, 40);
/* Retrieve the .ini (or registry) file name. */
LoadString(hMainInstance, idsIniFile, szIniFile,
MAXFILELEN);
/* Retrieve any redraw speed data from the registry. */
lSpeed = GetPrivateProfileInt(szAppName, szRedrawSpeed,
DEFVEL, szIniFile);
/*
* If the initialization file does not contain an entry
* for this screen saver, use the default value.
*/
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 to 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:
/*
* Write the current redraw speed variable to
* the .ini file.
*/
wsprintf(szTemp, "%ld", lSpeed);
WritePrivateProfileString(szAppName, szRedrawSpeed,
szTemp, szIniFile);
case ID_CANCEL:
EndDialog(hDlg, LOWORD(wParam) == ID_OK);
return TRUE;
}
}
return FALSE;
}
除了提供对话框模板和支持ScreenSaverConfigureDialog函数之外,程序还必须支持RegisterDialogClasses函数。这个函数用来注册任何屏保程序所需要的非标准窗口类。因为示例程序在其对话框过程中只使用了标准窗口类,这个函数只是简单地返回了TRUE,如下面代码这样:
BOOL WINAPI RegisterDialogClasses(hInst)
HANDLE hInst;
{
return TRUE;
}
支持屏保窗口过程
每个屏保程序都必须支持一个名称为ScreenSaverProc的窗口过程。代多数窗口过程一个,ScreenSaverProc处理一系列特定消息并将任何不处理的消息传给默认过程。然而,ScreenSaverProc是将不处理的消息传给DefScreenSaverProc,而不是DefWindowProc。ScreenSaverProc有别于普通窗口过程的另一点是传给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) file name. */
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
安装新屏保程序
控制面板在构造可用屏保程序列表时会搜索Windows启动目录中扩展名为.scr的文件。因为屏保程序是扩展名为.exe的标准Windows可执行文件,你必须对其重命名以使其扩展名为.scr,并将其拷贝到正确的目录。
为屏保程序配置对话框添加帮助
典型的屏保程序配置对话框中包含有一个帮助按钮。屏保程序可以用与其它基于Windows的程序一样的方式检查帮助按钮标识符并调用WinHelp函数。