4

「最近」(特定の間隔内で)変更されたファイルが含まれているフォルダーを特定する必要があります。含まれているファイルが変更されるたびにフォルダーの日付スタンプが更新されるように見えますが、この動作はツリーに反映されません。つまり、変更されたファイルを含むフォルダーを含むフォルダーの日付スタンプは更新されません。

この動作で作業することはできますが、プラットフォーム/ファイルシステム/ネットワークまたはローカルドライブなどに依存していると思われます。それでも可能な限りそれを利用したいので、プラットフォームがtrueを返す場合はブール関数が必要です。私のアプリを実行している/diskは、この動作をサポートしています。

私は木を通り抜けることができてとてもうれしいです。私が避けたいのは、すべてのフォルダー内のすべてのファイルに対してFindFirst / FindNextを実行して、(たとえば)最終日に変更されたかどうかを確認することです-日付スタンプが変更されていないフォルダーに対してそれを回避できるかどうか最終日以内に、それはかなりの時間を節約します。

4

4 に答える 4

3

FindFirstChangeNotificationおよび機能を確認してください。FindNextChangeNotificationもう 1 つのオプションは、TJvChangeNotifyJEDI コンポーネントを使用することです。

さらに、このリンクを確認できます

于 2010-07-22T22:07:26.543 に答える
2

これまでに投稿された解決策は、通知が発生したときに通知を取得することに関するものであり、その目的にはうまく機能します. リアルタイムで監視するのではなく、過去を調べて、何かが最後にいつ変更されたかを確認したい場合は、さらに巧妙になります。フォルダ ツリーを再帰的に検索して日付スタンプをチェックする以外に、これを行う方法はないと思います。

編集: OPのコメントに応えて、ええ、ファイルではなくディレクトリのみをヒットするようにFindFirst/FindNextを構成する方法はないようです。ただし、このフィルターを使用すると、ファイルの日付のチェックをスキップできます: (SearchRec.Attr and SysUtils.faDirectory <> 0). これで少しはスピードアップするはずです。ファイルの日付をまったくチェックしないでください。ただし、Windows API には、ファイルではなくフォルダーのみを照会する (私が知っている) 方法がないため、おそらくすべてをスキャンする必要があります。

于 2010-07-22T22:29:13.133 に答える
2

私は自分のプロジェクトの 1 つで、この目的のためにコードを書きました。これは、FindFirstChangeNotification および FindNextChangeNotification API 関数を使用します。コードは次のとおりです(プロジェクト固有の部分をいくつか削除しました):

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

これは 2 つのクラスを提供します。フォルダーの変更を監視するスレッド クラス。変更が検出された場合は、OnFolderChange イベントを通じて現在の変更時刻と前回の変更時刻を返します。また、監視スレッドのリストを格納するためのリスト クラス。このリストは、スレッドがリストから削除されると、それぞれのスレッドを自動的に終了します。

お役に立てば幸いです。

于 2010-07-23T15:12:13.070 に答える
0

http://help.delphi-jedi.org/item.php?Id=172977を見る必要があります。これはすぐに使えるソリューションです。JVCL全体をダウンロードしてインストールしたくない場合(ただし、これは優れたコードです;))、オンラインでファイルソースを確認することをお勧めします - http://jvcl.svn.sourceforge.net/viewvc/jvcl/trunk /jvcl/run/JvChangeNotify.pas?revision=12481&view=マークアップ

于 2010-07-22T22:08:39.240 に答える