2010-07-22 209 views
3

我需要确定哪些文件夹包含最近修改过的文件(在特定时间间隔内)。我注意到文件夹日期标记似乎在包含文件被修改时得到更新,但是这种行为不会传播到树上,即包含包含修改文件的文件夹的文件夹的日期戳不会得到更新。快速确定文件夹内容是否已被修改

我可以使用这种行为,但我怀疑它取决于平台/文件系统/网络或本地驱动器等,我仍然想利用它在我可以,因此我需要一个布尔函数返回如果运行我的应用程序的平台/磁盘支持此行为,则为true。

我很高兴通过树进行递归。我想要避免的是,必须为每个文件夹中的每个文件执行FindFirst/FindNext,以查看是否有人在最后一天被修改过 - 如果我可以避免为没有修改其日期戳的文件夹在最后一天内它将节省大量的时间。

回答

2

已发布的解决方案至今都有关获取通知发生时,他们会为此目的很好地工作。如果你想查看过去,看看什么时候最后发生了变化,而不是实时监控它,那么它就会变得更有趣。我认为除了通过递归搜索文件夹树并检查日期戳以外,没有办法这么做。

编辑:回应OP的评论,是的,它看起来没有什么办法来配置FindFirst/FindNext只命中目录而不是文件。但您可以跳过使用此筛选器检查文件的日期:(SearchRec.Attr and SysUtils.faDirectory <> 0)。这应该会加快一点。根本不检查文件的日期。不过,您可能仍然需要扫描所有内容,因为Windows API不提供任何方式(我知道)仅查询文件夹而不查询文件。

+0

我很高兴在树中递归。我想要避免的是,必须为每个文件夹中的每个文件执行FindFirst/FindNext,以查看是否有人在最后一天被修改过 - 如果我可以避免为没有修改其日期戳的文件夹在最后一天内它将节省大量的时间。 – rossmcm 2010-07-22 22:55:23

+0

@ user89691:已编辑。 – 2010-07-22 23:34:26

+0

@ user8961,不用担心检查文件的时间/大小/属性,CPU不会因为所有递归而晕眩。严重的是,一旦关于任何给定文件夹结构的信息已经在内存中,时间的惩罚就不明显了(I/O惩罚已经被支付了 - 这就是瓶颈所在)。此外,事情可能发生在日期和时间本身无法说明的目录内。示例:可以将新文件复制到目录(复制的文件保留创建和修改时间),并且该操作可能不会更改目录上的修改时间。 – 2010-07-23 05:10:26

2

我写了用于此目的的代码为我的项目之一。这使用FindFirstChangeNotification和FindNextChangeNotification API函数。 下面是代码(I去掉了一些项目的特定部分):

/// <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. 

这提供两个类;一个线程类监视文件夹的变化,如果检测到变化,它将通过OnFolderChange事件返回当前变更时间和上一次变更时间。还有一个用于存储监视线程列表的列表类。该列表在线程从列表中移除时自动终止每个自己的线程。

我希望它能帮助你。

+0

谢谢。可能不是我在寻找的东西(在一般情况下,我不需要每个文件夹都有一个我想要监视的线程 - 在我的情况下,我将扫描整个磁盘以查找需要备份的文件)。虽然有些通用的代码,但我相信我会在某个时候使用它。 – rossmcm 2010-07-23 21:17:32