| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- //
- // System.IO.FileSystemWatcher.cs
- //
- // Authors:
- // Tim Coleman ([email protected])
- // Gonzalo Paniagua Javier ([email protected])
- //
- // Copyright (C) Tim Coleman, 2002
- // (c) 2003 Ximian, Inc. (http://www.ximian.com)
- // (c) 2004 Novell, Inc. (http://www.novell.com)
- //
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- using System;
- using System.ComponentModel;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Threading;
- namespace System.IO {
- [DefaultEvent("Changed")]
- public class FileSystemWatcher : Component, ISupportInitialize {
- #region Fields
- bool enableRaisingEvents;
- string filter;
- bool includeSubdirectories;
- int internalBufferSize;
- NotifyFilters notifyFilter;
- string path;
- string fullpath;
- ISynchronizeInvoke synchronizingObject;
- WaitForChangedResult lastData;
- bool waiting;
- SearchPattern2 pattern;
- bool disposed;
- string mangledFilter;
- static IFileWatcher watcher;
- static object lockobj = new object ();
- #endregion // Fields
- #region Constructors
- public FileSystemWatcher ()
- {
- this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
- this.enableRaisingEvents = false;
- this.filter = "*.*";
- this.includeSubdirectories = false;
- this.internalBufferSize = 8192;
- this.path = "";
- InitWatcher ();
- }
- public FileSystemWatcher (string path)
- : this (path, "*.*")
- {
- }
- public FileSystemWatcher (string path, string filter)
- {
- if (path == null)
- throw new ArgumentNullException ("path");
- if (filter == null)
- throw new ArgumentNullException ("filter");
- if (path == String.Empty)
- throw new ArgumentException ("Empty path", "path");
- if (!Directory.Exists (path))
- throw new ArgumentException ("Directory does not exists", "path");
- this.enableRaisingEvents = false;
- this.filter = filter;
- this.includeSubdirectories = false;
- this.internalBufferSize = 8192;
- this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
- this.path = path;
- this.synchronizingObject = null;
- InitWatcher ();
- }
- void InitWatcher ()
- {
- lock (lockobj) {
- if (watcher != null)
- return;
- string managed = Environment.GetEnvironmentVariable ("MONO_MANAGED_WATCHER");
- int mode = 0;
- if (managed == null)
- mode = InternalSupportsFSW ();
- bool ok = false;
- if (mode == 3)
- ok = KeventWatcher.GetInstance (out watcher);
- else if (mode == 2)
- ok = FAMWatcher.GetInstance (out watcher);
- else if (mode == 1)
- ok = DefaultWatcher.GetInstance (out watcher);
- //ok = WindowsWatcher.GetInstance (out watcher);
- if (mode == 0 || !ok)
- DefaultWatcher.GetInstance (out watcher);
- }
- }
- #endregion // Constructors
- #region Properties
- /* If this is enabled, we Pulse this instance */
- internal bool Waiting {
- get { return waiting; }
- set { waiting = value; }
- }
- internal string MangledFilter {
- get {
- if (filter != "*.*")
- return filter;
- if (mangledFilter != null)
- return mangledFilter;
- string filterLocal = "*.*";
- if (!(watcher.GetType () == typeof (WindowsWatcher)))
- filterLocal = "*";
- return filterLocal;
- }
- }
- internal SearchPattern2 Pattern {
- get {
- if (pattern == null) {
- pattern = new SearchPattern2 (MangledFilter);
- }
- return pattern;
- }
- }
- internal string FullPath {
- get {
- if (fullpath == null) {
- if (path == null || path == "")
- fullpath = Environment.CurrentDirectory;
- else
- fullpath = System.IO.Path.GetFullPath (path);
- }
- return fullpath;
- }
- }
- [DefaultValue(false)]
- [IODescription("Flag to indicate if this instance is active")]
- public bool EnableRaisingEvents {
- get { return enableRaisingEvents; }
- set {
- if (value == enableRaisingEvents)
- return; // Do nothing
- enableRaisingEvents = value;
- if (value) {
- Start ();
- } else {
- Stop ();
- }
- }
- }
- [DefaultValue("*.*")]
- [IODescription("File name filter pattern")]
- [RecommendedAsConfigurable(true)]
- [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
- public string Filter {
- get { return filter; }
- set {
- if (value == null || value == "")
- value = "*.*";
- if (filter != value) {
- filter = value;
- pattern = null;
- mangledFilter = null;
- }
- }
- }
- [DefaultValue(false)]
- [IODescription("Flag to indicate we want to watch subdirectories")]
- public bool IncludeSubdirectories {
- get { return includeSubdirectories; }
- set {
- if (includeSubdirectories == value)
- return;
- includeSubdirectories = value;
- if (value && enableRaisingEvents) {
- Stop ();
- Start ();
- }
- }
- }
- [Browsable(false)]
- [DefaultValue(8192)]
- public int InternalBufferSize {
- get { return internalBufferSize; }
- set {
- if (internalBufferSize == value)
- return;
- if (value < 4196)
- value = 4196;
- internalBufferSize = value;
- if (enableRaisingEvents) {
- Stop ();
- Start ();
- }
- }
- }
- [DefaultValue(NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite)]
- [IODescription("Flag to indicate which change event we want to monitor")]
- public NotifyFilters NotifyFilter {
- get { return notifyFilter; }
- set {
- if (notifyFilter == value)
- return;
-
- notifyFilter = value;
- if (enableRaisingEvents) {
- Stop ();
- Start ();
- }
- }
- }
- [DefaultValue("")]
- [IODescription("The directory to monitor")]
- [RecommendedAsConfigurable(true)]
- [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
- [Editor ("System.Diagnostics.Design.FSWPathEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
- public string Path {
- get { return path; }
- set {
- if (path == value)
- return;
- bool exists = false;
- Exception exc = null;
- try {
- exists = Directory.Exists (value);
- } catch (Exception e) {
- exc = e;
- }
- if (exc != null)
- throw new ArgumentException ("Invalid directory name", "value", exc);
- if (!exists)
- throw new ArgumentException ("Directory does not exists", "value");
- path = value;
- fullpath = null;
- if (enableRaisingEvents) {
- Stop ();
- Start ();
- }
- }
- }
- [Browsable(false)]
- public override ISite Site {
- get { return base.Site; }
- set { base.Site = value; }
- }
- [DefaultValue(null)]
- [IODescription("The object used to marshal the event handler calls resulting from a directory change")]
- public ISynchronizeInvoke SynchronizingObject {
- get { return synchronizingObject; }
- set { synchronizingObject = value; }
- }
- #endregion // Properties
- #region Methods
-
- [MonoTODO]
- public void BeginInit ()
- {
- throw new NotImplementedException ();
- }
- protected override void Dispose (bool disposing)
- {
- if (!disposed) {
- disposed = true;
- Stop ();
- }
- base.Dispose (disposing);
- }
- ~FileSystemWatcher ()
- {
- disposed = true;
- Stop ();
- }
-
- [MonoTODO]
- public void EndInit ()
- {
- throw new NotImplementedException ();
- }
- private void RaiseEvent (Delegate ev, EventArgs arg)
- {
- if (ev == null)
- return;
- object [] args = new object [] {this, arg};
- if (synchronizingObject == null) {
- ev.DynamicInvoke (args);
- return;
- }
-
- synchronizingObject.BeginInvoke (ev, args);
- }
- protected void OnChanged (FileSystemEventArgs e)
- {
- RaiseEvent (Changed, e);
- }
- protected void OnCreated (FileSystemEventArgs e)
- {
- RaiseEvent (Created, e);
- }
- protected void OnDeleted (FileSystemEventArgs e)
- {
- RaiseEvent (Deleted, e);
- }
- protected void OnError (ErrorEventArgs e)
- {
- RaiseEvent (Error, e);
- }
- protected void OnRenamed (RenamedEventArgs e)
- {
- RaiseEvent (Renamed, e);
- }
- public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType)
- {
- return WaitForChanged (changeType, Timeout.Infinite);
- }
- public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType, int timeout)
- {
- WaitForChangedResult result = new WaitForChangedResult ();
- bool prevEnabled = EnableRaisingEvents;
- if (!prevEnabled)
- EnableRaisingEvents = true;
- bool gotData;
- lock (this) {
- waiting = true;
- gotData = Monitor.Wait (this, timeout);
- if (gotData)
- result = this.lastData;
- }
- EnableRaisingEvents = prevEnabled;
- if (!gotData)
- result.TimedOut = true;
- return result;
- }
- internal void DispatchEvents (FileAction act, string filename, ref RenamedEventArgs renamed)
- {
- if (waiting) {
- lastData = new WaitForChangedResult ();
- }
- switch (act) {
- case FileAction.Added:
- lastData.Name = filename;
- lastData.ChangeType = WatcherChangeTypes.Created;
- OnCreated (new FileSystemEventArgs (WatcherChangeTypes.Created, path, filename));
- break;
- case FileAction.Removed:
- lastData.Name = filename;
- lastData.ChangeType = WatcherChangeTypes.Deleted;
- OnDeleted (new FileSystemEventArgs (WatcherChangeTypes.Deleted, path, filename));
- break;
- case FileAction.Modified:
- lastData.Name = filename;
- lastData.ChangeType = WatcherChangeTypes.Changed;
- OnChanged (new FileSystemEventArgs (WatcherChangeTypes.Changed, path, filename));
- break;
- case FileAction.RenamedOldName:
- if (renamed != null) {
- OnRenamed (renamed);
- }
- lastData.OldName = filename;
- lastData.ChangeType = WatcherChangeTypes.Renamed;
- renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, path, null, filename);
- break;
- case FileAction.RenamedNewName:
- lastData.Name = filename;
- lastData.ChangeType = WatcherChangeTypes.Renamed;
- if (renamed != null) {
- renamed.SetName (filename);
- } else {
- renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, path, filename, null);
- }
- OnRenamed (renamed);
- renamed = null;
- break;
- default:
- break;
- }
- }
- void Start ()
- {
- watcher.StartDispatching (this);
- }
- void Stop ()
- {
- watcher.StopDispatching (this);
- }
- #endregion // Methods
- #region Events and Delegates
- [IODescription("Occurs when a file/directory change matches the filter")]
- public event FileSystemEventHandler Changed;
- [IODescription("Occurs when a file/directory creation matches the filter")]
- public event FileSystemEventHandler Created;
- [IODescription("Occurs when a file/directory deletion matches the filter")]
- public event FileSystemEventHandler Deleted;
- [Browsable(false)]
- public event ErrorEventHandler Error;
- [IODescription("Occurs when a file/directory rename matches the filter")]
- public event RenamedEventHandler Renamed;
- #endregion // Events and Delegates
- /* 0 -> not supported */
- /* 1 -> windows */
- /* 2 -> FAM */
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- static extern int InternalSupportsFSW ();
-
- /*[MethodImplAttribute(MethodImplOptions.InternalCall)]
- static extern IntPtr InternalOpenDirectory (string path, IntPtr reserved);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- static extern IntPtr InternalCloseDirectory (IntPtr handle);
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- static extern bool InternalReadDirectoryChanges (IntPtr handle,
- byte [] buffer,
- bool includeSubdirectories,
- NotifyFilters notifyFilter,
- out NativeOverlapped overlap,
- OverlappedHandler overlappedCallback);
- */
- }
- }
|