C#监控文件变化 C#如何使用FileSystemWatcher监控文件夹

2次阅读

Filesystemwatcher可监控创建、删除、重命名和内容修改,但changed事件不保证捕获每次保存(如vs code原子替换导致created+deleted);需显式设置notifyfilter并规避初始化触发;网络路径支持差,需轮询等替代方案;事件在后台线程触发,ui更新须调度;务必dispose释放资源。

C#监控文件变化 C#如何使用FileSystemWatcher监控文件夹

FileSystemWatcher 能监控哪些变化

它能捕获文件或子目录的创建、删除、重命名和内容修改,但要注意:Changed 事件默认只响应最后写入时间(LastWrite)或属性变更,**不保证每次文件保存都触发**——比如某些编辑器(VS Code、Notepad++)会先写临时文件再原子替换,此时你看到的是 Created + Deleted,而不是 Changed

实际使用中建议至少订阅这四个事件:CreatedDeletedRenamedChanged,并配合 NotifyFilter 显式指定关注维度:

  • NotifyFilter.LastWrite:捕获内容修改(最常用)
  • NotifyFilter.FileName | NotifyFilter.DirectoryName:支持重命名检测
  • NotifyFilter.Attributes:如只读/隐藏位变化

为什么刚启动就收到一 Changed 事件

默认情况下 FileSystemWatcher递归扫描初始状态,把已存在的文件当作“刚被修改”来触发 Changed。这不是 bug,是设计行为。

避免方法很简单:在调用 EnableRaisingEvents = true 之前,先完成事件注册;更稳妥的做法是加一个初始化标记:

var watcher = new FileSystemWatcher(@"C:target"); watcher.IncludeSubdirectories = true; watcher.NotifyFilter = NotifyFilter.LastWrite | NotifyFilter.FileName | NotifyFilter.DirectoryName;  // 先注册事件,再启用 watcher.Changed += OnChanged; watcher.Created += OnCreated; // ...其他事件  watcher.EnableRaisingEvents = false; // 暂不启用 bool isInitialized = false; watcher.EnableRaisingEvents = true; isInitialized = true; // 启用后再设标志  void OnChanged(object sender, FileSystemEventArgs e) {     if (!isInitialized) return; // 跳过初始化扫描产生的事件     // 正常处理逻辑 }

监听网络路径或 onedrive 同步文件夹时失效

FileSystemWatcher 依赖 windows API 的底层通知机制(ReadDirectoryChangesW),对 SMB 共享、OneDrive、Google Drive 等云同步目录支持极差——常见现象是事件完全不触发,或延迟高达数分钟。

原因很直接:这些路径不是本地 NTFS 卷,底层驱动无法可靠投递变更通知。

替代方案有限,但可考虑:

  • 改用轮询:Directory.GetFiles() + FileInfo.LastWriteTime 对比(适合低频、小目录)
  • microsoft.Win32.SafeHandles.SafeFileHandle 手动调用 ReadDirectoryChangesW(复杂且仍受限)
  • 换工具:如 Microsoft.Extensions.FileSystemGlobbing 配合定时扫描,或引入第三方库如 Alphaleonis.Win32.Filesystem(增强网络路径兼容性)

事件回调线程不是 UI 线程,也不能直接更新 WinForms/wpf 控件

FileSystemWatcher 的所有事件都在后台线程(IOCP 线程池)中触发,如果直接操作 TextBox.TextListBox.Items.Add(),会抛出 InvalidOperationException: “线程间操作无效”

WinForms 下必须用 Control.Invoke()BeginInvoke();WPF 下要用 Dispatcher.Invoke()

// WinForms 示例 private void OnCreated(object sender, FileSystemEventArgs e) {     if (this.InvokeRequired) {         this.Invoke((MethodInvoker)(() => UpdateLog(e.FullPath)));     } else {         UpdateLog(e.FullPath);     } }  // WPF 示例 private void OnCreated(object sender, FileSystemEventArgs e) {     this.Dispatcher.Invoke(() => UpdateLog(e.FullPath)); }

别忘了在窗体关闭前调用 watcher.Dispose(),否则可能引发资源泄漏或程序退出卡死——这是最容易被跳过的一步。

text=ZqhQzanResources