2010-07-22 145 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