Створення зберігача екрана h2>
Головне
про що варто згадати це, що ваш збереження екрану буде працювати у фоновому
режимі, і він не повинен заважати роботі інших запущених програм. Тому сам
зберігач повинен бути як можна меншого обсягу. Для зменшення об'єму файлу в
описаної нижче програмі не використовується візуальні компоненти Delphi,
включення хоча б одного з них приведе до збільшення розміру файлу понад
200Кб, а так, описана нижче програма, має розмір всього 20кб !!! p>
Технічно,
збереження екрану є нормальним EXE файлом (з розширенням. SCR), який
управляється через командні параметри рядка. Наприклад, якщо користувач хоче
змінити параметри вашого зберігача, Windows виконує його з параметром
"-c" в командному рядку. Тому розпочати створення вашого зберігача
екрана слід зі створення приблизно наступної функції: p>
Procedure RunScreenSaver; p>
Var S: String; p>
Begin p>
S: = ParamStr (1); p>
If (Length (S)> 1) Then Begin p>
Delete (S, 1,1); p>
(delete first char - usally
"/" Or "-") p>
S [1]: = UpCase (S [1 ]); p>
End; p>
LoadSettings; (load settings from
registry) p>
If (S = 'C') Then RunSettings p>
Else If (S = 'P') Then RunPreview p>
Else If (S = 'A') Then
RunSetPassword p>
Else
RunFullScreen; p>
End; p>
Оскільки
нам потрібно створювати невелике вікно попереднього перегляду і повноекранне
вікно, їх краще об'єднати, використовуючи єдиний клас вікна. Слідуючи правилам
хорошого тону, нам також потрібно використовувати численні нитки. Справа в тому,
що, по-перше, зберігач не повинен переставати працювати, навіть якщо щось
"важкий" сталося, і, по-друге, нам не потрібно використовувати таймер. p>
Процедура
для запуску зберігача на повному екрані - приблизно така: p>
Procedure RunFullScreen; p>
Var p>
R: TRect; p>
Msg: TMsg; p>
Dummy: Integer; p>
Foreground: hWnd; p>
Begin p>
IsPreview: = False; MoveCounter: =
3; p>
Foreground: = GetForegroundWindow; p>
While (ShowCursor (False)> 0) do
; p>
GetWindowRect (GetDesktopWindow, R); p>
CreateScreenSaverWindow (R.Right-R.Left,
R.Bottom-R.Top, 0); p>
CreateThread (nil, 0, @ PreviewThreadProc,
nil, 0, Dummy); p>
SystemParametersInfo (spi_ScreenSaverRunning,
1, @ Dummy, 0); p>
While GetMessage (Msg, 0,0,0) do Begin
//відповідаємо на повідомлення p>
TranslateMessage (Msg);// що б
не повісити windows p>
DispatchMessage (Msg); p>
End; p>
SystemParametersInfo (spi_ScreenSaverRunning, 0, @ Dummy, 0); p>
ShowCursor (True); p>
SetForegroundWindow (Foreground); p>
End; p>
По-перше,
ми проініціалізувати деякі глобальні змінні (описані далі), потім
ховаємо курсор миші і створюємо вікно зберігача екрана. Майте на увазі, що важливо
повідомляти Windows, що це - зберігача екрану через SystemParametersInfo (це
виводить з ладу Ctrl-Alt-Del щоб не можна було повернутися в Windows не ввівши
пароль). Створення вікна зберігача: p>
Function
CreateScreenSaverWindow (Width, Height: Integer; ParentWindow: hWnd): hWnd; p>
Var WC: TWndClass; p>
Begin p>
With WC do Begin p>
Style: = cs_ParentDC; p>
lpfnWndProc: = @ PreviewWndProc; p>
cbClsExtra: = 0; cbWndExtra: = 0; p>
hIcon: = 0; hCursor: = 0; p>
hbrBackground: = 0; lpszMenuName: =
nil; p>
lpszClassName: = 'MyDelphiScreenSaverClass'; p>
hInstance: = System.hInstance; p>
end; p>
RegisterClass (WC); p>
If (ParentWindow 0) Then p>
Result: =
CreateWindow ( 'MyDelphiScreenSaverClass', 'MySaver', ws_Child Or ws_Visible or
ws_Disabled, 0,0, Width, Height, ParentWindow, 0, hInstance, nil) p>
Else Begin p>
Result: =
CreateWindow ( 'MyDelphiScreenSaverClass', 'MySaver', ws_Visible or
ws_Popup, 0,0, Width, Height, 0,0, hInstance, nil); p>
SetWindowPos (Result, hwnd_TopMost, 0,0,0,0, swp_NoMove
or swp_NoSize or swp_NoRedraw); p>
End; p>
PreviewWindow: = Result; p>
End; p>
Тепер вікна створені використовуючи виклики API. Я видалив перевірку помилки, але
зазвичай все проходить добре, особливо в цьому типі програми. p>
Тепер
Ви можете поворожити, як ми отримаємо handle батьківського вікна попереднього
перегляду? Насправді, це зовсім просто: Windows просто передає
handle у командному рядку, коли це потрібно. Таким чином: p>
Procedure RunPreview; p>
Var p>
R: TRect; p>
PreviewWindow: hWnd; p>
Msg: TMsg; p>
Dummy: Integer; p>
Begin p>
IsPreview: = True; p>
PreviewWindow: =
StrToInt (ParamStr (2 )); p>
GetWindowRect (PreviewWindow, R); p>
CreateScreenSaverWindow (R.Right-R.Left, R.Bottom-R.Top, PreviewWindow); p>
CreateThread (nil, 0, @ PreviewThreadProc, nil, 0, Dummy); p>
While GetMessage (Msg, 0,0,0) do Begin p>
TranslateMessage (Msg); p>
DispatchMessage (Msg); p>
End; p>
End; p>
Як Ви
бачите, window handle є другим параметром (після "-p "). p>
Щоб
"виконувати" зберігача екрана - нам потрібна нитка. Це створюється з
вищевказаним CreateThread. Процедура нитки виглядає приблизно так: p>
Function
PreviewThreadProc (Data: Integer): Integer; StdCall; p>
Var R: TRect; p>
Begin p>
Result: = 0; Randomize; p>
GetWindowRect (PreviewWindow, R); p>
MaxX: = R.Right-R.Left; p>
MaxY: = R.Bottom-R.Top; p>
ShowWindow (PreviewWindow, sw_Show); p>
UpdateWindow (PreviewWindow); p>
Repeat p>
InvalidateRect (PreviewWindow, nil, False); p>
Sleep (30); p>
Until QuitSaver; p>
PostMessage (PreviewWindow, wm_Destroy, 0,0); p>
End; p>
Нитка
просто змушує оновлюватися зображення в нашому вікні, спить на деякий
час, і оновлює зображення знову. А Windows буде посилати повідомлення
WM_PAINT на наше вікно (не в нить!). Для того, щоб оперувати цим
повідомленням, нам потрібна процедура: p>
Function
PreviewWndProc (Window: hWnd; Msg, WParam, LParam: Integer): Integer; StdCall; p>
Begin p>
Result: = 0; p>
Case Msg of p>
wm_NCCreate: Result: = 1; p>
wm_Destroy: PostQuitMessage (0); p>
(paint something) p>
wm_Paint: DrawSingleBox; p>
wm_KeyDown: QuitSaver: =
AskPassword; p>
wm_LButtonDown, wm_MButtonDown, p>
wm_RButtonDown, wm_MouseMove: p>
Begin p>
If (Not IsPreview) Then Begin p>
Dec (MoveCounter); p>
If (MoveCounter <= 0) Then p>
QuitSaver: = AskPassword; p>
End; p>
End; p>
Else Result: = DefWindowProc p>
(Window, Msg, WParam, LParam); p>
End; p>
End; p>
Якщо
миша переміщається або натиснута кнопки, ми питаємо у користувача пароль: p>
Function AskPassword: Boolean; p>
Var p>
Key: hKey; p>
D1, D2: Integer; (two dummies) p>
Value: Integer; p>
Lib: THandle; p>
F: TVSSPFunc; p>
Begin p>
Result: = True; p>
If
(RegOpenKeyEx (hKey_Current_User, 'Control PanelDesktop', 0, Key_Read, Key) =
Error_Success) Then p>
Begin p>
D2: = SizeOf (Value); p>
If
(RegQueryValueEx (Key, 'ScreenSaveUsePassword', nil, @ D1, @ Value, @ D2) =
Error_Success) Then p>
Begin p>
If (Value 0) Then Begin p>
Lib: = LoadLibrary ( 'PASSWORD.CPL'); p>
If (Lib> 32) Then Begin p>
@ F: =
GetProcAddress (Lib, 'VerifyScreenSavePwd'); p>
ShowCursor (True); p>
If (@ F nil) Then Result: =
F (PreviewWindow); p>
ShowCursor (False); p>
(reset again if password was wrong
) p>
MoveCounter: = 3; p>
FreeLibrary (Lib); p>
End; p>
End; p>
End; p>
RegCloseKey (Key); p>
End; p>
End; p>
Це
також демонструє використання registry на рівні API. Також майте на увазі
як ми динамічно завантажуємо функції пароля, використовуючи LoadLibrary. Запам'ятайте
тип функції? p>
TVSSFunc
ВИЗНАЧЕНО, як: p>
Type p>
TVSSPFunc
= Function (Parent: hWnd): Bool; StdCall; p>
Тепер
майже все готово, окрім діалогу конфігурації. Це запросто: p>
Procedure RunSettings; p>
Var Result: Integer; p>
Begin p>
Result: = DialogBox (hInstance, 'SaverSettingsDlg', 0, @ SettingsDlgProc); p>
If (Result = idOK) Then
SaveSettings; p>
End; p>
Важка
частина-це створити діалоговий сценарій (запам'ятайте: ми не використовуємо тут
Delphi форми!). Я зробив це, використовуючи 16-бітову Resource Workshop (залишився
ще від Turbo Pascal для Windows). Я зберіг файл як сценарій (текст), і
скомпільований це з BRCC32: p>
SaverSettingsDlg DIALOG 70, 130,
166, 75 p>
STYLE WS_POPUP | WS_DLGFRAME |
WS_SYSMENU p>
CAPTION "Settings for
Boxes " p>
FONT 8, "MS Sans Serif" p>
BEGIN p>
DEFPUSHBUTTON "OK", 5,
115, 6, 46, 16 p>
PUSHBUTTON "Cancel", 6,
115, 28, 46, 16 p>
CTEXT "Box & Color:",
3, 2, 30, 39, 9 p>
COMBOBOX 4, 4, 40, 104, 50,
CBS_DROPDOWNLIST | p>
CBS_HASSTRINGS p>
CTEXT "Box & Type:", 1,
4, 3, 36, 9 p>
COMBOBOX 2, 5, 12, 103, 50, p>
CBS_DROPDOWNLIST | CBS_HASSTRINGS p>
LTEXT "Boxes Screen Saver for p>
Win32 Copyright (c) 1996 Jani
Jurvinen. " P>
, 7, 4, 57, 103, 16, p>
WS_CHILD | WS_VISIBLE | WS_GROUP p>
END p>
Майже
також легко зробити діалогове меню: p>
Function
SettingsDlgProc (Window: hWnd; Msg, WParam, LParam: Integer): Integer; StdCall; p>
Var S: String; p>
Begin p>
Result: = 0; p>
Case Msg of p>
wm_InitDialog: Begin p>
(initialize the dialog box) p>
Result: = 0; p>
End; p>
wm_Command: Begin p>
If (LoWord (WParam) = 5) Then p>
EndDialog (Window, idOK) p>
Else p>
If (LoWord (WParam) = 6) Then p>
EndDialog (Window, idCancel); p>
End; p>
wm_Close: DestroyWindow (Window); p>
wm_Destroy: PostQuitMessage (0); p>
Else Result: = 0; p>
End; p>
End; p>
Після
того, як користувач вибрав деякі настановні параметри, нам потрібно
зберегти їх. p>
Procedure SaveSettings; p>
Var p>
Key: hKey; p>
Dummy: Integer; p>
Begin p>
If
(RegCreateKeyEx (hKey_Current_User, p>
'SoftwareSilverStreamSSBoxes', p>
0, nil, Reg_Option_Non_Volatile, p>
Key_All_Access, nil, Key, p>
@ Dummy) = Error_Success) Then Begin p>
RegSetValueEx (Key, 'RoundedRectangles', 0, Reg_Binary, @ RoundedRectangles, p>
SizeOf (Boolean )); p>
RegSetValueEx (Key, 'SolidColors', 0, Reg_Binary,
@ SolidColors, SizeOf (Boolean )); p>
RegCloseKey (Key); p>
End; p>
End; p>
Завантажуємо параметри так: p>
Procedure LoadSettings; p>
Var p>
Key: hKey; p>
D1, D2: Integer; (two dummies) p>
Value: Boolean; p>
Begin p>
If
(RegOpenKeyEx (hKey_Current_User, 'SoftwareSilverStreamSSBoxes', 0, Key_Read, Key)
= Error_Success) Then Begin p>
D2: = SizeOf (Value); p>
If
(RegQueryValueEx (Key, 'RoundedRectangles', nil, @ D1, @ Value, @ D2) = Error_Success)
Then p>
Begin p>
RoundedRectangles: = Value; p>
End; p>
If
(RegQueryValueEx (Key, 'SolidColors', nil, @ D1, @ Value, @ D2) = Error_Success) Then p>
Begin p>
SolidColors: = Value; p>
End; p>
RegCloseKey (Key); p>
End; p>
End; p>
Легко?
Нам також потрібно дозволити користувачеві, встановити пароль. Я чесно не знаю
чому це залишено розробнику програм! Тим не менше: p>
Procedure RunSetPassword; p>
Var p>
Lib: THandle; p>
F: TPCPAFunc; p>
Begin p>
Lib: = LoadLibrary ( 'MPR.DLL'); p>
If (Lib> 32) Then Begin p>
@ F: =
GetProcAddress (Lib, 'PwdChangePasswordA'); p>
If (@ F nil) Then
F ( 'SCRSAVE', StrToInt (ParamStr (2)), 0,0); p>
FreeLibrary (Lib); p>
End; p>
End; p>
Ми
динамічно завантажуємо (недокументовані) бібліотеку MPR.DLL, яка має
функцію, щоб встановити пароль зберігача екрана, так що нам не потрібно
турбуватися про це. p>
TPCPAFund ВИЗНАЧЕНО як: p>
Type p>
TPCPAFunc = Function (A: PChar;
Parent: hWnd; B, C: Integer): Integer; StdCall; p>
(Не
питайте мене що за параметри B і C! :-) p>
Тепер
єдина річ, яку нам потрібно розглянути, - сама дивна частина:
створення графіки. Я не великий ГУРУ графіки, так що Ви не побачите затінюють
багатокутники, що обертаються в реальному часі. Я лише зробив деякі
ящики. p>
Procedure
DrawSingleBox; p>
Var p>
PaintDC
: HDC; p>
Info: TPaintStruct; p>
OldBrush: hBrush; p>
X, Y: Integer; p>
Color: LongInt; p>
Begin p>
PaintDC: =
BeginPaint (PreviewWindow, Info); p>
X: = Random (MaxX); Y: =
Random (MaxY); p>
If SolidColors Then p>
Color: = GetNearestColor (PaintDC, p>
RGB (Random (255), Random (255) p>
, Random (255 ))) p>
Else Color: = RGB (Random (255), p>
Random (255), Random (255 )); p>
OldBrush: = SelectObject (PaintDC, p>
CreateSolidBrush (Color )); p>
If RoundedRectangles Then p>
RoundRect (PaintDC, X, Y, X + Random (MaxX-X), p>
Y + Random (MaxY-Y), 20,20) p>
Else
Rectangle (PaintDC, X, Y, X + Random (MaxX-X), p>
Y + Random (MaxY-Y )); p>
DeleteObject (SelectObject (PaintDC, OldBrush )); p>
EndPaint (PreviewWindow, Info); p>
End; p>
І
останнє - глобальні змінні: p>
Var p>
IsPreview: Boolean; p>
MoveCounter: Integer; p>
QuitSaver: Boolean; p>
PreviewWindow: hWnd; p>
MaxX, MaxY: Integer; p>
RoundedRectangles: Boolean; p>
SolidColors: Boolean; p>
Потім
вихідна програма проекту (. dpr).
Красива, а!? P>
program MySaverIsGreat; p>
uses p>
windows, messages, Utility; p>
(defines all routines) p>
($ R SETTINGS.RES) p>
begin p>
RunScreenSaver; p>
end. p>
Ох, мало не забув! Якщо, Ви використовуєте
SysUtils у вашому проекті (наприклад фуекцію StrToInt) ви отримаєте EXE-файл
більше ніж обіцяний в 20k. :) Якщо Ви хочете все ж іметь20k, надо как-то
обійтися без SysUtils, наприклад самому написати власну StrToInt процедуру. p>
Якщо
все-таки дуже важко обійтися без використання Delphi-форм, то можна вступити
як у випадку з введенням пароля: форму зміни параметрів зберігання зберегти в
вигляді DLL і динамічно її завантажувати при необхідності. Т.ч. буде маленький і
спритний файл самого зберігача екрану і доважку DLL для конфігурації і
іншого (там об'єм і швидкість вже не критичні). p>
Список літератури h2>
Для
підготовки даної роботи були використані матеріали з сайту http://www.soch.imperium.by
p>