在BC或VC下可以利用OWL类库或MFC类库编制屏幕保护程序,但在更低一级层次上如完全采用API编程,如何来编制屏幕保护,特别是编制具有参数配置能力的屏幕保护程序呢?虽然利用某些类库编程具有方便性,但通过API编程编制屏幕保护程序,更能弄懂屏幕保护程序的原理,且执行代码不依赖任何类库而只依赖操作系统本身。下面就讲讲利用API编制具有参数配置能力的屏幕保护程序的原理与方法,希望对那些喜欢API编程的朋友或有某种特殊要求的朋友有所帮助。
一、屏幕保护程序原理
屏幕保护程序实际上是比较简单的WINDOWS应用程序,它不象一般应用程序需要创建主窗口,相反,它使用桌面即整个屏幕作为它的窗口。它既不包含WinMain()函数(不管任何编程语言下编制的WINDOWS应用程序都直接或间接包含有此函数),也不需要消息循环,但它一定需要三个函数。这三个函数是:ScreenSaverProc、ScreenSaverConfigDialog和RegisterDialogClasses。下面简单介绍这三个函数。
ScreenSaverProc()是一个窗口过程,原型为:
LRESULT WINAPI ScreenSaverProc(HWND hwnd,UINT message,WPARAM wParam, LPARAM lParam) 。它被传递消息,并必须作出反应。如果该函数不作出处理消息,则必须调用DefScreenProc()。hwnd中传递的是桌面的句柄,用户的屏幕保护程序要利用这一点。
ScreenSaverConfureDialog()是处理屏幕保护程序配置对话框的对话函数,原型为:
BOOL WINAPI ScreenSaverConfigureDialog(HWND hwnd,UINT message,WPARAM wParam, LPARAM lParam)。如果屏幕保护程序不需要配置,则该函数只要返回零就行了。如果屏幕保护程序支持配置,则必须在资源文件中定义它,并使ID值固定为DLG_SCRNSAVECONFIGURE,该值在必须包含的头文件SCRNSAVE.H中已经定义。
RegisterDialogClasses()用于定制窗口类,如定制控件,原型为:
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)。如果屏幕保护程序不使用定制窗口,则简单地返回1就行了。本程序就没有使用该函数。
另外,屏幕保护程序还必须包含一个图标和一个字符串。图标的ID必须为ID_APP,它用于标志屏幕保护程序;字符串资源的标志符必须是IDS_DESCRIPTION,屏幕保护程序并不使用该字符串,只简单地提供屏幕保护程序的描述而已。这两个ID值都在SCRNSAVE.H中已经定义。
每次激活屏幕保护程序,它就接收到两条消息。第一条是WM_CREATE,屏幕保护程序需要使用此消息执行所需要的初始化操作,并启动定时器,设置好定时器的周期。第二条消息是WM_ERASEBKGND,屏幕保护程序利用此消息来实现清除屏幕。屏幕保护程序的实现是由一个定时器来驱动,每当定时器完成记数时,屏幕保护程序就接收到WM_TIMER消息,并更新屏幕显示。而当用户按一个键或移动鼠标时,屏幕保护程序就接收到一条WM_DESTROY消息,此时,屏幕保护程序需要取消定时器,并执行其它关闭任务。当编译连结生成EXE执行文件文件后,需要将它改为以SCR为扩展名的文件,然后拷贝到WINDOWS目录下的SYSTEM目录中,就可以选用控制面板选取和配置该屏幕保护程序了。
二、注册表使用原理
当屏幕保护程序有配置信息,如本屏幕保护程序中上次显示的字符串、字体大小等信息,这些东西若在下次仍有效,就需要保存下来。那么这些信息保存在什么地方呢?用一个文件当然可以,但配置信息很少,用一个文件太费事,而且如果有很多屏幕保护程序都有配置信息,使用的文件岂不太多了?因此,最好的办法是将这些信息保存在WINDOWS的注册表中。注册表是WINDOWS保持的特殊层次数据库,它保存有用户、机器、和安装的软件三项信息,内置有六个表项,这里我们只使用HKEY_CURRENT_USER就可以了。管理注册表可以使用以下几个API函数来实现。
1.RegCreateKeyEx()-----该函数打开一个已经存在的注册表表项,当指定的注册表表项不存在时,就自动建立这个表项。如本程序利用该函数在注册表的内置项HKEY_CURRENT_USER下建立或读取一个表项"Software\\Programs\\屏幕保护程序".
2.RegSetValueEx()-----当打开一个表项时,就可以利用这个函数在其下存储值。本程序就是在前面打开的表项下用”delay”存储定时器的周期数,用”FontSize”存储字体的象素大小,用”message”存储屏幕保存程序要显示的字符串。
3.RegQueryValueEx()---在注册表中读取已存储的值。在本程序中,当不是首次运行本程序时,就需要利用该函数读取上次存储的三条信息。
4. RegCloseKey()-----最后利用该函数关闭已打开的表项
以上四个关于注册表管理的API函数,在程序中有具体应用,这里就不详细介绍其参数了。
本程序在VC6.0中调试通过,生成的程序可在WIN95和WIN98中作屏幕保护程序使用。我们首先在File\new下的Project中选择Win32 Application,输入工程名如save,然后选择”An empty project”,单击”OK”后就自动建立一个空的工程。在Source files下添加一个已存在的的C++源程序或重新输入下面的源程序w.cpp,同时将save.h及save.rc资源文件添加入该工程项目中。然后运行生成save.exe文件,将该文件拷贝到WINDOWS下SYSTEM目录中,并将该文件改名为save.scr。然后在屏幕上通过单击右键,在弹出菜单中选择属性,再在弹出的属性表中选择屏幕保护程序项,在下拉列表中选中save.scr屏幕保护程序后,可以对该屏幕保护程序进行设置。其弹出的对话框如下:
在延迟时间项中可以输入时间,也可以通过旋转按钮自动增大或减少延迟时间;字号大小这里采用的是一个字所占像素大小,可以输入数字,也可以通过对应旋转按钮改变;在显示信息栏中,可以自由输入一串新的中文或英文字符串,输入什么,屏幕保护程序就显示什么内容。完成配置后,单击确定就可以了。下次启动屏幕保护程序,使用就是上次配置的参数,因为这些参数都已经保存在注册表中了。
注意在VC中编译联结前,需要将屏幕保护程序库SCRNSAVE.LIB和通用控件库COMCTL32.LIB手工添入,具体操作方法是在Project\Settings\Link\Object\library modules编辑框中输入上面两个库名。若联结时找不到这两个库会出现错误。下面是源程序和资源文件。
1.源程序w.cpp
#include <windows.h>
#include <scrnsave.h>
#include <commctrl.h>
#include <string.h>
#include "scr.h"
#define DELAYMAX 1000 //定义最大周期数
#define FontSizeMax 400 //定义最大像素
#define MSGSIZE 80 //定义字符串最大长度
char str[MSGSIZE]="欢迎来到电脑世界"; //初始字符串
int delay,zhi_hao;
unsigned long datatype,datasize;
unsigned long result;
char str_key[]="Software\\Programs\\屏幕保护程序";
HKEY hRegKey;
HFONT hfont;
LOGFONT lf;
LRESULT WINAPI ScreenSaverProc(HWND hwnd,UINT message,
WPARAM wParam,LPARAM lParam)
{
static HDC hdc;
static RECT scrdim;
static SIZE size;
static int X,Y;
static HBRUSH hBlkBrush;
static TEXTMETRIC tm;
switch(message)
{
case WM_CREATE:
RegCreateKeyEx(HKEY_CURRENT_USER,str_key,
0,"Screen Saver",0,KEY_ALL_ACCESS,NULL,&hRegKey,&result);
if(result==REG_CREATED_NEW_KEY)
{
delay=200;
zhi_hao=120;//首次执行时在注册表中建立表项并设置周期数、字号大小和字符串信息
RegSetValueEx(hRegKey,"delay",0,REG_DWORD,(LPBYTE)&delay,
sizeof(DWORD));
RegSetValueEx(hRegKey,"FontSize",0,REG_DWORD,(LPBYTE)&zhi_hao,
sizeof(DWORD));
RegSetValueEx(hRegKey,"message",0,REG_SZ,(LPBYTE)str,strlen(str)+1);
}
else
{ datasize=sizeof(DWORD);
RegQueryValueEx(hRegKey,"delay",NULL,&datatype,(LPBYTE)&delay,&datasize);
RegQueryValueEx(hRegKey,"FontSize",NULL,&datatype,(LPBYTE)&zhi_hao,&datasize);
datasize=MSGSIZE;
RegQueryValueEx(hRegKey,"message",NULL,&datatype,
(LPBYTE)str,&datasize);
}//非首次执行就读取上次存储的信息
RegCloseKey(hRegKey);
SetTimer(hwnd,1,delay,NULL); //启动定时器
hBlkBrush=(HBRUSH)GetStockObject(BLACK_BRUSH);
lf.lfHeight=zhi_hao; lf.lfWidth=0;
lf.lfEscapement=0; lf.lfOrientation=0;
lf.lfWeight=FW_HEAVY;
lf.lfItalic=0; lf.lfUnderline=0;
lf.lfStrikeOut=0; lf.lfCharSet=ANSI_CHARSET;
lf.lfOutPrecision=OUT_DEFAULT_PRECIS;
lf.lfClipPrecision=CLIP_DEFAULT_PRECIS;
lf.lfQuality=PROOF_QUALITY;
lf.lfPitchAndFamily=VARIABLE_PITCH|FF_DONTCARE;
strcpy(lf.lfFaceName,"Courier New");
hfont=CreateFontIndirect(&lf); //初始化逻辑字体
break;
case WM_ERASEBKGND:
hdc=GetDC(hwnd);
GetClientRect(hwnd,&scrdim);
SelectObject(hdc,hBlkBrush);
PatBlt(hdc,0,0,scrdim.right,scrdim.bottom,PATCOPY);//将背景刷黑
SelectObject(hdc,hfont);
GetTextExtentPoint32(hdc,str,strlen(str),&size);//得到字符串尺寸
ReleaseDC(hwnd,hdc);
X=scrdim.right;
Y=(scrdim.top+scrdim.bottom)*1/3;//确定字符串显示的位置
break;
case WM_TIMER:
hdc=GetDC(hwnd);
SelectObject(hdc,hBlkBrush);
PatBlt(hdc,X,Y,size.cx,size.cy,PATCOPY);
X-=10;
if(X<-size.cx) X=scrdim.right;
SetBkColor(hdc,RGB(0,0,0));
SetTextColor(hdc,RGB(0,255,255));//设置字符串显示的颜色
SelectObject(hdc,hfont);
TextOut(hdc,X,Y,str,strlen(str));
ReleaseDC(hwnd,hdc);
break;
case WM_DESTROY:
KillTimer(hwnd,1);//关闭定时器
DeleteObject(hfont);
break;
default:
return DefScreenSaverProc(hwnd,message,wParam,lParam);
}
return 0;
}
BOOL WINAPI ScreenSaverConfigureDialog(HWND hdwnd,UINT message,
WPARAM wParam,LPARAM lParam)
{
static HWND hEboxWnd1,hEboxWnd2;
static HWND udWnd1,udWnd2;
INITCOMMONCONTROLSEX cc;
switch(message)
{
case WM_INITDIALOG:
cc.dwSize=sizeof(INITCOMMONCONTROLSEX);
cc.dwICC=ICC_UPDOWN_CLASS;
InitCommonControlsEx(&cc);//以下建立或读取注册表中有关信息
RegCreateKeyEx(HKEY_CURRENT_USER,str_key,
0,"Screen Saver",0,KEY_ALL_ACCESS,NULL,&hRegKey,&result);
if(result==REG_CREATED_NEW_KEY)
{
delay=200;
zhi_hao=120;
RegSetValueEx(hRegKey,"delay",0,REG_DWORD,(LPBYTE)&delay,
sizeof(DWORD));
RegSetValueEx(hRegKey,"FontSize",0,REG_DWORD,(LPBYTE)&zhi_hao,
sizeof(DWORD));
RegSetValueEx(hRegKey,"message",0,REG_SZ,(LPBYTE)str,strlen(str)+1);
}
else
{ datasize=sizeof(DWORD);
RegQueryValueEx(hRegKey,"delay",NULL,&datatype,(LPBYTE)&delay,&datasize);
RegQueryValueEx(hRegKey,"FontSize",NULL,&datatype,(LPBYTE)&zhi_hao,&datasize);
datasize=MSGSIZE;
RegQueryValueEx(hRegKey,"message",NULL,&datatype,
(LPBYTE)&str,&datasize);
}
hEboxWnd1=GetDlgItem(hdwnd,IDD_EB1);//得到编辑框IDD_EB1的句柄
hEboxWnd2=GetDlgItem(hdwnd,IDD_EB2); //得到编辑框IDD_EB2的句柄
udWnd1=CreateUpDownControl(WS_CHILD|WS_BORDER|WS_VISIBLE|
UDS_SETBUDDYINT|UDS_ALIGNRIGHT,
90,5,20,15,hdwnd,IDD_UPDOWN1,hMainInstance,hEboxWnd1,
DELAYMAX,1,delay);//建立第一个旋转按钮
udWnd2=CreateUpDownControl(WS_CHILD|WS_BORDER|WS_VISIBLE|
UDS_SETBUDDYINT|UDS_ALIGNRIGHT,
90,25,20,15,hdwnd,IDD_UPDOWN2,hMainInstance,hEboxWnd2,
FontSizeMax,40,zhi_hao); //建立第二个旋转按钮
SetDlgItemText(hdwnd,IDD_EB3,str);//在编辑框IDD_EB3中显示字符串
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK://将设置好的参数写入注册表中
delay=GetDlgItemInt(hdwnd,IDD_EB1,NULL,1);
zhi_hao=GetDlgItemInt(hdwnd,IDD_EB2,NULL,1);
GetDlgItemText(hdwnd,IDD_EB3,str,MSGSIZE);
RegSetValueEx(hRegKey,"delay",0,REG_DWORD,
(LPBYTE)&delay,sizeof(DWORD));
RegSetValueEx(hRegKey,"FontSize",0,REG_DWORD,
(LPBYTE)&zhi_hao,sizeof(DWORD));
RegSetValueEx(hRegKey,"message",0,REG_SZ,
(LPBYTE)str,strlen(str)+1);
case IDCANCEL:
RegCloseKey(hRegKey);
EndDialog(hdwnd,0);
break;
}
break;
}
return 0;
}
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
return 1;
}
2、资源文件scr.rc
#include <windows.h>
#include <scrnsave.h>
#include "scr.h"
ID_APP ICON SCRICON.ICO
STRINGTABLE
{
IDS_DESCRIPTION "It's a screen saver program!"
}
DLG_SCRNSAVECONFIGURE DIALOG 20,20,160,90
CAPTION "屏幕保护程序选项设置"
STYLE DS_MODALFRAME |WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU
{
DEFPUSHBUTTON "确定",IDOK,40,70,30,15,WS_CHILD|WS_VISIBLE|WS_TABSTOP
PUSHBUTTON "取消",IDCANCEL,80,70,30,15,WS_CHILD|WS_VISIBLE|WS_TABSTOP
EDITTEXT IDD_EB1,55,5,25,12,ES_LEFT|WS_CHILD|WS_VISIBLE|WS_BORDER
EDITTEXT IDD_EB2,55,25,25,12,ES_LEFT|WS_CHILD|WS_VISIBLE|WS_BORDER
|WS_TABSTOP
EDITTEXT IDD_EB3,55,45,70,12,ES_LEFT|WS_CHILD|WS_VISIBLE|WS_BORDER
|ES_AUTOHSCROLL|WS_TABSTOP
LTEXT "延迟时间:",IDD_TEXT1,10,7,40,12
LTEXT "字号大小:",IDD_TEXT2,10,25,40,12
LTEXT "显示信息:",IDD_TEXT3,10,45,40,12
}
3、头文件scr.h
#define IDD_EB1 200
#define IDD_EB2 201
#define IDD_EB3 202
#define IDD_UPDOWN1 203
#define IDD_UPDOWN2 204
#define IDD_TEXT1 205
#define IDD_TEXT2 206
#define IDD_TEXT3 207
|