这是我的代码片段:
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
。我怎样才能让它返回C:\mydir\etc
?
注意:我不想检查字符串
\\?\
,只是检查memmove
,这个程序的解决方案有点太老套了。
我怎样才能让它只返回 C:\mydir tc
你不能。
VOLUME_NAME_DOS
和 VOLUME_NAME_GUID
always 使用该格式,并记录如下:
此函数返回的字符串使用
语法。\\?\
请参阅文档的社区添加部分中的示例结果。
注意:我不想检查字符串
并只是 memmove 它,对于这个程序来说,它的解决方案有点太老套了。\\?\
这是最简单的解决方案。否则,您必须使用其他 API 将返回的路径转换为更易于理解的路径。例如将
VOLUME_NAME_NT
的结果与 QueryDosDevice()
一起使用,或者将 VOLUME_NAME_GUID
的结果与 GetVolumePathNamesForVolumeName()
一起使用。
我知道,您不想检查
\\?\
并将其删除,但正如 Remy 在他的 answer 中解释的那样,这是最简单的解决方案。但是,您应该小心,因为本地路径和网络路径具有不同的前缀。如您所知,本地路径以 \\?\
开头,但网络路径以 \\?\UNC\
开头(如此处所述),例如:
\\?\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::filesystem
和 GetFullPathName
都可以为您重新完成路径:
...
// 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 上的有效路径,即使它有点不常见。