Surveillance des modifications de fichiers

Description

C'est en réponse à une demande du forum.

Ce programme permet d'utiliser les notifications de Windows en cas de modification de fichier ou d'un répertoire. Il est basé sur les fonctions Wait de l'API. Ces fonctions étant bloquante pour le Thread qui les appellent, alors un Thread auxiliaire est créé pour gérer les attentes.

Utilisation : Saisir le répertoire dans le TEdit et cliquer sur le bouton 'Lancer !'

Source / Exemple :


unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Label1: TLabel;
    Edit1: TEdit;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

  TThreadSurveillance = class(TThread)
  private
    { Déclarations privées }
    ListeAjouts            : TStrings;
    HandleNotification     : THandle;
    CheminEnCours          : String;
    DateHeureVerification  : TDateTime;
    Procedure MiseAJourListe;
  protected
    procedure Execute; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

Var CheminASurveiller     : String              = '';
    ThreadSurveillance    : TThreadSurveillance = Nil;

procedure TThreadSurveillance.Execute;
Var Chemin  :Array[0..255]Of Char;
    Infos   :TSearchRec;
begin
  // Initialisation ds données du Thread
  ListeAjouts           := TStringList.Create;
  HandleNotification    := INVALID_HANDLE_VALUE;
  CheminEnCours         := '';

  // Boucle pincipale du Thread
  While Not Terminated Do
  Begin
    // Le chemin demandé à changé => Changer la surveillance
    If CheminASurveiller<>CheminEnCours
    Then Begin
      ListeAjouts.Clear;

      // Une demande est en cours, on libère le Handle
      If HandleNotification<>INVALID_HANDLE_VALUE
      Then Begin
        FindCloseChangeNotification(HandleNotification);
        HandleNotification:=INVALID_HANDLE_VALUE;
        ListeAjouts.Add('<FIN>');
      End;

      // Prise en compte de la nouvelle demande
      CheminEnCours:=CheminASurveiller;
      If CheminEnCours<>''
      Then Begin
        // Suppression du '\' final
        CheminEnCours := ExcludeTrailingPathDelimiter(CheminEnCours);
        // Création de la demande de notification
        HandleNotification := FindFirstChangeNotification(
          StrPCopy(Chemin,CheminEnCours),                              // 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
        // Mémorisation de l'heure de la demande
        DateHeureVerification:=Now;
        ListeAjouts.Add('<DEBUT:'+CheminEnCours+'>');
      End;

      Synchronize(MiseAJourListe);
    End;

    // Une demande de notification est en cours
    If HandleNotification<>INVALID_HANDLE_VALUE
    Then Begin
      // Il faut donc demander à Windows d'être prévenu en cas de modification
      // La sortie de WaitForSingleObject 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.
      Case WaitForSingleObject(HandleNotification,200) Of
        WAIT_OBJECT_0:Begin
            // Dans le cas d'une notification il faut rechercher
            // les fichiers modifiés depuis le dernier appel
            ListeAjouts.Clear;
            If FindFirst(CheminEnCours+PathDelim+'*.*',faAnyFile,Infos)=0
            Then Begin
              Repeat
                // Le fichier à été modifié, on l'ajoute à la liste
                If FileDateToDateTime(Infos.Time)>DateHeureVerification
                Then Begin
                  {$IFDEF MSWINDOWS}
                  // FindData.CFileName n'est utilisable que sous Windows
                  ListeAjouts.Add(FormatDateTime('DD/MM/YYYY HH:NN:SS',FileDateToDateTime(Infos.Time))+'='+
                                  StrPas(Infos.FindData.cFileName));
                  {$ENDIF}
                  {$IFDEF LINUX}
                  // Pour Linux le nom court est mémorisé
                  ListeAjouts.Add(FormatDateTime('DD/MM/YYYY HH:NN:SS',FileDateToDateTime(Infos.Time))+'='+
                                  StrPas(Infos.Name));
                  {$ENDIF}
                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 Memo1
              Synchronize(MiseAJourListe);
            End;
            // Mémorisation de l'heure en cours pour le prochain test
            DateHeureVerification:=Now;
          End;
        WAIT_TIMEOUT :;
      End;

      // Le handle doit être mis à jour pour pouvoir effectuer une nouvelle demande
      FindNextChangeNotification(HandleNotification);
    End;
  End;

  // Libération du Handle en cas de besoin
  If HandleNotification<>INVALID_HANDLE_VALUE Then FindCloseChangeNotification(HandleNotification);
  HandleNotification:=INVALID_HANDLE_VALUE;

  // Libération des objets
  ListeAjouts.Free;
end;

// Cette procédure ne doit être appelée que par l'intermédiaire de Synchronize
procedure TThreadSurveillance.MiseAJOurListe;
Begin
  Form1.Memo1.Lines.AddStrings(ListeAjouts);
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
  CheminASurveiller:=Edit1.Text;
end;

end.

Conclusion :


A la compilation il y a un avertissement 'Le symbole FindData est propre à une plateforme', mais ce message peut être ignoré.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.