| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948 | 
							- {
 
- Modifications:
 
- ==============
 
-  ie01: OnChange delayed for 500 ms.
 
-  ie02: No usage of BrowseDirectory component
 
-  ie03: Resume suspended thread when terminating the thread
 
-  ie04: Martin Prikryl, option to watch multiple directories
 
-  }
 
- // ==================== DISC DRIVE MONITOR =====================================
 
- //
 
- // Class and Component to encapsulate the FindXXXXChangeNotification API calls
 
- //
 
- // The FindXXXXChangeNotification API calls set up a disc contents change
 
- // notification handle.  You can set a filter to control which change types
 
- // are notified, the directory which is monitored and set whether subdirectories
 
- // from the monitored directory are monitored as well.
 
- //
 
- //------------------------------------------------------------------------------
 
- // This file contains a class derived from TThread which undertakes the disc
 
- // monitoring and a simple component which encapsulates the thread to make
 
- // a non-visual VCL component.  This component works at design time, monitoring
 
- // and notifying changes live if required.
 
- //
 
- // Version 1.00 - Grahame Marsh 14 January 1997
 
- // Version 1.01 - Grahame Marsh 30 December 1997
 
- //      Bug fix - really a Win 95 bug but only surfaces in D3, not D2
 
- //              - see notes in execute method
 
- // Version 1.02 - Grahame Marsh 30 January 1998
 
- //              - adapted to work with version 2.30 TBrowseDirectoryDlg
 
- //
 
- // Freeware - you get it for free, I take nothing, I make no promises!
 
- //
 
- // Please feel free to contact me: [email protected]
 
- {$DEFINE BUGFIX}
 
- unit DiscMon;
 
- interface
 
- uses
 
-   Windows, SysUtils, Classes, CompThread;
 
- //=== DISC MONITORING THREAD ===================================================
 
- // This thread will monitor a given directory and subdirectories (if required)
 
- // for defined filtered changes.  When a change occurs the OnChange event will
 
- // be fired, if an invalid condition is found (eg non-existent path) then
 
- // the OnInvalid event is fired. Each event is called via the Sychronize method
 
- // and so are VCL thread safe.
 
- //
 
- // The thread is created suspended, so after setting the required properties
 
- // you must call the Resume method.
 
- type
 
-   TDiscMonitorNotify = procedure(Sender: TObject; const Directory: string; var SubdirsChanged: Boolean) of object;
 
-   TDiscMonitorInvalid = procedure(Sender: TObject; const Directory: string; const ErrorStr: string) of object;
 
-   TDiscMonitorSynchronize = procedure(Sender: TObject; Method: TThreadMethod) of object;
 
-   TDiscMonitorFilter = procedure(Sender: TObject; const DirectoryName: string; var Add: Boolean) of object;
 
-   TDiscMonitorTooManyDirectories = procedure(Sender: TObject; var MaxDirectories: Integer) of object;
 
-   TDiscMonitorDirectoriesChange = procedure(Sender: TObject; Directories: Integer) of object;
 
-   TDiscMonitorThread = class(TCompThread)
 
-   private
 
-     FOnChange: TDiscMonitorNotify;
 
-     FOnInvalid: TDiscMonitorInvalid;
 
-     FOnSynchronize: TDiscMonitorSynchronize;
 
-     FOnFilter: TDiscMonitorFilter;
 
-     FOnDirectoriesChange: TDiscMonitorDirectoriesChange;
 
-     FDirectories: TStrings;
 
-     FFilters: DWORD;
 
-     FDestroyEvent,
 
-     FChangeEvent: THandle;
 
-     FSubTree: Boolean;
 
-     FChangeDelay: Integer;  {ie01}
 
-     FNotifiedDirectory: string;
 
-     FSubdirsChanged: Boolean;
 
-     FInvalidMessage: string;
 
-     FNotifiedDirectories: Integer;
 
-     FEnabled: Boolean;
 
-     procedure InformChange;
 
-     procedure InformInvalid;
 
-     procedure InformDirectoriesChange;
 
-     procedure SetDirectories(const Value: TStrings);
 
-     procedure SetFilters(Value: DWORD);
 
-     procedure SetSubTree(Value: Boolean);
 
-     procedure SetEnabled(Value: Boolean);
 
-     procedure SaveOSError;
 
-   protected
 
-     procedure Execute; override;
 
-     procedure Update;
 
-     procedure DoSynchronize(Method: TThreadMethod);
 
-   public
 
-     constructor Create;
 
-     destructor Destroy; override;
 
-     // The directory to monitor
 
-     property Directories: TStrings read FDirectories write SetDirectories;
 
-     // Filter condition, may be any of the FILE_NOTIFY_CHANGE_XXXXXXX constants
 
-     // ORed together.  Zero is invalid.
 
-     property Filters: DWORD read FFilters write SetFilters;
 
-     // Event called when change noted in directory
 
-     property OnChange: TDiscMonitorNotify read FOnChange write FOnChange;
 
-     // Event called for invalid parameters
 
-     property OnInvalid: TDiscMonitorInvalid read FOnInvalid write FOnInvalid;
 
-     property OnSynchronize: TDiscMonitorSynchronize read FOnSynchronize write FOnSynchronize;
 
-     property OnFilter: TDiscMonitorFilter read FOnFilter write FOnFilter;
 
-     property OnDirectoriesChange: TDiscMonitorDirectoriesChange read FOnDirectoriesChange write FOnDirectoriesChange;
 
-     // Include subdirectories below specified directory.
 
-     property SubTree: Boolean read FSubTree write SetSubTree;
 
-     // specify, how long the thread should wait, before the event OnChange is fired:
 
-     property ChangeDelay: Integer read FChangeDelay write FChangeDelay
 
-       default 500;
 
-     property Enabled: Boolean read FEnabled write SetEnabled;
 
-   end;
 
-   //===================== DISC MONITORING COMPONENT ==============================
 
-   // enumerated type for filter conditions (not directly usable in thread class)
 
-   // see the SetFilters procedure for the translation of these filter conditions
 
-   // into FILE_NOTIFY_CHANGE_XXXXXX constants.
 
-   TMonitorFilter = (moFilename, moDirName, moAttributes, moSize,
 
-                     moLastWrite, moSecurity);
 
-   // set of filter conditions
 
-   TMonitorFilters = set of TMonitorFilter;
 
-   TDiscMonitor = class(TComponent)
 
-   private
 
-     FActive: Boolean;
 
-     FMonitor: TDiscMonitorThread;
 
-     FFilters: TMonitorFilters;
 
-     FOnChange: TDiscMonitorNotify;
 
-     FOnInvalid: TDiscMonitorInvalid;
 
-     FOnSynchronize: TDiscMonitorSynchronize;
 
-     FOnFilter: TDiscMonitorFilter;
 
-     FOnTooManyDirectories: TDiscMonitorTooManyDirectories;
 
-     FOnDirectoriesChange: TDiscMonitorDirectoriesChange;
 
-     FShowMsg: Boolean;
 
-     FPending: Boolean;
 
-     FMaxDirectories: Integer;
 
-     function GetDirectories: TStrings;
 
-     function GetSubTree: Boolean;
 
-     function GetEnabled: Boolean;
 
-     procedure SetActive(Value: Boolean);
 
-     procedure SetDirectories(Value: TStrings);
 
-     procedure SetFilters(Value: TMonitorFilters);
 
-     procedure SetSubTree(Value: Boolean);
 
-     procedure SetEnabled(Value: Boolean);
 
-     function  GetChangeDelay: Integer;
 
-     procedure SetChangeDelay(Value: Integer);
 
-   protected
 
-     procedure Change(Sender: TObject; const Directory: string;
 
-       var SubdirsChanged: Boolean);
 
-     procedure Invalid(Sender: TObject; const Directory: string; const ErrorStr: string);
 
-     procedure Filter(Sender: TObject; const DirectoryName: string; var Add: Boolean);
 
-     procedure DirectoriesChange(Sender: TObject; Directories: Integer);
 
-     procedure DoSynchronize(Sender: TObject; Method: TThreadMethod);
 
-   public
 
-     constructor Create(AOwner : TComponent); override;
 
-     destructor Destroy; override;
 
-     // stop the monitoring thread running
 
-     procedure Close;
 
-     // start the monitoring thread running
 
-     procedure Open;
 
-     procedure AddDirectory(Directory: string; SubDirs: Boolean);
 
-     procedure SetDirectory(Directory: string);
 
-     // read-only property to access the thread directly
 
-     property Thread: TDiscMonitorThread read FMonitor;
 
-     property MaxDirectories: Integer read FMaxDirectories write FMaxDirectories;
 
-   published
 
-     // the directories to monitor
 
-     property Directories: TStrings read GetDirectories;
 
-     // control the appearance of information messages at design time (only)
 
-     property ShowDesignMsg: Boolean read FShowMsg write FShowMsg default False;
 
-     // event called when a change is notified
 
-     property OnChange: TDiscMonitorNotify read FOnChange write FOnChange;
 
-     // event called if an invalid condition is found
 
-     property OnInvalid: TDiscMonitorInvalid read FOnInvalid write FOnInvalid;
 
-     property OnSynchronize: TDiscMonitorSynchronize read FOnSynchronize write FOnSynchronize;
 
-     property OnFilter: TDiscMonitorFilter read FOnFilter write FOnFilter;
 
-     property OnTooManyDirectories: TDiscMonitorTooManyDirectories read FOnTooManyDirectories write FOnTooManyDirectories;
 
-     property OnDirectoriesChange: TDiscMonitorDirectoriesChange read FOnDirectoriesChange write FOnDirectoriesChange;
 
-     // notification filter conditions
 
-     property Filters: TMonitorFilters read FFilters write SetFilters default [moFilename];
 
-     // include subdirectories below the specified directory
 
-     property SubTree: Boolean read GetSubTree write SetSubTree default True;
 
-     // specify if the monitoring thread is active
 
-     property Active: Boolean read FActive write SetActive default False;
 
-     // specify, how long the thread should wait, before the event OnChange is fired:
 
-     property ChangeDelay: Integer read GetChangeDelay write SetChangeDelay
 
-       default 500;
 
-     property Enabled: Boolean read GetEnabled write SetEnabled default True;
 
-   end;
 
- procedure Register;
 
- implementation
 
- uses
 
-   PasTools;
 
- {$WARN SYMBOL_PLATFORM OFF}
 
- {$IFDEF BUGFIX}
 
- {$Z4}
 
- type TWinBool = (winFalse, winTrue);
 
- function FixFindFirstChangeNotification(const lpPathName: PChar;
 
-   bWatchSubtree: TWinBool; dwNotifyFilter: DWORD): THandle stdcall;
 
-   external kernel32 name 'FindFirstChangeNotificationW';
 
- {$ENDIF}
 
- procedure AddDirectory(Dirs: TStrings; Directory: string;
 
-   var MaxDirectories: Integer; OnFilter: TDiscMonitorFilter;
 
-   OnTooManyDirectories: TDiscMonitorTooManyDirectories; Tag: Boolean);
 
- var
 
-   Found: Boolean;
 
-   SearchRec: TSearchRec;
 
-   FileName: string;
 
-   FindAttrs: Integer;
 
-   Add: Boolean;
 
-   Index: Integer;
 
- begin
 
-   FindAttrs := faReadOnly or faHidden or faSysFile or faDirectory or faArchive;
 
-   Directory := IncludeTrailingBackslash(Directory);
 
-   Found := (FindFirst(ApiPath(Directory + '*.*'), FindAttrs, SearchRec) = 0);
 
-   if Found then
 
-   begin
 
-     try
 
-       while Found do
 
-       begin
 
-         if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') and
 
-            ((SearchRec.Attr and faDirectory) = faDirectory) then
 
-         begin
 
-           FileName := Directory + SearchRec.Name;
 
-           Add := True;
 
-           if Assigned(OnFilter) then OnFilter(nil, FileName, Add);
 
-           if Add then
 
-           begin
 
-             if Tag then
 
-             begin
 
-               Index := Dirs.IndexOf(FileName);
 
-               if Index >= 0 then
 
-               begin
 
-                 Assert(Dirs.Objects[Index] = TObject(1));
 
-                 Dirs.Objects[Index] := TObject(0);
 
-               end;
 
-             end
 
-               else Index := -1;
 
-             if Index < 0 then
 
-             begin
 
-               if Tag then Dirs.AddObject(FileName, TObject(2))
 
-                 else Dirs.Add(FileName);
 
-               if (MaxDirectories >= 0) and (Dirs.Count > MaxDirectories) and
 
-                  Assigned(OnTooManyDirectories) then
 
-                    OnTooManyDirectories(nil, MaxDirectories);
 
-             end;
 
-             // note that we are not re-scaning subdirectories of duplicate directory
 
-             AddDirectory(Dirs, FileName, MaxDirectories, OnFilter, OnTooManyDirectories, Tag);
 
-           end;
 
-         end;
 
-         Found := (FindNext(SearchRec) = 0);
 
-       end;
 
-     finally
 
-       FindClose(SearchRec);
 
-     end;
 
-   end;
 
- end;
 
- //=== MONITOR THREAD ===========================================================
 
- // Create the thread suspended.  Create two events, each are created using
 
- // standard security, in the non-signalled state, with auto-reset and without
 
- // names.  The FDestroyEvent will be used to signal the thread that it is to close
 
- // down.  The FChangeEvent will be used to signal the thread when the monitoring
 
- // conditions (directory, filters or sub-directory search) have changed.
 
- // OnTerminate is left as false, so the user must Free the thread.
 
- constructor TDiscMonitorThread.Create;
 
- begin
 
-   inherited Create(True);
 
-   FDirectories := TStringList.Create;
 
-   FDestroyEvent := CreateEvent(nil, True,  False, nil);
 
-   FChangeEvent  := CreateEvent(nil, False, False, nil);
 
-   FOnFilter := nil;
 
-   FOnDirectoriesChange := nil;
 
-   FEnabled := True;
 
- end;
 
- // close OnXXXXX links, signal the thread that it is to close down
 
- destructor TDiscMonitorThread.Destroy;
 
- var
 
-   D: TStrings;
 
- begin
 
-   FOnChange := nil;
 
-   FOnInvalid := nil;
 
-   if Suspended then Resume;
 
-   SetEvent(FDestroyEvent);
 
-   D := FDirectories;
 
-   inherited Destroy;
 
-   // cannot free before destroy as the thread is using it
 
-   D.Free;
 
- end;
 
- // called by the Execute procedure via Synchronize.  So this is VCL thread safe
 
- procedure TDiscMonitorThread.InformChange;
 
- begin
 
-   if Assigned(FOnChange) then FOnChange(Self, FNotifiedDirectory, FSubdirsChanged);
 
- end;
 
- // called by the Execute procedure via Synchronize.  So this is VCL thread safe
 
- procedure TDiscMonitorThread.InformInvalid;
 
- begin
 
-   if Assigned(FOnInvalid) then FOnInvalid(Self, FNotifiedDirectory, FInvalidMessage);
 
- end;
 
- procedure TDiscMonitorThread.InformDirectoriesChange;
 
- begin
 
-   if Assigned(FOnDirectoriesChange) then FOnDirectoriesChange(Self, FNotifiedDirectories);
 
- end;
 
- procedure TDiscMonitorThread.SaveOSError;
 
- begin
 
-   try
 
-     RaiseLastOSError;
 
-   except
 
-     on E: Exception do FInvalidMessage := E.Message;
 
-   end;
 
- end;
 
- procedure TDiscMonitorThread.SetDirectories(const Value: TStrings);
 
- begin
 
-   if Value <> nil then
 
-   begin
 
-     FDirectories.Assign(Value);
 
-     (FDirectories as TStringList).CaseSensitive := False;
 
-     (FDirectories as TStringList).Sorted := True;
 
-   end
 
-     else FDirectories.Clear;
 
-   Update;
 
- end;
 
- // Change the current filters
 
- procedure TDiscMonitorThread.SetFilters(Value: DWORD);
 
- begin
 
-   if Value <> FFilters then
 
-   begin
 
-     FFilters := Value;
 
-     Update;
 
-   end
 
- end;
 
- // Change the current sub-tree condition
 
- procedure TDiscMonitorThread.SetSubTree(Value: Boolean);
 
- begin
 
-   if Value <> FSubTree then
 
-   begin
 
-     FSubtree := Value;
 
-     Update;
 
-   end
 
- end;
 
- procedure TDiscMonitorThread.SetEnabled(Value: Boolean);
 
- begin
 
-   if Value <> FEnabled then
 
-   begin
 
-     FEnabled := Value;
 
-     Update;
 
-   end
 
- end;
 
- function TDiscMonitor.GetChangeDelay: Integer;  {ie01}
 
- begin
 
-   Result := FMonitor.ChangeDelay;
 
- end;
 
- procedure TDiscMonitor.SetChangeDelay(Value: Integer);  {ie01}
 
- begin
 
-   FMonitor.ChangeDelay := Value;
 
- end;
 
- // On any of the above three changes, if the thread is running then
 
- // signal it that a change has occurred.
 
- procedure TDiscMonitorThread.Update;
 
- begin
 
-   if not Suspended then
 
-   begin
 
-     SetEvent(FChangeEvent);
 
-   end;
 
- end;
 
- procedure TDiscMonitorThread.DoSynchronize(Method: TThreadMethod);
 
- begin
 
-   if Assigned(OnSynchronize) then OnSynchronize(Self, Method);
 
- end;
 
- // The EXECUTE procedure
 
- //     -------
 
- // Execute needs to:
 
- // 1. Call FindFirstChangeNotification and use the Handle in a WaitFor...
 
- //    to wait until the thread become signalled that a notification has occurred.
 
- //    The OnChange event is called and then the FindNextChangeNotification is
 
- //    the called and Execute loops back to the WaitFor
 
- // 2. If an invalid handle is obtained from the above call, the the OnInvalid
 
- //    event is called and then Execute waits until valid conditions are set.
 
- // 3. If a ChangeEvent is signalled then FindCloseChangeNotification is called,
 
- //    followed by a new FindFirstChangeNotification to use the altered
 
- //    conditions.
 
- // 4. If a DestroyEvent is signalled then FindCloseChangeNotification is
 
- //    called and the two events are closed and the thread terminates.
 
- //
 
- // In practice WaitForMultipleObjects is used to wait for any of the conditions
 
- // to be signalled, and the returned value used to determine which event occurred.
 
- procedure TDiscMonitorThread.Execute;
 
- var
 
-   // used to give the handles to WaitFor...
 
-   Handles: PWOHandleArray;
 
-   BaseHandles: Word;
 
-   SysHandles: Word;
 
-   Count: Word;
 
-   function StartMonitor(const Directory: string; ForceSubTree: Boolean): THandle;
 
-   // There appears to be a bug in win 95 where the bWatchSubTree parameter
 
-   // of FindFirstChangeNotification which is a BOOL only accepts values of
 
-   // 0 and 1 as valid, rather than 0 and any non-0 value as it should.  In D2
 
-   // BOOL was defined as 0..1 so the code worked, in D3 it is 0..-1 so
 
-   // fails. The result is FindF... produces and error message.  This fix (bodge) is
 
-   // needed to produce a 0,1 bool pair, rather that 0,-1 as declared in D3
 
-   {$IFDEF BUGFIX}
 
-   const R : array[False..True] of TWinBOOL = (WinFalse, WinTrue);
 
-   {$ELSE}
 
-   const R : array[False..True] of LongBool = (LongBool(0), LongBool(1));
 
-   {$ENDIF}
 
-   var
 
-     Again: Boolean;
 
-     SubTree: Boolean;
 
-   begin
 
-     repeat
 
-       SubTree := FSubTree or ForceSubTree;
 
-       {$IFDEF BUGFIX}
 
-       Result := FixFindFirstChangeNotification(PChar(Directory), R[SubTree], FFilters);
 
-       {$ELSE}
 
-       Result := FindFirstChangeNotification(PChar(Directory), R[SubTree], FFilters);
 
-       {$ENDIF}
 
-       Again := (Result = INVALID_HANDLE_VALUE);
 
-       if Again then
 
-       begin
 
-         // call the OnInvalid event
 
-         FNotifiedDirectory := Directory;
 
-         SaveOSError;
 
-         DoSynchronize(InformInvalid);
 
-         // wait until either DestroyEvent or the ChangeEvents are signalled
 
-         Result := WaitForMultipleObjects(2, Handles, False, INFINITE);
 
-         if Result = WAIT_FAILED then
 
-         begin
 
-           FNotifiedDirectory := '';
 
-           SaveOSError;
 
-           DoSynchronize(InformInvalid);
 
-           Again := False;
 
-         end
 
-           else Again := (Result - WAIT_OBJECT_0 = 1);
 
-       end
 
-     until (not Again);
 
-   end; {StartMonitor}
 
-   function UpdateSubdirectories(Directory: Integer): Cardinal;
 
-   var
 
-     Path: string;
 
-     NewHandles: PWOHandleArray;
 
-     OrigDirectory: Integer;
 
-     NewAlloc: Integer;
 
-     MaxDirectories: Integer;
 
-     Changed: Boolean;
 
-   begin
 
-     Assert(Directory >= 0);
 
-     Path := ExcludeTrailingBackslash(FDirectories[Directory]);
 
-     while ((Directory + 1 < FDirectories.Count) and
 
-            SameText(Copy(FDirectories.Strings[Directory + 1], 1, Length(Path)), Path)) do
 
-     begin
 
-       FDirectories.Objects[Directory + 1] := TObject(1);
 
-       Inc(Directory);
 
-     end;
 
-     MaxDirectories := -1;
 
-     AddDirectory(FDirectories, Path, MaxDirectories, OnFilter, nil, True);
 
-     Result := WAIT_OBJECT_0;
 
-     // worst case limit
 
-     NewAlloc := SysHandles + FDirectories.Count;
 
-     GetMem(NewHandles, SizeOf(THandle) * NewAlloc);
 
-     Move(Handles^, NewHandles^, SizeOf(THandle) * SysHandles);
 
-     OrigDirectory := SysHandles;
 
-     Directory := SysHandles;
 
-     Changed := False;
 
-     while Directory < SysHandles + FDirectories.Count do
 
-     begin
 
-       // removed directory
 
-       if Integer(FDirectories.Objects[Directory - SysHandles]) = 1 then
 
-       begin
 
-         FDirectories.Delete(Directory - SysHandles);
 
-         Assert(OrigDirectory < Count);
 
-         FindCloseChangeNotification(Handles^[OrigDirectory]);
 
-         Handles^[OrigDirectory] := INVALID_HANDLE_VALUE;
 
-         Inc(OrigDirectory);
 
-         Changed := True;
 
-       end
 
-         else
 
-       // newly added
 
-       if Integer(FDirectories.Objects[Directory - SysHandles]) = 2 then
 
-       begin
 
-         Assert(Directory < NewAlloc);
 
-         NewHandles^[Directory] := StartMonitor(FDirectories[Directory - SysHandles], False);
 
-         if NewHandles^[Directory] = INVALID_HANDLE_VALUE then
 
-         begin
 
-           // currently we resign on correct resource freeing
 
-           Result := WAIT_FAILED;
 
-           Break;
 
-         end;
 
-         FDirectories.Objects[Directory - SysHandles] := TObject(0);
 
-         Inc(Directory);
 
-         Changed := True;
 
-       end
 
-         else
 
-       begin
 
-         Assert(Integer(FDirectories.Objects[Directory - SysHandles]) = 0);
 
-         Assert(Directory < NewAlloc);
 
-         Assert(OrigDirectory < Count);
 
-         NewHandles^[Directory] := Handles^[OrigDirectory];
 
-         Inc(Directory);
 
-         Inc(OrigDirectory);
 
-       end;
 
-     end;
 
-     if Result <> WAIT_FAILED then
 
-     begin
 
-       Assert(Count = OrigDirectory);
 
-       FreeMem(Handles);
 
-       Handles := NewHandles;
 
-       Count := SysHandles + FDirectories.Count;
 
-       Assert(Count = Directory);
 
-       Assert(Count <= NewAlloc);
 
-     end;
 
-     if Changed and Assigned(OnDirectoriesChange) then
 
-     begin
 
-       FNotifiedDirectories := FDirectories.Count;
 
-       DoSynchronize(InformDirectoriesChange);
 
-     end;
 
-   end;
 
-   function Notify(Directory: Integer; Handle: THandle): Cardinal;
 
-   begin
 
-     // Notification signalled, so fire the OnChange event and then FindNext..
 
-     // loop back to re-WaitFor... the thread
 
-     Sleep(FChangeDelay);
 
-     Result := WAIT_TIMEOUT;
 
-     // When deleting a tree, we may get notification about change in the directory
 
-     // before notification about deleting the directory.
 
-     // While this does not 100% protect against an attempt to synchronize the deleted directory,
 
-     // it may greatly reduce the risk (as checked after the sleep above).
 
-     // Though actually it may not even be possible to delete the directory as we have it locked.
 
-     if DirectoryExists(FDirectories[Directory]) then
 
-     begin
 
-       FNotifiedDirectory := FDirectories[Directory];
 
-       FSubdirsChanged := False;
 
-       DoSynchronize(InformChange);
 
-       if FSubdirsChanged then
 
-         Result := UpdateSubDirectories(Directory)
 
-     end;
 
-     FindNextChangeNotification(Handle);
 
-   end;
 
-   function CheckAllObjects(Count: Integer; DirHandles: PWOHandleArray): Cardinal;
 
-   const
 
-     Offset = MAXIMUM_WAIT_OBJECTS;
 
-   var
 
-     C, Start, Directory: Cardinal;
 
-   begin
 
-     Result := WAIT_TIMEOUT;
 
-     Start := 0;
 
-     while Start < Cardinal(Count) do
 
-     begin
 
-       if Cardinal(Count) - Start > Offset then C := Offset
 
-         else C := Cardinal(Count) - Start;
 
-       Result := WaitForMultipleObjects(C, @DirHandles[Start], false, 0);
 
-       Directory := Start + Result - WAIT_OBJECT_0;
 
-       // (Result - WAIT_OBJECT_0 >= 0) is always true
 
-       if Result - WAIT_OBJECT_0 < C then
 
-       begin
 
-         Result := Notify(Directory, DirHandles^[Directory]);
 
-         // when new directory is found, restart,
 
-         // if not check the same range again for possibly different notification
 
-         if Result = WAIT_OBJECT_0 then Start := 0;
 
-       end
 
-         else Inc(Start, C);
 
-       if Result <> WAIT_TIMEOUT then Break;
 
-       Result := WaitForMultipleObjects(2, Handles, False, 0);
 
-       if Result <> WAIT_TIMEOUT then Break;
 
-     end;
 
-   end;
 
- const
 
-   DestroySlot = 0;
 
-   ChangeSlot = 1;
 
-   HierNotifySlot = 2;
 
- var
 
-   HierMode: Boolean;
 
-   WaitCount: Word;
 
-   I: Integer;
 
-   Result: Cardinal;
 
-   WasEnabled: Boolean;
 
- begin {Execute}
 
-   BaseHandles := 2;
 
-   SysHandles := BaseHandles;
 
-   Count := SysHandles + FDirectories.Count;
 
-   HierMode := (Count > MAXIMUM_WAIT_OBJECTS);
 
-   if HierMode then
 
-   begin
 
-     Inc(SysHandles);
 
-     Inc(Count);
 
-   end;
 
-   GetMem(Handles, SizeOf(THandle) * Count);
 
-   try
 
-     Handles^[DestroySlot] := FDestroyEvent;      // put DestroyEvent handle in slot 0
 
-     Handles^[ChangeSlot] := FChangeEvent;       // put ChangeEvent handle in slot 1
 
-     repeat
 
-       WasEnabled := Enabled;
 
-       if WasEnabled then
 
-       begin
 
-         if HierMode then
 
-         begin
 
-           // expect that the first directory is the top level one
 
-           Handles^[HierNotifySlot] := StartMonitor(FDirectories[0], True);
 
-           if Handles^[HierNotifySlot] = INVALID_HANDLE_VALUE then Exit;
 
-         end;
 
-         for I := SysHandles to Count - 1 do
 
-         begin
 
-           Handles^[I] := StartMonitor(FDirectories[I - SysHandles], False);
 
-           if Handles^[I] = INVALID_HANDLE_VALUE then Exit;
 
-         end;
 
-       end;
 
-       repeat
 
-         if WasEnabled then
 
-         begin
 
-           if HierMode then WaitCount := SysHandles
 
-             else WaitCount := Count;
 
-         end
 
-           else WaitCount := BaseHandles;
 
-         // wait for any of the change notification, destroy or
 
-         // change events to be signalled
 
-         Result := WaitForMultipleObjects(WaitCount, Handles, False, INFINITE);
 
-         if Result = WAIT_FAILED then
 
-         begin
 
-           FNotifiedDirectory := '';
 
-           SaveOSError;
 
-           DoSynchronize(InformInvalid);
 
-         end
 
-           else
 
-         if HierMode and (Result - WAIT_OBJECT_0 = HierNotifySlot) then
 
-         begin
 
-           FindNextChangeNotification(Handles[HierNotifySlot]);
 
-           Result := CheckAllObjects(Count - SysHandles, @Handles[SysHandles]);
 
-           // (Result >= WAIT_OBJECT_0) = always true
 
-           if Result < Cardinal(WAIT_OBJECT_0 + (Count - SysHandles)) then
 
-           begin
 
-             Result := WAIT_OBJECT_0 + HierNotifySlot;
 
-           end;
 
-         end
 
-           else
 
-         if (Result >= WAIT_OBJECT_0 + SysHandles) and
 
-            (Result < WAIT_OBJECT_0 + WaitCount) then
 
-         begin
 
-           Result := Notify(Result - WAIT_OBJECT_0 - SysHandles,
 
-             Handles^[Result - WAIT_OBJECT_0]);
 
-           if Result = WAIT_OBJECT_0 then Result := WAIT_TIMEOUT;
 
-         end;
 
-       // note that WaitCount can be different here than when
 
-       // WaitForMultipleObjects  was called, but it should not matter as it is
 
-       until (Result = WAIT_FAILED) or (Result = WAIT_OBJECT_0 + DestroySlot) or
 
-         (Result = WAIT_OBJECT_0 + ChangeSlot) or
 
-         ((Result >= WAIT_ABANDONED_0) and (Result < WAIT_ABANDONED_0 + WaitCount));
 
-       if WasEnabled then
 
-       begin
 
-         if HierMode then
 
-         begin
 
-           FindCloseChangeNotification(Handles^[HierNotifySlot]);
 
-         end;
 
-         for I := SysHandles to Count - 1 do
 
-         begin
 
-           FindCloseChangeNotification(Handles^[I]);
 
-         end;
 
-       end;
 
-       // loop back to restart if ChangeEvent was signalled
 
-     until (Result - WAIT_OBJECT_0 <> ChangeSlot) or Self.Terminated;
 
-     // closing down so chuck the two events
 
-     CloseHandle(FChangeEvent);
 
-     CloseHandle(FDestroyEvent);
 
-   finally
 
-     FreeMem(Handles);
 
-   end;
 
- end;
 
- //=== MONITOR COMPONENT ========================================================
 
- // This component encapsulates the above thread.  It has properties for
 
- // directory, sub-directory conditions, filters, whether information messages
 
- // should be given at design time and if the thread is active.
 
- constructor TDiscMonitor.Create(AOwner: TComponent);
 
- begin
 
-   inherited Create (AOwner);
 
-   FOnFilter := nil;
 
-   FOnTooManyDirectories := nil;
 
-   FOnDirectoriesChange := nil;
 
-   FMonitor := TDiscMonitorThread.Create;  // create a monitor thread
 
-   FMonitor.ChangeDelay := 500;            {ie01}
 
-   FMonitor.OnChange := Change;            // hook into its event handlers
 
-   FMonitor.OnInvalid := Invalid;
 
-   FMonitor.OnSynchronize := DoSynchronize;
 
-   FMonitor.OnFilter := Filter;
 
-   FMonitor.OnDirectoriesChange := DirectoriesChange;
 
-   Filters := [moFilename];                // default filters to moFilename
 
-   SubTree := True;                        // default sub-tree search to on
 
-   FPending := True;
 
- end;
 
- destructor TDiscMonitor.Destroy;
 
- begin
 
-   FMonitor.Free;                          // chuck the thread
 
-   inherited Destroy;
 
- end;
 
- // Change notification from the thread has occurred. Call the component's event
 
- // handler
 
- procedure TDiscMonitor.Change(Sender: TObject; const Directory: string;
 
-   var SubdirsChanged: Boolean);
 
- begin
 
-   if Assigned(FOnChange) then
 
-     FOnChange(Self, Directory, SubdirsChanged)
 
- end;
 
- // Invalid notification from the thread has occurred. Call the component's event
 
- // handler
 
- procedure TDiscMonitor.Invalid(Sender: TObject; const Directory: string; const ErrorStr: string);
 
- begin
 
-   if Assigned(FOnInvalid) then
 
-     FOnInvalid(Self, Directory, ErrorStr)
 
- end;
 
- procedure TDiscMonitor.Filter(Sender: TObject; const DirectoryName: string; var Add: Boolean);
 
- begin
 
-   if Assigned(FOnFilter) then
 
-     FOnFilter(Self, DirectoryName, Add)
 
- end;
 
- procedure TDiscMonitor.DirectoriesChange(Sender: TObject; Directories: Integer);
 
- begin
 
-   if Assigned(FOnDirectoriesChange) then
 
-     FOnDirectoriesChange(Self, Directories)
 
- end;
 
- procedure TDiscMonitor.DoSynchronize(Sender: TObject; Method: TThreadMethod);
 
- begin
 
-   if Assigned(FOnSynchronize) then FOnSynchronize(Self, Method)
 
-     else FMonitor.Synchronize(Method);
 
- end;
 
- // Stop the monitor running
 
- procedure TDiscMonitor.Close;
 
- begin
 
-   Active := False;
 
- end;
 
- // Run the monitor
 
- procedure TDiscMonitor.Open;
 
- begin
 
-   Active := True
 
- end;
 
- // Control the thread by using it's resume and suspend methods
 
- procedure TDiscMonitor.SetActive(Value: Boolean);
 
- begin
 
-   if Value <> FActive then
 
-   begin
 
-     FActive := Value;
 
-     if Active then
 
-     begin
 
-       FMonitor.Resume;
 
-       if not FPending then FMonitor.Update;
 
-       FPending := False;
 
-     end else
 
-       FMonitor.Suspend;
 
-   end
 
- end;
 
- // get the current directory from the thread
 
- function TDiscMonitor.GetDirectories: TStrings;
 
- begin
 
-   Result := FMonitor.Directories;
 
- end;
 
- // get the current sub-tree status from the thread
 
- function TDiscMonitor.GetSubTree: Boolean;
 
- begin
 
-   Result := FMonitor.SubTree;
 
- end;
 
- function TDiscMonitor.GetEnabled: Boolean;
 
- begin
 
-   Result := FMonitor.Enabled;
 
- end;
 
- // set the directory to monitor
 
- procedure TDiscMonitor.SetDirectories(Value: TStrings);
 
- begin
 
-   FMonitor.Directories := Value;
 
- end;
 
- procedure TDiscMonitor.AddDirectory(Directory: string; SubDirs: Boolean);
 
- var
 
-   Dirs: TStringList;
 
- begin
 
-   if Directory <> '' then
 
-   begin
 
-     Dirs := TStringList.Create;
 
-     try
 
-       Dirs.Assign(Directories);
 
-       Dirs.Add(Directory);
 
-       if SubDirs then
 
-         DiscMon.AddDirectory(Dirs, Directory, FMaxDirectories, OnFilter,
 
-           OnTooManyDirectories, False);
 
-       SetDirectories(Dirs);
 
-     finally
 
-       Dirs.Free;
 
-     end;
 
-   end
 
-     else
 
-   begin
 
-     SetDirectories(nil);
 
-   end;
 
- end;
 
- procedure TDiscMonitor.SetDirectory(Directory: string);
 
- var
 
-   Dirs: TStringList;
 
- begin
 
-   if Directory <> '' then
 
-   begin
 
-     Dirs := TStringList.Create;
 
-     try
 
-       Dirs.Add(Directory);
 
-       SetDirectories(Dirs);
 
-     finally
 
-       Dirs.Free;
 
-     end;
 
-   end
 
-     else
 
-   begin
 
-     SetDirectories(nil);
 
-   end;
 
- end;
 
- // Change the filter conditions.  The thread uses the raw windows constants
 
- // (FILE_NOTIFY_CHANGE_XXXX) but the components uses a set of enumurated type.
 
- // It is therefore necessary to translate from the component format into
 
- // an integer value for the thread.
 
- procedure TDiscMonitor.SetFilters(Value: TMonitorFilters);
 
- const
 
-   XlatFileNotify: array [moFilename..moSecurity] of DWORD =
 
-     (FILE_NOTIFY_CHANGE_FILE_NAME,  FILE_NOTIFY_CHANGE_DIR_NAME,
 
-      FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_SIZE,
 
-      FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_SECURITY);
 
- var
 
-   L: TMonitorFilter;
 
-   I: DWORD;
 
- begin
 
-   if Value <> FFilters then
 
-     if Value = [] then
 
-       raise Exception.Create('Some filter condition must be set.')
 
-     else begin
 
-       FFilters := Value;
 
-       I := 0;
 
-       for L := moFilename to moSecurity do
 
-         if L in Value then
 
-           I := I or XlatFileNotify [L];
 
-       FMonitor.Filters := I;
 
-     end
 
- end;
 
- // set the sub-tree status in the thread
 
- procedure TDiscMonitor.SetSubTree(Value: Boolean);
 
- begin
 
-   FMonitor.SubTree := Value;
 
- end;
 
- procedure TDiscMonitor.SetEnabled(Value: Boolean);
 
- begin
 
-   FMonitor.Enabled := Value;
 
- end;
 
- procedure Register;
 
- begin
 
-   RegisterComponents('Martin', [TDiscMonitor]);
 
- end;
 
- end.
 
 
  |