home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d6 / RX275D6.ZIP / Units / RxNotify.pas < prev    next >
Pascal/Delphi Source File  |  1999-10-12  |  10KB  |  373 lines

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