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

FileSystemWatcher调用后重置EnableRaisingEvents及多目录文件监控类问题

看起来你已经搭好了Watcher类的基础框架,但在多目录监控和EnableRaisingEvents意外重置的问题上遇到了卡点——我来帮你优化这个实现,同时解决这两个核心问题。

优化后的多目录监控Watcher类

先上完整的改进代码,后面再拆解关键细节:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public class Watcher
{
    // 用集合存储多个监控实例,支持同时监控多目录
    private readonly List<FileSystemWatcher> _watchers = new List<FileSystemWatcher>();
    
    // 保留你定义的委托与事件
    public delegate void FileCreatedEventHandler(object sender, FileSystemEventArgs e);
    public delegate void FileRenamedEventHandler(object sender, RenamedEventArgs e);
    public delegate void LogEventHandler(object sender, string e);
    
    public event FileCreatedEventHandler FileCreated;
    public event FileRenamedEventHandler FileRenamed;
    public event LogEventHandler Log;

    // 添加要监控的目录,支持可选监控子目录
    public void AddWatchDirectory(string path, bool includeSubdirectories = false)
    {
        if (!Directory.Exists(path))
        {
            OnLog($"警告:目录不存在,无法监控 -> {path}");
            return;
        }

        var watcher = new FileSystemWatcher(path)
        {
            IncludeSubdirectories = includeSubdirectories,
            EnableRaisingEvents = true,
            // 只关注我们需要的事件类型,减少不必要的触发
            NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName
        };

        // 绑定核心事件
        watcher.Created += OnFileCreated;
        watcher.Renamed += OnFileRenamed;
        // 重点:绑定Error事件,解决EnableRaisingEvents被意外禁用的问题
        watcher.Error += OnWatcherError;

        _watchers.Add(watcher);
        OnLog($"已开始监控目录 -> {path}");
    }

    // 移除指定目录的监控
    public void RemoveWatchDirectory(string path)
    {
        var targetWatcher = _watchers.FirstOrDefault(w => 
            w.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
        
        if (targetWatcher != null)
        {
            targetWatcher.EnableRaisingEvents = false;
            targetWatcher.Dispose();
            _watchers.Remove(targetWatcher);
            OnLog($"已停止监控目录 -> {path}");
        }
    }

    // 停止所有监控并释放资源
    public void StopAllWatches()
    {
        foreach (var watcher in _watchers)
        {
            watcher.EnableRaisingEvents = false;
            watcher.Dispose();
        }
        _watchers.Clear();
        OnLog("已停止所有目录监控");
    }

    // 触发FileCreated事件
    private void OnFileCreated(object sender, FileSystemEventArgs e)
    {
        FileCreated?.Invoke(sender, e);
        OnLog($"检测到文件创建 -> {e.FullPath}");
    }

    // 触发FileRenamed事件
    private void OnFileRenamed(object sender, RenamedEventArgs e)
    {
        FileRenamed?.Invoke(sender, e);
        OnLog($"检测到文件重命名 -> {e.OldFullPath} → {e.FullPath}");
    }

    // 处理监控器错误,修复EnableRaisingEvents被重置的问题
    private void OnWatcherError(object sender, ErrorEventArgs e)
    {
        var watcher = sender as FileSystemWatcher;
        if (watcher == null) return;

        var exception = e.GetException();
        OnLog($"监控器错误[{watcher.Path}]:{exception.Message},尝试恢复监控...");
        
        // 核心修复逻辑:当监控器因错误自动禁用事件触发时,重新启用
        try
        {
            watcher.EnableRaisingEvents = false;
            watcher.EnableRaisingEvents = true;
            OnLog($"已恢复目录监控 -> {watcher.Path}");
        }
        catch (Exception ex)
        {
            OnLog($"恢复监控失败[{watcher.Path}]:{ex.Message}");
        }
    }

    // 统一触发日志事件
    private void OnLog(string message)
    {
        Log?.Invoke(this, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
    }
}

关键改进细节

  • 多目录支持:用List<FileSystemWatcher>替代单个实例,通过AddWatchDirectory可以自由添加任意多个监控目录,满足多目录监控需求。
  • 解决EnableRaisingEvents重置问题:
    • FileSystemWatcher遇到错误(比如目录被删除、权限不足、网络目录断开)时,会自动把EnableRaisingEvents设为false,我们通过绑定Error事件,在错误发生后尝试重新启用事件触发。
    • 在添加目录时显式设置EnableRaisingEvents = true,避免依赖默认值导致的意外问题。
  • 资源安全:提供RemoveWatchDirectory和StopAllWatches方法,确保在停止监控时正确释放FileSystemWatcher资源,避免内存泄漏。
  • 日志调试:统一的日志输出,方便你跟踪监控状态、错误信息和事件触发情况。

简单使用示例

public class Program
{
    static void Main(string[] args)
    {
        var watcher = new Watcher();
        
        // 订阅事件
        watcher.FileCreated += (sender, e) => Console.WriteLine($"新文件:{e.FullPath}");
        watcher.FileRenamed += (sender, e) => Console.WriteLine($"文件改名:{e.OldFullPath} → {e.FullPath}");
        watcher.Log += (sender, msg) => Console.WriteLine(msg);
        
        // 添加两个监控目录
        watcher.AddWatchDirectory(@"D:\Work\Dir1", includeSubdirectories: true);
        watcher.AddWatchDirectory(@"D:\Work\Dir2");
        
        Console.WriteLine("按任意键停止监控...");
        Console.ReadKey();
        
        watcher.StopAllWatches();
    }
}

另外还有几个小提醒:

  • 确保程序有足够的权限访问监控目录,否则会频繁触发错误。
  • 如果在事件处理中对文件进行读写操作,可能会触发重复事件,必要时可以添加延迟处理或者简单的锁机制。
  • 如果监控的目录被删除后又重建,当前实现不会自动重新监控,你可以在OnWatcherError中添加目录存在性检查,自动重新添加监控逻辑。

内容的提问来源于stack exchange,提问作者Paul S Chapman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:28:01