FileSystemWatcher监控子目录崩溃及文件时间获取问题求助
问题分析与修复方案
首先看你遇到的崩溃问题,核心有两个关键原因:
1. 全局时间变量的逻辑错误
你在代码顶部声明的modification和creation只获取了根目录的时间,不管是子目录里的文件还是新创建的资源,都用这个固定值,这不仅不符合需求,还会导致所有事件的时间展示完全错误。
2. 未区分文件与目录的操作(崩溃的直接诱因)
错误日志显示你尝试获取一个目录的Length属性:C:\Users\SIMPSJSH\Documents\Lets Test\This is for folder checking是个子目录,而FileInfo.Length是文件专属属性,访问目录的这个属性会直接抛出FileNotFoundException。另外,当文件被快速删除时,事件触发后再去访问文件也会出现同类异常。
修复后的完整代码
using System; using System.IO; using System.Threading; namespace IDGFileSystemWatcher { class Program { // 用于去重:避免FileSystemWatcher重复触发Changed事件 private static string _lastChangedPath; private static DateTime _lastChangedTime; private static readonly object _lockObj = new object(); static void Main(string[] args) { string path = @"C:\Users\SIMPSJSH\Documents\Lets Test"; MonitorDirectory(path); Console.WriteLine("监控已启动,按任意键退出..."); Console.ReadKey(); } private static void MonitorDirectory(string path) { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(); fileSystemWatcher.Path = path; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; // 绑定事件(注意不要重复绑定Renamed事件,你之前绑了两次) fileSystemWatcher.Created += FileSystemWatcher_Created; fileSystemWatcher.Changed += FileSystemWatcher_Changed; fileSystemWatcher.Renamed += FileSystemWatcher_Renamed; fileSystemWatcher.Deleted += FileSystemWatcher_Deleted; fileSystemWatcher.Error += OnError; // 初始化目录信息展示:包含所有子目录文件 DirectoryInfo di = new DirectoryInfo(path); FileInfo[] fiArr = di.GetFiles("*", SearchOption.AllDirectories); Console.WriteLine("目录 {0} 及其子目录包含以下文件:", di.FullName); foreach (FileInfo f in fiArr) { Console.WriteLine("- {0} | 大小: {1} bytes | 创建时间: {2}", f.Name, f.Length, f.CreationTime.ToString("yyyy-MM-dd HH:mm:ss")); } Console.WriteLine("\n----------------------------------------\n变更日志开始:\n"); fileSystemWatcher.EnableRaisingEvents = true; } private static void FileSystemWatcher_Created(object sender, FileSystemEventArgs e) { // 判断是文件还是目录 if (Directory.Exists(e.FullPath)) { Console.WriteLine("[目录创建] {0} | 时间: {1}", e.Name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } else { try { var fileInfo = new FileInfo(e.FullPath); Console.WriteLine("[文件创建] {0} | 大小: {1} bytes | 创建时间: {2}", e.Name, fileInfo.Length, fileInfo.CreationTime.ToString("yyyy-MM-dd HH:mm:ss")); } catch (FileNotFoundException) { Console.WriteLine("[文件创建] {0} | 已被快速删除,无法获取详情", e.Name); } } } private static void FileSystemWatcher_Renamed(object sender, RenamedEventArgs e) { if (Directory.Exists(e.FullPath)) { Console.WriteLine("[目录重命名] {0} → {1} | 时间: {2}", e.OldName, e.Name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } else { try { var fileInfo = new FileInfo(e.FullPath); Console.WriteLine("[文件重命名] {0} → {1} | 大小: {2} bytes | 时间: {3}", e.OldName, e.Name, fileInfo.Length, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } catch (FileNotFoundException) { Console.WriteLine("[文件重命名] {0} → {1} | 已被删除,无法获取详情", e.OldName, e.Name); } } } private static void FileSystemWatcher_Deleted(object sender, FileSystemEventArgs e) { Console.WriteLine("[删除] {0} | 时间: {1}", e.Name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } private static void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e) { // 去重:同一文件1秒内的重复变更只处理一次 lock (_lockObj) { if (_lastChangedPath == e.FullPath && (DateTime.Now - _lastChangedTime).TotalSeconds < 1) { return; } _lastChangedPath = e.FullPath; _lastChangedTime = DateTime.Now; } if (Directory.Exists(e.FullPath)) { Console.WriteLine("[目录变更] {0} | 时间: {1}", e.Name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); return; } try { var fileInfo = new FileInfo(e.FullPath); Console.WriteLine("[文件变更] {0} | 大小: {1} bytes | 修改时间: {2}", e.Name, fileInfo.Length, fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")); } catch (FileNotFoundException) { Console.WriteLine("[文件变更] {0} | 已被删除,无法获取详情", e.Name); } catch (IOException) { // 处理文件被占用的情况:短时间后重试一次 Thread.Sleep(100); try { var fileInfo = new FileInfo(e.FullPath); Console.WriteLine("[文件变更] {0} | 大小: {1} bytes | 修改时间: {2}", e.Name, fileInfo.Length, fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")); } catch { Console.WriteLine("[文件变更] {0} | 文件被占用或已删除,无法获取详情", e.Name); } } } private static void OnError(object source, ErrorEventArgs e) { Console.WriteLine("[监控错误] 时间: {0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); if (e.GetException() is InternalBufferOverflowException overflowEx) { Console.WriteLine("内部缓冲区溢出:{0},部分变更事件可能丢失", overflowEx.Message); } else { Console.WriteLine("错误详情:{0}", e.GetException().Message); } } } }
关键修改点说明
- 移除全局时间变量:每个事件处理时,直接针对当前文件/目录获取对应的创建/修改时间,或者使用事件触发的实时时间,符合实际监控需求。
- 区分文件与目录:在所有事件里先判断
e.FullPath是文件还是目录,避免对目录执行文件专属操作(比如获取Length)。 - 增加异常处理:捕获
FileNotFoundException和IOException,处理文件被快速删除或占用的情况,彻底避免程序崩溃。 - 修复重复绑定事件:你之前重复绑定了
Renamed事件,现在只绑定一次,避免重复输出日志。 - 添加变更去重逻辑:
FileSystemWatcher在文件保存时可能多次触发Changed事件,通过记录上次处理的路径和时间,过滤短时间内的重复事件。 - 优化目录信息展示:初始化时展示所有子目录的文件,而不仅仅是根目录,监控启动时的信息更完整。
这样修改后,程序就能稳定监控根目录和所有子目录的文件/目录变更,不会再因为子目录操作而崩溃了。
内容的提问来源于stack exchange,提问作者Simpson.Joshua
相关产品推荐
相关产品推荐

