GetFullPathNameW 和长路径名在Windows中的应用

9
在我的个人项目的Windows版本中,我希望支持扩展长度文件路径。因此,我有点困惑如何使用GetFullPathNameW API来解析长文件路径的完整名称。
根据MSDN(关于lpFileName参数):
“在此函数的ANSI版本中,名称限制为MAX_PATH字符。要将此限制扩展到32,767个宽字符,请调用函数的Unicode版本并在路径前面添加"\?\"。有关详细信息,请参阅命名文件。”
如果我理解正确,为了使用带有GetFullPathNameW的扩展长度文件路径,我需要指定一个带有\\?\前缀附加的路径。由于\\?\前缀仅在卷字母或UNC路径之前有效,这意味着该API无法用于解析相对于当前目录的路径的完整名称。
如果是这样,是否有另一个API可以用来解析像..\somedir\somefile.txt这样的文件路径的完整名称,如果结果名称的长度超过MAX_PATH?如果没有,我能否将GetCurrentDirectory与相对路径(\\?\C:\my\cwd\..\somedir\somefile.txt)组合使用,并将其与GetFullPathNameW一起使用,还是我需要自己处理所有的文件路径解析?

一个寻找完整路径的函数需要你提供完整路径,这听起来并不合理。所以为什么不尝试文档中所说的呢?也许你会感到惊喜。 - Cheers and hth. - Alf
2
GetCurrentDirectory() 是一个基于 Unix 的函数,它在本质上受到 MAX_PATH 的限制。原生操作系统没有相对路径或默认目录的概念,您必须始终提供完整的路径名。如果想要进步,就必须摆脱它。 - Hans Passant
1
@Cheersandhth.-Alf。不是很清楚您是建议我尝试\\?\C:\my\cwd\..\somedir\somefile.txt还是\\?\..\somedir\somefile.txt,所以我都尝试了。第一个正确解析为\\?\C:\my\somedir\somefile.txt(这回答了我的部分问题),而第二个错误解析为\\?\somedir\somefile.txt - Charles Grunwald
很不幸,但我感谢你花时间解释。我正在处理的项目仍然可行 - 我只需要记住这一点。 - Charles Grunwald
2
“GetFullPathName”完全基于字符串;它只是在你提供的字符串上进行字符串操作,加上当前目录。关于\?\的段落只是样板复制和粘贴——函数实际上并不关心你传递给它什么字符串。 - Jonathan Potter
显示剩余3条评论
2个回答

12
  1. GetFullPathNameA 限制在 MAX_PATH 个字符内,因为它先将 ANSI 名称转换为 UNICODE 名称,使用一个硬编码的大小为 MAX_PATH(以字符计)的 UNICODE 缓冲区。如果由于长度限制而未能进行转换,则调用 GetFullPathNameW(或直接调用 GetFullPathName_U[Ex]),并将结果 UNICODE 名称转换为 ANSI。

  2. GetFullPathNameW 是对 GetFullPathName_U 的非常薄的包装。它在 WCHAR 中被限制为最大短整型(0x7fff)长度,与 \\?\ 文件前缀无关。即使没有 \\?\,它也可以处理长的(> MAX_PATH)相对名称。但是,如果 lpFileName 参数不以 \\?\ 前缀开头,则 lpBuffer 参数中的结果名称也不会以 \\?\ 开头。

  3. 如果您将使用像 CreateFileW 这样的函数来使用 lpBuffer - 此函数会将 Win32Name 内部转换为 NtName。结果将取决于名称类型(RTL_PATH_TYPE)。如果名称不以 \\?\ 前缀开头,则转换失败,因为 RtlDosPathNameToRelativeNtPathName_U[_WithStatus] 失败(因为如果路径不以 \\?\ 开头,则会在内部调用 GetFullPathName_U(由 GetFullPathNameW 调用的同一函数)并将 nBufferLength 硬编码为 MAX_PATH(NTDLL 函数使用字节数组大小,而不是 WCHAR)。如果名称以 \\?\ 前缀开头,则在 RtlDosPathNameToRelativeNtPathName_U[_WithStatus] 中执行另一种情况 - RtlpWin32NtNameToNtPathName,它将 \\?\ 替换为 \??\ 并且没有 MAX_PATH 限制。

因此,解决方案可能如下所示:

if(ULONG len = GetFullPathNameW(FileName, 0, 0, 0))
{
    PWSTR buf = (PWSTR)_alloca((4 + len) * sizeof(WCHAR));
    buf[0] = L'\\', buf[1] = L'\\',  buf[2] = L'?', buf[3] = L'\\';
    if (len - 1 == GetFullPathName(FileName, len, buf + 4, &c))
    {
        CreateFile(buf, ...);
    }
}

因此,我们需要在GetFullPathName之后附加带有\\?\前缀的路径。

欲了解更多信息,请阅读此文:Win32到NT路径转换的权威指南


仅凭详细程度和分享知识的水平,就为此点赞。不过认为假设所有 Windows 版本的内部实现都相同有些冒险。是吗? - Cheers and hth. - Alf
1
Win32到NT路径转换将始终存在。然而,这种转换的详细信息在不同版本中可能会有所改变。有些Win32路径根本无法正确转换。一般来说,如果使用Win32路径,我们有MAX_PATH实际限制。例如,如果绝对路径长度超过了MAX_PATH,则无法从exe创建进程。如果需要使用没有任何限制或某些特殊路径的长路径名,则需要使用NT路径(本机)和ntdll API。 - RbMm
谢谢,这非常有帮助。希望你不介意——我提交了一份编辑,以便在前两个要点上清理英语。 - Charles Grunwald

-1

仅更新当前状态:

从 Windows 10 版本 1607 开始,常见的 Win32 文件和目录函数已经移除了 MAX_PATH 的限制。但是,您必须选择新行为。要启用新的长路径行为,必须同时满足以下两个条件:...

有关其余内容,请参见我的答案:https://dev59.com/vlnUa4cB1Zd3GeqPb531#57624626


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接