//******************************************************************************************************// SafeFileWatcher.cs - Gbtc//// Copyright © 2015, Grid Protection Alliance. All Rights Reserved.//// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See// the NOTICE file distributed with this work for additional information regarding copyright ownership.// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may// not use this file except in compliance with the License. You may obtain a copy of the License at://// http://opensource.org/licenses/MIT//// Unless agreed to in writing, the subject software distributed under the License is distributed on an// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the// License for the specific language governing permissions and limitations.//// Code Modification History:// ----------------------------------------------------------------------------------------------------// 07/28/2015 - J. Ritchie Carroll// Generated original version of source code.////****************************************************************************************************** using System;using System.ComponentModel;using System.Diagnostics.CodeAnalysis;using System.IO;using System.Security; namespace GSF.IO{ /// <summary> /// Represents a wrapper around the native .NET <see cref="FileSystemWatcher"/> that avoids problems with /// dangling references when using a file watcher instance as a class member that never gets disposed. /// </summary> /// <remarks> /// <para> /// The design goal of the SafeFileWatcher is to avoid accidental memory leaks caused by use of .NET's native /// file system watcher when used as a member of a class that consumers fail to properly dispose. If a class /// has a reference to a file watcher as a member variable and attaches to the file watcher's events, the /// file watcher will maintain a reference the parent class so it can call its event handler. If the parent /// class is not disposed properly, the file watcher will thusly not be disposed and will maintain the /// reference to the parent class - the garbage collector will never collect the parent because it has a /// valid reference and no collection means the parent finalizer will never get called and the file system /// watcher will never get disposed. Creating multiple instances of parent class and not disposing of them /// will cause a memory leak even with a properly designed disposable pattern. Using the SafeFileWatcher /// instead of directly using the FileSystemWatcher will resolve this potential issue. /// </para> /// <para> /// Note that component model implementation is not fully replicated - if you are using a file system watcher /// on a design surface, this safety wrapper will usually not be needed. This class has benefit when a class /// will dynamically use a file watcher and needs to make sure any unmanaged resources get properly released /// even if a consumer neglects to call the dispose function. /// </para> /// </remarks> [SecurityCritical] public class SafeFileWatcher : IDisposable { #region [ Members ] // Events /// <summary> /// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is changed. /// </summary> public event FileSystemEventHandler Changed; /// <summary> /// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is created. /// </summary> public event FileSystemEventHandler Created; /// <summary> /// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is deleted. /// </summary> public event FileSystemEventHandler Deleted; /// <summary> /// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is renamed. /// </summary> public event RenamedEventHandler Renamed; /// <summary> /// Occurs when the internal buffer overflows. /// </summary> public event ErrorEventHandler Error; // Fields private readonly FileSystemWatcher m_fileSystemWatcher; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Initializes a new instance of the <see cref="SafeFileWatcher"/> class. /// </summary> public SafeFileWatcher() { m_fileSystemWatcher = new FileSystemWatcher(); InitializeFileSystemWatcher(); } /// <summary> /// Initializes a new instance of the <see cref="SafeFileWatcher"/> class, given the specified directory to monitor. /// </summary> /// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation. /// </param> /// <exception cref="ArgumentNullException">The <paramref name="path"/> parameter is null.</exception> /// <exception cref="ArgumentException"> /// The <paramref name="path"/> parameter is an empty string (""). -or- /// The path specified through the <paramref name="path"/> parameter does not exist. /// </exception> public SafeFileWatcher(string path) { m_fileSystemWatcher = new FileSystemWatcher(path); InitializeFileSystemWatcher(); } /// <summary> /// Initializes a new instance of the <see cref="SafeFileWatcher"/> class, given the specified directory and type of files to monitor. /// </summary> /// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param> /// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param> /// <exception cref="ArgumentNullException"> /// The <paramref name="path"/> parameter is null. -or- /// The <paramref name="filter"/> parameter is null. /// </exception> /// <exception cref="ArgumentException"> /// The <paramref name="path"/> parameter is an empty string (""). -or- /// The path specified through the <paramref name="path"/> parameter does not exist. /// </exception> public SafeFileWatcher(string path, string filter) { m_fileSystemWatcher = new FileSystemWatcher(path, filter); InitializeFileSystemWatcher(); } // Attach to file system watcher events via lambda function using a weak reference to this class instance // connected through static method so that file watcher cannot hold onto a reference to this class - this // way even if consumer neglects to dispose this class, it will get properly garbage collected and finalized // because there will be no remaining references to this class instance. Also, even though the following // intermediate lambda classes that get created will be attached to the file system watcher event handlers, // they will also be freed because this class will make sure the file system watcher instance is handled // like an unmanaged resource, i.e., it always gets disposed, via the finalizer if need be. private void InitializeFileSystemWatcher() { WeakReference<SafeFileWatcher> reference = new WeakReference<SafeFileWatcher>(this); m_fileSystemWatcher.Changed += (_, e) => OnChanged(reference, e); m_fileSystemWatcher.Created += (_, e) => OnCreated(reference, e); m_fileSystemWatcher.Deleted += (_, e) => OnDeleted(reference, e); m_fileSystemWatcher.Renamed += (_, e) => OnRenamed(reference, e); m_fileSystemWatcher.Error += (_, e) => OnError(reference, e); } /// <summary> /// Terminates <see cref="SafeFileWatcher"/> instance making sure to release unmanaged resources. /// </summary> ~SafeFileWatcher() { // Finalizer desired because we want to dispose FileSystemWatcher to force release of pinned buffers // and thus release reference to any associated event delegates allowing garbage collection Dispose(false); } #endregion #region [ Properties ] /// <summary> /// Gets or sets the path of the directory to watch. /// </summary> /// <returns> /// The path to monitor. The default is an empty string (""). /// </returns> /// <exception cref="ArgumentException"> /// The specified path does not exist or could not be found. -or- /// The specified path contains wildcard characters. -or- /// The specified path contains invalid path characters. /// </exception> public string Path { get => m_fileSystemWatcher.Path; set => m_fileSystemWatcher.Path = value; } /// <summary> /// Gets or sets the filter string used to determine what files are monitored in a directory. /// </summary> /// <returns> /// The filter string. The default is "*.*" (Watches all files.) /// </returns> public string Filter { get => m_fileSystemWatcher.Filter; set => m_fileSystemWatcher.Filter = value; } /// <summary> /// Gets or sets the type of changes to watch for. /// </summary> /// <returns> /// One of the <see cref="NotifyFilters"/> values. The default is the bitwise OR combination of LastWrite, FileName, and DirectoryName.</returns> /// <exception cref="ArgumentException">The value is not a valid bitwise OR combination of the <see cref="NotifyFilters"/> values.</exception> /// <exception cref="InvalidEnumArgumentException">The value that is being set is not valid.</exception> public NotifyFilters NotifyFilter { get => m_fileSystemWatcher.NotifyFilter; set => m_fileSystemWatcher.NotifyFilter = value; } /// <summary> /// Gets or sets a value indicating whether the component is enabled. /// </summary> /// <returns> /// true if the component is enabled; otherwise, false. The default is false. If you are using the component on a designer in Visual Studio 2005, the default is true.</returns> /// <exception cref="ObjectDisposedException">The <see cref="FileSystemWatcher"/> object has been disposed.</exception> /// <exception cref="PlatformNotSupportedException">The current operating system is not Microsoft Windows NT or later.</exception> /// <exception cref="FileNotFoundException">The directory specified in <see cref="SafeFileWatcher.Path"/> could not be found.</exception> /// <exception cref="ArgumentException"><see cref="SafeFileWatcher.Path"/> has not been set or is invalid.</exception> public bool EnableRaisingEvents { get => m_fileSystemWatcher.EnableRaisingEvents; set => m_fileSystemWatcher.EnableRaisingEvents = value; } /// <summary> /// Gets or sets a value indicating whether subdirectories within the specified path should be monitored. /// </summary> /// <returns> /// true if you want to monitor subdirectories; otherwise, false. The default is false. /// </returns> public bool IncludeSubdirectories { get => m_fileSystemWatcher.IncludeSubdirectories; set => m_fileSystemWatcher.IncludeSubdirectories = value; } /// <summary> /// Gets or sets the size of the internal buffer. /// </summary> /// <returns> /// The internal buffer size. The default is 8192 (8K). /// </returns> public int InternalBufferSize { get => m_fileSystemWatcher.InternalBufferSize; set => m_fileSystemWatcher.InternalBufferSize = value; } /// <summary> /// Gets or sets the object used to marshal the event handler calls issued as a result of a directory change. /// </summary> /// <returns> /// The <see cref="ISynchronizeInvoke"/> that represents the object used to marshal the event handler calls issued as a result of a directory change. The default is null. /// </returns> public ISynchronizeInvoke SynchronizingObject { get => m_fileSystemWatcher.SynchronizingObject; set => m_fileSystemWatcher.SynchronizingObject = value; } /// <summary> /// Gets or sets an <see cref="ISite"/> for the <see cref="SafeFileWatcher"/>. /// </summary> /// <returns> /// An <see cref="ISite"/> for the <see cref="SafeFileWatcher"/>. /// </returns> public ISite Site { get => m_fileSystemWatcher.Site; set => m_fileSystemWatcher.Site = value; } #endregion #region [ Methods ] /// <summary> /// Releases all the resources used by the <see cref="SafeFileWatcher"/> object. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Releases the unmanaged resources used by the <see cref="SafeFileWatcher"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_fileSystemWatcher")] protected virtual void Dispose(bool disposing) { if (m_disposed) return; try { // Treating file system watcher like an unmanaged resource m_fileSystemWatcher?.Dispose(); } finally { m_disposed = true; } } /// <summary> /// A synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor. /// </summary> /// <returns>A <see cref="WaitForChangedResult"/> that contains specific information on the change that occurred.</returns> /// <param name="changeType">The <see cref="WatcherChangeTypes"/> to watch for.</param> public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => m_fileSystemWatcher.WaitForChanged(changeType); /// <summary> /// A synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor and the time (in milliseconds) to wait before timing out. /// </summary> /// <returns>A <see cref="WaitForChangedResult"/> that contains specific information on the change that occurred.</returns> /// <param name="changeType">The <see cref="WatcherChangeTypes"/> to watch for.</param> /// <param name="timeout">The time (in milliseconds) to wait before timing out.</param> public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => m_fileSystemWatcher.WaitForChanged(changeType, timeout); /// <summary> /// Begins the initialization of a <see cref="SafeFileWatcher"/> used on a form or used by another component. The initialization occurs at run time. /// </summary> public void BeginInit() => m_fileSystemWatcher.BeginInit(); /// <summary> /// Ends the initialization of a <see cref="SafeFileWatcher"/> used on a form or used by another component. The initialization occurs at run time. /// </summary> public void EndInit() => m_fileSystemWatcher.EndInit(); private void OnChanged(FileSystemEventArgs e) => Changed?.Invoke(this, e); private void OnCreated(FileSystemEventArgs e) => Created?.Invoke(this, e); private void OnDeleted(FileSystemEventArgs e) => Deleted?.Invoke(this, e); private void OnRenamed(RenamedEventArgs e) => Renamed?.Invoke(this, e); private void OnError(ErrorEventArgs e) => Error?.Invoke(this, e); #endregion #region [ Static ] // Static Methods private static void OnChanged(WeakReference<SafeFileWatcher> reference, FileSystemEventArgs e) { if (reference.TryGetTarget(out SafeFileWatcher instance)) instance.OnChanged(e); } private static void OnCreated(WeakReference<SafeFileWatcher> reference, FileSystemEventArgs e) { if (reference.TryGetTarget(out SafeFileWatcher instance)) instance.OnCreated(e); } private static void OnDeleted(WeakReference<SafeFileWatcher> reference, FileSystemEventArgs e) { if (reference.TryGetTarget(out SafeFileWatcher instance)) instance.OnDeleted(e); } private static void OnRenamed(WeakReference<SafeFileWatcher> reference, RenamedEventArgs e) { if (reference.TryGetTarget(out SafeFileWatcher instance)) instance.OnRenamed(e); } private static void OnError(WeakReference<SafeFileWatcher> reference, ErrorEventArgs e) { if (reference.TryGetTarget(out SafeFileWatcher instance)) instance.OnError(e); } #endregion }}
//******************************************************************************************************
// SafeFileWatcher.cs - Gbtc
//
// Copyright © 2015, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 07/28/2015 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Security;
namespace GSF.IO
{
/// <summary>
/// Represents a wrapper around the native .NET <see cref="FileSystemWatcher"/> that avoids problems with
/// dangling references when using a file watcher instance as a class member that never gets disposed.
/// </summary>
/// <remarks>
/// <para>
/// The design goal of the SafeFileWatcher is to avoid accidental memory leaks caused by use of .NET's native
/// file system watcher when used as a member of a class that consumers fail to properly dispose. If a class
/// has a reference to a file watcher as a member variable and attaches to the file watcher's events, the
/// file watcher will maintain a reference the parent class so it can call its event handler. If the parent
/// class is not disposed properly, the file watcher will thusly not be disposed and will maintain the
/// reference to the parent class - the garbage collector will never collect the parent because it has a
/// valid reference and no collection means the parent finalizer will never get called and the file system
/// watcher will never get disposed. Creating multiple instances of parent class and not disposing of them
/// will cause a memory leak even with a properly designed disposable pattern. Using the SafeFileWatcher
/// instead of directly using the FileSystemWatcher will resolve this potential issue.
/// </para>
/// <para>
/// Note that component model implementation is not fully replicated - if you are using a file system watcher
/// on a design surface, this safety wrapper will usually not be needed. This class has benefit when a class
/// will dynamically use a file watcher and needs to make sure any unmanaged resources get properly released
/// even if a consumer neglects to call the dispose function.
/// </para>
/// </remarks>
[SecurityCritical]
public class SafeFileWatcher : IDisposable
{
#region [ Members ]
// Events
/// <summary>
/// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is changed.
/// </summary>
public event FileSystemEventHandler Changed;
/// <summary>
/// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is created.
/// </summary>
public event FileSystemEventHandler Created;
/// <summary>
/// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is deleted.
/// </summary>
public event FileSystemEventHandler Deleted;
/// <summary>
/// Occurs when a file or directory in the specified <see cref="SafeFileWatcher.Path"/> is renamed.
/// </summary>
public event RenamedEventHandler Renamed;
/// <summary>
/// Occurs when the internal buffer overflows.
/// </summary>
public event ErrorEventHandler Error;
// Fields
private readonly FileSystemWatcher m_fileSystemWatcher;
private bool m_disposed;
#endregion
#region [ Constructors ]
/// <summary>
/// Initializes a new instance of the <see cref="SafeFileWatcher"/> class.
/// </summary>
public SafeFileWatcher()
{
m_fileSystemWatcher = new FileSystemWatcher();
InitializeFileSystemWatcher();
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeFileWatcher"/> class, given the specified directory to monitor.
/// </summary>
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.
/// </param>
/// <exception cref="ArgumentNullException">The <paramref name="path"/> parameter is null.</exception>
/// <exception cref="ArgumentException">
/// The <paramref name="path"/> parameter is an empty string (""). -or-
/// The path specified through the <paramref name="path"/> parameter does not exist.
/// </exception>
public SafeFileWatcher(string path)
{
m_fileSystemWatcher = new FileSystemWatcher(path);
InitializeFileSystemWatcher();
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeFileWatcher"/> class, given the specified directory and type of files to monitor.
/// </summary>
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
/// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
/// <exception cref="ArgumentNullException">
/// The <paramref name="path"/> parameter is null. -or-
/// The <paramref name="filter"/> parameter is null.
/// </exception>
/// <exception cref="ArgumentException">
/// The <paramref name="path"/> parameter is an empty string (""). -or-
/// The path specified through the <paramref name="path"/> parameter does not exist.
/// </exception>
public SafeFileWatcher(string path, string filter)
{
m_fileSystemWatcher = new FileSystemWatcher(path, filter);
InitializeFileSystemWatcher();
}
// Attach to file system watcher events via lambda function using a weak reference to this class instance
// connected through static method so that file watcher cannot hold onto a reference to this class - this
// way even if consumer neglects to dispose this class, it will get properly garbage collected and finalized
// because there will be no remaining references to this class instance. Also, even though the following
// intermediate lambda classes that get created will be attached to the file system watcher event handlers,
// they will also be freed because this class will make sure the file system watcher instance is handled
// like an unmanaged resource, i.e., it always gets disposed, via the finalizer if need be.
private void InitializeFileSystemWatcher()
{
WeakReference<SafeFileWatcher> reference = new WeakReference<SafeFileWatcher>(this);
m_fileSystemWatcher.Changed += (_, e) => OnChanged(reference, e);
m_fileSystemWatcher.Created += (_, e) => OnCreated(reference, e);
m_fileSystemWatcher.Deleted += (_, e) => OnDeleted(reference, e);
m_fileSystemWatcher.Renamed += (_, e) => OnRenamed(reference, e);
m_fileSystemWatcher.Error += (_, e) => OnError(reference, e);
}
/// <summary>
/// Terminates <see cref="SafeFileWatcher"/> instance making sure to release unmanaged resources.
/// </summary>
~SafeFileWatcher()
{
// Finalizer desired because we want to dispose FileSystemWatcher to force release of pinned buffers
// and thus release reference to any associated event delegates allowing garbage collection
Dispose(false);
}
#endregion
#region [ Properties ]
/// <summary>
/// Gets or sets the path of the directory to watch.
/// </summary>
/// <returns>
/// The path to monitor. The default is an empty string ("").
/// </returns>
/// <exception cref="ArgumentException">
/// The specified path does not exist or could not be found. -or-
/// The specified path contains wildcard characters. -or-
/// The specified path contains invalid path characters.
/// </exception>
public string Path
{
get => m_fileSystemWatcher.Path;
set => m_fileSystemWatcher.Path = value;
}
/// <summary>
/// Gets or sets the filter string used to determine what files are monitored in a directory.
/// </summary>
/// <returns>
/// The filter string. The default is "*.*" (Watches all files.)
/// </returns>
public string Filter
{
get => m_fileSystemWatcher.Filter;
set => m_fileSystemWatcher.Filter = value;
}
/// <summary>
/// Gets or sets the type of changes to watch for.
/// </summary>
/// <returns>
/// One of the <see cref="NotifyFilters"/> values. The default is the bitwise OR combination of LastWrite, FileName, and DirectoryName.</returns>
/// <exception cref="ArgumentException">The value is not a valid bitwise OR combination of the <see cref="NotifyFilters"/> values.</exception>
/// <exception cref="InvalidEnumArgumentException">The value that is being set is not valid.</exception>
public NotifyFilters NotifyFilter
{
get => m_fileSystemWatcher.NotifyFilter;
set => m_fileSystemWatcher.NotifyFilter = value;
}
/// <summary>
/// Gets or sets a value indicating whether the component is enabled.
/// </summary>
/// <returns>
/// true if the component is enabled; otherwise, false. The default is false. If you are using the component on a designer in Visual Studio 2005, the default is true.</returns>
/// <exception cref="ObjectDisposedException">The <see cref="FileSystemWatcher"/> object has been disposed.</exception>
/// <exception cref="PlatformNotSupportedException">The current operating system is not Microsoft Windows NT or later.</exception>
/// <exception cref="FileNotFoundException">The directory specified in <see cref="SafeFileWatcher.Path"/> could not be found.</exception>
/// <exception cref="ArgumentException"><see cref="SafeFileWatcher.Path"/> has not been set or is invalid.</exception>
public bool EnableRaisingEvents
{
get => m_fileSystemWatcher.EnableRaisingEvents;
set => m_fileSystemWatcher.EnableRaisingEvents = value;
}
/// <summary>
/// Gets or sets a value indicating whether subdirectories within the specified path should be monitored.
/// </summary>
/// <returns>
/// true if you want to monitor subdirectories; otherwise, false. The default is false.
/// </returns>
public bool IncludeSubdirectories
{
get => m_fileSystemWatcher.IncludeSubdirectories;
set => m_fileSystemWatcher.IncludeSubdirectories = value;
}
/// <summary>
/// Gets or sets the size of the internal buffer.
/// </summary>
/// <returns>
/// The internal buffer size. The default is 8192 (8K).
/// </returns>
public int InternalBufferSize
{
get => m_fileSystemWatcher.InternalBufferSize;
set => m_fileSystemWatcher.InternalBufferSize = value;
}
/// <summary>
/// Gets or sets the object used to marshal the event handler calls issued as a result of a directory change.
/// </summary>
/// <returns>
/// The <see cref="ISynchronizeInvoke"/> that represents the object used to marshal the event handler calls issued as a result of a directory change. The default is null.
/// </returns>
public ISynchronizeInvoke SynchronizingObject
{
get => m_fileSystemWatcher.SynchronizingObject;
set => m_fileSystemWatcher.SynchronizingObject = value;
}
/// <summary>
/// Gets or sets an <see cref="ISite"/> for the <see cref="SafeFileWatcher"/>.
/// </summary>
/// <returns>
/// An <see cref="ISite"/> for the <see cref="SafeFileWatcher"/>.
/// </returns>
public ISite Site
{
get => m_fileSystemWatcher.Site;
set => m_fileSystemWatcher.Site = value;
}
#endregion
#region [ Methods ]
/// <summary>
/// Releases all the resources used by the <see cref="SafeFileWatcher"/> object.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the <see cref="SafeFileWatcher"/> object and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
[SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_fileSystemWatcher")]
protected virtual void Dispose(bool disposing)
{
if (m_disposed)
return;
try
{
// Treating file system watcher like an unmanaged resource
m_fileSystemWatcher?.Dispose();
}
finally
{
m_disposed = true;
}
}
/// <summary>
/// A synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor.
/// </summary>
/// <returns>A <see cref="WaitForChangedResult"/> that contains specific information on the change that occurred.</returns>
/// <param name="changeType">The <see cref="WatcherChangeTypes"/> to watch for.</param>
public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) =>
m_fileSystemWatcher.WaitForChanged(changeType);
/// <summary>
/// A synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor and the time (in milliseconds) to wait before timing out.
/// </summary>
/// <returns>A <see cref="WaitForChangedResult"/> that contains specific information on the change that occurred.</returns>
/// <param name="changeType">The <see cref="WatcherChangeTypes"/> to watch for.</param>
/// <param name="timeout">The time (in milliseconds) to wait before timing out.</param>
public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) =>
m_fileSystemWatcher.WaitForChanged(changeType, timeout);
/// <summary>
/// Begins the initialization of a <see cref="SafeFileWatcher"/> used on a form or used by another component. The initialization occurs at run time.
/// </summary>
public void BeginInit() =>
m_fileSystemWatcher.BeginInit();
/// <summary>
/// Ends the initialization of a <see cref="SafeFileWatcher"/> used on a form or used by another component. The initialization occurs at run time.
/// </summary>
public void EndInit() =>
m_fileSystemWatcher.EndInit();
private void OnChanged(FileSystemEventArgs e) =>
Changed?.Invoke(this, e);
private void OnCreated(FileSystemEventArgs e) =>
Created?.Invoke(this, e);
private void OnDeleted(FileSystemEventArgs e) =>
Deleted?.Invoke(this, e);
private void OnRenamed(RenamedEventArgs e) =>
Renamed?.Invoke(this, e);
private void OnError(ErrorEventArgs e) =>
Error?.Invoke(this, e);
#endregion
#region [ Static ]
// Static Methods
private static void OnChanged(WeakReference<SafeFileWatcher> reference, FileSystemEventArgs e)
{
if (reference.TryGetTarget(out SafeFileWatcher instance))
instance.OnChanged(e);
}
private static void OnCreated(WeakReference<SafeFileWatcher> reference, FileSystemEventArgs e)
{
if (reference.TryGetTarget(out SafeFileWatcher instance))
instance.OnCreated(e);
}
private static void OnDeleted(WeakReference<SafeFileWatcher> reference, FileSystemEventArgs e)
{
if (reference.TryGetTarget(out SafeFileWatcher instance))
instance.OnDeleted(e);
}
private static void OnRenamed(WeakReference<SafeFileWatcher> reference, RenamedEventArgs e)
{
if (reference.TryGetTarget(out SafeFileWatcher instance))
instance.OnRenamed(e);
}
private static void OnError(WeakReference<SafeFileWatcher> reference, ErrorEventArgs e)
{
if (reference.TryGetTarget(out SafeFileWatcher instance))
instance.OnError(e);
}
#endregion
}
}