2014-08-13 10:39:27 +01:00
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
// Copyright (C) 2004, 2006 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.ComponentModel;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using System.Security.Permissions;
|
|
|
|
using System.Threading;
|
2018-06-01 08:44:40 +00:00
|
|
|
using System.Threading.Tasks;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
namespace System.IO {
|
|
|
|
[DefaultEvent("Changed")]
|
|
|
|
[IODescription ("")]
|
|
|
|
public class FileSystemWatcher : Component, ISupportInitialize {
|
|
|
|
|
|
|
|
#region Fields
|
|
|
|
|
2018-05-10 08:37:03 +00:00
|
|
|
bool inited;
|
|
|
|
bool start_requested;
|
2014-08-13 10:39:27 +01:00
|
|
|
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;
|
2018-05-10 08:37:03 +00:00
|
|
|
object watcher_handle;
|
2014-08-13 10:39:27 +01:00
|
|
|
static object lockobj = new object ();
|
|
|
|
|
|
|
|
#endregion // Fields
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
|
|
public FileSystemWatcher ()
|
|
|
|
{
|
|
|
|
this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
|
|
|
|
this.enableRaisingEvents = false;
|
2018-08-07 15:19:03 +00:00
|
|
|
this.filter = "*";
|
2014-08-13 10:39:27 +01:00
|
|
|
this.includeSubdirectories = false;
|
|
|
|
this.internalBufferSize = 8192;
|
|
|
|
this.path = "";
|
|
|
|
InitWatcher ();
|
|
|
|
}
|
|
|
|
|
|
|
|
public FileSystemWatcher (string path)
|
2018-08-07 15:19:03 +00:00
|
|
|
: this (path, "*")
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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 exist", "path");
|
|
|
|
|
2018-05-10 08:37:03 +00:00
|
|
|
this.inited = false;
|
|
|
|
this.start_requested = false;
|
2014-08-13 10:39:27 +01:00
|
|
|
this.enableRaisingEvents = false;
|
|
|
|
this.filter = filter;
|
2018-08-07 15:19:03 +00:00
|
|
|
if (this.filter == "*.*")
|
|
|
|
this.filter = "*";
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
this.includeSubdirectories = false;
|
|
|
|
this.internalBufferSize = 8192;
|
|
|
|
this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
|
|
|
|
this.path = path;
|
|
|
|
this.synchronizingObject = null;
|
|
|
|
InitWatcher ();
|
|
|
|
}
|
|
|
|
|
|
|
|
[EnvironmentPermission (SecurityAction.Assert, Read="MONO_MANAGED_WATCHER")]
|
|
|
|
void InitWatcher ()
|
|
|
|
{
|
|
|
|
lock (lockobj) {
|
2018-06-01 08:44:40 +00:00
|
|
|
if (watcher_handle != null)
|
2014-08-13 10:39:27 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
string managed = Environment.GetEnvironmentVariable ("MONO_MANAGED_WATCHER");
|
|
|
|
int mode = 0;
|
|
|
|
if (managed == null)
|
|
|
|
mode = InternalSupportsFSW ();
|
|
|
|
|
|
|
|
bool ok = false;
|
|
|
|
switch (mode) {
|
|
|
|
case 1: // windows
|
|
|
|
ok = DefaultWatcher.GetInstance (out watcher);
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = this;
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case 2: // libfam
|
|
|
|
ok = FAMWatcher.GetInstance (out watcher, false);
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = this;
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case 3: // kevent
|
|
|
|
ok = KeventWatcher.GetInstance (out watcher);
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = this;
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case 4: // libgamin
|
|
|
|
ok = FAMWatcher.GetInstance (out watcher, true);
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = this;
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case 5: // inotify
|
|
|
|
ok = InotifyWatcher.GetInstance (out watcher, true);
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = this;
|
|
|
|
break;
|
|
|
|
case 6: // CoreFX
|
|
|
|
ok = CoreFXFileSystemWatcherProxy.GetInstance (out watcher);
|
|
|
|
watcher_handle = (watcher as CoreFXFileSystemWatcherProxy).NewWatcher (this);
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == 0 || !ok) {
|
|
|
|
if (String.Compare (managed, "disabled", true) == 0)
|
|
|
|
NullFileWatcher.GetInstance (out watcher);
|
2018-05-10 08:37:03 +00:00
|
|
|
else {
|
2014-08-13 10:39:27 +01:00
|
|
|
DefaultWatcher.GetInstance (out watcher);
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = this;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
2018-05-10 08:37:03 +00:00
|
|
|
this.inited = true;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
ShowWatcherInfo ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Conditional ("DEBUG"), Conditional ("TRACE")]
|
|
|
|
void ShowWatcherInfo ()
|
|
|
|
{
|
|
|
|
Console.WriteLine ("Watcher implementation: {0}", watcher != null ? watcher.GetType ().ToString () : "<none>");
|
|
|
|
}
|
|
|
|
|
|
|
|
#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 = "*.*";
|
|
|
|
return filterLocal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal SearchPattern2 Pattern {
|
|
|
|
get {
|
|
|
|
if (pattern == null) {
|
2018-05-10 08:37:03 +00:00
|
|
|
if (watcher?.GetType () == typeof (KeventWatcher))
|
2015-01-13 10:44:36 +00:00
|
|
|
pattern = new SearchPattern2 (MangledFilter, true); //assume we want to ignore case (OS X)
|
|
|
|
else
|
|
|
|
pattern = new SearchPattern2 (MangledFilter);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
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 {
|
2018-05-10 08:37:03 +00:00
|
|
|
if (disposed)
|
|
|
|
throw new ObjectDisposedException (GetType().Name);
|
|
|
|
|
|
|
|
start_requested = true;
|
|
|
|
if (!inited)
|
|
|
|
return;
|
2014-08-13 10:39:27 +01:00
|
|
|
if (value == enableRaisingEvents)
|
|
|
|
return; // Do nothing
|
|
|
|
|
|
|
|
enableRaisingEvents = value;
|
|
|
|
if (value) {
|
|
|
|
Start ();
|
|
|
|
} else {
|
|
|
|
Stop ();
|
2018-05-10 08:37:03 +00:00
|
|
|
start_requested = false;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[DefaultValue("*.*")]
|
|
|
|
[IODescription("File name filter pattern")]
|
2017-04-10 11:41:01 +00:00
|
|
|
[SettingsBindable(true)]
|
2014-08-13 10:39:27 +01:00
|
|
|
[TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
|
|
|
|
public string Filter {
|
|
|
|
get { return filter; }
|
|
|
|
set {
|
|
|
|
if (value == null || value == "")
|
2018-08-07 15:19:03 +00:00
|
|
|
value = "*";
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2018-05-10 08:37:03 +00:00
|
|
|
if (!string.Equals(filter, value, PathInternal.StringComparison)) {
|
2018-08-07 15:19:03 +00:00
|
|
|
filter = value == "*.*" ? "*" : value;
|
2014-08-13 10:39:27 +01:00
|
|
|
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;
|
|
|
|
|
2018-05-10 08:37:03 +00:00
|
|
|
if (value < 4096)
|
|
|
|
value = 4096;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
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")]
|
2017-04-10 11:41:01 +00:00
|
|
|
[SettingsBindable(true)]
|
2014-08-13 10:39:27 +01:00
|
|
|
[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 {
|
2018-05-10 08:37:03 +00:00
|
|
|
if (disposed)
|
|
|
|
throw new ObjectDisposedException (GetType().Name);
|
|
|
|
|
|
|
|
value = (value == null) ? string.Empty : value;
|
|
|
|
if (string.Equals(path, value, PathInternal.StringComparison))
|
2014-08-13 10:39:27 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
bool exists = false;
|
|
|
|
Exception exc = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
exists = Directory.Exists (value);
|
|
|
|
} catch (Exception e) {
|
|
|
|
exc = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exc != null)
|
2018-05-10 08:37:03 +00:00
|
|
|
throw new ArgumentException(SR.Format(SR.InvalidDirName, value), nameof(Path));
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
if (!exists)
|
2018-05-10 08:37:03 +00:00
|
|
|
throw new ArgumentException(SR.Format(SR.InvalidDirName_NotExists, value), nameof(Path));
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
path = value;
|
|
|
|
fullpath = null;
|
|
|
|
if (enableRaisingEvents) {
|
|
|
|
Stop ();
|
|
|
|
Start ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[Browsable(false)]
|
|
|
|
public override ISite Site {
|
|
|
|
get { return base.Site; }
|
2018-05-10 08:37:03 +00:00
|
|
|
set
|
|
|
|
{
|
|
|
|
base.Site = value;
|
|
|
|
if (Site != null && Site.DesignMode)
|
|
|
|
this.EnableRaisingEvents = true;
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
[DefaultValue(null)]
|
|
|
|
[IODescription("The object used to marshal the event handler calls resulting from a directory change")]
|
|
|
|
[Browsable (false)]
|
|
|
|
public ISynchronizeInvoke SynchronizingObject {
|
|
|
|
get { return synchronizingObject; }
|
|
|
|
set { synchronizingObject = value; }
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion // Properties
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
|
|
public void BeginInit ()
|
|
|
|
{
|
|
|
|
// Not necessary in Mono
|
2018-05-10 08:37:03 +00:00
|
|
|
// but if called, EndInit() must be called
|
|
|
|
inited = false;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override void Dispose (bool disposing)
|
|
|
|
{
|
2018-05-10 08:37:03 +00:00
|
|
|
if (disposed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
try {
|
|
|
|
watcher?.StopDispatching (watcher_handle);
|
|
|
|
watcher?.Dispose (watcher_handle);
|
|
|
|
} catch (Exception) { }
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2018-05-10 08:37:03 +00:00
|
|
|
watcher_handle = null;
|
|
|
|
watcher = null;
|
|
|
|
|
|
|
|
disposed = true;
|
2014-08-13 10:39:27 +01:00
|
|
|
base.Dispose (disposing);
|
2018-05-10 08:37:03 +00:00
|
|
|
GC.SuppressFinalize (this);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
~FileSystemWatcher ()
|
|
|
|
{
|
2018-05-10 08:37:03 +00:00
|
|
|
if (disposed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Dispose (false);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void EndInit ()
|
|
|
|
{
|
2018-05-10 08:37:03 +00:00
|
|
|
inited = true;
|
|
|
|
if (start_requested)
|
|
|
|
this.EnableRaisingEvents = true;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
enum EventType {
|
|
|
|
FileSystemEvent,
|
|
|
|
ErrorEvent,
|
|
|
|
RenameEvent
|
|
|
|
}
|
2015-04-07 09:35:12 +01:00
|
|
|
private void RaiseEvent (Delegate ev, EventArgs arg, EventType evtype)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
if (ev == null)
|
2015-01-13 10:44:36 +00:00
|
|
|
return;
|
|
|
|
|
2015-04-07 09:35:12 +01:00
|
|
|
if (synchronizingObject == null) {
|
|
|
|
foreach (var target in ev.GetInvocationList()) {
|
|
|
|
switch (evtype) {
|
|
|
|
case EventType.RenameEvent:
|
2018-05-10 08:37:03 +00:00
|
|
|
((RenamedEventHandler)target).Invoke (this, (RenamedEventArgs)arg);
|
2015-04-07 09:35:12 +01:00
|
|
|
break;
|
|
|
|
case EventType.ErrorEvent:
|
2018-05-10 08:37:03 +00:00
|
|
|
((ErrorEventHandler)target).Invoke (this, (ErrorEventArgs)arg);
|
2015-04-07 09:35:12 +01:00
|
|
|
break;
|
|
|
|
case EventType.FileSystemEvent:
|
2018-05-10 08:37:03 +00:00
|
|
|
((FileSystemEventHandler)target).Invoke (this, (FileSystemEventArgs)arg);
|
2015-04-07 09:35:12 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronizingObject.BeginInvoke (ev, new object [] {this, arg});
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2015-04-07 09:35:12 +01:00
|
|
|
protected void OnChanged (FileSystemEventArgs e)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
RaiseEvent (Changed, e, EventType.FileSystemEvent);
|
|
|
|
}
|
2015-01-13 10:44:36 +00:00
|
|
|
|
2015-04-07 09:35:12 +01:00
|
|
|
protected void OnCreated (FileSystemEventArgs e)
|
|
|
|
{
|
|
|
|
RaiseEvent (Created, e, EventType.FileSystemEvent);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected void OnDeleted (FileSystemEventArgs e)
|
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
RaiseEvent (Deleted, e, EventType.FileSystemEvent);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2015-04-07 09:35:12 +01:00
|
|
|
protected void OnError (ErrorEventArgs e)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
RaiseEvent (Error, e, EventType.ErrorEvent);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected void OnRenamed (RenamedEventArgs e)
|
|
|
|
{
|
2015-04-07 09:35:12 +01:00
|
|
|
RaiseEvent (Renamed, e, EventType.RenameEvent);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-04-07 09:35:12 +01:00
|
|
|
internal void DispatchErrorEvents (ErrorEventArgs args)
|
|
|
|
{
|
|
|
|
OnError (args);
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
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;
|
2018-06-01 08:44:40 +00:00
|
|
|
Task.Run (() => OnCreated (new FileSystemEventArgs (WatcherChangeTypes.Created, path, filename)));
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case FileAction.Removed:
|
|
|
|
lastData.Name = filename;
|
|
|
|
lastData.ChangeType = WatcherChangeTypes.Deleted;
|
2018-06-01 08:44:40 +00:00
|
|
|
Task.Run (() => OnDeleted (new FileSystemEventArgs (WatcherChangeTypes.Deleted, path, filename)));
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case FileAction.Modified:
|
|
|
|
lastData.Name = filename;
|
|
|
|
lastData.ChangeType = WatcherChangeTypes.Changed;
|
2018-06-01 08:44:40 +00:00
|
|
|
Task.Run (() => OnChanged (new FileSystemEventArgs (WatcherChangeTypes.Changed, path, filename)));
|
2014-08-13 10:39:27 +01:00
|
|
|
break;
|
|
|
|
case FileAction.RenamedOldName:
|
|
|
|
if (renamed != null) {
|
|
|
|
OnRenamed (renamed);
|
|
|
|
}
|
|
|
|
lastData.OldName = filename;
|
|
|
|
lastData.ChangeType = WatcherChangeTypes.Renamed;
|
|
|
|
renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, path, filename, "");
|
|
|
|
break;
|
|
|
|
case FileAction.RenamedNewName:
|
|
|
|
lastData.Name = filename;
|
|
|
|
lastData.ChangeType = WatcherChangeTypes.Renamed;
|
|
|
|
if (renamed == null) {
|
|
|
|
renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, path, "", filename);
|
|
|
|
}
|
2018-06-01 08:44:40 +00:00
|
|
|
var renamed_ref = renamed;
|
|
|
|
Task.Run (() => OnRenamed (renamed_ref));
|
2014-08-13 10:39:27 +01:00
|
|
|
renamed = null;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Start ()
|
|
|
|
{
|
2018-05-10 08:37:03 +00:00
|
|
|
if (disposed)
|
|
|
|
return;
|
|
|
|
if (watcher_handle == null)
|
|
|
|
return;
|
|
|
|
watcher?.StartDispatching (watcher_handle);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Stop ()
|
|
|
|
{
|
2018-05-10 08:37:03 +00:00
|
|
|
if (disposed)
|
|
|
|
return;
|
|
|
|
if (watcher_handle == null)
|
|
|
|
return;
|
|
|
|
watcher?.StopDispatching (watcher_handle);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
#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 */
|
|
|
|
/* 3 -> Kevent */
|
|
|
|
/* 4 -> gamin */
|
|
|
|
/* 5 -> inotify */
|
2018-05-10 08:37:03 +00:00
|
|
|
/* 6 -> CoreFX */
|
2014-08-13 10:39:27 +01:00
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
static extern int InternalSupportsFSW ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|