Windows 螢幕保護程式本質是基於命令列參數的應用程式,副檔名為 .scr。系統依不同情境以特定參數啟動:/p(預覽)、/s(正常運行)、/c(設定對話框)、/a(密碼設定,NT 無效)。本文通過完整專案示範開發全功能螢幕保護程式,包含註冊表儲存設定、密碼驗證、預覽模式支援。
| 函數 | 用途 |
|---|---|
GetWindowLong / SetWindowLong | 獲取/設定視窗長整數屬性(風格、擴展風格等) |
SetParent | 變更視窗父子關係(預覽模式嵌入) |
GetClientRect | 取得視窗客戶區矩形 |
SetWindowPos | 調整視窗大小、位置、Z-order |
SystemParametersInfo | 查詢/設定系統參數(螢保運行狀態、動畫等) |
ShowCursor | 顯示/隱藏滑鼠游標 |
GetVersion | 取得作業系統版本資訊 |
BorderStyle=bsNone,黑色背景,含 TImage + TTimer。/c),含 TTrackBar 設定切圖頻率、SelectDirectory 選擇圖片資料夾,設定寫入註冊表 HKEY_CURRENT_USER\Software\CODEHUNTER。/p),固定 160×130,TImage.Stretch=true。WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR p, int) {
String StartType;
AnsiString Command = p, temp;
HWND CPWindow = NULL;
if (Command == "") StartType = "/c"; // 無參數預設開啟設定
else StartType = Command.SubString(1, 2);
try {
Application->Initialize();
if (StartType == "/c") {
Application->CreateForm(__classid(TFrmConfig), &FrmConfig);
} else if (StartType == "/s") {
Application->CreateForm(__classid(TFrmmain), &Frmmain);
} else if (StartType == "/p") { // 預覽模式
Application->CreateForm(__classid(TFrmControl), &FrmControl);
temp = Command.SubString(3, Command.Length() - 2);
CPWindow = (HWND)temp.ToInt(); // 取得預覽視窗 HWND
// 將 FrmControl 設為子視窗並嵌入預覽區
LONG style = GetWindowLong(Application->MainForm->Handle, GWL_STYLE);
SetWindowLong(Application->MainForm->Handle, GWL_STYLE, style | WS_CHILD);
SetParent(Application->MainForm->Handle, CPWindow);
RECT lookrect;
GetClientRect(CPWindow, &lookrect);
SetWindowPos(Application->MainForm->Handle, HWND_TOP, 0, 0,
lookrect.right, lookrect.bottom,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
} else if (StartType == "/a") { // 密碼設定
temp = Command.SubString(3, Command.Length() - 2);
CPWindow = (HWND)temp.ToInt();
// 動態載入 mpr.dll 呼叫 PwdChangePasswordA
typedef UINT (CALLBACK *FUN)(LPSTR, HWND, UINT, UINT);
HINSTANCE hDll = LoadLibrary("mpr.DLL");
if (hDll) {
FUN myfun = (FUN)GetProcAddress(hDll, "PwdChangePasswordA");
if (myfun) myfun("SCRSAVE", CPWindow, 0, 0);
FreeLibrary(hDll);
}
}
Application->Run();
} catch (Exception &e) {
Application->ShowException(&e);
}
return 0;
}
void __fastcall TFrmmain::FormCreate(TObject *Sender) {
// 置頂、全螢幕覆蓋
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
Width = Screen->Width; Height = Screen->Height; Top = 0; Left = 0;
Version = GetVersion();
TRegistry *Reg = new TRegistry;
try {
if (Version > 0x80000000) { // Win9x
Reg->RootKey = HKEY_CURRENT_USER;
Reg->OpenKey("\\Control Panel\\Desktop", false);
PWProtect = Reg->ReadInteger("ScreenSaveUsePassword");
Reg->CloseKey();
}
Reg->RootKey = HKEY_CURRENT_USER;
Reg->OpenKey("\\Software\\CODEHUNTER", true);
picdir = Reg->ReadString("PicDir"); // 圖片目錄
frequence = Reg->ReadInteger("frequence"); // 切換頻率(秒)
if (picdir == "") picdir = "no";
if (frequence < 0 || frequence > 6) frequence = 2;
Timer1->Interval = 1000 * frequence;
} __finally { delete Reg; picdir = "no"; }
// 非 NT 且啟用密碼保護時,設定螢保運行狀態並隱藏游標
if (Version != 0 && PWProtect && Version > 0x80000000)
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 1, 0, 0);
while (!ShowCursor(false) < -5); // 徹底隱藏游標
}
void __fastcall TFrmmain::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { Close(); }
void __fastcall TFrmmain::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { Close(); }
void __fastcall TFrmmain::Image1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) {
static int MouseMoves = 0;
if (++MouseMoves > 4) { Close(); MouseMoves = 0; }
}
void __fastcall TFrmmain::FormCloseQuery(TObject *Sender, bool &CanClose) {
if (PWProtect && Version > 0x80000000) {
while (!ShowCursor(true) > 5); // 顯示游標
typedef UINT (CALLBACK *FUN)(HWND);
HINSTANCE hDll = LoadLibrary("password.cpl");
bool PassChck = false;
if (hDll) {
FUN myfun = (FUN)GetProcAddress(hDll, "VerifyScreenSavePwd");
if (myfun) PassChck = myfun(Handle);
FreeLibrary(hDll);
}
if (!PassChck) {
while (!ShowCursor(false) < -5); CanClose = false;
}
}
}
void __fastcall TFrmmain::Timer1Timer(TObject *Sender) {
if (picdir == "no") {
randomize();
int i = (rand() % 2) ? 1 : -1;
Image1->Top = i * (rand() % Height);
Image1->Left = i * (rand() % Width);
}
// TODO: picdir != "no" 時讀取目錄下圖片輪播(留作練習)
}
.exe,重命名為 .scr。Windows 目錄(或 System32)。程式碼中已實作 SelectDirectory 選擇圖片資料夾並寫入註冊表 PicDir。請自行完成:當 picdir != "no" 時,從該目錄讀取圖片檔(.bmp/.jpg),於 Timer 事件中輪流顯示,實現真正的相簿式螢幕保護程式。
本範例展示了螢幕保護程式的完整生命週期處理、預覽模式嵌入、密碼驗證、系統參數互動等核心技術,為開發專業級 .scr 程式奠定堅實基礎。