FileSystemWatcher 在作为 Release 运行时无法从两个目录获取事件

Posted

技术标签:

【中文标题】FileSystemWatcher 在作为 Release 运行时无法从两个目录获取事件【英文标题】:FileSystemWatcher can't get events from two directories when run as Release 【发布时间】:2021-11-23 22:47:32 【问题描述】:

如果作为 Release 运行仅从第一个目录发生事件。不管哪个是第一。在调试模式下工作正常。

public class Program

    private static void Main(string[] args)
    
        string[] paths = File.ReadAllLines("config.txt");
        List<FolderWatch> lw = new List<FolderWatch>();
        foreach (string path in paths)
            if (Path.IsPathFullyQualified(path))
                lw.Add(new FolderWatch(path));
        Thread.Sleep(Timeout.InfiniteTimeSpan);
    

public class FolderWatch

    private FileSystemWatcher watcher;
    public FolderWatch(string path)
    
        watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.Created += OnCreated;
        watcher.EnableRaisingEvents = true;            
    
    private static void OnCreated(object source, FileSystemEventArgs e)
    
            try
            
                File.AppendAllText("log.txt", "Event occured");
            
            catch  
    

更新: 关于watcher 的范围,我做了一些更改。

string[] paths = File.ReadAllLines(configpath);

FileSystemWatcher[] w_arr = new FileSystemWatcher[paths.Length];

这行得通:

w_arr[0] = new FileSystemWatcher();
if (Path.IsPathFullyQualified(paths[0]))
    SetupWatcher(w_arr[0], paths[0]);

w_arr[1] = new FileSystemWatcher();
if (Path.IsPathFullyQualified(paths[1]))
    SetupWatcher(w_arr[1], paths[1]);

在一个循环中它不起作用。仅发生第一个目录中的事件。

for (int i = 0; i < paths.Length; i++)

    if (Path.IsPathFullyQualified(paths[i]))
    
        w_arr[i] = new FileSystemWatcher();
        SetupWatcher(w_arr[i], paths[i]);
    

最后线程休眠并等待事件。

Thread.Sleep(Timeout.InfiniteTimeSpan);

private static void SetupWatcher(FileSystemWatcher watcher, string path)

    watcher.Path = path;
    watcher.EnableRaisingEvents = true;
    watcher.Filter = "*.pdf";
    watcher.Created += OnCreated;
    watcher.Error += OnError;
    GC.KeepAlive(watcher);

【问题讨论】:

抱歉,无法重现。 当从任务管理器运行时你到底做了什么 您可以尝试在程序末尾添加这一行:GC.KeepAlive(lw);,看看是否有什么不同? 编辑 OnCreated() 并添加 throw new Exception("test"); 确保您在日志文件中获得此异常的良好诊断。 我发现了不同之处:它是调试与发布,调试工作。 KeepAlive 没有帮助,甚至没有为第二个目录调用 OnCreated。 您确定没有遗漏 AppendAllText() 抛出的异常吗?尝试将它放在共享静态对象的锁中,以强制“几乎同时”引发的事件等待轮到更新日志文件。还要给 log.txt 一个明确的路径。 【参考方案1】:

为确保FileSystemWatchers 在程序结束前不会被垃圾回收,您可以这样做:

public class FolderWatch

    private FileSystemWatcher watcher;

    //...

    public void KeepAlive() => GC.KeepAlive(watcher);

...并在Main 方法的结束处为所有FolderWatchs 调用KeepAlive

private static void Main(string[] args)

    var lw = new List<FolderWatch>();
    //...
    Thread.Sleep(Timeout.InfiniteTimeSpan);
    lw.ForEach(w => w.KeepAlive());

【讨论】:

谢谢。我试过了,但没有帮助。反正Sleep之后代码不会被执行。 @petr KeepAlive 方法没有代码。您可以查看source code。它的目的只是为了防止垃圾收集器回收一个在垃圾收集器看来似乎已经超出范围的对象。此问题会影响 FileSystemWatcherSystem.Threading.Timer 等组件。其他组件,如System.Timers.Timer,不受此影响。 你是完全正确的,我在Thread.Sleep(Timeout.InfiniteTimeSpan); 之后添加了 'GC.KeepAlive(w_arr);` 并且这完成了工作。谢谢! 真的需要ForEach吗?不只是GC.KeepAlive(lw) 可以解决问题吗? @GuruStron 根据我的实验,GC.KeepAlive(lw) 就足够了。但是由于 OP 报告说这不起作用,我提出了一个更核心的替代方案。 :-)

以上是关于FileSystemWatcher 在作为 Release 运行时无法从两个目录获取事件的主要内容,如果未能解决你的问题,请参考以下文章

FileSystemWatcher 事件未在 WPF 应用程序中触发

FileSystemWatcher - 在删除时,复制一个文件

FileSystemWatcher 无法正常工作

FileSystemWatcher - 文件复制期间的 3 个事件

Filesystemwatcher 没有在看子目录

FileSystemWatcher .Net 3.5