RxNotify.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:10k
源码类别:

Delphi控件源码

开发平台:

Delphi

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