WatchedFileUpdateEventHandlerProperty.cs
using GenXdev.Helpers;
using System.Collections.Concurrent; namespace GenXdev.Events { public class WatchedFileUpdateEventHandlerProperty : FileUpdateEventHandlerProperty, IDisposable { FileSystemWatcher Watcher; ConcurrentDictionary<string, System.Threading.Timer> WatchedFiles = new ConcurrentDictionary<string, System.Threading.Timer>(); public WatchedFileUpdateEventHandlerProperty( string directory, string searchMask, bool includeSubDirectories, bool startNow ) : base(directory, searchMask, includeSubDirectories) { if (startNow) { Start(); } } public bool Started { get; private set; } public void Stop() { if (!Started) return; Started = false; if (Watcher != null) { Watcher.EnableRaisingEvents = false; Watcher.Dispose(); Watcher = null; } } public void Start() { if (Started) return; Started = true; RecreateWatcher(); } void RecreateWatcher() { if (!Started) return; try { if (Watcher != null) { Watcher.Dispose(); } } catch { } try { GenXdev.Helpers.FileSystem.ForciblyPrepareTargetDirectory(Directory); Watcher = new FileSystemWatcher(Directory, SearchMask); Watcher.IncludeSubdirectories = IncludeSubdirectories; Watcher.InternalBufferSize = 1024 * 64; Watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; Watcher.Error += new ErrorEventHandler(OnError); Watcher.Changed += new FileSystemEventHandler(OnChanged); Watcher.Created += new FileSystemEventHandler(OnChanged); Watcher.Deleted += new FileSystemEventHandler(OnChanged); Watcher.Renamed += new RenamedEventHandler(OnRenamed); if (Started) { Watcher.EnableRaisingEvents = true; } } catch { } } void OnError(object sender, ErrorEventArgs e) { ThreadPool.QueueUserWorkItem((object state) => { System.Threading.Thread.Sleep(10); RecreateWatcher(); }); } void OnRenamed(object source, RenamedEventArgs e) { OnChanged(e.FullPath); } void OnChanged(object source, FileSystemEventArgs e) { OnChanged(e.FullPath); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")] private void OnChanged(string fullPath) { if (!TaskHelpers.NotLockedOrExpired("fileLocks", fullPath)) return; if (WatchedFiles.TryGetValue(fullPath, out System.Threading.Timer timer)) { lock (timer) try { timer.Change(Timeout.Infinite, Timeout.Infinite); timer.Change(1000, 30000); } catch { } } else { timer = null; } if (timer == null) { timer = new System.Threading.Timer(TimerCallback, fullPath, 1000, 30000); if (!WatchedFiles.TryAdd(fullPath, timer)) { timer.Dispose(); } } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")] void TimerCallback(object state) { var fullPath = (string)state; if (!WatchedFiles.TryGetValue(fullPath, out System.Threading.Timer timer)) return; try { timer.Change(Timeout.Infinite, Timeout.Infinite); if (TaskHelpers.NotLockedOrExpired("fileLocks", fullPath)) { if (WatchedFiles.TryRemove(fullPath, out System.Threading.Timer removedTimer)) { lock (removedTimer) try { removedTimer.Change(Timeout.Infinite, Timeout.Infinite); removedTimer.Dispose(); } catch { } } base.TriggerEvent(this, fullPath); return; } timer.Change(1000, 30000); } catch { try { timer.Change(30000, 30000); } catch { } } } #region IDisposable Support bool disposedValue = false; protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { if (Watcher != null) { Watcher.Dispose(); Watcher = null; } } disposedValue = true; } } // This code added to correctly implement the disposable pattern. public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); } #endregion } } |