Progetto Screen Saver.

Descrizione:

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:

paperino.dpr                 contiene il corpo principale .
MainRoutine.inc            contiene le funzioni per la creazione e gestione della 
                                     finestra principale.
MessageHandler.inc      contiene le funzioni per la gestione dei messaggi
                                    della finestra principale.
GlobalInit.pas               contiene le funzioni per la inizializzazione del processo.
GlobalVar.pas              contiene tutte le variabili globali e le dichiarazioni di costanti.
MsgError.inc                contiene le costanti per i messaggi e gli errori.
dialogID.inc                  contiene le costanti per gli ID del dialogo.
Preview.pas                  contiene le funzioni per la gestione della finestra di preview
                                    del dialogo di personalizzazione dello schermo.
SSavRoutine.pas           contiene le funzioni per la gestione della inizializzazione, gestione
                                     e distruzione della superficie di disegno.
SSRes.rc                       file di risorse dove è descritto il dialogo e la risorsa di versione.
paperino.res                  file binario di risorse mantenuto automaticamente dall'ambiente
                                    di sviluppo Delphi. Altri file automatici di mantenimento del progetto
                                    gestiti dall'ambiente di sviluppo.
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:
CountFile.dpr                contiene tutto il corpo del programma e le funzioni necessarie.
Paperino.dpr, MainRoutine.inc, MessageHandler.inc.

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:

WM_KEYDOWN
WM_SYSKEYDOWN
WM_RBUTTONDOWN
WM_LBUTTONDOWN
WM_MBUTTONDOWN
WM_MOUSEMOVE
WM_TIMER
WM_PAINT
WM_DESTROY
I messaggi WM_KEYDOWN, WM_RBUTTONDOWN, WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_MOUSEMOVE gestiscono l'uscita dello screen saver. Al presentarsi degli eventi, i messaggi relativi al mouse sono rallentati con un test in modo da rendere possibile la partenza, visto che la procedura potrebbe rispondere ai cambiamenti di stato del mouse durante l'inizializzazione; gli stessi eventi possono essere anche disabilitati, e quindi impedire il rilascio del processo al loro verificarsi, settando la variabile globale RespToMouse a True. L'evento WM_SYSKEYDOWN è usato per evitare che lo screen saver venga lanciato più di una volta settando il parametro della struttura message cracker TWMSysCommand, cmdType a battito del timer che è stato SC_SCREENSAVE. WM_TIMER viene mandato ad ogni settato a 500 msec, ad ogni timer parte un messaggio WM_PAINT che forza il disegno della finestra.WM_DESTROY esegue PostQuitMessage(0) per la chiusura dell'applicazione.

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.
La funzione WinCreate costruisce la finestra principale con una chiamata alla API CreateWindowEx, il valore di ritorno è l'handle della finestra costruita:
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.
Quindi il ciclo di interpretazione dei messaggi:
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.
Lo screen saver supporta diversi tipo di esecuzione : un modo full screen, che è il modo normale; in modo configurazione, che visualizza solo un dialogo; un modo preview, che visualizza in piccolo lo screen saver nella finestra di preview del sistema; un modo password, dove si può settare la password per la risoluzione dello schermo. Il tutto è testato da un ciclo case of.

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.
La procedura CloseIfOk controlla se le condizioni di chiusura dello screen saver sono rispettate, in particolare, se è stata abilitata la password tramite ExecSetPwd, se la stessa è stata inserita correttamente.
 

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

This page hosted by  Get your own Free Home Page
1