FileSystemWatcher事件被触发两次,尽管已经采取措施防止这种情况发生

4

我在这里和其他网站上浏览了很多帖子,寻找解决这个问题的方法。

以下是我的FileMonitor类:

class FileMonitor
{
    public FileMonitor(String path)
    {
        try
        {
            var watcher = new FileSystemWatcher()
            {
                Path = path,
                IncludeSubdirectories = true,
                InternalBufferSize = 65536,
                EnableRaisingEvents = true
            };

            watcher.Changed += new FileSystemEventHandler(OnFileChanged);
            watcher.Created += new FileSystemEventHandler(OnFileCreated);
            watcher.Deleted += new FileSystemEventHandler(OnFileDeleted);
            watcher.Renamed += new RenamedEventHandler(OnFileRenamed);
            watcher.Error += new ErrorEventHandler(OnWatcherError);
        }
        catch (Exception)
        {

            throw;
        }
    }

    private void OnWatcherError(object sender, ErrorEventArgs e)
    {

    }

    private void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        try
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = false;

            LogFileSystemChanges(e);
        }

        finally
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = true;
        }
    }

    private void OnFileCreated(object sender, FileSystemEventArgs e)
    {
        try
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = false;

            LogFileSystemChanges(e);
        }

        finally
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = true;
        }
    }

    private void OnFileDeleted(object sender, FileSystemEventArgs e)
    {
        try
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = false;

            LogFileSystemChanges(e);
        }

        finally
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = true;
        }
    }

    private void OnFileRenamed(object sender, RenamedEventArgs e)
    {
        try
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = false;

            LogFileSystemRenaming(e);
        }

        finally
        {
            ((FileSystemWatcher)sender).EnableRaisingEvents = true;
        }
    }

    private void LogFileSystemChanges(FileSystemEventArgs e)
    {
        string log = string.Format("{0:G}: {1} | {2}", DateTime.Now, e.FullPath, e.ChangeType);
        Console.WriteLine(log);
    }

    private void LogFileSystemRenaming(RenamedEventArgs e)
    {
        string log = string.Format("{0:G}: {1} | Old name: {2}", DateTime.Now, e.FullPath, e.OldName);
        Console.WriteLine(log);
    }
}

你可以看出,我已经尝试了((FileSystemWatcher)sender).EnableRaisingEvents = false; 的“锁定”,但从我的控制台输出可以看出,我的事件触发了两次。 有什么想法吗?我真的不确定该怎么做。

三周过去了,但你仍然没有跟进。你解决了这个问题吗?能投票回答或告诉我们你是如何解决的吗?谢谢! - pid
抱歉,我还没有找到解决方案。当我意识到有多少人在使用FileSystemWatcher时遇到了很多问题时,我有点放弃了。 - MrGraversen
2个回答

6

我曾遇到同样的问题,经过一番努力,得出结论:记事本会连续写入文件三次!如果一个应用程序真的需要频繁保存(可能是为了三角备份等),你没有太多可以做的。

我还注意到你已经附加了所有事件,你应该缩小范围,只筛选出你最需要的内容,并使用NotifyFilters过滤掉那些不感兴趣的事件。


也许会创建一个新文件并赋予新名称,然后删除旧文件,再将新文件重命名为旧文件名?这样会触发3个写入事件吗? - Stein Åsmul
1
是的,*.txt文件也有可能被重命名为*.bak,创建一个新的*.txt文件,然后删除备份。这也会在文件夹上触发3个事件。将txt/bak扩展名替换为您使用的任何内容。这完全取决于所使用的程序。 - pid
我认为这种方法至少在MFC中被使用 - 磁盘事件很常见且难以处理。 - Stein Åsmul
1
也许有人应该开始一个Github项目,实现一个包装器,利用这个leaky abstraction并添加最佳实践控制代码,使它以更有用的方式工作,具有更高级别的事件、超时、轮询回退等等…这些通常在Linux中看到的典型解决方法。 - pid

4
我以前(实际上是在2008年)尝试过使用FileSystemWatcher类,并遇到了严重的问题。它最多只是一个泄漏的抽象。当时我在CodeProject上报告了我的发现,请查找评论列表中的“Glytzhkof”。据我回忆,我几乎在类的每个方面都遇到了问题,但经过这么多年后,它可能已经得到改进

总之,我当时的经验是,事件要么完全消失,要么堆积起来导致异常,这取决于诸如磁盘写缓存是否启用、是否访问RAID、NAS或常规磁盘以及其他我不太记得的随机硬件问题等变量。有趣的是,它似乎可以使用UNC路径。不知道为什么。映射的驱动器失败了。

请查看Code Project讨论中的摘要。我相信自从我试用它以来已经有了改进,也许还会出现一些新的错误 :-)。似乎将磁盘存储硬件作为高级抽象处理是很困难的 - 它会泄漏。个人最终使用服务和常规磁盘功能手动扫描文件。在手动操作之前,我浪费了很多时间。

更新: 请参阅此处的新信息:https://dev59.com/ZGYr5IYBdhLWcg3wfKP_#23704476


对你所说的一切表示赞同,我也有同样的经验。此外,这似乎也取决于格式。在NTFS上的UNC可以工作,但是对于非NTFS驱动器或甚至更糟的非Windows系统的UNC...它不起作用的情况比起起作用的情况更多。我理解如果没有控制器/驱动程序/格式方面的任何支持,就无法实现它,因此要使其正常工作,需要跨平台的完全硬件/驱动程序/协议支持。这不是你可以轻易假设拥有的东西。尽管如此,当我需要时我仍然会使用它。它非常有用,直到...它停止工作 :) - pid
1
我看了你的CodeProject!如果可以的话,我会再点赞+1的 :) 做得非常好!这正是我在之前评论中所指的。 - pid
1
定期轮询会导致非常不良的副作用,例如在笔记本模式下重新启动磁盘,或者完全阻止磁盘停转,因为计时器比停转延迟时间更短。这就是错误的™。 - v.oddou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接