Il progetto si propone la realizzazione di uno screen saver in grado di lanciare un processo esterno per la piattaforma Microsoft Win32 usando come linguaggio l'Object Pascal nella implementazione Borland versione 10, non sono state usate feature specifiche della versione, quindi è stato compilato con successo anche con il compilatore della versione 9. Si è scelto di non usare la libreria di classi che accompagna il compilatore e l'ambiente di sviluppo Delphi per mostrare alcune tecniche di programmazione tipiche del sistema operativo Windows, in particolare per quello che riguarda: gestione dei messaggi, creazione di risorse dialogo e loro gestione, oltre alla implementazione di finestre child di dialoghi di sistema. Il progetto è stato diviso in file per mantenere il più ragionevolmente possibile una struttura modulare con distinzione tra interfaccia e implementazione delle funzioni create.
La struttura è divisa come segue:
Per quello che riguarda il processo esterno da lanciare durante il funzionamento si è scelto di fornire lo Screen Saver di un dialogo di configurazione che permette di specificare il percorso dell'eseguibile e la sua linea di comando, per non legare l'utente ad un programma prefissato.Si è comunque elaborato, un piccolo eseguibile di tipo console per il test che semplicemente conta il numero dei file e delle directory della cartella passatagli come parametro alla linea di comando, salvando il risultato in un file di testo. La struttura è così composta:Questo è il file principale e contiene quello che serve per la creazione, gestione e distruzione della finestra principale. Si è cercato di dividere l'interfaccia delle funzioni dalla loro implemetazione facendo uso della direttiva OP forward per le dichiarazioni anticipate e ponendo la definizione in file di inclusione usando la direttiva del compilatore {$INCLUDE}. La scelta è stata fatta per mantenere coerenza di file e modularità in modo da avere dimensioni facilmente gestibili. La funzione ScreenSaverProc è il corpo che gestisce il ciclo dei messaggi, si è affrontato il problema dei messaggi tramite l'uso dei Message Cracker, strutture per la scomposizione dei messaggi nelle lo componenti, per poter avere una maggiore modularità. I messaggi che la procedura gestisce in maniera personalizzata sono:
La funzione WinRegister cerca di registrare al sistema operativo la classe della finestra definita dalla struttura WClass: TWndClass:
WClass.Style := CS_VREDRAW or CS_HREDRAW or CS_SAVEBITS or CS_DBLCLKS ; WClass.lpfnWndProc := @ScreenSaverProc; WClass.cbClsExtra := 0; WClass.cbWndExtra := 0; WClass.hInstance := HInstance; WClass.hIcon := 0; WClass.hCursor := 0; {lo screen saver non ha cursore} WClass.hbrBackground := GetStockObject(BLACK_BRUSH); {colore di background} WClass.lpszMenuName := nil; {lo screen saver non ha menu} WClass.lpszClassName := SSClass;Se la funzione fallisce la registrazione (ritorna False) il programma esce con un Dialog box con un messaggio di errore.
function WinCreate: HWnd; begin Result := CreateWindowEx(WS_EX_TOPMOST, SSClass, nil, WS_OVERLAPPED, 0, 0, ScreenDim.Right, ScreenDim.Bottom, 0, 0, HInstance, nil); end;La finestra è costruita con la flag estesa WS_EX_TOPMOST che pone la finestra sopra tutte le altre mentre lo stile WS_OVERLAPPED la crea con titolo e bordo, in particolare si è scelto di non creare la finestra inizialmente senza bordo a causa di alcuni possibili malfunzionamenti della stessa durante la propria inizializzazione, per questo il titolo e il bordo viene rimosso in un secondo tempo chiamando la funzione Windows SetWindowLong, che permette di cambiare gli attributi delle finestre dopo la loro costruzione, nella funzione WinConfigure ci si preoccupa anche di fare in modo che l'applicazione non risponda ai tasti di controllo alt+tab e ctrl+alt+canc per evitare che un possibile utente non resetti la macchina nell'impossibilità di evitare la password. Nasconde il cursore del mouse e spedisce il messaggio WM_SHOWMAXIMIZED che porta le dimensioni a tutto schermo, quindi si forza il ridisegno della finestra con UpdateWindow e risetta la finestra sopra tutte le altre non attivandola e ignorando le coordinate con SetWindowPos.
while GetMessage(AMessage, 0, 0, 0) do {Ciclo dei messaggi} begin TranslateMessage(AMessage); DispatchMessage(AMessage); end;All'uscita del ciclo La chiamata alla DestroyWindow elimina la finestra ed esce dal programma.
Nel file MainRoutine.inc risiede anche la routine
per la gestione dei messaggi del dialogo di configurazione, qui per la
piccola dimensione, si è scelto il modo tradizionale di descrizione
del flusso. I messaggi a cui la procedura risponde sono soltanto due :WM_INITDIALOG,
che definisce l'avvenuta creazione del dialogo e WM_COMMAND che
risponde agli eventi generati dai due pulsanti di OK e Cancel. La procedura
presenta al suo interno una procedura annidata chiamata GetText che carica
nei controlli edit del dialogo due stringhe.
Notabile nei gestori degli eventi è la
procedura SS_OnPaint che pilota il messaggio WM_PAINT, dove
viene creato un array di handle a thread, di tre elementi, che permette
ad una sola routine di piccole dimensioni che risiede nel file SSavRoutine.pas
chiamata Design di essere chiamata per tre volte come thread di
esecuzione separati. Il rilascio degli handle, per evitare la sovrappopolazione
è affidato alla API WaitForMultipleObject che controlla che
gli handle nell'array siano in stato segnalato, nel caso di handle a thread
che il thread sia finito, e quindi permette che gli handle siano rilasciati.
GlobalInit.pas
Nella unit risiedono le funzioni che occorrono
prima che lo screen saver venga eseguito, le funzioni prendono le dimensioni
dello schermo (GetScreenDim), controllano se rispondere o no agli
eventi mouse (RespondToMouse), verificano se il parametro passato
dal sistema operativo all'eseguibile sia un handle (SetParamHandle),
prendere gli altri parametri e così lanciare il file nel modo appropriato
(GetSaverMode), in ultimo se esiste il file di descrizione del processo
da lanciare alla partenza (ReadProcessInfo). La unit fa uso della
parola chiave initialization, che permette di descrivere le azioni che
devono essere fatte quando la unit viene chiamata, per questo motivo nella
direttiva uses del file principale (paperino.dpr) deve essere dichiarata
in testa a tutte le altre.
GlobalVar.pas
Segue la lista delle variabili globali usate:
Dichiarazione Costanti
{$INCLUDE MsgError.inc} {$INCLUDE dialogID.inc} ID del dialogo di configurazione. SSClass = 'SSClass'; SSPrevClass = 'SSPreviewClass'; NamePreview = 'FunSaver'; nome della finestra di preview. MouseEvIgnore = 2; indica quanti eventi dl mouse ignorare prima di chiudere. ModeConfExpl = 1; right-click nell' explorer per configurare. ModeConf = 2; configura dalle proprietà dello schermo. ModeExec = 3; gira a tutto schermo. ModePrev = 4; gira nella finestra di preview. ModeSetPwd = 5; mostra il dialogo della password. SSFileDat = 'Paperino.dat'; nome del file di configurazione.Dichiarazione Tipi
HThread =THandle; TProcAddrHandle =function (Parent : THandle) : Boolean; stdcall; TProcAddrHandleEx =function (a : PChar; ParentHandle : THandle; b, c : Integer) :Integer; stdcall;Dichiarazione Variabili
StartTime :longint; Carica il tempo iniziale in msec. HSSWnd :HWND; Handle Globale dello ScreenSaver. HSSDc :HDc; Handle del Device Context. AMessage :TMsg; ParamHandle :THandle; L' Handle alla linea di comando come parametro. TimeOut :integer=500; Intervallo di TimeOut in msec. IgnoreMouse :integer; Numero degli eventiMouse da ignorare. SvdScrState :wordbool; Flag del cursore. timerID :integer; ID del timer dello ScreenSaver. ScreenDim :TRect; Contiene le dimensioni dello schermo. RespToMouse :boolean; Flag per il controllo del mouse. SaverMode :Integer; Modo di partenza dello SSaver in relazione alle costanti sopra dichiarate. ConfigParent :hWnd; La finestra genitrice del dialogo di configurazione. PaintDc :HDC; Handle del Device Context. PS :TPaintStruct; Struttura TPaintStruct. ThreadDesignID :DWORD; ID del thread per disegnare. HThreadDesign :HThread; Handle del thread per disegnare. StrProg :string; Processo da lanciare. StrCmd :string; Parametri della linea di comando del Processo. StrSys :string; NoProcessInfo :boolean; Flag per controllare se il file di Info esiste. Tick :integer=0; Tempo per il lancio del programma. SAtom : ATOM; Atomo della finestra preview. AtomName :PChar; Nome dell'atomo della finestra preview.Preview.pas
La unit implementa le routine che occorrono per
il funzionamento della finestra di preview standard di Windows 95/NT 4.0.
La finestra di configurazione delle proprietà
dello schermo di Windows, passa all'applicazione, durante il caricamento,
l'handle del controllo che dovrà contenere la finestra di preview,
il problema è quindi quello di costruire una finestra figlia di
tale controllo e iniziare a disegnare su tale superficie:
HPrevWin := CreateWindow(SSPrevClass, NamePreview, WS_CHILD or WS_DISABLED or WS_VISIBLE, 0, 0, Width, Dimensioni della finestra Height, ParamHandle, Handle del controllo padre. 0, hInstance, nil);Per il disegno si è scelto di utilizzare la classe predefinita della VCL Delphi, TCanvas, assegnandole l'handle del Device Context della nostra finestra dopo averlo ricavato con la funzione:
DC:=GetWindowDc (HPrevWin). PreviewCanvas.Handle := DC;Una particolarità risiede nel fatto che il dialogo tende a scaricare e ricaricare l'applicazione ogni volta che perde e riprende il focus, è quindi molto importante porre attenzione al ciclo dei messaggi e chiudere lo screen saver preview con il messaggio: WM_DESTROY che esegue PostMessage (HPrevWin, WM_QUIT, 0, 0); altrimenti si potrebbe perdere riferimento alla memoria allocata con conseguente bloccaggio del programma.
SSavRoutine.pas
Qui è descritto il codice della gestione
della della parte grafica dell'applicazione.
Il codice è suddiviso in varie routine
che si occupano della inizializzazione, disegno, e fine delle procedure
necessarie per il suo funzionamento.
La procedura SSInit(TimeOut:integer),
inizializza il generatore dei numeri casuali, fa partire il timer, costruisce
l'oggetto TCanvas che si occuperà di disegnare sullo schermo.
La procedura Design(Space:TCanvas; RandX,RandY:integer;
NumPix,Jump:byte), si occupa del disegno vero e proprio, accendendo e spengendo
pixel con colori casuali in punti casuali dello schermo, formando quattro
linee che partendo da un punto comune si espandono e si contraggono in
base al parametro NumPix mentre Jump indica la distanza tra un punto e
l'altro.
Questa procedura è incapsulata nella funzione
ThreadDesign(P:Pointer):longint;stdcall, che è la funzione
Thread chiamata nel file principale dal messaggio WM_PAINT, a sua
volta scatenato dai battiti del timer:
function ThreadDesign(P:Pointer):longint;stdcall; begin Design(Tela,800,600,25,4); Result:=GetLastError; ritorna l'ultimo errore presentatosi end;I compiti di pulizia delle risorse allocate è devoluto alla procedura SSEnd.
Ssres.rc
Il file di risorse è stato costruito visualmente
con il Borland Resource WorkShop, e quindi salvato come script.
Nota:
Descrizioni più dettagliate delle singole
parti e delle scelte fatte sono presenti come commenti all'interno dei
file di progetto.
Appendice 1
esempio di file di configurazione dello screen saver:
file Paperino.dat: Processo=C:\Paperino\bin\Prog\3\CountFile.exe CmdLine=C:\W95
Appendice 2
Esempio di file di report del programma lanciato:
file CountFile.txt: File di report del programma CountFile, seguono tutti i nomi dei file trovati File n° 1 -> zdlib.dll File n° 2 -> TUTIL32.DLL File n° 3 -> DriveSpace.ocx File n° 4 -> VBRUN300.DLL File n° 5 -> ZIPDLL.DLL File n° 6 -> UNZDLL.DLL Directory n°1 -> cartella File Trovati n° 6 Directory Trovate n° 1