File System Monitoring

Vaibhav • September 10, 2025

As applications grow more dynamic and data-driven, they often need to respond to changes in the file system - such as when a file is created, modified, deleted, or renamed. Instead of constantly polling the disk, C# provides a powerful and efficient way to monitor these changes in real time using the FileSystemWatcher class. In this article, we’ll explore how to use FileSystemWatcher to track file system events, how to configure it properly, and how to avoid common pitfalls when building reactive file-based workflows.

Why Monitor the File System?

File system monitoring is useful in many scenarios:

  • Automatically reloading configuration files when they change
  • Triggering processing when new files are added to a folder
  • Logging or auditing file access
  • Synchronizing files between systems

Instead of manually checking for changes, FileSystemWatcher lets your application subscribe to events and react immediately.

Introducing FileSystemWatcher

FileSystemWatcher is part of the System.IO namespace. It monitors a specified directory and raises events when changes occur. You can track changes to files, subdirectories, or both.

using System.IO;

FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "C:\\Logs";
watcher.Filter = "*.txt";
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;

watcher.Created += (s, e) => Console.WriteLine($"Created: {e.Name}");
watcher.Changed += (s, e) => Console.WriteLine($"Changed: {e.Name}");
watcher.Deleted += (s, e) => Console.WriteLine($"Deleted: {e.Name}");
watcher.Renamed += (s, e) => Console.WriteLine($"Renamed: {e.OldName} → {e.Name}");

watcher.EnableRaisingEvents = true;

This sets up a watcher on the C:\Logs folder, listening for changes to .txt files. When a file is created, modified, deleted, or renamed, the corresponding event handler is triggered.

EnableRaisingEvents must be set to true for the watcher to start listening. You can toggle it off to temporarily pause monitoring.

Understanding NotifyFilters

NotifyFilter controls what kinds of changes the watcher should detect. You can combine multiple filters using bitwise OR:

  • FileName - detects file creation, deletion, renaming
  • DirectoryName - detects folder changes
  • LastWrite - detects content changes
  • Size - detects file size changes
  • Attributes, CreationTime, etc.

Choose filters based on what your application cares about. For example, a log processor might only care about Created and LastWrite.

Filtering by File Type

Use the Filter property to limit monitoring to specific file types:

watcher.Filter = "*.csv"; // Only watch CSV files

You can also leave Filter empty to watch all files:

watcher.Filter = "*.*"; // Watch all files

This is useful when you don’t know the file types in advance or want to monitor everything.

Watching Subdirectories

By default, FileSystemWatcher only watches the specified folder. To include subdirectories, set IncludeSubdirectories to true:

watcher.IncludeSubdirectories = true;

This is useful for monitoring entire folder trees - for example, a shared project directory or a nested configuration folder.

Handling Events

Each event provides a FileSystemEventArgs or RenamedEventArgs object with details:

watcher.Created += (s, e) =>
{
    Console.WriteLine($"New file: {e.FullPath}");
};

You can use e.FullPath to get the full path, or e.Name for just the file name. For renamed events, use e.OldName and e.Name.

Debouncing Rapid Events

Some operations (like saving a file) may trigger multiple events rapidly. To avoid duplicate processing, you can debounce events using a timer or a short delay:

watcher.Changed += (s, e) =>
{
    Thread.Sleep(100); // Wait briefly to avoid duplicate triggers
    Console.WriteLine($"Changed: {e.FullPath}");
};

This helps prevent race conditions or redundant processing when files are updated frequently.

Handling Exceptions

FileSystemWatcher may throw exceptions if the path is invalid, inaccessible, or too many watchers are active. Always wrap setup in a try-catch block:

try
{
    watcher.Path = "C:\\Logs";
    watcher.EnableRaisingEvents = true;
}
catch (Exception ex)
{
    Console.WriteLine($"Watcher error: {ex.Message}");
}

This ensures your app fails gracefully and provides useful diagnostics.

Disposing the Watcher

FileSystemWatcher holds unmanaged resources. Always dispose it when done:

watcher.Dispose();

Or use a using block:

using FileSystemWatcher watcher = new FileSystemWatcher();
// watcher is automatically disposed

Monitoring Multiple Folders

To monitor multiple folders, create separate watchers for each path:

string[] folders = { "C:\\Logs", "C:\\Reports" };

foreach (string folder in folders)
{
    FileSystemWatcher w = new FileSystemWatcher(folder);
    w.Created += (s, e) => Console.WriteLine($"New file in {folder}: {e.Name}");
    w.EnableRaisingEvents = true;
}

This lets you track changes across multiple directories independently.

Limitations and Caveats

While FileSystemWatcher is powerful, it has some limitations:

  • It may miss events if too many changes happen quickly
  • It relies on OS-level notifications - not all file systems support it
  • It doesn’t detect changes inside files (only metadata)
  • It may raise duplicate or delayed events

For mission-critical monitoring, consider combining FileSystemWatcher with periodic validation or checksums.

Real-World Use Case: Auto-Reload Config

Let’s build a simple example that reloads a configuration file when it changes:

string configPath = "config.json";

FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(configPath);
watcher.Filter = Path.GetFileName(configPath);
watcher.NotifyFilter = NotifyFilters.LastWrite;

watcher.Changed += (s, e) =>
{
    Console.WriteLine("Config changed. Reloading...");
    string config = File.ReadAllText(configPath);
    Console.WriteLine(config);
};

watcher.EnableRaisingEvents = true;

This watches a single file and reloads its contents when modified. You can extend this to parse JSON, update settings, or trigger reinitialization.

Summary

FileSystemWatcher is a powerful tool for building reactive applications that respond to file system changes. You’ve learned how to monitor folders and files, handle events like creation and modification, filter by file type, and manage performance and reliability. These skills are essential for building modern apps that adapt to changing data and environments. In the next article, we’ll explore compression and archives - how to zip and unzip files, reduce storage, and manage bundled data efficiently.