FileSystemWatcher 默认配置下常因路径非法、权限不足、NotifyFilter与Filter不匹配、缓冲区溢出或未设IncludeSubdirectories而失效;须用本地绝对路径、合理设置过滤器、启用子目录监听、增大缓冲区并强引用防止GC回收。

FileSystemWatcher 能用,但默认配置下大概率不工作——不是代码写错了,而是路径、权限、生命周期或缓冲区这四点卡住了。
Path 必须是本地绝对路径,UNC 和映射盘符直接失效
常见现象:EnableRaisingEvents = true 也设了,事件回调一个没触发。根本原因常是路径非法。
-
Path不能是相对路径(如"./logs")、UNC 路径(如r"\servershare")或网络映射盘符(如"Z:\data"),否则静默失败,无异常 - 必须用本地物理路径,例如
r"C:MyAppData";可用Path.GetFullPath()转换相对路径确保合规 - 若程序以管理员身份运行,而目标目录属普通用户(如
C:UsersAliceDocuments),UAC 隔离可能导致监听被拦截 - 避免监控系统路径(如
C:Windows),部分 Windows 版本会直接屏蔽通知
NotifyFilter 和 Filter 不协同,事件就“看不见”
Filter 控文件名匹配,NotifyFilter 控变更类型——两者缺一不可,且默认值极窄。
- 设了
Filter = "*.log"却没调NotifyFilter,连重命名都收不到:因为默认只含FileName | DirectoryName,不含LastWrite - 关心内容修改?用
NotifyFilters.LastWrite或NotifyFilters.Size - 要捕获新建/删除/重命名?组合写:
NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.CreationTime - 跳过编辑器临时文件?在事件处理器里加判断:
if (e.Name.StartsWith("~$") || e.Name.EndsWith(".tmp")) return;
Changed 事件触发太早?防抖和线程安全必须做
大文件写入时 Changed 常在写完前触发,直接读会抛 IOException(“正由另一进程使用”)。
- 别在事件回调里立刻打开文件——先加
await Task.Delay(100),再检查File.GetLastWriteTimeUtc(path)是否稳定 - 更可靠方式:尝试以独占模式打开
File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None),捕获IOException就说明还在写 - 事件回调运行在线程池线程上,更新 WinForm/WPF 控件前必须用
Control.Invoke或Dispatcher.BeginInvoke - 禁止在回调里做耗时操作(解析 JSON、上传 HTTP),否则事件队列堆积,后续通知直接丢失
服务或后台程序中失效?GC 回收和目录校验是主因
Windows Service 或长时间运行的后台任务里,FileSystemWatcher 经常“突然不响”,基本不是它坏了,而是被回收或启动条件不满足。
- 必须用
private readonly FileSystemWatcher _watcher强引用,否则 GC 可能在任意时刻回收对象 - 启动前务必校验目录存在:
if (!Directory.Exists(path)) throw new InvalidOperationException($"监控路径不存在: {path}"); -
EnableRaisingEvents = true必须在所有属性(Path、Filter、IncludeSubdirectories等)设置完毕后最后调用,否则初始事件可能漏掉 - 缓冲区溢出是隐形杀手:解压、Git checkout 等批量操作易填满默认 8KB 缓冲区,导致静默丢事件;可设
InternalBufferSize = 64 * 1024,但治标不治本
最易被忽略的是:递归监听子目录必须显式设 IncludeSubdirectories = true,默认只看一级——这个开关不打开,C:logssubpp.log 的修改永远进不了你的 Changed 回调。











