您的问题的答案是,是的,将 \\?\ 和 \??\ 传递给用户模式函数是有区别的。
在内部,NT 总是用\??\ 前缀表示路径。通常,当您使用C:\foo 之类的普通路径调用用户模式函数(例如CreateDirectoryW)时,用户模式函数会调用名为RtlDosPathNameToNtPathName_U 的内部函数,该函数会将其转换为以\??\ 为前缀的NT 样式路径.这种转换是使用固定大小的静态缓冲区完成的,这就是著名的MAX_PATH 限制的来源。
当您调用指定\\?\ 前缀的用户模式函数时(注意,只有一个?),RtlDosPathNameToNtPathName_U 被不调用。相反,第二个反斜杠变成了 ?字符和路径是逐字使用的。这就是文档在谈论 \\?\ 关闭“...路径字符串的自动扩展”时的意思。
但是,当您调用带有 \??\ 前缀的用户模式函数时,记住这是一个内部 NT 前缀,这个扩展仍然完成。
用户模式函数专门寻找\\?\ 来禁用自动扩展过程,并且由于您没有提供它,因此您的路径被视为无前缀路径并提供给RtlDosPathNameToNtPathName_U。这个函数很聪明,不会在路径的开头添加额外的\??\ 前缀,但是仍然使用固定大小的静态缓冲区。
这是关键的区别。当您将 \??\ 作为前缀传递时,您的路径仍受 MAX_PATH 长度限制。
以下示例程序演示了这一点。 TestNestedDir 函数只是尝试创建(然后删除)长度大于MAX_PATH 字符的路径,一次一层。如果您运行此代码,您将看到的结果是:
CreateDir, no prefix = 0
CreateDir, prefix \\?\ = 1
CreateDir, prefix \??\ = 0
只有带有\\?\前缀的创建是成功的。
#include <stdio.h>
#include <tchar.h>
#include <string>
#include <assert.h>
#include <Windows.h>
const wchar_t* pszLongPath =
L"C:\\"
L"12345678901234567890123456789012345678901234567890\\"
L"12345678901234567890123456789012345678901234567890\\"
L"12345678901234567890123456789012345678901234567890\\"
L"12345678901234567890123456789012345678901234567890\\"
L"12345678901234567890123456789012345678901234567890\\"
L"12345678901234567890123456789012345678901234567890";
bool TestCreateNestedDir(LPCWSTR pszPath)
{
std::wstring strPath = pszPath;
std::wstring::size_type pos = 0, first = std::wstring::npos;
bool fDirs = false, fResult = false;
// step through each level in the path, but only try to start creating directories
// after seeing a : character
while ((pos = strPath.find_first_of(L'\\', pos)) != std::wstring::npos)
{
if (fDirs)
{
// get a substring for this level of the path
std::wstring strSub = strPath.substr(0, pos);
// check if the level already exists for some reason
DWORD dwAttr = ::GetFileAttributesW(strSub.c_str());
if (dwAttr != -1 && (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
{
++pos;
continue;
}
// try to make the dir. if it exists, remember the first one we successfully made for later cleanup
if (!::CreateDirectoryW(strSub.c_str(), nullptr))
break;
if (first == std::wstring::npos) first = pos;
}
else
if (pos > 0 && strPath[pos - 1] == L':')
fDirs = true;
++pos;
}
if (pos == std::wstring::npos)
{
// try to create the last level of the path (we assume this one doesn't exist)
if (::CreateDirectoryW(pszPath, nullptr))
{
fResult = true;
::RemoveDirectoryW(pszPath);
}
}
else
--pos;
// now delete any dirs we successfully made
while ((pos = strPath.find_last_of(L'\\', pos)) != std::wstring::npos)
{
::RemoveDirectoryW(strPath.substr(0, pos).c_str());
if (pos == first) break;
--pos;
}
return fResult;
}
int _tmain(int argc, _TCHAR* argv[])
{
assert(wcslen(pszLongPath) > MAX_PATH);
printf("CreateDir, no prefix = %ld\n", TestCreateNestedDir(pszLongPath));
std::wstring strPrefix = L"\\\\?\\" + std::wstring(pszLongPath);
printf("CreateDir, prefix \\\\?\\ = %ld\n", TestCreateNestedDir(strPrefix.c_str()));
strPrefix[1] = L'?';
printf("CreateDir, prefix \\??\\ = %ld\n", TestCreateNestedDir(strPrefix.c_str()));
return 0;
}