【问题标题】:GetFullPathNameW and long Windows file pathsGetFullPathNameW 和长 Windows 文件路径
【发布时间】:2016-06-26 09:14:42
【问题描述】:

在我当前个人项目的 Windows 版本中,我希望支持 extended length filepaths。结果,我有点困惑如何使用GetFullPathNameW API 来解析长文件路径的全名。

根据 MSDN(关于 lpFileName 参数):

在此函数的 ANSI 版本中,名称仅限于 MAX_PATH 个字符。要将此限制扩展到 32,767 个宽字符,请调用函数的 Unicode 版本并在路径前添加“\?\”。有关详细信息,请参阅命名文件。

如果我理解正确,为了使用带有GetFullPathNameW 的扩展长度文件路径,我需要指定一个带有\\?\ 前缀的路径。由于 \\?\ 前缀仅在卷字母或 UNC 路径之前有效,这意味着 API 无法用于解析相对于当前目录的路径的全名。

如果是这种情况,如果结果名称的长度超过 MAX_PATH,我是否可以使用另一个 API 来解析文件路径的全名,例如 ..\somedir\somefile.txt?如果没有,我是否可以将 GetCurrentDirectory 与相对文件路径 (\\?\C:\my\cwd\..\somedir\somefile.txt) 结合使用并将其与 GetFullPathNameW 一起使用,还是我需要自己处理所有文件路径解析?

【问题讨论】:

  • 查找完整路径的函数要求您提供完整路径听起来不合理。那么为什么不试试文档所说的。也许你会感到惊喜。
  • GetCurrentDirectory() 是一个从根本上受 MAX_PATH 阻碍的unixism。本机操作系统没有相对路径或默认目录的概念,您必须始终为其提供完整路径名。你必须摆脱它才能取得成功。
  • @Cheersandhth.-Alf 不太清楚你是否建议我尝试\\?\C:\my\cwd\..\somedir\somefile.txt\\?\..\somedir\somefile.txt,所以我都尝试了。第一个正确解析为\\?\C:\my\somedir\somefile.txt,(它回答了我的部分问题),而第二个错误解析为\\?\somedir\somefile.txt
  • @HansPassant 我很清楚:我们说的是GetCurrentDirectory,而不是_getcwd,对吧?假设是这种情况,您是说 GetCurrentDirectory 不适用于长 cwd 路径,即使我使用 GetCurrentDirectoyW 和来自使用 CreateProcesslpCurrentDirectory 参数创建的进程的足够大小的缓冲区使用\\?\ 前缀?如果是这样,那真是令人讨厌的限制。
  • GetFullPathName 完全基于字符串;它只是对你给它的内容进行字符串操作+无论当前目录是什么。关于 \\?\ 的段落只是样板复制和粘贴 - 该函数实际上并不关心您传递的字符串。

标签: c++ windows winapi ntfs


【解决方案1】:
  1. GetFullPathNameA 仅限于 MAX_PATH 字符,因为它会预先使用硬编码的 MAX_PATH 大小(以字符为单位)UNICODE 缓冲区将 ANSI 名称转换为 UNICODE 名称。如果转换没有由于长度限制而失败,则调用GetFullPathNameW(或直接GetFullPathName_U[Ex])并将生成的UNICODE名称转换为ANSI。

  2. GetFullPathNameWGetFullPathName_U 之上的一个非常薄的外壳。它被限制为 WCHAR 中的 MAXSHORT (0x7fff) 长度,与 \\?\ 文件前缀无关。即使没有\\?\,它也适用于长(> MAX_PATH)相对名称。但是,如果lpFileName 参数不以\\?\ 前缀开头,则lpBuffer 参数中的结果名称也不会以\\?\ 开头。

  3. 如果您将lpBufferCreateFileW 之类的函数一起使用-此函数在内部将Win32Name 转换为NtName。结果将取决于颈背类型(RTL_PATH_TYPE)。如果名称不以\\?\ 前缀开头,则转换失败,因为RtlDosPathNameToRelativeNtPathName_U[_WithStatus] 失败(因为如果路径不以\\?\ 开头,它将在内部调用GetFullPathName_U(由GetFullPathNameW 调用的相同函数)和@ 987654350@ 硬编码到 MAX_PATH(确切地说是2*MAX_PATH,以字节为单位——NTDLL 函数使用以字节为单位的缓冲区大小,而不是在WCHARs 中)。如果名称以\\?\ 前缀开头,则执行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之前-之后!

欲了解更多信息,请阅读 - The Definitive Guide on Win32 to NT Path Conversion

【讨论】:

  • 只为纯粹的细节水平和知识共享而投票。虽然认为假设所有 Windows 版本的内部实现都相同有点冒险。是吗?
  • Win32 到 NT 路径转换将始终进行。但是,这种转换的细节确实可以从一个版本到另一个版本进行更改。一些win32路径完全不正确转换。一般来说,如果使用 Win32 路径 - 我们有 MAX_PATH 实际限制。例如,如果绝对路径长度超过 MAX_PATH,我们无法从 exe 创建进程。如果需要使用没有任何限制的长路径名或一些特殊路径 - 需要使用 NT 路径(本机)和 ntdll API
  • 谢谢,这很有帮助。希望你不介意 - 我提交了一个编辑来清理前两点的英文。
【解决方案2】:

只是为了更新当前状态:

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

其他的请看我的回答:https://stackoverflow.com/a/57624626/3736444

【讨论】:

    猜你喜欢
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    • 2014-11-27
    • 2019-09-12
    • 2016-06-08
    • 1970-01-01
    • 2012-04-19
    相关资源
    最近更新 更多