【问题标题】:FileSystemWatcher and windows 7FileSystemWatcher 和 Windows 7
【发布时间】:2011-03-16 02:05:57
【问题描述】:

我正在编写一个监视网络目录并在 Windows Server 2008 机器上运行的工具,FileSystemWatcher 的 OnChanged 事件正在由任何未使用 Windows 7 的计算机放置在网络驱动器上的文件正确触发,由于某种原因,如果在 Windows 7 计算机上(一次)复制的文件数量超过 19 个,则不会触发任何事件,尽管如果文件是单独完成的,它会起作用。是否有解决方法,或者这正是 Windows 7 内核对 FSW 事件的行为方式?

只是为了澄清它适用于从 XP 机器复制的数千个文件。 (该软件仍在 2008 服务器机器上)。

【问题讨论】:

    标签: c# windows-7 filesystemwatcher


    【解决方案1】:

    来自MSDN

    Windows 操作系统会在由 FileSystemWatcher 创建的缓冲区中通知您的组件。如果短时间内有很多变化,缓冲区可能会溢出。这会导致组件失去对目录更改的跟踪,并且它只会提供一揽子通知。使用InternalBufferSize 属性增加缓冲区的大小是昂贵的,因为它来自无法换出到磁盘的非分页内存,因此请保持缓冲区小而大,以免错过任何文件更改事件。为避免缓冲区溢出,请使用 NotifyFilterIncludeSubdirectories 属性,以便过滤掉不需要的更改通知。

    如果增加缓冲区大小还不够,并且您无法控制一次触发事件的文件数量,则必须添加额外的轮询。

    另请参阅此相关问题:

    FileSystemWatcher does not work properly when many files are added to the directory at the same time…

    更新:

    简单地增加缓冲区大小可能很诱人,但应该小心操作。事实上,在网络访问方面存在 64k 的限制。 FileSystemWatcher 类正在使用下面的 Windows API 函数 ReadDirectoryChangesW,它有这个限制:

    当缓冲区长度大于 64 KB 并且应用程序正在通过网络监视目录时,ReadDirectoryChangesW 失败并显示 ERROR_INVALID_PARAMETER。这是由于底层文件共享协议的数据包大小限制。

    如果您想更深入地了解修改缓冲区大小的成本,您应该在这里查看 Microsoft 的 Walter Wang 的帖子:

    FileSystemWatcher across the network(全文引用如下)

    很抱歉, FileSystemWatcher.InternalBufferSize 没有说得很清楚 监控网络时的缓冲区大小 小路。建议不要超过64K 监控网络路径时。

    FileSystemWatcher 基本上是一个 .Net Win32 的包装器 ReadDirectoryChangesW API。使用 ReadDirectoryChangesW,您创建并 指定操作系统将使用的缓冲区 填充更改。然而, 中没有提到的 阅读DirectoryChangesW 文档 (但在 FileSystemWatcher 文档)是 文件系统创建一个内部内核 用于存储更改信息的缓冲区 暂时直到它有机会 更新用户缓冲区。的大小 创建的内核缓冲区是 中指定的相同尺寸 ReadDirectoryChangesW 并被创建 在非分页池内存中。每次 一个 FileSystemWatcher / 创建 ReadDirectoryChangesW / 调用,一个新的内核缓冲区也是 已创建。

    内核内存池(分页和 非分页)在系统中预留 设备驱动程序的地址空间和 其他要使用的内核组件。他们 动态增长和收缩 必要的。的当前大小 去游泳池可以很容易地看到 任务的性能选项卡 经理。池子会变大 动态直到达到最大值 在启动时计算的值 并且取决于可用的系统 资源(主要是 RAM)。你不 想要达到这个最大值,否则 各种系统服务和驱动程序 将开始失败。然而,这 计算出的最大值并不容易 可用的。确定最大值 池大小,您需要使用内核 调试器。如果你有兴趣 有关系统的更多信息 内存池,我建议你 查看 MSPress 书中的第 7 章 所罗门的 Windows 2000 内部和 鲁西诺维奇。

    考虑到这一点,没有 关于缓冲区大小的建议 您可以使用。当前和最大值 系统池的大小将 因客户而异。 但是,您可能不应该去 每个 FileSystemWatcher 超过 64k / ReadDirectoryChangesW 缓冲区。这 源于有一个事实 网络访问的 64k 限制为 记录在 ReadDirectoryChangesW 中。 但最终你将拥有 测试各种应用程序 预期的目标系统,以便您 可以调整你的缓冲区。

    存在与 .Net 相关的开销 应用程序,我想一个 Win32 ReadDirectoryChangesW 程序 或许能够取得更好的成绩 相同缓冲区大小的性能。 然而,以非常快和众多 文件更改,缓冲区溢出将是 不可避免的,开发商正在走 必须处理案件时 发生溢出,例如手动 枚举要检测的目录 变化。

    总之,FileSystemWatcher 和 ReadDirectoryChangesW 是一个 轻量级文件更改检测 将有它的机制 限制。更改日记帐现为 另一种机制,我们会 考虑一个中等重量的解决方案,但是 仍然有限制:

    http://msdn.microsoft.com/en-us/library/aa363798%28VS.85%29.aspx

    重量级的解决方案是 编写一个专用的文件系统过滤器 位于文件系统中的驱动程序 堆栈和监控文件系统 变化。当然这将是 最复杂的方法。大多数病毒 扫描仪、备份软件和文件 系统监控实用程序,例如 filemon (www.sysinternals.com) 实现一个过滤器驱动程序。

    我希望以上解释能帮助你 了解问题的根本原因 你正在经历。请回复 让我们知道您是否需要 更多信息。谢谢。

    【讨论】:

    • 只是一个小补充:默认缓冲区大小为 8192。
    【解决方案2】:

    在多次尝试使用 FileSystemWatcher 之后,我放弃了它。它不会在错误的时间、错误的类型正确地触发事件。老实说,我认为它是 .net 框架中最糟糕的类之一。我总是最终编写自己的类,它需要一个 System.Timer,并且在经过 x 毫秒后,它将手动检查目录和文件。是的,它需要更多的工作,是的,它可能是一个轻微的 PITA,但是一旦你编写了它,你就可以在任何你想要的地方使用它。我希望 FileSystemWatcher 能像宣传的那样工作,但我从来没有发现它这样做。

    【讨论】:

    • 我同意FileSystemWatcher 显然不适用于所有情况。但是,它提供的一项很难通过简单的目录枚举实现的功能是检测文件重命名。可以做到(使用文件属性比较、启发式、MD5 总和等),但很麻烦。
    • @stakx,确实如此,我从未尝试检查文件名更改。我只是被那门课烫到了,我从没用过。
    【解决方案3】:

    大多数情况下,当 FileSystemWatcher 事件触发时,我会忽略对象正在传递的文件,因为此列表可能不完整。

    我只是爬取 FileSystemWatcher 对象正在监视的目录并执行手动扫描以查找可能不在 FileSystemWatcher 缓冲区中的文件。它不是很优雅,但可以确保您不会错过任何文件。

    【讨论】:

    • 这似乎与 OP 所说的不一致是完全不同的问题。
    • @Chad:这怎么看起来完全不相关?我想说这听起来就像解决 OP 问题的一种方法。
    • 因为 OP 说该事件根本不会触发。 Laurens 建议 当事件触发时 他们手动检查目录,但如果事件从一开始就没有触发,则 OP 无法执行此操作。因此,劳伦斯的评论与 OP 所看到的问题不同。至少,这就是我阅读 OP 关于 Laurens 评论的问题的方式。
    • 问题是事件根本没有被触发,缓冲区设置为最大 64k,当我使用 windows xp 机器时,它会捕获所有事件,即使一次删除 1000 多个文件,但是任何 Windows 7 机器只能触发 19 个文件的事件,之后它不会触发任何东西
    【解决方案4】:

    这是一个非常不可靠的类,当文件高于某个阈值时,我在初始副本上触发了大约 7 次事件,然后当文件传输对话框完成时,我又收到了 7 个事件,这个问题出现在所有支持 FSW 的操作系统。虽然 Windows 7(还没有尝试过 vista)仍然一次只接受一个文件或 19 个文件,而如果我将文件从 XP 机器拖放到网络驱动器中,我可以一次读取数千个文件而没有任何问题。这可能只是将 ReadDirectoryChangesW 从 XP 更改为 7。过去 19 个文件我无法触发任何事件,所以我想这将成为我现在工具的“功能”。如果有人有任何其他信息,请随时贡献。

    【讨论】:

      【解决方案5】:

      如果您使用 MacOSX + Parallels Desktop + Windows, 你的代码不起作用, 因为你的 FileSystemWatcher.Path 属性是 mac Path 的目标,它是一个 UNC 路径,不支持!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-27
        • 2010-12-30
        相关资源
        最近更新 更多