【问题标题】:Win32 function to tell if a file is descendant of directoryWin32 函数判断文件是否是目录的后代
【发布时间】:2011-03-28 18:35:10
【问题描述】:

在 C++、Windows 中,我有 2 个文件路径:

  • Path1:目录
  • Path2:文件或目录

如何判断 Path2 是否是 Path1 的后代?有没有一个Shell函数呢?我已经搜索了 Shell API,但没有找到。

注意:我不想自己比较字符串,除非有一种安全的方法可以使路径具有可比性(处理短名称、相关项等...)。

【问题讨论】:

  • @shellter:wsh指的是Windows Scripting Host,与Shell API无关。
  • @gabe,我删除了我无用的评论。感谢您的澄清!

标签: windows path filesystems windows-shell


【解决方案1】:

我不相信 API 中有一个方便的函数可以为您回答这个问题。但我也不认为编写一个健壮的函数来做到这一点太难了。这是大纲:

  1. 确保 Path1 和 Path2 是绝对路径。
  2. 检查 Path1 和 Path2 是否引用同一个文件系统对象(更多内容见下文)。
  3. 如果是,那么您的函数将返回 true。
  4. 如果不从 Path2 中删除姓氏,即删除最后的路径分隔符(\ 或 /)及其后面的所有内容。
  5. 如果第 4 步未能删除任何文本(例如,没有留下路径分隔符),那么您已到达根级别并且您的函数返回 false。
  6. 转到 2。

现在,如何检查两个名称是否引用同一个文件系统对象。最安全的方法是调用GetFileInformationByHandle() 获取两个名称,然后比较dwVolumeSerialNumbernFileIndexLownFileIndexHigh。如果都相等,那么这两个名称指的是同一个对象。

请注意,仅比较字符串是不够的,因为单个文件系统对象可以有许多不同的名称。例如硬链接、符号链接、联结、UNC 与映射驱动器号等等。

【讨论】:

  • @Gabe 我会调用PathIsRelative,如果返回true,那么您需要将其设为绝对(例如,与当前目录或您的应用程序规定的相对路径相结合)到)。
  • 您还必须在不经过根目录的情况下删除路径中的任何 .. 段。
  • @Gabe 说得好。打几个电话PathCanonicalize() 应该可以解决问题。
【解决方案2】:

您可以使用PathCommonPrefix 比较两个路径以获得共同的前缀。然后将公共前缀与 Path1(您的目录)进行比较。

如果两者相等,则 Path2 应该是后代。

或者你也许可以不使用out 缓冲区而逃脱,只需将NULL 传递给第三个参数并检查返回值,即公共前缀字符的计数,是否等于Path1 的长度。

PathCommonPrefix 的文档示例中,PathCommonPrefix 看起来不像在“通用”前缀中包含尾随目录分隔符,因此您必须去掉 Path1 中的尾随分隔符或调整比较由于缺少尾随 sep,结果相应地。

【讨论】:

  • 谢谢,最初,在阅读 MSDN 中的函数摘要后,我认为它不起作用,但在阅读示例后,我认为它应该可以工作。 (并且已经过验证,可以正常工作!)
  • @decasteljau 您需要规范化您的路径才能使其真正起作用。例如,输入 C:\trick 和 C:\trick\.. 会导致 C:\trick 的公共前缀不正确。您也确实意识到,在存在连接点、符号链接等的情况下,这会失败。也许这对您来说没问题,但我只是想确保您清楚这一点的局限性。
【解决方案3】:

这是我提出的一个解决方案,到目前为止似乎有效(在我有限的测试中)。它测试文件是否是直系后代,并获得相对路径。它基于PathRelativePathTo

BOOL PathDirectRelativePathTo(LPWSTR pszPath, LPCWSTR pszFrom, DWORD dwAttrFrom, LPCWSTR pszTo, DWORD dwAttrTo)
{
    return PathRelativePathTo(pszPath, pszFrom, dwAttrFrom, pszTo, dwAttrTo) && pszPath[1] == L'\\';
}

前提是当一个文件是直系后代时,PathRelativePathTo得到的相对路径以.\开头。当它不是直系后代时,相对路径以.. 开头。所以这个函数测试相对路径的第二个字符是否是\。但同样,我只是想出了这个,所以也许有一些问题潜伏在那里。

【讨论】: