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
45 - SURVEILLANCE D'UN RÉPERTOIRE AVEC READDIRECTORYCHANGESW

PRÉSENTATION : Programme d'exemple de l'utilisation de la fonction ReadDirectoryChangesW.
ZIP : Téléchargez le zip APERÇUS :

NOTES : Ce programme montre l'utilisation de la fonction de sureveillance d'un répertoire de Windows NT.
C'est utile pour suivre les modifications apportées aux fichiers dans un répertoire.
Par exemple si vous affichez la liste de certains fichiers dans une ListBox ou un TreeView, il est possible de mettre la liste à jour seulement quand un fichier est ajouté et/ou supprimé. La surveillance est effectuée dans un autre Thread afin de ne pas bloquer l'application principale.

Attention, ce programme ne fonctionne que sous Windows NT4, Windows 2000 et Windows XP.

CODE :
Unit Unit1;
//
// Sujet : Surveillance des modification dans un répertoire
//          Avec la fonction API  ReadDirectoryChangesW
//
// Par Nono40 : http://nono40.developpez.com   http://nono40.fr.st
//              mailTo:nono40@fr.st
//
// Le 15/03/2003
//
// Attention : ne fonctionne qu'avec Windows NT4, Windows2000 et WindowsXP
//
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 }
    HandleDirectory        : THandle;
    OverLapped             : TOverlapped;
    ListeAjouts            : TStrings;
    CheminEnCours          : String;
    Procedure MiseAJourListe;
  Protected
    Procedure Execute; override;
  End;

Var
  Form1: TForm1;

Implementation

{$R *.dfm}

Var CheminASurveiller     : String              = '';
    ThreadSurveillance    : TThreadSurveillance = Nil;
Const FILE_LIST_DIRECTORY=$0001;

Procedure TThreadSurveillance.Execute;
Var Buffer   : Array[0..9999]Of Byte;
    Retour   : DWord;
    i        : DWord;
    Suivant  : DWord;
    Action   : DWord;
    Long     : DWord;
    Chaine   : WideString;
    Fin      : Boolean;
Begin
  // Initialisation ds données du Thread
  ListeAjouts           := TStringList.Create;
  // La structure OverLapped permet à Windows de garder une trace
  // de la fonctione en cours. hEvent permet de tester si la fonction
  // est finie ou non
  FillChar(Overlapped,SizeOf(OverLapped),#0);
  OverLapped.hEvent:=CreateEvent(Nil,False,False,Nil);

  HandleDirectory    := INVALID_HANDLE_VALUE;
  CheminEnCours      := '';

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

      // Une demande est en cours, on libère le Handle
      If HandleDirectory<>INVALID_HANDLE_VALUE
      Then Begin
        CloseHandle(HandleDirectory);
        HandleDirectory:=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 du Handle sur le répertoire à surveiller
        HandleDirectory:=CreateFile (
          PChar(CheminEnCours),       // Chemin a surveiller
          FILE_LIST_DIRECTORY,        // C'est un chemin qu'il faut ouvrir
          FILE_SHARE_READ   Or        // Mode de partage, l'exemple donné dans le SDK est
          FILE_SHARE_DELETE Or        // faux, du moins avec Xp car il manque le partage
          FILE_SHARE_WRITE,           // en écriture
          Nil,                        // sécurité par défaut
          OPEN_EXISTING,              // Le chemin doit exister
          FILE_FLAG_BACKUP_SEMANTICS  // Attribut de surcharge des droits liés au dossier
            Or FILE_FLAG_OVERLAPPED,  // Attribut de fonction effectuée après coup
          0);                         // Pas d'attributs à copier

        ListeAjouts.Add('<DEBUT:'+CheminEnCours+'>');
      End;

      Synchronize(MiseAJourListe);
    End;

    // Une demande de notification est en cours
    If HandleDirectory<>INVALID_HANDLE_VALUE
    Then Begin
      // Appel de la fonction de test des changements dans le répertoire
      If ReadDirectoryChangesW(
          HandleDirectory,                  // Handle du chemin à surveiller
          @Buffer,                          // Adresse du buffer qui contiendra la
                                            // description des modifications
          SizeOf(Buffer),                   // Taille du buffer
          True,                             // On surveille aussi les sous-répertoires
             FILE_NOTIFY_CHANGE_FILE_NAME   // Tous les changement de nom et modification
          Or FILE_NOTIFY_CHANGE_DIR_NAME    // sont signalés
          Or FILE_NOTIFY_CHANGE_LAST_WRITE, //
          @Retour,                          // Adresse de la longueur retournée dans buffer
          @OverLapped,                      // Structure de réponse différée
          Nil)                              // Pas de fonction de rappel
      Then Begin
        // Maintenant on attend une modification
        Repeat
          Fin:=GetOverLappedResult(HandleDirectory,OverLapped,Retour,False);
        Until  Fin
            Or(GetLastError<>ERROR_IO_INCOMPLETE)
            Or Terminated
            Or(CheminASurveiller<>CheminEnCours);
        // Si la fonction a fini correctement
        // et que l'on ne doit pas terminer le thread
        If Fin And Not Terminated And (CheminASurveiller=CheminEnCours) Then
        Begin
          // il faut alors décoder le buffer reçu
          ListeAjouts.Clear;
          i:=0;
          Repeat
            // Le premier DWord contient le décalage vers l'information suivante
            Move(Buffer[i  ],Suivant,4);
            // Le deuxième DWord contient le type de changement détecté
            Move(Buffer[i+4],Action ,4);
            // Le troisième DWord contient la longueur en octets du nom concerné
            Move(Buffer[i+8],Long   ,4);
            // Le nom du fichier suit
            SetLength(Chaine,Long Div 2);
            Move(Buffer[i+12],Chaine[1],Long);
            If Action=FILE_ACTION_ADDED            Then ListeAjouts.Add('FILE_ACTION_ADDED');
            If Action=FILE_ACTION_REMOVED          Then ListeAjouts.Add('FILE_ACTION_REMOVED');
            If Action=FILE_ACTION_MODIFIED         Then ListeAjouts.Add('FILE_ACTION_MODIFIED');
            If Action=FILE_ACTION_RENAMED_OLD_NAME Then ListeAjouts.Add('FILE_ACTION_RENAMED_OLD_NAME');
            If Action=FILE_ACTION_RENAMED_NEW_NAME Then ListeAjouts.Add('FILE_ACTION_RENAMED_NEW_NAME');
            ListeAjouts.Add(CheminEnCours+'\'+Chaine);
            // On passe à la donnée suivante
            i:=i+Suivant;
          Until (i>=Retour)Or(Suivant=0);
          // 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;
      End;
    End;
  End;

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

  // Libération des objets
  ListeAjouts.Free;
  CloseHandle(OverLapped.hEvent);
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.

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 -