【问题标题】:How to deal with Windows' ReadDirectoryChangesW() and its mixed long/short filename output?如何处理 Windows 的 ReadDirectoryChangesW() 及其混合长/短文件名输出?
【发布时间】:2026-01-25 01:10:01
【问题描述】:

我正在开发一段 C 代码,它使用 ReadDirectoryChangesW() 来监视 Windows 目录下的更改。我已经阅读了 ReadDirectoryChangesW() 和 FILE_NOTIFY_INFORMATION 结构的相关 MSDN 条目,以及其他几篇文档。在这一点上,我已经设法监控多个目录,监控本身没有明显问题。问题是这个函数放在 FILE_NOTIFY_INFORMATION 结构中的文件名不是规范的。

根据 MSDN,它们可以是长格式或短格式。我发现了几篇建议缓存短路径名和长路径名来处理这种情况的帖子。不幸的是,根据我自己在 Windows 7 系统上的测试,这不足以消除该问题,因为每个文件名不仅有两种选择。问题是路径名中的每个组件都可以是长格式或短格式。以下路径名都可以引用同一个文件:

c:\PROGRA~1\MYPROG~1\MYDATA~1.TXT

c:\PROGRA~1\MYPROG~1\MyDataFile.txt

c:\PROGRA~1\MyProgram\MYDATA~1.TXT

c:\PROGRA~1\MyProgram\MyDataFile.txt

c:\Program Files\MYPROG~1\MYDATA~1.TXT

...

据我使用 cmd.exe 进行的测试可以看出,它们都是完全可以接受的。本质上,每个文件的有效路径名的数量随着其路径名中的组件数量呈指数增长。

不幸的是,ReadDirectoryChangesW() 似乎使用提供给导致每个操作的系统调用的文件名填充其输出缓冲区。例如,如果您使用 cmd.exe 命令创建、重命名、删除等。文件,FILE_NOTIFY_INFORMATION 将包含在命令行中指定的文件名。

现在,在大多数情况下,我可以使用 GetLongPathName() 和朋友来获得一条独特的路径供我使用。不幸的是,删除文件时无法做到这一点 - 当我收到通知时,文件已经消失,Get*PathName() 函数将无法工作。

目前我正在考虑使用更广泛的缓存来确定应用程序对每个文件使用哪些替代路径名,这将处理任何情况,除了有人决定使用看不见的混合路径名。而且我正在考虑从父目录修改事件中进行创造性数据挖掘,并回退到检查该案例的实际目录。

对于更简单的方法有什么建议吗?

PS1:虽然 Change Journals 可以有效地处理这个问题(我希望),但由于它们与 NTFS 的联系以及我的应用程序缺乏管理员权限,我不相信我可以使用它们。除非迫不得已,否则我宁愿不去那里。

PS2:请记住,我主要在 Unix 上编写代码,所以要温柔...

【问题讨论】:

  • 如果一切都失败了,也许微过滤器驱动可以工作?
  • 我相信大多数防病毒程序都是这样做的,我想这将是解决这个问题的方法。不幸的是,它需要系统管理员权限才能安装,就像 Change Journals 一样,它会使我的应用程序的架构变得更加复杂,因为我必须考虑我现在不必处理的安全性和稳定性问题。我们不要忘记为任何操作系统编写内核模式或准内核模式驱动程序的固有困难。
  • 哦,目前,为了监视几个用户目录而监视所有内容似乎有点矫枉过正。不过感谢您的建议...
  • Windows 中的更改通知 API 是一种耻辱。它也有几个比赛条件。即使是 Explorer 也无法做到这一点。 (事实上​​,Win7 中的资源管理器似乎在该区域有一些新的错误,命令提示符程序修改文件。:))

标签: c windows winapi filenames readdirectorychangesw


【解决方案1】:

您不需要缓存每个组合。如果您缓存每个子路径以便能够将其转换为长格式,它将做到这一点。例如存储这个:

  • C:\PROGRA~1 => c:\Program Files
  • c:\Program Files\MYPROG~1 => c:\Program Files\MyProgram
  • c:\Program Files\MyProgram\MYDATA~1.TXT => c:\Program Files\MyProgram\MyDataFile.txt
  • c:\Program Files\MyProgram\MYDATA~2.TXT => c:\Program Files\MyProgram\MyDataFile2.txt

现在,如果您收到c:\PROGRA~1\MYPROG~1\MYDATA~1.TXT 的通知,请将其拆分为每个\,并查找每个部分的长格式。

不要忘记MyDataFile.txtMYDATAFILE.TXT 也指向同一个文件。所以比较不区分大小写或将所有内容都转换为大写。

如果c:\PROGRA~1\MYPROG~1\MYDATA~1.TXT 被删除,你可能仍然在c:\PROGRA~1\MYPROG~1 上使用GetLongPathName()

【讨论】:

  • 当我提到广泛的缓存和数据挖掘时,我想到的是您建议的稍微增强的版本。现在,我在可用时存储短名称和长名称,当看到路径时,我已经可以找到所有替代路径名。使用 c:\A\B\C -> c:\a\b\c 关联我可以检测到所有 c:\{A,a}\{B,b}\{C,c} 组合。删除看不见的文件仍然存在问题。我正在考虑递归存储目录的初始状态以处理此问题-不确定是否可以避免它...
最近更新 更多