FileSystemWatcher 默认配置下常因路径非法、权限不足、生命周期管理不当或缓冲区溢出而静默失效;必须使用本地绝对物理路径、合理设置NotifyFilter与Filter、防抖处理Changed事件、强引用防止GC回收,并校验目录存在性及避免同步云盘目录。

FileSystemWatcher 能用,但默认配置下大概率不工作——不是代码写错了,而是路径、权限、生命周期或缓冲区这四点卡住了。
Path 必须是本地绝对路径,UNC 和映射盘符直接失效
常见现象:EnableRaisingEvents = true 也设了,事件回调一个没触发。根本原因常是路径非法。
-
Path不能是相对路径(如"./logs")、UNC 路径(如r"\servershare")或网络映射盘符(如"Z:\data"),否则静默失败,无异常 - 必须用本地物理路径,例如
r"C:MyAppData";可用Path.GetFullPath(path)转换相对路径确保合规 - 尾部斜杠易出问题:写成
"C:\Temp\"可能因路径解析或权限校验失败而静默失效,建议统一用Directory.GetFullPath(path)标准化 - 监控 OneDrive 或 Google Drive 同步目录?别试——它们不提供可靠内核通知,事件漏报、延迟数分钟甚至完全不触发都是常态
NotifyFilter 和 Filter 不协同,事件就“看不见”
Filter 控文件名匹配,NotifyFilter 控变更类型——两者缺一不可,且默认值极窄。
- 设了
Filter = "*.log"却没改NotifyFilter,结果连重命名都收不到。因为默认NotifyFilter只含FileName | DirectoryName,不包含LastWrite - 只关心内容修改:用
NotifyFilter = NotifyFilters.LastWrite - 同时关注新建/删除/重命名:组合写
NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.CreationTime - 跳过编辑器临时文件?在事件处理器里加判断:
if (e.Name.StartsWith("~$") || e.Name.EndsWith(".tmp")) return;
Changed 事件触发太早,直接读会抛 IOException
大文件写入时 Changed 常在写完前触发,直接 File.OpenRead() 会崩,错误是 "The process cannot access the file"。
- 别在事件回调里立刻打开文件——先拼出完整路径:
Path.Combine(watcher.Path, e.Name) - 加简单防抖:用
Task.Delay(100)后检查File.GetLastWriteTimeUtc(path)是否稳定(两次读取间隔 > 200ms 且值不变) - 更稳做法:尝试以独占模式打开
File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None),捕获IOException就说明还在写 - 禁止在回调里做耗时操作(解析 JSON、上传 HTTP),否则事件队列堆积,后续通知直接丢失
服务或后台程序中失效?GC 回收和目录校验是主因
Windows Service 或长时间运行的后台任务里,FileSystemWatcher 经常“突然不响”,基本不是它坏了,而是被回收或启动条件不满足。
- 必须用
private readonly FileSystemWatcher _watcher强引用,否则 GC 可能在任意时刻回收它 - 事件回调运行在线程池线程上,更新 WinForm/WPF 控件前必须用
Control.Invoke或Dispatcher.BeginInvoke - 服务启动时若目标目录不存在,
FileSystemWatcher不报错也不工作——得自己加Directory.Exists(watcher.Path)校验 - 缓冲区溢出会导致事件丢失:
InternalBufferSize默认仅 8KB,解压百个文件或 Git checkout 就可能填满;可设为 64KB,但更稳妥的是加去重+重试逻辑,而非盲目调大
最麻烦的不是写错代码,而是事件来了你却不知道它来过——比如缓冲区溢出后静默丢事件、OneDrive 目录下永远等不到 Changed、或者 UI 线程里直接更新控件崩掉整个监听链。这些点不手动兜底,上线后只会靠日志猜半天。


















