-// \r
-// System.IO.FileSystemWatcher.cs\r
-//\r
-// Authors:\r
-// Tim Coleman (tim@timcoleman.com)\r
-// Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
-//\r
-// Copyright (C) Tim Coleman, 2002 \r
-// (c) 2003 Ximian, Inc. (http://www.ximian.com)\r
-//\r
-\r
-using System;\r
-using System.ComponentModel;\r
-using System.Threading;\r
-\r
-namespace System.IO {\r
- [DefaultEvent("Changed")]\r
- public class FileSystemWatcher : Component, ISupportInitialize {\r
-\r
- #region Fields\r
-\r
- bool enableRaisingEvents;\r
- string filter;\r
- bool includeSubdirectories;\r
- int internalBufferSize;\r
- NotifyFilters notifyFilter;\r
- string path;\r
- ISite site;\r
- ISynchronizeInvoke synchronizingObject;\r
-\r
- #endregion // Fields\r
-\r
- #region Constructors\r
-\r
- public FileSystemWatcher ()\r
- {\r
- this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;\r
- this.enableRaisingEvents = false;\r
- this.filter = "*.*";\r
- this.includeSubdirectories = false;\r
- this.internalBufferSize = 8192;\r
- this.path = "";\r
- }\r
-\r
- public FileSystemWatcher (string path)\r
- : this (path, String.Empty)\r
- {\r
- }\r
-\r
- public FileSystemWatcher (string path, string filter)\r
- {\r
- if (path == null)\r
- throw new ArgumentNullException ("path");\r
-\r
- if (filter == null)\r
- throw new ArgumentNullException ("filter");\r
-\r
- if (path == String.Empty)\r
- throw new ArgumentException ("Empty path", "path");\r
-\r
- if (!Directory.Exists (path))\r
- throw new ArgumentException ("Directory does not exists", "path");\r
-\r
- this.enableRaisingEvents = false;\r
- this.filter = filter;\r
- this.includeSubdirectories = false;\r
- this.internalBufferSize = 8192;\r
- this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;\r
- this.path = path;\r
- this.synchronizingObject = null;\r
- }\r
-\r
- #endregion // Constructors\r
-\r
- #region Properties\r
-\r
- [DefaultValue(false)]\r
- [IODescription("Flag to indicate if this instance is active")]\r
- public bool EnableRaisingEvents {\r
- get { return enableRaisingEvents; }\r
- set { enableRaisingEvents = value; }\r
- }\r
-\r
- [DefaultValue("*.*")]\r
- [IODescription("File name filter pattern")]\r
- [RecommendedAsConfigurable(true)]\r
- public string Filter {\r
- get { return filter; }\r
- set {\r
- filter = value;\r
- if (filter == null || filter == "")\r
- filter = "*.*";\r
- }\r
- }\r
-\r
- [DefaultValue(false)]\r
- [IODescription("Flag to indicate we want to watch subdirectories")]\r
- public bool IncludeSubdirectories {\r
- get { return includeSubdirectories; }\r
- set { includeSubdirectories = value; }\r
- }\r
-\r
- [Browsable(false)]\r
- [DefaultValue(8192)]\r
- public int InternalBufferSize {\r
- get { return internalBufferSize; }\r
- set { internalBufferSize = value; }\r
- }\r
-\r
- [DefaultValue(NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite)]\r
- [IODescription("Flag to indicate which change event we want to monitor")]\r
- public NotifyFilters NotifyFilter {\r
- get { return notifyFilter; }\r
- [MonoTODO ("Perform validation.")]\r
- set { notifyFilter = value; }\r
- }\r
-\r
- [DefaultValue("")]\r
- [IODescription("The directory to monitor")]\r
- [RecommendedAsConfigurable(true)]\r
- public string Path {\r
- get { return path; }\r
- set {\r
- bool exists = false;\r
- Exception exc = null;\r
-\r
- try {\r
- exists = Directory.Exists (value);\r
- } catch (Exception e) {\r
- exists = false;\r
- exc = e;\r
- }\r
-\r
- if (exc != null)\r
- throw new ArgumentException ("Invalid directory name", "value", exc);\r
-\r
- if (!exists)\r
- throw new ArgumentException ("Directory does not exists", "value");\r
-\r
- path = value;\r
- }\r
- }\r
-\r
- [Browsable(false)]\r
- public override ISite Site {\r
- get { return site; }\r
- set { site = value; }\r
- }\r
-\r
- [DefaultValue(null)]\r
- [IODescription("The object used to marshal the event handler calls resulting from a directory change")]\r
- public ISynchronizeInvoke SynchronizingObject {\r
- get { return synchronizingObject; }\r
- set { synchronizingObject = value; }\r
- }\r
-\r
- #endregion // Properties\r
-\r
- #region Methods\r
- \r
- [MonoTODO]\r
- public void BeginInit ()\r
- {\r
- throw new NotImplementedException (); \r
- }\r
-\r
- [MonoTODO]\r
- protected override void Dispose (bool disposing)\r
- {\r
- if (disposing) {\r
- // \r
- }\r
- base.Dispose (disposing);\r
- }\r
-\r
- [MonoTODO]\r
- public void EndInit ()\r
- {\r
- throw new NotImplementedException (); \r
- }\r
-\r
- private void RaiseEvent (Delegate ev, EventArgs arg)\r
- {\r
- if (ev == null)\r
- return;\r
-\r
- object [] args = new object [] {this, arg};\r
-\r
- if (synchronizingObject == null) {\r
- ev.DynamicInvoke (args);\r
- return;\r
- }\r
- \r
- synchronizingObject.BeginInvoke (ev, args);\r
- }\r
-\r
- protected void OnChanged (FileSystemEventArgs e)\r
- {\r
- RaiseEvent (Changed, e);\r
- }\r
-\r
- protected void OnCreated (FileSystemEventArgs e)\r
- {\r
- RaiseEvent (Created, e);\r
- }\r
-\r
- protected void OnDeleted (FileSystemEventArgs e)\r
- {\r
- RaiseEvent (Deleted, e);\r
- }\r
-\r
- protected void OnError (ErrorEventArgs e)\r
- {\r
- RaiseEvent (Error, e);\r
- }\r
-\r
- protected void OnRenamed (RenamedEventArgs e)\r
- {\r
- RaiseEvent (Renamed, e);\r
- }\r
-\r
- public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType)\r
- {\r
- return WaitForChanged (changeType, Timeout.Infinite);\r
- }\r
-\r
- [MonoTODO]\r
- public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType, int timeout)\r
- {\r
- throw new NotImplementedException (); \r
- }\r
-\r
- #endregion // Methods\r
-\r
- #region Events and Delegates\r
-\r
- [IODescription("Occurs when a file/directory change matches the filter")]\r
- public event FileSystemEventHandler Changed;\r
-\r
-\r
- [IODescription("Occurs when a file/directory creation matches the filter")]\r
- public event FileSystemEventHandler Created;\r
-\r
- [IODescription("Occurs when a file/directory deletion matches the filter")]\r
- public event FileSystemEventHandler Deleted;\r
-\r
- [Browsable(false)]\r
- public event ErrorEventHandler Error;\r
-\r
- [IODescription("Occurs when a file/directory rename matches the filter")]\r
- public event RenamedEventHandler Renamed;\r
-\r
- #endregion // Events and Delegates\r
- }\r
-}\r
+//
+// System.IO.FileSystemWatcher.cs
+//
+// Authors:
+// Tim Coleman (tim@timcoleman.com)
+// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// Copyright (C) Tim Coleman, 2002
+// (c) 2003 Ximian, Inc. (http://www.ximian.com)
+// (c) 2004 Novell, Inc. (http://www.novell.com)
+//
+
+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;
+ static IFileWatcher watcher;
+
+ #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 (typeof (FileSystemWatcher)) {
+ if (watcher != null)
+ return;
+
+ string managed = Environment.GetEnvironmentVariable ("MONO_MANAGED_WATCHER");
+ int mode = 0;
+ if (managed == null)
+ mode = InternalSupportsFSW ();
+
+ bool ok = false;
+ 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 SearchPattern2 Pattern {
+ get {
+ if (pattern == null) {
+ string f = Filter;
+ if (f == "*.*" && !(watcher.GetType () == typeof (WindowsWatcher)))
+ f = "*";
+
+ pattern = new SearchPattern2 (f);
+ }
+ 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;
+ }
+ }
+ }
+
+ [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 (disposing) {
+ Stop ();
+ }
+ base.Dispose (disposing);
+ }
+
+ [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);
+
+ */
+ }
+}