Профессиональное создание хранителей экрана
Вместо введения ...
Данная статья, до некоторого времени путешествовала по просторам сайтов русской документации в совершенно первобытном виде - в ней были множественные ошибки, у неё часто менялись авторы-переводчики, но что самое главное - у нее не было рабочего примера! Количество ошибок при переписывании статьи достигло такого количества, что при компиляции проект на 100% не работал. Я переписал пример, добавил в него некоторые улучшения и сделал после статьи послесловие, которое возможно интереснее самой статьи. Итак читайте бестселлер всех серверов документации по Delphi, статью скрывавшуюся под различными названиями о создании ScreenSaver'а:
Главное о чем стоит упомянуть это, что ваш хранитель экрана будет работать в фоновом режиме и он не должен мешать работе других запущенных программ. Поэтому сам хранитель должен быть как можно меньшего объема.
Для уменьшения объема файла в описанной ниже программе не используется визуальные компоненты Delphi (VCL), включение хотя бы одного из них приведет к увеличению размера файла свыше 200 кб, а так, описанная ниже программа, имеет размер всего ~ 20 - 30 кб, в зависимости от версии компилятора. В конце статьи я приведу ещё пару приемов, которые смогут сократить размер приложения до десятков кб.
Технически, хранитель экрана является нормальным PE файлом, который управляется через командную строку. Например, если пользователь хочет изменить параметры вашего хранителя, Windows выполняет его с параметром "c" в командной строке. Поэтому начать создание вашего хранителя экрана следует с создания примерно по следующему каркасу:Program DelphiScreenSaver;
Uses Windows, Messages;
{$R SETTINGS.RES}
Var
...
Function A1;
Function A2;
Procedure A1;
Procedure A2;
...
Begin
LoadSettings;
If ParamCount > 0 Then
Begin
If Length(ParamStr(1)) = 1 Then
Command := ParamStr(1)[1]
Else Command := ParamStr(1)[2];
Case Command Of
'C', 'c': RunSettings;
'P', 'p': RunPreview;
'A', 'a': RunSetPassword;
'S', 's': RunFullScreen;
End;
End Else RunSettings;
End.
Поскольку нам нужно создавать небольшое окно предварительного просмотра и полноэкранное окно, их лучше объединить, используя единственный класс окна. Следуя правилам хорошего тона, нам также нужно использовать потоки. Дело в том, что, во-первых, хранитель не должен переставать работать даже если что-то "тяжелое" случилось, и во-вторых, нам не нужно использовать таймер.Procedure RunFullScreen;
Var
R : TRect;
Msg : TMsg;
Dummy : DWORD;
Foreground : hWnd;
Begin
IsPreview := False;
MoveCounter := 3;
Foreground := GetForegroundWindow;
While ShowCursor(False) > 0 Do ;
GetWindowRect(GetDesktopWindow, R);
CreateScreenSaverWindow(R.Right - R.Left, R.Bottom - R.Top, 0);
CreateThread(Nil, 0, @PreviewThreadProc, Nil, 0, Dummy);
SystemParametersInfo(spi_ScreenSaverRunning, 1, @Dummy, 0);
While GetMessage(Msg, 0, 0, 0) Do
Begin
TranslateMessage(Msg);
DispatchMessage(Msg);
End;
SystemParametersInfo(spi_ScreenSaverRunning, 0, @Dummy, 0);
ShowCursor(True);
SetForegroundWindow(Foreground);
End;
Во-первых, мы проинициализировали некоторые глобальные переменные (описанные далее), затем прячем курсор мыши и создаем окно хранителя экрана. Имейте в виду, что важно уведомлять Windows, что это - хранителя экрана через SystemParametersInfo (это выводит из строя Ctrl-Alt-Del чтобы нельзя было вернуться в Windows не введя пароль). Создание окна хранителя:Function CreateScreenSaverWindow(Width, Height: Integer; ParentWindow: hWnd): hWnd;
Var WC : TWndClass;
Begin
With WC Do
Begin
Style := cs_ParentDC;
lpfnWndProc := @PreviewWndProc;
cbClsExtra := 0;
cbWndExtra := 0;
hIcon := 0;
hCursor := 0;
hbrBackground := 0;
lpszMenuName := Nil;
lpszClassName := 'MyDelphiScreenSaverClass';
hInstance := System.MainInstance;
End;
RegisterClass(WC);
If ParentWindow <> 0 Then
Result := CreateWindow('MyDelphiScreenSaverClass', 'MySaver',
ws_Child Or ws_Visible Or ws_Disabled, 0, 0,
Width, Height, ParentWindow, 0, hInstance, Nil)
Else
Begin
Result := CreateWindow('MyDelphiScreenSaverClass', 'MySaver',
ws_Visible Or ws_Popup, 0, 0, Width, Height, 0, 0, hInstance, Nil);
SetWindowPos(Result, hwnd_TopMost, 0, 0, 0, 0, swp_NoMove Or swp_NoSize Or swp_NoRedraw);
End;
PreviewWindow := Result;
End;
Теперь окна созданы используя вызовы API. Я удалил проверку ошибки, но обычно все проходит хорошо, особенно в этом типе приложения.
Теперь Вы можете погадать, как мы получим handle родительского окна предварительного просмотра? В действительности, это совсем просто: Windows просто передает handle в командной строке, когда это нужно. Таким образом:Procedure RunPreview;
Var
R : TRect;
PreviewWindow : hWnd;
Msg : TMsg;
Dummy : DWord;
Begin
IsPreview := True;
If ParamCount > 1 Then Val(ParamStr(2), PreviewWindow, Dummy)
Else PreviewWindow := GetForegroundWindow;
GetWindowRect(PreviewWindow, R);
CreateScreenSaverWindow(R.Right - R.Left, R.Bottom - R.Top, PreviewWindow);
CreateThread(Nil, 0, @PreviewThreadProc, Nil, 0, Dummy);
While GetMessage(Msg, 0, 0, 0) Do
Begin
TranslateMessage(Msg);
DispatchMessage(Msg);
End;
End;
Как Вы видите, window handle является вторым параметром (после "p"). Чтобы "выполнять" хранителя экрана - нам нужен поток. Это создается с вышеуказанным CreateThread. Процедура нити выглядит примерно так:
Источник: http://www.delphigfx.narod.ru