.NET provides a handy way to deal with monitoring different file system changes. In this article, we will discuss what FileSystemWatcher is, how to set it up, and how to configure it to observe various file system changes. In addition, we will take a look at the caveats of FileSystemWatcher.
Let’s dig in.
What is FileSystemWatcher?
FileSystemWatcher
is a class in the System.IO
namespace and it helps us monitor file system changes. It is composed of different properties that enable us to configure the event types we want to listen to. Also, we can apply file and directory filtering.
Let’s inspect how we can set up the FileSystemWatcher in our project:
public class TextFileManager { private readonly FileSystemWatcher _fileSystemWatcher; public TextFileManager(string rootDirectory) { _fileSystemWatcher = new FileSystemWatcher(rootDirectory); } }
We can initialize FileSystemWatcher
simply by passing the path, we want to monitor. Throughout this article, we will use our TextFileManager
class to configure and work with FileSystemWatcher
instance. Of course, you can always download our source code to see the full implementation.
FileSystemWatcher Filters
There is a wide range of file system changes we can monitor using FileSystemWatcher
. However, we can have a scenario where we don’t want to monitor all kinds of changes in our file system. Instead, we want to monitor specific file changes or specific file types. This is where filters are useful because they help us narrow down the type of changes we monitor. FileSystemWatcher
contains two properties Filter
and NotifyFilter
to configure filtering.
Filter
Filter
is FileSystemWatcher
property that enables us to monitor specific files by specifying a file pattern. The default Filter’s string value is "*.*"
, which means to monitor all files.
Let’s configure the type of files we want to monitor:
private void ConfigureFilters() { _fileSystemWatcher.Filter = "*.txt"; }
Now _fileSystemWatcher
monitors only text file changes.
NotifyFilter
NotifyFilter
is another property of FileSystemWatcher
that enables us to specify and monitor specific changes in the file system. We can configure NotifyFilter
to listen to multiple types of changes using the bitwise OR operator("|"
). By default, the value of NotifyFilter
is a bitwise OR combination of LastWrite
, DirectoryName
, and FileName
, but we can expand that:
private void ConfigureFilters() { ... _fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size; }
By combining NotifyFilters
enumeration we can expand the range of changes we monitor. The NotifyFilters
enumeration values specify changes to monitor in a file or directory.
The Attributes
value adds the capability to monitor file or directory attribute changes. File or directory attributes are meta-data information that describes a file or a directory behavior (such as file visibility, modifiable, encryption, and more ).
CreationTime
as its name suggests it enables us to monitor the file or a directory creation time.
To monitor read or open operation on a file or directory we can use LastAccess
.
To monitor file or directory security settings (such as read-write permissions, execution, and share permission ) changes we simply add Security
into the combination.
In addition, if we want to monitor file or directory size changes that can happen as a result of file modification, we simply add Size
.
We can also monitor changes inside subdirectories by using the IncludeSubdirectories
property:
private void ConfigureFilters() { ... _fileSystemWatcher.IncludeSubdirectories = true; ... }
FileSystemWatcher Events
FileSystemWatcher
consists of 5 different event types we can use. Namely: Created, Deleted, Renamed, Error, and Changed.
Created
We can configure it to trigger an event on the creation of a file or a directory:
private void ConfigureEvents() { _fileSystemWatcher.Created += HandleCreated; } private void HandleCreated(object sender, FileSystemEventArgs e) { Console.WriteLine($"Create: {e.FullPath}"); }
Deleted
We can configure it to trigger an event on the deletion of a file or directory:
private void ConfigureEvents() { _fileSystemWatcher.Deleted += HandleDeleted; ... } private void HandleDeleted(object sender, FileSystemEventArgs e) { Console.WriteLine($"Delete: {e.FullPath}"); }
Changed
We can configure it to trigger an event when there is a change to the size, system attributes, the last update, the last access time, or permission of a file or directory:
private void ConfigureEvents() { _fileSystemWatcher.Changed += HandleChanged; ... } private void HandleChanged(object sender, FileSystemEventArgs e) { Console.WriteLine($"Change: {Enum.GetName(e.ChangeType)} {e.FullPath}"); }
The event handlers of Created
, Deleted
and Changed
have similar method signatures.
Renamed
We can use it to trigger an event when there is a file name or directory name change:
private void ConfigureEvents() { _fileSystemWatcher.Renamed += HandleRenamed; ... } private void HandleRenamed(object sender, RenamedEventArgs e) { Console.WriteLine($"Rename: {e.OldName} => {e.Name}"); }
Renamed
event handlers have a different method signature from Created
, Deleted
, and Changed
event handlers. The second argument of type RenamedEventArgs
contains the old name and new name properties of the affected file or directory.
Error
For different reasons FileSystemWatcher
could reach a state where it can no longer monitor file system changes in the specified path. To trigger an event in this scenario we use the Error
event handler:
private void ConfigureEvents() { _fileSystemWatcher.Error += HandleError; ... } private void HandleError(object sender, ErrorEventArgs e) { Console.WriteLine($"Error: {e.GetException().Message}"); }
Finally, to enable _fileSystemWatcher
to raise events, we have to set EnableRaisingEvents
property to true:
private void ConfigureEvents() { ... _fileSystemWatcher.EnableRaisingEvents = true; }
FileSystemWatcher
events are raised to corresponding file system changes inside the path being monitored and in accordance with the filters. It is important to note that common file system operations (such as move or copy) might trigger more than one event.
FileSystemWatcher Caveats
FileSystemWatcher
makes it easy to monitor our file system and trigger events accordingly. However, it is important to keep in mind when the buffer capacity is exceeded FileSystemWatcher
can miss an event. The default buffer size is 8 KB, but we can set it to any value between 4 KB and 64 KB using InternalBufferSize
property. The buffer is storage for all system notifications that relate to file changes and exclude file names. Each event can use up to 16 bytes of memory. If numerous changes take place in a short time it can cause a buffer overflow, as a result, FileSystemWatcher
misses subsequent events.
To avoid buffer overflow:
- Minimize the types of changes we want to monitor using
NotifyFilter
andIncludeSubdirectories
- Monitor only the files that are of interest by specifying the file pattern, more importantly, avoid monitoring files with long names as they have a high contribution to filling up the buffer
- To have more room in the buffer, increase the buffer size by setting
InternalBufferSize
property - Keep the event handler code to the possible minimum
Conclusion
In this article, we have learned how we can monitor a file system using FileSystemWatcher
, how to configure the different events, ways of filtering, and finally the caveats and recommendations.