1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Runtime.Caching.Hosting; 7 using System.Runtime.Caching.Resources; 8 using System.Collections; 9 using System.IO; 10 using System.Security; 11 using System.Security.Permissions; 12 13 namespace System.Runtime.Caching 14 { 15 internal sealed class FileChangeNotificationSystem : IFileChangeNotificationSystem 16 { 17 private Hashtable _dirMonitors; 18 private object _lock; 19 20 internal class DirectoryMonitor 21 { 22 internal FileSystemWatcher Fsw; 23 } 24 25 internal class FileChangeEventTarget 26 { 27 private string _fileName; 28 private OnChangedCallback _onChangedCallback; 29 private FileSystemEventHandler _changedHandler; 30 private ErrorEventHandler _errorHandler; 31 private RenamedEventHandler _renamedHandler; 32 EqualsIgnoreCase(string s1, string s2)33 private static bool EqualsIgnoreCase(string s1, string s2) 34 { 35 if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) 36 { 37 return true; 38 } 39 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) 40 { 41 return false; 42 } 43 if (s2.Length != s1.Length) 44 { 45 return false; 46 } 47 return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase); 48 } 49 OnChanged(Object sender, FileSystemEventArgs e)50 private void OnChanged(Object sender, FileSystemEventArgs e) 51 { 52 if (EqualsIgnoreCase(_fileName, e.Name)) 53 { 54 _onChangedCallback(null); 55 } 56 } 57 OnError(Object sender, ErrorEventArgs e)58 private void OnError(Object sender, ErrorEventArgs e) 59 { 60 _onChangedCallback(null); 61 } 62 OnRenamed(Object sender, RenamedEventArgs e)63 private void OnRenamed(Object sender, RenamedEventArgs e) 64 { 65 if (EqualsIgnoreCase(_fileName, e.Name) || EqualsIgnoreCase(_fileName, e.OldName)) 66 { 67 _onChangedCallback(null); 68 } 69 } 70 71 internal FileSystemEventHandler ChangedHandler { get { return _changedHandler; } } 72 internal ErrorEventHandler ErrorHandler { get { return _errorHandler; } } 73 internal RenamedEventHandler RenamedHandler { get { return _renamedHandler; } } 74 FileChangeEventTarget(string fileName, OnChangedCallback onChangedCallback)75 internal FileChangeEventTarget(string fileName, OnChangedCallback onChangedCallback) 76 { 77 _fileName = fileName; 78 _onChangedCallback = onChangedCallback; 79 _changedHandler = new FileSystemEventHandler(this.OnChanged); 80 _errorHandler = new ErrorEventHandler(this.OnError); 81 _renamedHandler = new RenamedEventHandler(this.OnRenamed); 82 } 83 } 84 FileChangeNotificationSystem()85 internal FileChangeNotificationSystem() 86 { 87 _dirMonitors = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase)); 88 _lock = new object(); 89 } 90 IFileChangeNotificationSystem.StartMonitoring(string filePath, OnChangedCallback onChangedCallback, out Object state, out DateTimeOffset lastWriteTime, out long fileSize)91 void IFileChangeNotificationSystem.StartMonitoring(string filePath, OnChangedCallback onChangedCallback, out Object state, out DateTimeOffset lastWriteTime, out long fileSize) 92 { 93 if (filePath == null) 94 { 95 throw new ArgumentNullException("filePath"); 96 } 97 if (onChangedCallback == null) 98 { 99 throw new ArgumentNullException("onChangedCallback"); 100 } 101 FileInfo fileInfo = new FileInfo(filePath); 102 string dir = Path.GetDirectoryName(filePath); 103 DirectoryMonitor dirMon = _dirMonitors[dir] as DirectoryMonitor; 104 if (dirMon == null) 105 { 106 lock (_lock) 107 { 108 dirMon = _dirMonitors[dir] as DirectoryMonitor; 109 if (dirMon == null) 110 { 111 dirMon = new DirectoryMonitor(); 112 dirMon.Fsw = new FileSystemWatcher(dir); 113 dirMon.Fsw.NotifyFilter = NotifyFilters.FileName 114 | NotifyFilters.DirectoryName 115 | NotifyFilters.CreationTime 116 | NotifyFilters.Size 117 | NotifyFilters.LastWrite 118 | NotifyFilters.Security; 119 dirMon.Fsw.EnableRaisingEvents = true; 120 } 121 _dirMonitors[dir] = dirMon; 122 } 123 } 124 125 FileChangeEventTarget target = new FileChangeEventTarget(fileInfo.Name, onChangedCallback); 126 127 lock (dirMon) 128 { 129 dirMon.Fsw.Changed += target.ChangedHandler; 130 dirMon.Fsw.Created += target.ChangedHandler; 131 dirMon.Fsw.Deleted += target.ChangedHandler; 132 dirMon.Fsw.Error += target.ErrorHandler; 133 dirMon.Fsw.Renamed += target.RenamedHandler; 134 } 135 136 state = target; 137 lastWriteTime = File.GetLastWriteTime(filePath); 138 fileSize = (fileInfo.Exists) ? fileInfo.Length : -1; 139 } 140 IFileChangeNotificationSystem.StopMonitoring(string filePath, Object state)141 void IFileChangeNotificationSystem.StopMonitoring(string filePath, Object state) 142 { 143 if (filePath == null) 144 { 145 throw new ArgumentNullException("filePath"); 146 } 147 if (state == null) 148 { 149 throw new ArgumentNullException("state"); 150 } 151 FileChangeEventTarget target = state as FileChangeEventTarget; 152 if (target == null) 153 { 154 throw new ArgumentException(SR.Invalid_state, "state"); 155 } 156 string dir = Path.GetDirectoryName(filePath); 157 DirectoryMonitor dirMon = _dirMonitors[dir] as DirectoryMonitor; 158 if (dirMon != null) 159 { 160 lock (dirMon) 161 { 162 dirMon.Fsw.Changed -= target.ChangedHandler; 163 dirMon.Fsw.Created -= target.ChangedHandler; 164 dirMon.Fsw.Deleted -= target.ChangedHandler; 165 dirMon.Fsw.Error -= target.ErrorHandler; 166 dirMon.Fsw.Renamed -= target.RenamedHandler; 167 } 168 } 169 } 170 } 171 } 172