home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 June / Chip_2002-06_cd1.bin / zkuste / delphi / kolekce / d6 / rxlibsetup.exe / {app} / units / RxNotify.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2002-02-19  |  9.8 KB  |  376 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 2001,2002 SGB Software          }
  6. {         Copyright (c) 1997, 1998 Fedor Koshevnikov,   }
  7. {                        Igor Pavluk and Serge Korolev  }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11.  
  12. unit RxNotify;
  13.  
  14. interface
  15.  
  16. {$I RX.INC}
  17.  
  18. {$IFDEF WIN32}
  19.  
  20. uses Windows, SysUtils, Classes, Messages, ExtCtrls;
  21.  
  22. type
  23.   TFileChangeFilter = (fnFileName, fnDirName, fnAttributes, fnSize,
  24.     fnLastWrite, fnLastAccess, fnCreation, fnSecurity);
  25.   TFileChangeFilters = set of TFileChangeFilter;
  26.   TNotifyThread = class;
  27.  
  28. { TRxFolderMonitor }
  29.  
  30.   TRxFolderMonitor = class(TComponent)
  31.   private
  32.     FNotifyThread: TNotifyThread;
  33.     FFilter: TFileChangeFilters;
  34.     FDelayTimer: TTimer;
  35.     FDelayTime: Cardinal;
  36.     FMonitorSubtree: Boolean;
  37.     FFolderName: string;
  38.     FStreamedActive: Boolean;
  39.     FOnChange: TNotifyEvent;
  40.     function GetActive: Boolean;
  41.     function GetDelayTime: Cardinal;
  42.     procedure SetActive(Value: Boolean);
  43.     procedure SetFilter(Value: TFileChangeFilters);
  44.     procedure SetMonitorSubtree(Value: Boolean);
  45.     procedure SetFolderName(const Value: string);
  46.     procedure SetDelayTime(Value: Cardinal);
  47.     procedure Timer(Sender: TObject);
  48.     procedure ThreadNotification(Sender: TObject);
  49.   protected
  50.     procedure Loaded; override;
  51.     procedure Changed; dynamic;
  52.     procedure FreeNotifyThread;
  53.     procedure ResetNotifyThread(Activate: Boolean); virtual;
  54.   public
  55.     constructor Create(AOwner: TComponent); override;
  56.     destructor Destroy; override;
  57.   published
  58.     property Active: Boolean read GetActive write SetActive default False;
  59.     property DelayTime: Cardinal read GetDelayTime write SetDelayTime default 0;
  60.     property Filter: TFileChangeFilters read FFilter write SetFilter
  61.       default [fnFileName, fnDirName, fnSize, fnLastWrite];
  62.     property FolderName: string read FFolderName write SetFolderName;
  63.     property MonitorSubtree: Boolean read FMonitorSubtree write SetMonitorSubtree
  64.       default True;
  65.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  66.   end;
  67.  
  68. { TNotifyThread }
  69.  
  70.   TNotifyThread = class(TThread)
  71.   private
  72.     FNotifyHandle: THandle;
  73.     FEvent: THandle;
  74.     FOnChange: TNotifyEvent;
  75.     FFinished: Boolean;
  76.     FLastError: DWORD;
  77.     procedure CallOnChange;
  78.     procedure StopWaiting;
  79.   protected
  80.     procedure DoChange; virtual;
  81.     procedure DoTerminate; override;
  82.     procedure Execute; override;
  83.   public
  84.     constructor Create(const FolderName: string; WatchSubtree: Boolean;
  85.       Filter: TFileChangeFilters);
  86.     destructor Destroy; override;
  87.     procedure Terminate;
  88.     property Terminated;
  89.     property Finished: Boolean read FFinished;
  90.     property LastError: DWORD read FLastError;
  91.     property NotifyHandle: THandle read FNotifyHandle;
  92.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  93.   end;
  94.  
  95. function CreateNotifyThread(const FolderName: string; WatchSubtree: Boolean;
  96.   Filter: TFileChangeFilters): TNotifyThread;
  97.  
  98. {$ENDIF WIN32}
  99.  
  100. implementation
  101.  
  102. {$IFDEF WIN32}
  103.  
  104. uses Forms, VCLUtils, FileUtil;
  105.  
  106. {$IFNDEF RX_D3}
  107. const
  108.   FILE_NOTIFY_CHANGE_LAST_ACCESS  = $00000020;
  109.   FILE_NOTIFY_CHANGE_CREATION     = $00000040;
  110. {$ENDIF}
  111.  
  112. { TNotifyThread }
  113.  
  114. constructor TNotifyThread.Create(const FolderName: string;
  115.   WatchSubtree: Boolean; Filter: TFileChangeFilters);
  116. const
  117.   NotifyFilters: array[TFileChangeFilter] of DWORD = (
  118.     FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_DIR_NAME,
  119.     FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_SIZE,
  120.     FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_LAST_ACCESS,
  121.     FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_SECURITY);
  122. var
  123.   Filters: DWORD;
  124.   I: TFileChangeFilter;
  125.   Subtree: Integer;
  126. begin
  127.   FLastError := ERROR_SUCCESS;
  128.   Filters := 0;
  129.   for I := Low(TFileChangeFilter) to High(TFileChangeFilter) do
  130.     if I in Filter then Filters := Filters or NotifyFilters[I];
  131.   if WatchSubtree then Subtree := 1 else Subtree := 0;
  132.   FNotifyHandle := FindFirstChangeNotification(PChar(FolderName),
  133.     Bool(Subtree), Filters);
  134.   if FNotifyHandle <> INVALID_HANDLE_VALUE then
  135.     FEvent := CreateEvent(nil, BOOL(1), BOOL(0), nil)
  136.   else FLastError := GetLastError;
  137.   inherited Create(False);
  138. end;
  139.  
  140. destructor TNotifyThread.Destroy;
  141. begin
  142.   FOnChange := nil;
  143.   StopWaiting;
  144.   inherited Destroy;
  145. end;
  146.  
  147. procedure TNotifyThread.Terminate;
  148. begin
  149.   inherited Terminate;
  150.   StopWaiting;
  151. end;
  152.  
  153. procedure TNotifyThread.CallOnChange;
  154. begin
  155.   if Assigned(FOnChange) then FOnChange(Self);
  156. end;
  157.  
  158. procedure TNotifyThread.DoChange;
  159. begin
  160.   if Assigned(FOnChange) then Synchronize(CallOnChange);
  161. end;
  162.  
  163. procedure TNotifyThread.DoTerminate;
  164. begin
  165.   if FNotifyHandle <> INVALID_HANDLE_VALUE then
  166.     FindCloseChangeNotification(FNotifyHandle);
  167.   FNotifyHandle := INVALID_HANDLE_VALUE;
  168.   if FEvent <> 0 then CloseHandle(FEvent);
  169.   FEvent := 0;
  170.   inherited DoTerminate;
  171. end;
  172.  
  173. procedure TNotifyThread.Execute;
  174. var
  175.   Handles: array[0..1] of THandle;
  176. begin
  177.   while not Terminated and (FNotifyHandle <> INVALID_HANDLE_VALUE) do
  178.   begin
  179.     Handles[0] := FNotifyHandle;
  180.     Handles[1] := FEvent;
  181.     case WaitForMultipleObjects(2, PWOHandleArray(@Handles), False, INFINITE) of
  182.       WAIT_OBJECT_0: { notification }
  183.         if not Terminated then begin
  184.           DoChange;
  185.           if not FindNextChangeNotification(FNotifyHandle) then begin
  186.             FLastError := GetLastError;
  187.             Break;
  188.           end;
  189.         end;
  190.       WAIT_OBJECT_0 + 1: { event is signaled }
  191.         Break;
  192.       WAIT_FAILED:
  193.         begin
  194.           FLastError := GetLastError;
  195.           Break;
  196.         end;
  197.     end;
  198.   end;
  199.   FFinished := True;
  200. end;
  201.  
  202. procedure TNotifyThread.StopWaiting;
  203. begin
  204.   if FEvent <> 0 then SetEvent(FEvent);
  205. end;
  206.  
  207. function CreateNotifyThread(const FolderName: string; WatchSubtree: Boolean;
  208.   Filter: TFileChangeFilters): TNotifyThread;
  209. begin
  210.   Result := TNotifyThread.Create(FolderName, WatchSubtree, Filter);
  211.   try
  212.     if Result.LastError <> ERROR_SUCCESS then
  213.       RaiseWin32Error(Result.LastError);
  214.   except
  215.     Result.Free;
  216.     raise;
  217.   end;
  218. end;
  219.  
  220. { TRxFolderMonitor }
  221.  
  222. constructor TRxFolderMonitor.Create(AOwner: TComponent);
  223. begin
  224.   inherited Create(AOwner);
  225.   FFilter := [fnFileName, fnDirName, fnSize, fnLastWrite];
  226.   FMonitorSubtree := True;
  227. end;
  228.  
  229. destructor TRxFolderMonitor.Destroy;
  230. begin
  231.   if FDelayTimer <> nil then
  232.     FDelayTimer.OnTimer := nil;
  233.   FreeNotifyThread;
  234.   FDelayTimer.Free;
  235.   inherited Destroy;
  236. end;
  237.  
  238. procedure TRxFolderMonitor.Loaded;
  239. begin
  240.   inherited Loaded;
  241.   try
  242.     if FStreamedActive then Active := True;
  243.   except
  244.     if csDesigning in ComponentState then
  245.       Application.HandleException(Self)
  246.     else raise;
  247.   end;
  248. end;
  249.  
  250. function TRxFolderMonitor.GetActive: Boolean;
  251. begin
  252.   Result := FNotifyThread <> nil;
  253. end;
  254.  
  255. procedure TRxFolderMonitor.SetActive(Value: Boolean);
  256. begin
  257.   if (csReading in ComponentState) then begin
  258.     if Value then FStreamedActive := True;
  259.   end
  260.   else if Active <> Value then begin
  261.     ResetNotifyThread(Value);
  262.   end;
  263. end;
  264.  
  265. procedure TRxFolderMonitor.SetFilter(Value: TFileChangeFilters);
  266. var
  267.   SaveFilter: TFileChangeFilters;
  268.   IsActive: Boolean;
  269. begin
  270.   if FFilter <> Value then begin
  271.     SaveFilter := FFilter;
  272.     IsActive := Active;
  273.     FFilter := Value;
  274.     try
  275.       ResetNotifyThread(IsActive);
  276.     except
  277.       FFilter := SaveFilter;
  278.       if IsActive then
  279.       try
  280.         ResetNotifyThread(True);
  281.       except
  282.       end;
  283.       raise;
  284.     end;
  285.   end;
  286. end;
  287.  
  288. procedure TRxFolderMonitor.SetMonitorSubtree(Value: Boolean);
  289. begin
  290.   if FMonitorSubtree <> Value then begin
  291.     FMonitorSubtree := Value;
  292.     ResetNotifyThread(Active);
  293.   end;
  294. end;
  295.  
  296. procedure TRxFolderMonitor.SetFolderName(const Value: string);
  297. begin
  298.   if FFolderName <> Value then begin
  299.     FFolderName := Value;
  300.     ResetNotifyThread(Active);
  301.   end;
  302. end;
  303.  
  304. procedure TRxFolderMonitor.FreeNotifyThread;
  305. begin
  306.   if FNotifyThread <> nil then
  307.     with FNotifyThread do begin
  308.       OnChange := nil;
  309.       if FFinished then Free
  310.       else begin
  311.         FreeOnTerminate := True;
  312.         Terminate;
  313.       end;
  314.     end;
  315.   FNotifyThread := nil;
  316. end;
  317.  
  318. procedure TRxFolderMonitor.ResetNotifyThread(Activate: Boolean);
  319. begin
  320.   FreeNotifyThread;
  321.   if Activate and DirExists(FFolderName) then begin
  322.     FNotifyThread := CreateNotifyThread(FolderName, MonitorSubtree, Filter);
  323.     FNotifyThread.OnChange := ThreadNotification;
  324.   end;
  325. end;
  326.  
  327. function TRxFolderMonitor.GetDelayTime: Cardinal;
  328. begin
  329.   if FDelayTimer <> nil then
  330.     Result := FDelayTimer.Interval
  331.   else Result := FDelayTime;
  332. end;
  333.  
  334. procedure TRxFolderMonitor.SetDelayTime(Value: Cardinal);
  335. begin
  336.   if (FDelayTimer <> nil) then begin
  337.     if Value > 0 then
  338.       FDelayTimer.Interval := Value
  339.     else begin
  340.       FDelayTimer.OnTimer := nil;
  341.       FDelayTimer.Free;
  342.       FDelayTimer := nil;
  343.     end;
  344.   end;
  345.   FDelayTime := Value;
  346. end;
  347.  
  348. procedure TRxFolderMonitor.ThreadNotification(Sender: TObject);
  349. begin
  350.   if FDelayTime <= 0 then
  351.     Changed
  352.   else if FDelayTimer = nil then begin
  353.     FDelayTimer := TTimer.Create(Self);
  354.     with FDelayTimer do begin
  355.       Interval := FDelayTime;
  356.       OnTimer := Timer;
  357.       Enabled := True;
  358.     end;
  359.   end;
  360. end;
  361.  
  362. procedure TRxFolderMonitor.Timer(Sender: TObject);
  363. begin
  364.   FDelayTimer.Free;
  365.   FDelayTimer := nil;
  366.   Changed;
  367. end;
  368.  
  369. procedure TRxFolderMonitor.Changed;
  370. begin
  371.   if Assigned(FOnChange) then FOnChange(Self);
  372. end;
  373.  
  374. {$ENDIF WIN32}
  375.  
  376. end.