О том, что такое скринсэйвер (хранитель экрана), знает каждый школьник. Но не каждый представляет его себе настолько хорошо, чтобы написать свой. Я предлагаю вам создать собственный скринсэйвер на Дельфи. Почему на Дельфи? Во-первых, опыт работы с ним у меня намного богаче, чем с другими языками программирования, а во-вторых, он не предоставляет никаких дополнительных модулей, облегчающих создание скринсэйвера. То есть, нам предстоит написать скринсэйвер от начала и до конца.

Чтобы упростить скринсэйвер и сделать его код понятным даже новичкам в программировании, я постараюсь использовать минимум API-функций. Таким образом, основную часть скринсэйвера мы будем делать на VCL. Несколько слов о том, что такое скринсэйвер. Это обыкновенный программный файл с расширением .scr, который запускается в разных режимах в зависимости от параметров командной строки. Какие же параметры предстоит нам обрабатывать? Пусть наш скринсэйвер запускается в основном, полноэкранном режиме при наличии командной строки без параметров. При параметре С запускается окно настроек, при P — окно предварительного просмотра, а с параметром A устанавливается функция указания пароля заставки. Не знаю почему, но возможность установки пароля заставки оставили за разработчиками скринсэйверов. Для нашего скринсэйвера потребуется две формы (в моем примере они называются frmMain и frmSet), одна для заставки, другая для настроек. Параметры будем обрабатывать непосредственно в главном файле проекта. Для этого создадим функцию ExecSS:

function ExecSS : boolean; var S : string; begin Result:=true; S := ParamStr(1); If Length(S) > 1 then begin Delete(S, 1, 1); {нам точно неизвестен первый символ параметра, он может быть не только -, но и / или \} S[1] := UpCase(S[1]); {чтобы не обрабатывать поочередно символы верхнего и нижнего регистров} If S[1] = 'C' then begin Result:=false; Application.CreateForm(TfrmSet,frmSet); //создаем форму установки параметров Application.Run; //и запускаем хранитель в режиме настроек end; If S[1]='A' then begin SetPassword; //приведу текст этой функции попозже Result:=false; end; end; end;

Теперь о том, как же должен выглядеть сам файл. Замечу, что надо исключать запуск двух копий хранителя, что и делают третья и четвертая строчки проекта:

var wnd : hWnd; begin Wnd:=FindWindow('TfrmMain','MyScreenSaver'); {ищем окно, первый параметр — класс окна формы заставки, а второй — ее название } If Wnd <> 0 then PostMessage(Wnd, WM_CLOSE ,0,0); // если находим — закрываем Application.Initialize; If ExecSS then {если не запустился режим настроек или установки пароля, запускаем сам хранитель} begin Application.CreateForm(TfssMain, fssMain); Application.Run; End.

Теперь хорошо подумайте, что должен делать ваш скринсэйвер. Я не буду приводить здесь пример его графического исполнения. Я только замечу, что если вы хотите создать несложный скринсэйвер, используйте графические функции GDI, то есть рисуйте средствами TCanvas. Если же вы желаете графику посолидней, используйте OpenGL. Не советую использовать в этих целях DirectDraw или Direct3D. Стандарт DirectX слишком часто обновляется, и нет гарантии, что он установлен на том компьютере, где вы хотите установить заставку. Итак, для начала сделаем из основной формы форму скринсэйвера. Установим ее следующие параметры:

Caption = 'MyScreenSaver' BorderStyle = bsNone FormStyle = fsStayOnTop WindowState = wsMaximized

Если вы создаете GDI`евский скринсэйвер, установите цвет окна, желательно черный, и создайте дополнительную канву, то есть объект класса TCanvas. В ней мы и будем рисовать скринсэйвер — окно превью скринсэйвера. Так что назовите ее соответствующим образом (у меня она называется ssCanvas). Если вам потребуется таймер, поместите его на форму. Теперь обработаем события формы: OnKeyDown, OnMouseDown, OnMouseMove. Как вы понимаете, при всех этих событиях скринсэйвер должен закрываться. Это мы сделаем так:

CheckPassword; //функция проверки пароля, описана ниже Close

Замечу, что при перемещении мыши мы должны закрывать хранитель, только если курсор переместился на 2-4 пикселя. То есть, для обработки события OnMouseMove мы сначала должны сохранить позицию курсора при старте, а затем сравнить с текущей.

If (abs(mPos.X-x)>3) or (abs(mPos.Y-y)>3) then begin CheckPassword; Close; end;

Здесь mPos — переменная типа TPoint, в которой сохранены координаты курсора. Теперь инициализируем скринсэйвер при событии OnCreate. Первое, что нам предстоит сделать, это узнать, находится ли хранитель в режиме превью или в основном. Проверка параметров будет осуществляться в функции IsPreview, приведенной ниже.

function TfrmMain.IsPreview : boolean; var S : string; begin Result:=False; S :=ParamStr(1); if Length(S) > 1 then begin Delete(S, 1, 1); S[1] := UpCase(S[1]); If S[1] = 'P' then Result:= True end; end;

Результат желательно сохранить в какой-то переменной. Если это режим предварительного просмотра, мы также должны получить дескриптор окна просмотра — он передается в качестве второго параметра, после -P.

Var Preview : boolean; Begin Preview:=IsPreview; If Preview then begin frmMain.ParentWindow:=strtoint(ParamStr(2)); {установим наше окно в качестве потомка окна, указанного в параметрах. Для GDI инициализируйте созданную вами ранее канву, как в моем примере} ssCanvas:=TCanvas.Create; ssCanvas.Handle:=GetDC(StrToINt(ParamStr(2))); //рисовать она должна в окно просмотора {А для OpenGL просто инициализируйте его в канве полученого дескриптора окна, ее DC можно определить с помощью функции GetDC} end else begin Left:=0; Top:=0; Width:=Screen.Width; Height:=Screen.Height; {Если хранитель в основном режиме, распахиваем его на весь экран} GetCursorPos(mPos); //сохраняем текущие координаты курсора в перменной mPos end;

Для вывода графики GDI в основном режиме используйте канву формы — или рисуйте в той же канве, что и для окна просмотра, перед этим сделав так:

SsCanvas.Handle:=Canvas.Handle;

Замечу, что прорисовка окна через другую канву требует чуть больше ресурсов. Для инициализации графики в режиме preview вам потребуется размер контекста воспроизведения. Его можно получить с помощью функции GetClientRect, ее первый параметр — дескриптор окна вывода (в нашем случае Strtoint(ParamStr(2))), а второй — структура типа TRect, в которую заносится результат. В ее полях Right и Bottom содержится размер окна по вертикали и горизонтали. Вот, с инициализацией закончили. Теперь рассмотрим событие OnShow:

If not Preview then ShowCursor(False); //прячем курсор ShowWindow(Application.Handle,SW_HIDE); //прячем приложение с таскбара

Наша заставка запустилась и должна нормально работать как в основном режиме, так и предварительного просмотра. Теперь перейдем к окну настроек, для которого мы создали вторую форму. Если вам необходимо задать какие-то настройки для хранителя, поставьте задающие их компоненты на форму. Также снабдите окно стандартными кнопками Ok, Cancel и Set Default. При нажатии на Ok все настройки, указанные пользователем, мы должны будем записать в реестр или в файл инициализации. Для этого можно воспользоваться классами TRegistry, TRegIniFile и TIniFile, перед этим добавив в список Uses модуль Registry. В моем примере это выглядит так:

var R : TRegIniFile; begin R:=TRegIniFile.Create; R.RootKey:=HKey_Local_Machine; If r.OpenKey('SOFTWARE',false) then r.WriteInteger(‘MyScreenSaver’,'Speed',tbSpeed.Position); {Записываем данные в ключ Speed ключа MyScreenSaver} r.Free;

В событии OnCreate основной формы нам предстоит считать эти настройки из реестра или ini-файла. Не забудьте: окно настроек нам тоже необходимо скрыть, убрать с панели задач. Теперь вернемся к пропущенным мною функциям CheckPassword и SetPassword. Если вы хотите добавить в хранитель проверку и установку пароля, просто перепишите эти функции. Первую пишем в модуль основной формы, а вторую — в модуль проекта. Вот они:

procedure CheckPassword; type TVSSP = function(Parent : hWnd):Bool;Stdcall; //тип API-функции проверки пароля var Key: hKey; D1,D2 : Integer; Value: Integer; Lib : THandle; VSSP : TVSSP; begin SystemParametersInfo(SPI_SCREENSAVERRUNNING,1,NIL,0); {отключаем нажатие Alt+Tab и Ctrl+Alt+Del} If RegOpenKeyEx(hKey_Current_User,'Control Panel\Desktop',0,Key_read,Key)=Error_Success then begin D2:=SizeOf(Value); If RegQueryValueEx(Key,'ScreenSaveUsePassword',nil,@D1,@Value,@D2) = Error_Success then begin {проверить, установлен ли пароль, можно также с помощью классов, работающих с реестром} If Value<>0 then begin Lib :=LoadLibrary('PASSWORD.CPL'); {загружаем модуль, содержащий функцию проверки пароля} If Lib > 32 then begin @VSSP:=GetProcAddress(Lib,'VerifyScreenSavePwd'); ShowCursor(True); If @VSSP<> nil then VSSP(frmMain.Handle); //Запускаем функцию FreeLibrary(Lib); end; end; end; RegCloseKey(Key); SystemParametersInfo(SPI_SCREENSAVERRUNNING,0,NIL,0); {включаем обработку нажатия Alt+Tab и Ctrl+Alt+Del} end; end;

Замечу, что использовать SystemParametersInfo(SPI_SCREENSAVERRUNNING,1,NIL,0) можно и в других целях, когда нужно заблокировать доступ к функциональным клавишам. Отключить это можно, указав второй параметр равным нулю.

procedure SetPassword; type TPCPA=function(A:PChar;Parent:hWnd;B,c:Integer):Integer;stdcall; //тип API-функции установки пароля var Lib : THandle; PCPA : TPCPA; begin Lib:=LoadLibrary('MPR.DLL'); If Lib>32 then begin @PCPA:=GetProcAddress(Lib,'PwdChangePasswordA'); If @PCPA<>nil then PCPA('SCRSAVE',StrToINt(ParamStr(2)),0,0); FreeLibrary(Lib); end;

Вот и все, что требуется для скринсэйвера. Остается только в файл проекта добавить директиву {$E scr}, чтобы он сразу же создавался с расширением .scr. А затем скопировать получившийся скринсэйвер в каталог Windows\System. Теперь он будет отображаться в списке заставок Windows и называться по имени scr-файла Как видите, код получился несложный, единственный его недостаток — размер. Все программы, написанные с использованием VCL, занимают довольно много места. Если вы будете писать скринсэйвер только на API, вы вполне можете достичь уменьшения его размера до 20-40 Кб, мой же хранитель занимает около 500 Кб. Но неужели вам жалко каких-нибудь 500 Кб :-)?