没有前缀 '\\?\' 的 GetFinalPathNameByHandle() 结果

GetFinalPathNameByHandle() result without prepended '\\?\'

这是我的代码片段:

char existingTarget[MAX_PATH]; 
HANDLE hFile = CreateFile(linkPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hFile)
{
    GetFinalPathNameByHandle(hFile, existingTarget, MAX_PATH, FILE_NAME_OPENED);
    CloseHandle(hFile);
}

然而,existingTarget 即将成为 \?\C:\mydir\etc。我怎样才能 return 只是 C:\mydir\etc

注意:我不想检查字符串 \?\ 而只是 memmove 它,对于这个程序。

how can I get it to return just C:\mydir\etc

你不能。 VOLUME_NAME_DOSVOLUME_NAME_GUID 始终 使用该格式,并记录如下:

The string that is returned by this function uses the \?\ syntax.

请参阅documentation 的“社区添加”部分中的示例结果

Note: I don't want to check for the string \?\ and just memmove it, its a bit too hackish of a solution for this program.

这是最简单的解决方案。否则,您必须使用其他 API 将返回的路径转换为更易于阅读的路径。比如用VOLUME_NAME_NT的结果和QueryDosDevice(), or using the result of VOLUME_NAME_GUID with GetVolumePathNamesForVolumeName().

我知道,您不想检查 \?\ 并将其删除,但正如雷米在他的 , that's the easiest solution. However, you should be careful, because local paths and network paths have different prefixes. As you know, a local path starts with \?\, but a network path starts with \?\UNC\ (as described here 中解释的那样,例如:

\?\UNC\My server\My share\Directory

因此,根据您的代码,我去除前缀的解决方案如下:

char existingTarget[MAX_PATH];
HANDLE hFile = CreateFileA(linkPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hFile)
{
    DWORD ret = GetFinalPathNameByHandleA(hFile, existingTarget, MAX_PATH, FILE_NAME_OPENED);
    CloseHandle(hFile);

    // Check whether existingTarget is large enough to hold the final path.
    if (ret < MAX_PATH)
    {
        // The local path prefix is also a prefix of the network path prefix.
        // Therefore, look for the network path prefix first.
        // Please note that backslashes have to be escaped.
        std::string targetPath(existingTarget);
        if (targetPath.substr(0, 8).compare("\\?\UNC\") == 0)
        {
            // In case of a network path, replace `\?\UNC\` with `\`.
            targetPath = "\" + targetPath.substr(7);
        }
        else if (targetPath.substr(0, 4).compare("\\?\") == 0)
        {
            // In case of a local path, crop `\?\`.
            targetPath = targetPath.substr(4);
        }
    }
}

如果需要,您仍然可以使用 memmove()targetPath 复制到另一个变量中。

如果您不想进行解析,还有其他一些方法:

  • std::filesystem 可以处理它(至少使用 MSVC,没有在 MinGW 或 Cygwin 上检查 GCC),如果你不介意 std::string:

    #include <filesystem>
    #include <string>
    ...
    std::string cleanerPath = std::filesystem::canonical(existingTarget).string();
    // use weakly_canonical instead if the file might not exist.
    
  • 如果您将 VOLUME_NAME_NONE 传递给 GetFinalPathNameByHandle,它将去除前缀 驱动器号,它最终会给你一条以 \ 开头的无驱动路径,根据情况可能有用也可能没用,但是...

  • 如果你传递了VOLUME_NAME_NONE但你也知道该文件与当前工作目录在同一个驱动器上,那么std::filesystemGetFullPathName都可以重新- 为您完成路径:

    ...
    // result will not include drive letter component
    GetFinalPathNameByHandle(hFile, existingTarget, MAX_PATH, 
                             FILE_NAME_OPENED | VOLUME_NAME_NONE);
    ...
    {
      // with std::filesystem into an std::string: 
      std::string cleanerPath = std::filesystem::absolute(existingTarget).string();
    }
    ...
    {
      // or with win api: 
      char cleanerPath[MAX_PATH];
      GetFullPathNameA(existingTarget, sizeof(cleanerPath), cleanerPath, nullptr);
    }
    
  • 您也可以使用 GetFileInformationByHandleEx 而不是 GetFinalPathNameByHandle,但代价是 API(并且没有 ANSI 版本)。 (待办事项:示例;现在没有更多时间)

当然,您可以保留 Windows 上的有效路径的前缀,即使它有点不常见。