繁体   English   中英

如何从文件“ HANDLE”中获取一个“ HANDLE”到包含目录?

[英]How do I get a HANDLE to the containing directory from a file HANDLE?

给定一个句柄的文件(例如C:\\\\FolderA\\\\file.txt ),我想这将返回的句柄包含目录(前面例子中的功能,这将是一个HANDLE到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;
}

但是,有没有一种方法可以someFunc没有getParentPath情况下实现someFunc ,也可以不使它查看字符串并删除最后一个目录分隔符之后的所有内容(因为从性能角度来看这很糟糕)?

我不知道什么是getParentPath 我假设它是一个在字符串中搜索尾部反斜杠并将其用于剥离文件规范的函数。 您不必自己定义这样的功能。 Windows已经为您提供了一个PathCchRemoveFileSpec (请注意,这假定指定的路径实际上包含要删除的文件名。如果该路径不包含文件名,它将删除结尾的目录名。您可以使用其他功能来验证路径是否包含文件规格。)

该功能的较旧版本是PathRemoveFileSpec ,这是您在下级操作系统上将要使用的版本,而较新的,更安全的功能不可用。

在Windows API之外,还有其他方法可以执行相同的操作。 如果您的目标是C ++ 17,则有filesystem::path类。 Boost提供了类似的功能。 或者,如果绝对需要,也可以使用std::string类的find_last_of成员函数自己编写。 (但最好不要重新发明轮子。当涉及到路径操作时,您可能不会想到很多边缘情况,并且您的测试可能不会发现。)

您对这种方法的性能表示担忧。 这是无稽之谈。 从字符串中剥离某些字符并不是一项缓慢的操作。 如果您从字符串的开头开始搜索,然后找到文件规范,然后又从字符串的开头开始创建了字符串的第二个副本,这甚至不会很慢。 这是一个简单的循环,搜索一个合理长度的字符串的字符,然后搜索一个简单的memcpy 绝对不可能使该操作成为执行文件I / O的代码中的性能瓶颈。

但是,实现可能还不是那么幼稚。 您可以通过从路径字符串的末尾开始搜索来进行优化,以减少必须迭代的字符数,并且如果可以操作原始字符串,则可以完全避免使用任何类型的内存复制。 使用C样式的字符串,只需用NUL字符( \\0 )替换尾随路径分隔符(用于分隔路径规范开头的分隔符)。 使用C ++样式的字符串,您只需调用erase成员函数。

实际上,如果您真的在乎性能,那么实际上可以保证这比进行系统调用从文件对象中检索包含的文件夹要快。 系统调用比一些编译器生成的,可插入的代码迭代字符串并去除子字符串要慢得多。

一旦有了目录的路径,就可以通过使用带有FILE_FLAG_BACKUP_SEMANTICS标志的CreateFile函数来获取该目录的HANDLE (如果要检索目录的句柄,则必须传递该标志。


我测得这很慢,正在寻找更快的方法。

您的测量是错误的。 您可能犯了基准测试调试版本的常见错误,即标准库功能(例如std::string )未优化,和/或真正的性能瓶颈是文件I / O。 通过任何想象力, CreateFile 都不是一种快速的功能。 我几乎可以保证这将是您的热点。


请注意,如果您还没有该路径,则很容易获得从HANDLE到文件的路径。 正如评论中指出的那样,在Windows Vista及更高版本上,您只需要调用GetFinalPathNameByHandle函数。 MSDN上的这篇文章提供了更多详细信息,包括示例代码和在Windows较低版本上使用的替代方法。

正如在问题注释中已经提到的那样,您可以通过在堆栈上分配长度为MAX_PATH (甚至更大)的缓冲区来进一步优化此方法。 该指令将编译为一条指令以调整堆栈指针,因此也不会成为性能瓶颈。 (好吧,我撒谎:您实际上将需要两条指令-一个指令在堆栈上创建空间,而另一个指令释放在堆栈上分配的空间。仍然不是性能问题。)这样,您甚至不必做任何动态内存分配。

请注意,为了获得最大的鲁棒性,尤其是在Windows 10上,您要处理路径长于MAX_PATH 在这种情况下,您分配给堆栈的缓冲区会太小,而您调用以填充它的函数将返回错误。 处理该错误,并在免费存储区上分配更大的缓冲区。 这样会比较慢,但这只是一个边缘情况,可能不是值得优化的情况。 99%的常见情况将使用堆栈分配的缓冲区。

此外,eryksun指出(在此答案的注释中),尽管很方便,但GetFinalPathNameByHandle需要多个系统调用才能在NT和DOS名称空间之间映射文件对象并标准化路径。 我尚未反汇编此功能,因此无法确认他的主张,但我没有理由怀疑它们。 在正常情况下,您不必担心此类开销或可能的性能成本,但是由于这似乎是应用程序的主要问题,因此可以使用eryksun的替代建议,即调用GetFileInformationByHandleEx并请求FileNameInfo类。 GetFileInformationByHandleEx是一个通用的通用函数,可以检索有关文件的所有不同种类的信息,包括路径。 它的实现更简单,直接调用本机的NtQueryInformationFile函数。 我本以为GetFinalPathNameByHandle只是提供此服务的用户模式包装器,但是eryksun的研究表明,它确实在做额外的工作,如果这确实是性能热点,您可能要避免。 我必须稍微指出一点,以指出GetFileInformationByHandleEx (为了检索FileNameInfo )将必须创建I / O请求数据包(IRP)并调用基础设备驱动程序。 这不是一个便宜的操作,所以我不确定标准化路径的额外开销是否真的很重要。 但是在这种情况下,使用GetFileInformationByHandleEx方法并没有真正的危害,因为它是一个文档化的函数。


如果您已经按照说明编写了代码,但是仍然存在可衡量的性能问题,那么请发布该代码以供其他人进行检查并帮助您进行优化。 Code Review Stack Exchange网站是获得类似工作代码帮助的好地方。 请随时在此答案下的评论中给我留下这样一个问题的链接,这样我就不会错过它。

无论做什么, 停止调用Windows API函数的ANSI版本(以A后缀结尾的版本)。 您需要宽字符(Unicode)版本。 这些以W后缀结尾,并使用由WCHAR (== wchar_t )字符组成的字符串。 除了ANSI版本已经过了几十年的事实,因为它们不提供Unicode支持(对于2000年之后编写的任何应用程序都不支持路径中的Unicode字符,这不是可选的),就像您关心性能一样,您应该知道以下事实:所有带有A后缀的API函数都是存根,它们将传入的ANSI字符串转换为Unicode字符串,然后委托给带有W版本。 如果函数返回字符串,则必须使用A后缀的版本进行第二次转换,因为所有本机API均使用Unicode字符串。 性能并不是您应该避免调用ANSI函数的真正原因,但也许这是使您更有说服力的原因。

可能有一种方法可以执行您想要的操作(通过HANDLE将文件对象映射到其包含的目录),但是这需要使用未记录的NT本机API。 我在记录的函数中根本看不到任何可以让您获得此信息的东西。 当然,不能通过GetFileInformationByHandleEx函数访问它。 不管好坏,用户模式文件系统API几乎完全基于路径。 据推测,它是在内部跟踪的,但是即使采用根目录HANDLE的已记录的NT本机API函数(例如,通过OBJECT_ATTRIBUTES结构的NtDeleteFile )也允许该字段为NULL,在这种情况下,将使用完整路径字符串。

与往常一样,如果您提供了更多的详细信息,我们可能会提供更合适的解决方案。 这是评论者提到XY问题时要进行的工作。 是的,人们在质疑您的动机,因为这是我们提供最适当帮助的方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM