NOTES :
Ce programme montre l'utilisation des notifications de modification de répertoire. C'est utile pour suivre les modifications apportées aux fichiers dans un répertoire.
Le principe est le même que celui de la surveillance d'un répertoire, mais appliqué plusieurs fois... La liste des demandes est stockée dans un TList.
Ce programme est donc un exemple de mise en oeuvre des points suivants :
- Thread
- Notification de changement
- Gestion d'un TList
Type
PSurveille = ^TSurveille;
TSurveille = Record
sChemin : String[255];
sHandle : Cardinal;
sDateHeure : TDateTime; End;
Var
OrdreAFAire : String='';
ThreadSurveillance : TThreadSurveillance = Nil;
Function TThreadSurveillance.Recherche(Chemin:String):Integer; Var i:Integer; Begin
Result:=-1; With ListeOrdres DoFor i:=0 To Count-1 Do If (Items[i]<>Nil)And(PSurveille(Items[i]).sChemin=Chemin) Then Result:=i; End;
Procedure TThreadSurveillance.Execute; Var CheminAZT :Array[0..255]Of Char;
Infos :TSearchRec;
Position :Integer;
Chemin :String;
Ajout :PSurveille;
Handles :TWOHandleArray;
i :Integer;
CodeRetour :Integer; Begin // Initialisation des données du Thread
ListeOrdres := TList.Create;
ListeAjouts := TStringList.Create;
OrdreEnCours := '';
// Boucle pincipale du Thread WhileNot Terminated Do Begin // On à reçu un ordre de changement dans la liste If (OrdreAFAire<>OrdreEnCours)And(OrdreAFaire<>'') ThenBegin
OrdreEnCours:=OrdreAFaire;
ListeAjouts.Clear;
// Recherche si le chemin est déjà demandé
Chemin := Copy(OrdreEnCours,2,Length(OrdreEnCours)-1); // Suppression du '\' final
Chemin := ExcludeTrailingPathDelimiter(Chemin);
Position := Recherche(Chemin);
// Suivant le premier caractère de OrdreEnCours, il s'agit d'un ajout ou d'une suppression Case OrdreAFAire[1] Of
'+':Begin// Ajout d'un chemin // Le chemin est ajouté seulement s'il n'est pas déjà dans la liste If (Position=-1)And(Chemin<>'') ThenBegin // Création du nouvel ordre
New(Ajout);
Ajout.sChemin :=Chemin;
Ajout.sHAndle :=INVALID_HANDLE_VALUE;
Ajout.sDateHeure :=Now;
ListeOrdres.Add(Ajout); // Création de la demande de notification
Ajout.sHandle := FindFirstChangeNotification(
StrPCopy(CheminAZT,Chemin), // Chemin à surveiler
False, // Ne pas surveiller les sous-répertoires
File_NOTIFY_CHANGE_File_NAME //
+File_NOTIFY_CHANGE_LAST_WRITE); // Surveiller les écriture et changement de noms // Petit message
ListeAjouts.Add('<DEBUT:'+Chemin+'>'); End; End;
'-':Begin// Supression d'un chemin // Bien sur, seul un élément existant est supprimé ! If Position<>-1 ThenBegin With PSurveille(ListeOrdres.Items[Position])^ Do Begin // Petit message
ListeAjouts.Add('<FIN:'+sChemin+'>'); // Libération du handle If sHandle<>INVALID_HANDLE_VALUE ThenBegin
FindCloseChangeNotification(sHandle);
sHandle:=INVALID_HANDLE_VALUE; End; // Libération de la mémoire
Dispose(ListeOrdres.Items[Position]); // Supression de l'ordre
ListeOrdres.Delete(Position); End; End; End; End;
// Mise à jour de la fenêtre
Synchronize(MiseAJourListeMessages);
Synchronize(MiseAJourListeOrdres);
// Préparation de la liste des Handles With ListeOrdres DoFor i:=0 To Count-1 Do Handles[i]:=PSurveille(ListeOrdres.Items[i]).sHandle; End;
// Une demande de notification est en cours ( <=> la liste n'est pas vide ) If ListeOrdres.Count<>0 ThenBegin // Il faut donc demander à Windows d'être prévenu en cas de modification // La sortie de WaitForMultipleObjects est effectuée dans le cas d'une notification // ou dans le cas d'un TimeOut. Il ne faut pas ici utiliser le timeout INFINITE // sinon le thread risque d'être bloqué en permanence.
CodeRetour:=WaitForMultipleObjects(ListeOrdres.Count,@Handles,False,200); If (CodeRetour>=WAIT_Object_0)And(CodeRetour<WAIT_Object_0+ListeOrdres.Count) ThenBegin // Dans le cas d'une notification il faut rechercher // les fichiers modifiés depuis le dernier appel With PSurveille(ListeOrdres.Items[CodeRetour-WAIT_Object_0])^ Do Begin
ListeAjouts.Clear; If FindFirst(sChemin+PathDelim+'*.*',faAnyFile,Infos)=0 ThenBegin Repeat // Le fichier à été modifié, on l'ajoute à la liste If FileDateToDateTime(Infos.Time)>sDateHeure ThenBegin // FindData.CFileName n'est utilisable que sous Windows
ListeAjouts.Add(FormatDateTime('DD/MM/YYYY HH:NN:SS',
FileDateToDateTime(Infos.Time))+'='+StrPas(Infos.FindData.cFileName)); End; Until FindNext(Infos)<>0;
FindClose(Infos);
// Ajout des fichiers modifiés dans le Memo // Comme c'est un Thread, il n'est pas possible de modifier directement Form1.lbMessages
Synchronize(MiseAJourListeMessages); End;
// Mémorisation de l'heure en cours pour le prochain test
sDateHeure:=Now;
// Le handle doit être mis à jour pour pouvoir effectuer une nouvelle demande // Seul le handle testé doit être mis à jour par FindNextChangeNotification, Car // en cas de modifications multiple simultannée ceci permet de voir toutes les // modifications.
FindNextChangeNotification(sHandle); End; End; End; End;
// Libération des objets
ListeAjouts.Free; With ListeOrdres DoFor i:=0 To Count-1 DoWith PSurveille(Items[i])^ Do Begin // Libération Handle If sHandle<>INVALID_HANDLE_VALUE Then FindCloseChangeNotification(sHandle); // Libération If Items[i]<>NilThen Dispose(Items[i]); // Suppression de la référence
ListeOrdres.Items[i]:=Nil; End;
ListeOrdres.Free; End;
// Cette procédure ne doit être appelée que par l'intermédiaire de Synchronize Procedure TThreadSurveillance.MiseAJOurListeMessages; Begin
Form1.lbMessages.Lines.AddStrings(ListeAjouts); End;
// Cette procédure ne doit être appelée que par l'intermédiaire de Synchronize Procedure TThreadSurveillance.MiseAJOurListeOrdres; Var i:Integer; Begin
Form1.lbOrdres.Items.Clear; With ListeOrdres DoFor i:=0 To Count-1 Do Form1.lbOrdres.Items.Add(PSurveille(Items[i]).sChemin); End;
// Création du Thread Procedure TForm1.FormCreate(Sender: TObject); Begin
ThreadSurveillance:=TThreadSurveillance.Create(False); End;
// Libération du Thread Procedure TForm1.FormClose(Sender: TObject; Var Action: TCloseAction); Begin If ThreadSurveillance<>Nil ThenBegin
ThreadSurveillance.Terminate;
ThreadSurveillance.WaitFor; End; End;
Procedure TForm1.Button1Click(Sender: TObject); Begin
OrdreAFAire:='+'+Edit1.Text; End;
Procedure TForm1.Button2Click(Sender: TObject); Begin With lbOrdres DoIf ItemIndex>=0 Then OrdreAFaire:='-'+Items[ItemIndex] Else ShowMessage('Vous devez sélectionner un chemin dans la liste !'); End;