JD2022-TU1/main/tools/framework/JD.Core/FileWatcher.cs

294 lines
10 KiB
C#

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace JD.Core
{
/// <summary>
/// A wrapper around the System.IO.FileSystemWatcher class.
///
/// As the System.IO.FileSystemWatcher class may be unreliable under heavy charge, the wrapper transmits the events to a local queue and
/// another thread will execute events on a regular and configurable way. You can disable this feature to make it behave like the
/// original System.IO.FileSystemWatcher.
/// Please notice that, even with this feature, there is no guarantee that this implementation will catch all events, and that this implementation
/// shall not be used on network drives.
/// </summary>
public class FileWatcher : IDisposable
{
private string _path;
private string _filter;
private string _error = "";
private FileSystemWatcher _watcher;
private bool _immediateEventCall;
private int _pollingInterval = 10;
private int _bufferSize = 8192;
private readonly ConcurrentQueue<FileEvent> _fileEventQueue;
private FileSystemEventHandler _onCreated;
private FileSystemEventHandler _onChanged;
private FileSystemEventHandler _onDeleted;
private RenamedEventHandler _onRenamed;
private readonly FileSystemEventHandler _onCreatedDeferred;
private readonly FileSystemEventHandler _onChangedDeferred;
private readonly FileSystemEventHandler _onDeletedDeferred;
private readonly RenamedEventHandler _onRenamedDeferred;
private Task _task;
private CancellationTokenSource _cancellationTokenSource;
public FileWatcher(string path, string filter = "*.*")
{
_path = path;
_filter = filter;
_fileEventQueue = new ConcurrentQueue<FileEvent>();
_onCreatedDeferred = (sender, e) => _fileEventQueue.Enqueue(new FileEvent { Type = FileEventType.Created, Args = e });
_onChangedDeferred = (sender, e) => _fileEventQueue.Enqueue(new FileEvent { Type = FileEventType.Changed, Args = e });
_onDeletedDeferred = (sender, e) => _fileEventQueue.Enqueue(new FileEvent { Type = FileEventType.Created, Args = e });
_onRenamedDeferred = (sender, e) => _fileEventQueue.Enqueue(new FileEvent { Type = FileEventType.Renamed, Args = e });
}
public void Dispose()
{
Stop();
_watcher.Dispose();
_watcher = null;
DisposeTask();
}
private void DisposeTask()
{
if (_task != null)
{
if (!_task.IsCompleted)
_task.Wait();
_task.Dispose();
_task = null;
}
}
public string Path
{
get { return _path; }
set
{
if (_watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the Path property. ");
_path = value;
}
}
public string Filter
{
get { return _filter; }
set
{
if (_watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the Filter property. ");
_filter = value;
}
}
public int BufferSize
{
get { return _bufferSize; }
set
{
if (_watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the BufferSize property. ");
_bufferSize = value;
}
}
public bool ImmediateEventCall
{
get { return _immediateEventCall; }
set
{
if (_watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the ImmediateEventCall property. ");
_immediateEventCall = value;
}
}
public int PollingInterval
{
get { return _pollingInterval; }
set { _pollingInterval = value; }
}
public FileSystemEventHandler FileChanged
{
get { return _onChanged; }
set
{
if (_watcher != null && _watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the FileChanged property. ");
_onChanged = value;
}
}
public FileSystemEventHandler FileCreated
{
get { return _onCreated; }
set
{
if (_watcher != null && _watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the FileChanged property. ");
_onCreated = value;
}
}
public FileSystemEventHandler FileDeleted
{
get { return _onDeleted; }
set
{
if (_watcher != null && _watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the FileDeleted property. ");
_onDeleted = value;
}
}
public RenamedEventHandler FileRenamed
{
get { return _onRenamed; }
set
{
if (_watcher != null && _watcher.EnableRaisingEvents)
throw new InvalidOperationException("Stop the FileWatcher before changing the FileRenamed property. ");
_onRenamed = value;
}
}
[Obsolete("Unsafe. Use FileChanged / FileCreated / FileDeleted / FileRenamed instead. ")]
public void SetEventHandler(FileSystemEventHandler onChanged = null, FileSystemEventHandler onCreated = null, FileSystemEventHandler onDeleted = null, RenamedEventHandler onRenamed = null)
{
_onCreated = onCreated;
_onChanged = onChanged;
_onDeleted = onDeleted;
_onRenamed = onRenamed;
}
public bool Start()
{
if (_watcher != null) return true;
_error = "";
try
{
Directory.CreateDirectory(_path);
_watcher = new FileSystemWatcher
{
Path = _path,
NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName,
Filter = _filter,
InternalBufferSize = _bufferSize
};
// Add event handlers.
if (_onChanged != null)
_watcher.Changed += ImmediateEventCall ? _onChanged : _onChangedDeferred;
if (_onCreated != null)
_watcher.Created += ImmediateEventCall ? _onCreated : _onCreatedDeferred;
if (_onDeleted != null)
_watcher.Deleted += ImmediateEventCall ? _onDeleted : _onDeletedDeferred;
if (_onRenamed != null)
_watcher.Renamed += ImmediateEventCall ? _onRenamed : _onRenamedDeferred;
if (!ImmediateEventCall)
{
DisposeTask();
_task = TaskManager.Instance.CreateTask(FileEventThread);
_cancellationTokenSource = new CancellationTokenSource();
_task.Start();
}
// Begin watching.
_watcher.EnableRaisingEvents = true;
return true;
}
catch (Exception ex)
{
_error = $"Error creating watcher on {_path}\n\nException:\n{ex.Message}\n";
//MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
public void FileEventThread()
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
FileEvent fileEvent;
while (_fileEventQueue.TryDequeue(out fileEvent))
{
switch (fileEvent.Type)
{
case FileEventType.Changed:
_onChanged?.Invoke(this, (FileSystemEventArgs)fileEvent.Args);
break;
case FileEventType.Created:
_onCreated?.Invoke(this, (FileSystemEventArgs)fileEvent.Args);
break;
case FileEventType.Deleted:
_onDeleted?.Invoke(this, (FileSystemEventArgs)fileEvent.Args);
break;
case FileEventType.Renamed:
_onRenamed?.Invoke(this, (RenamedEventArgs)fileEvent.Args);
break;
}
}
if (PollingInterval != 0)
Thread.Sleep(PollingInterval);
}
}
public void Stop()
{
_watcher.EnableRaisingEvents = false;
// Remove event handlers.
if (_onChanged != null)
_watcher.Changed -= ImmediateEventCall ? _onChanged : _onChangedDeferred;
if (_onCreated != null)
_watcher.Created -= ImmediateEventCall ? _onCreated : _onCreatedDeferred;
if (_onDeleted != null)
_watcher.Deleted -= ImmediateEventCall ? _onDeleted : _onDeletedDeferred;
if (_onRenamed != null)
_watcher.Renamed -= ImmediateEventCall ? _onRenamed : _onRenamedDeferred;
_cancellationTokenSource.Cancel();
}
public enum FileEventType
{
Changed,
Created,
Deleted,
Renamed
}
public struct FileEvent
{
public FileEventType Type;
public EventArgs Args;
}
}
}