代码之家  ›  专栏  ›  技术社区  ›  chacham15

如何获取包含目录的句柄?

  •  0
  • chacham15  · 技术社区  · 6 年前

    给一个文件的句柄(例如 C:\\FolderA\\file.txt ,我想要一个函数,它将返回一个句柄到包含目录(在前面的示例中,它将是 C:\\FolderA )中。例如:

    HANDLE hFile = CreateFileA(
                      "C:\\FolderA\\file.txt",
                      GENERIC_READ,
                      FILE_SHARE_READ,
                      NULL,
                      OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL);
    HANDLE hDirectory = somefunc(hFile);
    

    可能的实施 someFunc 以下内容:

    HANDLE someFunc(HANDLE h)
    {
        char *path = getPath(h);             // "C:\\FolderA\\file.txt"
        char *parent = getParentPath(path);  // "C:\\FolderA"
        HANDLE hFile = CreateFileA(
                  parent,
                  GENERIC_READ,
                  FILE_SHARE_READ,
                  NULL,
                  OPEN_EXISTING,
                  FILE_ATTRIBUTE_NORMAL,
                  NULL);
        free(parent);
        free(path);
        return hFile;
    }
    

    但是有没有办法实现 萨莫芬 没有 getParentPath 或者不让它查看字符串并删除最后一个目录分隔符之后的所有内容(因为从性能角度看这很糟糕)?

    0 回复  |  直到 6 年前
        1
  •  4
  •   Cody Gray    6 年前

    我不知道 getParentPath 是。我假设这是一个函数,它搜索字符串中的尾随反斜杠,并使用该函数去除文件规范。您不必自己定义这样一个函数;windows已经为您提供了一个- PathCchRemoveFileSpec . (注意,这假定指定的路径实际上包含要删除的文件名。如果路径不包含文件名,它将删除后面的目录名。您还可以使用其他函数来验证路径是否包含文件规范。)

    此函数的旧版本是 PathRemoveFileSpec ,这是您在没有更新、更安全的功能的低级操作系统上使用的功能。

    除了windows api之外,还有其他方法可以做同样的事情。如果你瞄准C++ 17,那就是 filesystem::path 上课。boost提供了类似的功能。或者你可以自己写 find_last_of 成员函数 std::string 同学们,如果你必须的话。(但不想重新发明轮子。当涉及到路径操作时,有很多边缘情况是您可能不会想到的,并且您的测试可能不会揭示出来。)

    您对这种方法的性能表示关注。这是胡说八道。 从字符串中剥离某些字符不是一个缓慢的操作。 如果从字符串的开头开始搜索,然后在找到文件规范后,再次从字符串的开头开始复制该字符串,甚至不会很慢。它是一个简单的循环,通过一个合理长度的字符串的字符进行搜索,然后 memcpy . 在执行文件I/O的代码中,此操作绝对不可能成为性能瓶颈。

    但是,实施可能不会那么幼稚。您可以从 结束 减少需要遍历的字符数,如果允许操作原始字符串,则可以完全避免任何类型的内存复制。使用c样式的字符串,只需用nul字符替换后面的路径分隔符(分隔路径规范开头的分隔符)( \0 )中。使用C++风格的字符串,您只需调用 erase 成员函数。

    事实上,如果你 真正地 关心性能,实际上 放心 比从文件对象中检索包含文件夹的系统调用更快。 系统调用比某些编译器生成的、不可内联的代码要慢得多,这些代码可以遍历字符串并去掉子字符串。

    一旦找到目录的路径,就可以获得 HANDLE 给它打电话 CreateFile 使用 FILE_FLAG_BACKUP_SEMANTICS 旗帜。 (如果要检索目录的句柄,则必须传递该标志。


    我已经测量到这是缓慢的,并正在寻找一个更快的方式。

    你的尺寸不对。要么你犯了一个常见的错误,基准测试一个调试构建,其中的标准库功能(例如, STD::字符串 )未优化,和/或真正的性能瓶颈是文件I/O。 创建文件 任何想象力的迅速发挥。我几乎可以保证那将是你的热点。


    注意,如果您还没有路径,那么从 把手 一个文件。 正如评论中指出的,在WindowsVista和更高版本上,您只需要调用 GetFinalPathNameByHandle 功能。更多详情请参见 this article 在msdn上,包括示例代码和在windows的低级版本上使用的替代代码。

    正如在问题的注释中已经提到的,您可以通过分配一个长度为 MAX_PATH (或者更大)在堆栈上。它编译成一条指令来调整堆栈指针,因此也不会成为性能瓶颈。(好吧,我撒谎了:你真的需要 指令一个在堆栈上创建空间,另一个释放堆栈上分配的空间。仍然不是性能问题。)这样,您甚至不必进行任何动态内存分配。

    注意,对于最大的健壮性,特别是在Windows 10上,要处理路径比 最大路径 是的。在这种情况下,堆栈分配的缓冲区将太小,调用以填充缓冲区的函数将返回错误。处理该错误,并在空闲存储区上分配更大的缓冲区。这会比较慢,但这是一个边缘情况,可能不值得优化。99%的常见情况将使用堆栈分配的缓冲区。

    此外,eryksun(在对这个答案的评论中)指出,尽管方便, GetFinalPathNameByHandle 需要多个系统调用来映射NT和DOS命名空间之间的文件对象并规范化路径。我还没有分解这个函数,所以我不能确认他的说法,但我没有理由怀疑他们。在正常情况下,您不必担心这种开销或可能的性能成本,但是由于这似乎是您的应用程序的一个大问题,您可以使用eryksun的另一个建议 GetFileInformationByHandleEx 并请求 FileNameInfo 上课。 通过handleex获取文件信息 是一个通用的多用途函数,可以检索有关文件的所有不同类型的信息,包括路径。它的实现更简单,直接调用本机 NtQueryInformationFile 功能。我会想 GetFinalPathNameByHandle 只是一个用户模式的包装器提供了这项服务,但eryksun的研究表明,如果这是一个真正的性能热点,它可能会做一些您可能希望避免的额外工作。我必须注意到 通过handleex获取文件信息 ,以便检索 文件名信息 ,必须创建一个I/O请求包(IRP)并向下调用底层设备驱动程序。这不是一个便宜的操作,所以我不确定使路径正常化的额外开销是否真的重要。但在这种情况下,使用 通过handleex获取文件信息 方法,因为它是一个文档化的函数。


    如果您已经按照描述编写了代码,但仍有可测量的性能问题,请将该代码发布给其他人以供审阅和帮助您优化。这个 Code Review 堆栈交换站点是一个很好的地方,可以在工作代码上获得类似的帮助。请在这个答案下面的评论中给我留下这样一个问题的链接,这样我就不会错过了。

    不管你做什么, 拜托 停止调用Windows API函数的ANSI版本(以 A 后缀)。 您需要宽字符(Unicode)版本。最后是一个 W 后缀,并使用由 WCHAR (== wchar_t )角色。除了由于ANSI版本不提供Unicode支持(对于2000年以后编写的任何应用程序来说,在路径中支持Unicode字符都是不可选的)而几十年来一直被弃用的事实之外,您还应该意识到以下事实: 一个 -后缀api函数只是将传入的ansi字符串转换为unicode字符串,然后委托给 西 -后缀版本。如果函数 回报 字符串,第二次转换也必须由 一个 -后缀版本,因为所有本机api都使用unicode字符串。性能并不是避免调用ansi函数的真正原因,但也许您会发现它更令人信服。

    在那里 可以 做你想做的事(通过 把手 但它需要NT本机API的未经记录的使用。我在文档化的函数中根本看不到任何东西可以让您获得这些信息。当然不能通过 通过handleex获取文件信息 功能。不管好坏,用户模式的文件系统api 完全 基于路径。大概是吧 在内部跟踪,但即使是记录在案的NT本机API函数,也会获取根目录 把手 (例如, NtDeleteFile 通过 OBJECT_ATTRIBUTES 结构)允许此字段为空,在这种情况下使用完整路径字符串。

    一如既往,如果您提供了更详细的大局,我们可能会提供一个更合适的解决方案。这就是评论人士在提到xy问题时所说的。是的,人们质疑你的动机,因为这是我们提供最适当帮助的方式。