Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :

Nono40.developpez.com
Le petit coin du web de Nono40
SOURCES TESTS DELPHI WIN32 AUTOMATISMES DELPHI .NET QUICK-REPORT
Retour à l'accueil
18 - SURVEILLANCE MULTIPLE DE REPERTOIRES

PRÉSENTATION : Programme d'exemple de l'utilisation des notifications de modification de Windows.
ZIP : Téléchargez le zip APERÇUS :

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

CODE :
Unit Unit1;

Interface

Uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

Type
  TForm1 = Class(TForm)
    lbMessages: TMemo;
    Panel1: TPanel;
    Label1: TLabel;
    Edit1: TEdit;
    Button1: TButton;
    lbOrdres: TListBox;
    Button2: TButton;
    Procedure FormCreate(Sender: TObject);
    Procedure FormClose(Sender: TObject; Var Action: TCloseAction);
    Procedure Button1Click(Sender: TObject);
    Procedure Button2Click(Sender: TObject);
  Private
    { Déclarations privées }
  Public
    { Déclarations publiques }
  End;

  TThreadSurveillance = Class(TThread)
  Private
    { Déclarations privées }
    ListeOrdres            : TList;
    ListeAjouts            : TStrings;
    OrdreEnCours           : String;
    Procedure MiseAJourListeMessages;
    Procedure MiseAJourListeOrdres;
    Function  Recherche(Chemin:String):Integer;
  Protected
    Procedure Execute; override;
  End;

Var
  Form1: TForm1;

Implementation

{$R *.dfm}

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 Do For 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
  While Not Terminated Do
  Begin
    // On à reçu un ordre de changement dans la liste
    If (OrdreAFAire<>OrdreEnCours)And(OrdreAFaire<>'')
    Then Begin
      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[1Of
        '+':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<>'')
              Then Begin
                // 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
              Then Begin
                With PSurveille(ListeOrdres.Items[Position])^ Do
                Begin
                  // Petit message
                  ListeAjouts.Add('<FIN:'+sChemin+'>');
                  // Libération du handle
                  If sHandle<>INVALID_HANDLE_VALUE
                  Then Begin
                    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 Do For 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
    Then Begin
      // 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)
      Then Begin
        // 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
          Then Begin
            Repeat
              // Le fichier à été modifié, on l'ajoute à la liste
              If FileDateToDateTime(Infos.Time)>sDateHeure
              Then Begin
                // 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 Do For i:=0 To Count-1 Do With PSurveille(Items[i])^ Do
  Begin
    // Libération Handle
    If sHandle<>INVALID_HANDLE_VALUE Then FindCloseChangeNotification(sHandle);
    // Libération
    If Items[i]<>Nil Then 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 Do For 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
  Then Begin
    ThreadSurveillance.Terminate;
    ThreadSurveillance.WaitFor;
  End;
End;

Procedure TForm1.Button1Click(Sender: TObject);
Begin
  OrdreAFAire:='+'+Edit1.Text;
End;

Procedure TForm1.Button2Click(Sender: TObject);
Begin
  With lbOrdres Do If ItemIndex>=0
    Then OrdreAFaire:='-'+Items[ItemIndex]
    Else ShowMessage('Vous devez sélectionner un chemin dans la liste !');
End;

End.

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2003 Bruno Guérangé. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Responsables bénévoles de la rubrique Delphi : Gilles Vasseur - Alcatîz -