2010-07-22 6 views
3

Je dois déterminer quels dossiers contiennent des fichiers qui ont été modifiés «récemment» (dans un certain intervalle). Je remarque que les datestamps de dossiers semblent être mis à jour chaque fois qu'un fichier contenu est modifié, mais ce comportement ne se propage pas dans l'arborescence, c'est-à-dire que l'horodatage du dossier contenant le dossier contenant le fichier modifié n'est pas mis à jour.Déterminer rapidement si un contenu de dossier a été modifié

Je peux travailler avec ce comportement, mais je soupçonne que cela dépend de la plateforme/du système de fichiers/du réseau ou du lecteur local, etc. J'aimerais toujours en profiter là où je peux, donc j'ai besoin d'une fonction booléenne pour retourner true si la plate-forme/le disque exécutant mon application prend en charge ce comportement.

Je suis plutôt content de me repasser à travers l'arbre. Ce que je veux éviter est d'avoir à faire un FindFirst/FindNext pour chaque fichier dans chaque dossier pour voir si certains ont été modifiés dans (disons) le dernier jour - si je peux éviter de le faire pour les dossiers qui n'ont pas leurs dates modifiées au cours de la dernière journée, il permettra d'économiser beaucoup de temps.

Répondre

2

Les solutions qui ont été affichées à ce jour sont sur le point d'obtenir notifications au fur et à mesure qu'elles se produisent, et elles fonctionneront bien à cette fin. Si vous voulez regarder dans le passé et voir quand quelque chose a été modifié pour la dernière fois, plutôt que de le surveiller en temps réel, cela devient plus délicat. Je pense qu'il n'y a aucun moyen de le faire, sauf en effectuant une recherche récursive dans l'arborescence des dossiers et en vérifiant les datestamps.

EDIT: En réponse au commentaire de l'OP, oui, il ne semble pas possible de configurer FindFirst/FindNext uniquement pour les répertoires de hit et non les fichiers. Mais vous pouvez ignorer la vérification des dates sur les fichiers avec ce filtre: (SearchRec.Attr and SysUtils.faDirectory <> 0). Cela devrait accélérer un peu les choses. Ne vérifiez pas les dates sur les fichiers du tout. Cependant, vous aurez probablement encore à parcourir tout, puisque l'API Windows ne fournit aucun moyen (que je connaisse) de ne rechercher que des dossiers et non des fichiers.

+0

Je suis très heureux de recurse à travers l'arbre.Ce que je veux éviter est d'avoir à faire un FindFirst/FindNext pour chaque fichier dans chaque dossier pour voir si certains ont été modifiés dans (disons) le dernier jour - si je peux éviter de le faire pour les dossiers qui n'ont pas leurs dates modifiées au cours de la dernière journée, il permettra d'économiser beaucoup de temps. – rossmcm

+0

@ user89691: Modifié. –

+0

@ user8961, ne vous inquiétez pas de vérifier l'heure/taille/attributs sur les fichiers, le processeur ne va pas être étourdi de toute cette récursivité. Sérieusement, une fois que les informations sur la structure d'un dossier donné sont déjà en mémoire, la pénalité de temps est insignifiante (la pénalité d'E/S a déjà été payée - et c'est là que se trouve le goulot d'étranglement). En outre, des choses peuvent se produire dans le répertoire que la date et l'heure seules ne peuvent pas dire. Exemple: Il est possible de copier un nouveau fichier dans le répertoire (les fichiers copiés conservent les temps de création et de modification), et cette opération peut ne pas modifier l'heure du mod sur le répertoire. –

2

J'ai écrit un code à cet effet pour l'un de mes projets. Cela utilise les fonctions API FindFirstChangeNotification et FindNextChangeNotification. Voici le code (j'ai supprimé certaines parties spécifiques du projet):

/// <author> Ali Keshavarz </author> 
/// <date> 2010/07/23 </date> 

unit uFolderWatcherThread; 

interface 

uses 
    SysUtils, Windows, Classes, Generics.Collections; 

type 
    TOnThreadFolderChange = procedure(Sender: TObject; PrevModificationTime, CurrModificationTime: TDateTime) of object; 
    TOnThreadError = procedure(Sender: TObject; const Msg: string; IsFatal: Boolean) of object; 

    TFolderWatcherThread = class(TThread) 
    private 
    class var TerminationEvent : THandle; 
    private 
    FPath : string; 
    FPrevModificationTime : TDateTime; 
    FLatestModification : TDateTime; 
    FOnFolderChange : TOnThreadFolderChange; 
    FOnError : TOnThreadError; 
    procedure DoOnFolderChange; 
    procedure DoOnError(const ErrorMsg: string; IsFatal: Boolean); 
    procedure HandleException(E: Exception); 
    protected 
    procedure Execute; override; 

    public 
    constructor Create(const FolderPath: string; 
         OnFolderChangeHandler: TOnThreadFolderChange; 
         OnErrorHandler: TOnThreadError); 
    destructor Destroy; override; 
    class procedure PulseTerminationEvent; 
    property Path: string read FPath; 
    property OnFolderChange: TOnThreadFolderChange read FOnFolderChange write FOnFolderChange; 
    property OnError: TOnThreadError read FOnError write FOnError; 
    end; 

    /// <summary> 
    /// Provides a list container for TFolderWatcherThread instances. 
    /// TFolderWatcherThreadList can own the objects, and terminate removed items 
    /// automatically. It also uses TFolderWatcherThread.TerminationEvent to unblock 
    /// waiting items if the thread is terminated but blocked by waiting on the 
    /// folder changes. 
    /// </summary> 
    TFolderWatcherThreadList = class(TObjectList<TFolderWatcherThread>) 
    protected 
    procedure Notify(const Value: TFolderWatcherThread; Action: TCollectionNotification); override; 
    end; 

implementation 

{ TFolderWatcherThread } 

constructor TFolderWatcherThread.Create(const FolderPath: string; 
    OnFolderChangeHandler: TOnThreadFolderChange; OnErrorHandler: TOnThreadError); 
begin 
    inherited Create(True); 
    FPath := FolderPath; 
    FOnFolderChange := OnFolderChangeHandler; 
    Start; 
end; 

destructor TFolderWatcherThread.Destroy; 
begin 
    inherited; 
end; 

procedure TFolderWatcherThread.DoOnFolderChange; 
begin 
    Queue(procedure 
     begin 
      if Assigned(FOnFolderChange) then 
      FOnFolderChange(Self, FPrevModificationTime, FLatestModification); 
     end); 
end; 

procedure TFolderWatcherThread.DoOnError(const ErrorMsg: string; IsFatal: Boolean); 
begin 
    Synchronize(procedure 
       begin 
       if Assigned(Self.FOnError) then 
        FOnError(Self,ErrorMsg,IsFatal); 
       end); 
end; 

procedure TFolderWatcherThread.Execute; 
var 
    NotifierFielter : Cardinal; 
    WaitResult : Cardinal; 
    WaitHandles : array[0..1] of THandle; 
begin 
try 
    NotifierFielter := FILE_NOTIFY_CHANGE_DIR_NAME + 
         FILE_NOTIFY_CHANGE_LAST_WRITE + 
         FILE_NOTIFY_CHANGE_FILE_NAME + 
         FILE_NOTIFY_CHANGE_ATTRIBUTES + 
         FILE_NOTIFY_CHANGE_SIZE; 
    WaitHandles[0] := FindFirstChangeNotification(PChar(FPath),True,NotifierFielter); 
    if WaitHandles[0] = INVALID_HANDLE_VALUE then 
     RaiseLastOSError; 
    try 
     WaitHandles[1] := TerminationEvent; 
     while not Terminated do 
     begin 
     //If owner list has created an event, then wait for both handles; 
     //otherwise, just wait for change notification handle. 
     if WaitHandles[1] > 0 then 
     //Wait for change notification in the folder, and event signaled by 
     //TWatcherThreads (owner list). 
      WaitResult := WaitForMultipleObjects(2,@WaitHandles,False,INFINITE) 
     else 
      //Wait just for change notification in the folder 
      WaitResult := WaitForSingleObject(WaitHandles[0],INFINITE); 

     case WaitResult of 
      //If a change in the monitored folder occured 
      WAIT_OBJECT_0 : 
      begin 
      // notifiy caller. 
      FLatestModification := Now; 
      DoOnFolderChange; 
      FPrevModificationTime := FLatestModification; 
      end; 

      //If event handle is signaled, let the loop to iterate, and check 
      //Terminated status. 
      WAIT_OBJECT_0 + 1: Continue; 
     end; 
     //Continue folder change notification job 
     if not FindNextChangeNotification(WaitHandles[0]) then 
      RaiseLastOSError; 
     end; 
    finally 
     FindCloseChangeNotification(WaitHandles[0]); 
    end; 
    except 
    on E: Exception do 
     HandleException(E); 
    end; 
end; 

procedure TFolderWatcherThread.HandleException(E: Exception); 
begin 
    if E is EExternal then 
    begin 
    DoOnError(E.Message,True); 
    Terminate; 
    end 
    else 
    DoOnError(E.Message,False); 
end; 

class procedure TFolderWatcherThread.PulseTerminationEvent; 
begin 
    /// All instances of TFolderChangeTracker which are waiting will be unblocked, 
    /// and blocked again immediately to check their Terminated property. 
    /// If an instance is terminated, then it will end its execution, and the rest 
    /// continue their work. 
    PulseEvent(TerminationEvent); 
end; 


{ TFolderWatcherThreadList } 

procedure TFolderWatcherThreadList.Notify(const Value: TFolderWatcherThread; 
    Action: TCollectionNotification); 
begin 
    if OwnsObjects and (Action = cnRemoved) then 
    begin 
    /// If the thread is running, terminate it, before freeing it. 
    Value.Terminate; 
    /// Pulse global termination event to all TFolderWatcherThread instances. 
    TFolderWatcherThread.PulseTerminationEvent; 
    Value.WaitFor; 
    end; 

    inherited; 
end; 

end. 

Cela fournit deux classes; une classe de threads qui surveille un dossier pour les changements, et si une modification est détectée, elle renvoie l'heure de modification en cours et l'heure de modification précédente via l'événement OnFolderChange. Et une classe de liste pour stocker une liste de threads de surveillance. Cette liste termine automatiquement chaque thread lorsque le thread est supprimé de la liste.

J'espère que cela vous aide.

+0

merci. Probablement pas ce que je cherche (dans le cas général je n'aurais pas besoin d'un thread par dossier que je voulais surveiller - dans mon cas, je vais scanner un disque entier pour rechercher les fichiers qui ont besoin d'être sauvegardés). Un code généralement utile là-dedans et je suis sûr que je vais l'utiliser un jour. – rossmcm

Questions connexes