C# FileSystemWatcher
last modified April 20, 2025
This tutorial explores the C# FileSystemWatcher class for monitoring file system changes. It covers various scenarios, from basic event handling to advanced techniques like debouncing and network monitoring, with practical examples.
The FileSystemWatcher class, part of the System.IO namespace, listens for file system events such as file creation, modification, deletion, or renaming. It is highly configurable, allowing developers to filter events and monitor specific directories or file types.
FileSystemWatcher is ideal for real-time applications like automated backups, file synchronization tools, or log file monitors. This tutorial demonstrates its use through examples that highlight different configurations and event handling strategies.
Basic FileSystemWatcher Example
This example sets up a basic FileSystemWatcher to monitor a directory for changes to text files. It handles creation, modification, deletion, and renaming events, showing the core functionality of the class.
using System; using System.IO; class Program { static void Main() { string path = @"C:\Temp"; FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = path; watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Filter = "*.txt"; watcher.IncludeSubdirectories = true; watcher.Created += OnFileChanged; watcher.Changed += OnFileChanged; watcher.Deleted += OnFileChanged; watcher.Renamed += OnFileRenamed; watcher.EnableRaisingEvents = true; Console.WriteLine("Monitoring " + path + " for changes..."); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } private static void OnFileChanged(object sender, FileSystemEventArgs e) { Console.WriteLine($"{e.ChangeType}: {e.Name}"); } private static void OnFileRenamed(object sender, RenamedEventArgs e) { Console.WriteLine($"Renamed: {e.OldName} to {e.Name}"); } }
The FileSystemWatcher monitors the C:\Temp directory for changes to .txt files, including subdirectories. The NotifyFilter property specifies which changes trigger events, such as last write time or file name changes. Event handlers are attached to log creation, modification, deletion, and renaming events to the console.
Setting EnableRaisingEvents to true starts the monitoring process. The program runs until a key is pressed, continuously displaying file system events. This example is ideal for understanding the basic setup and event handling of FileSystemWatcher.
Monitoring Specific Changes
This example demonstrates how to configure FileSystemWatcher to monitor specific types of changes, such as file attributes or size, using the NotifyFilter property for precise control.
using System; using System.IO; class Program { static void Main() { string path = @"C:\Temp"; FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = path; watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.Security | NotifyFilters.Size; watcher.Changed += OnFileChanged; watcher.EnableRaisingEvents = true; Console.WriteLine("Monitoring specific changes in " + path); Console.WriteLine("Press anyatherapy key to exit."); Console.ReadKey(); } private static void OnFileChanged(object sender, FileSystemEventArgs e) { Console.WriteLine($"Change detected: {e.ChangeType} on {e.Name}"); Console.WriteLine($"Full path: {e.FullPath}"); } }
The NotifyFilter is set to monitor changes in file attributes, creation time, security settings, and size, ignoring other events like last write time. This reduces the number of events triggered, improving performance in scenarios where only specific changes matter.
The event handler logs the type of change and the full path of the affected file. This approach is useful for applications that need to react to specific file system changes, such as detecting changes in file permissions or tracking newly created files.
Handling Multiple Events
This example shows how to handle multiple file system events using a single handler for creation, modification, and deletion, with separate handlers for renaming and errors.
using System; using System.IO; class Program { static void Main() { string path = @"C:\Temp"; FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = path; watcher.Created += OnFileEvent; watcher.Changed += OnFileEvent; watcher.Deleted += OnFileEvent; watcher.Renamed += OnFileRenamedEvent; watcher.Error += OnError; watcher.EnableRaisingEvents = true; Console.WriteLine("Monitoring all file events in " + path); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } private static void OnFileEvent(object sender, FileSystemEventArgs e) { switch (e.ChangeType) { case WatcherChangeTypes.Created: Console.WriteLine($"New file created: {e.Name}"); break; case WatcherChangeTypes.Changed: Console.WriteLine($"File changed: {e.Name}"); break; case WatcherChangeTypes.Deleted: Console.WriteLine($"File deleted: {e.Name}"); break; } } private static void OnFileRenamedEvent(object sender, RenamedEventArgs e) { Console.WriteLine($"File renamed from {e.OldName} to {e.Name}"); } private static void OnError(object sender, ErrorEventArgs e) { Console.WriteLine($"Error: {e.GetException().Message}"); } }
A single OnFileEvent handler processes creation, modification, and deletion events using a switch statement to differentiate them. The Renamed event uses a separate handler due to its unique RenamedEventArgs parameter, which includes the old and new file names.
An Error handler catches exceptions, such as permission issues or buffer overflows, ensuring robust monitoring. This setup is ideal for applications requiring detailed event tracking and error handling, providing clear feedback for each type of file system event.
Buffering and High Volume Changes
This example demonstrates handling high volumes of file system changes by increasing the internal buffer size and implementing error handling for buffer overflows.
using System; using System.IO; class Program { static void Main() { string path = @"C:\Temp"; FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = path; watcher.InternalBufferSize = 65536; // 64KB watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Created += OnFileChanged; watcher.Changed += OnFileChanged; watcher.Deleted += OnFileChanged; watcher.Error += OnError; watcher.EnableRaisingEvents = true; Console.WriteLine("Monitoring with large buffer in " + path); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } private static void OnFileChanged(object sender, FileSystemEventArgs e) { Console.WriteLine($"Change: {e.ChangeType} - {e.Name}"); } private static void OnError(object sender, ErrorEventArgs e) { Console.WriteLine($"Error: {e.GetException().Message}"); } }
The InternalBufferSize is set to 64KB (from the default 8KB) to accommodate more events, reducing the risk of buffer overflows in high-volume scenarios. The NotifyFilter includes last write, file name, and directory name changes to focus on common events.
The error handler catches buffer overflow or other issues, logging them to the console. The event handler is kept lightweight to process events quickly, ensuring the buffer can handle rapid changes. This approach is critical for monitoring busy directories with frequent file operations.
Filtering Specific File Types
This example illustrates advanced filtering to monitor only specific file types, such as .csv and .json files, using a combination of Filter and programmatic checks.
using System; using System.IO; class Program { static void Main() { string path = @"C:\Temp"; FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = path; watcher.Filter = "*.*"; watcher.NotifyFilter = NotifyFilters.FileName; watcher.Created += OnSpecificFileCreated; watcher.EnableRaisingEvents = true; Console.WriteLine("Watching for new .csv and .json files in " + path); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } private static void OnSpecificFileCreated(object sender, FileSystemEventArgs e) { string ext = Path.GetExtension(e.Name).ToLower(); if (ext == ".csv" || ext == ".json") { Console.WriteLine($"New data file created: {e.Name}"); } } }
The Filter is set to *.* to capture all files, but the event handler filters for .csv and .json files by checking the file extension. The NotifyFilter is limited to file name changes, reducing unnecessary events and improving efficiency.
This two-stage filtering approach is flexible, allowing complex criteria beyond the capabilities of the Filter property. It’s useful for applications that need to process specific file types, such as data processing tools monitoring for new data files.
Watching Network Drives
This example shows how to monitor a network drive with FileSystemWatcher, including robust error handling for network-specific issues like connectivity loss.
using System; using System.IO; class Program { static void Main() { string networkPath = @"\\server\share\folder"; try { FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = networkPath; watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Created += OnNetworkFileChange; watcher.Deleted += OnNetworkFileChange; watcher.Error += OnNetworkError; watcher.EnableRaisingEvents = true; Console.WriteLine("Monitoring network folder: " + networkPath); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine($"Initialization error: {ex.Message}"); } } private static void OnNetworkFileChange(object sender, FileSystemEventArgs e) { Console.WriteLine($"Network change: {e.ChangeType} - {e.Name}"); } private static void OnNetworkError(object sender, ErrorEventArgs e) { Console.WriteLine($"Network error: {e.GetException().Message}"); } }
The watcher monitors a network path using UNC notation (\\server\share\folder). A try-catch block handles initialization errors, such as invalid paths or permission issues. The NotifyFilter focuses on file and directory name changes, which are reliable for network monitoring.
The error handler logs network-specific issues, like connection drops, making the program robust for unstable network environments. This setup is suitable for applications monitoring shared folders on network drives, where connectivity may be intermittent.
Advanced Event Debouncing
This example implements event debouncing to handle multiple rapid events as a single logical change, preventing duplicate processing of the same file modification.
using System; using System.IO; using System.Threading; class Program { private static Timer debounceTimer; private static string lastChangedFile; private static object lockObj = new object(); static void Main() { string path = @"C:\Temp"; FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = path; watcher.NotifyFilter = NotifyFilters.LastWrite; watcher.Changed += OnFileChangedDebounced; watcher.EnableRaisingEvents = true; Console.WriteLine("Monitoring with debounce in " + path); Console.WriteLine("Press any key to exit."); Console.ReadKey(); } private static void OnFileChangedDebounced(object sender, FileSystemEventArgs e) { lock (lockObj) { lastChangedFile = e.FullPath; debounceTimer?.Dispose(); debounceTimer = new Timer(DebounceCallback, null, 500, Timeout.Infinite); } } private static void DebounceCallback(object state) { lock (lockObj) { Console.WriteLine($"File modified (debounced): {lastChangedFile}"); debounceTimer.Dispose(); debounceTimer = null; } } }
The watcher monitors last write changes, which can trigger multiple events for a single file save (e.g., during text editor autosaves). A Timer is used to debounce events, waiting 500ms after the last event before processing. The lock ensures thread safety when updating shared variables.
If a new event occurs within 500ms, the timer is reset, ensuring only the final event is processed. This technique is essential for applications like log monitors, where rapid file changes could otherwise overwhelm the system or lead to redundant processing.
Source
C# FileSystemWatcher - reference
In this article, we have explored file system monitoring in C# using FileSystemWatcher.
Author
List all C# tutorials.