You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
            }
        }
    }
}

关键修改点说明

  1. 移除全局时间变量:每个事件处理时,直接针对当前文件/目录获取对应的创建/修改时间,或者使用事件触发的实时时间,符合实际监控需求。
  2. 区分文件与目录:在所有事件里先判断e.FullPath是文件还是目录,避免对目录执行文件专属操作(比如获取Length)。
  3. 增加异常处理:捕获FileNotFoundException和IOException,处理文件被快速删除或占用的情况,彻底避免程序崩溃。
  4. 修复重复绑定事件:你之前重复绑定了Renamed事件,现在只绑定一次,避免重复输出日志。
  5. 添加变更去重逻辑:FileSystemWatcher在文件保存时可能多次触发Changed事件,通过记录上次处理的路径和时间,过滤短时间内的重复事件。
  6. 优化目录信息展示:初始化时展示所有子目录的文件,而不仅仅是根目录,监控启动时的信息更完整。

这样修改后,程序就能稳定监控根目录和所有子目录的文件/目录变更,不会再因为子目录操作而崩溃了。

内容的提问来源于stack exchange,提问作者Simpson.Joshua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:52:45