Winapi \??\和\\?\路径之间有区别吗?

Winapi \??\和\\?\路径之间有区别吗?,winapi,internals,Winapi,Internals,MSDN文档讨论了\\?\前缀。引述: 对于文件I/O,路径字符串的“\?\”前缀告诉Windows API禁用所有字符串解析,并将其后的字符串直接发送到文件系统 实验表明,\??\前缀具有相同的效果,既可以禁用路径解析(。处理),也可以启用长度超过MAX\u path的路径 MSDN将\\?称为“Win32文件名称空间”,那么它是否纯粹由Win32 usermode API所知,并在NT名称空间中转换为\???无论如何,通过Winobj,我在NT名称空间中看到了GLOBAL???,而不是?,您

MSDN文档讨论了
\\?\
前缀。引述:

对于文件I/O,路径字符串的“\?\”前缀告诉Windows API禁用所有字符串解析,并将其后的字符串直接发送到文件系统

实验表明,
\??\
前缀具有相同的效果,既可以禁用路径解析(
处理),也可以启用长度超过
MAX\u path
的路径


MSDN将
\\?
称为“Win32文件名称空间”,那么它是否纯粹由Win32 usermode API所知,并在NT名称空间中转换为
\??
?无论如何,通过Winobj,我在NT名称空间中看到了
GLOBAL???
,而不是

,您的问题的答案是,将
\\?\
\??
传递给用户模式函数是有区别的

在内部,NT总是以
\??\
前缀表示路径。通常,当您使用正常路径(如
C:\foo
)调用用户模式函数(例如
CreateDirectoryW
)时,用户模式函数调用名为
rtldospathname tontpathname\U
的内部函数,该函数将此路径转换为前缀为
\??\
的NT样式路径。这种转换是通过一个固定大小的静态缓冲区完成的,这就是著名的
MAX\u路径
限制的来源

当您调用指定
\?\
前缀的用户模式函数(注意,只有一个
)时,
rtldospathnametpathname\U
不会被调用。相反,第二个反斜杠变成了?字符和路径被逐字使用。这就是文档在谈论关闭“…自动扩展路径字符串”时的意思

但是,当您使用
\??\
前缀调用用户模式函数时(记住该前缀是内部NT前缀),此扩展仍然完成

用户模式功能专门查找
\\?\
以禁用自动扩展过程,由于您不提供自动扩展过程,因此您的路径将被视为非前缀路径,并馈送至
rtldospathnametpathname\U
。此函数足够智能,不会在路径的开头添加额外的
\??\
前缀,但是仍然使用固定大小的静态缓冲区

这是关键区别。当您将
\??\
作为前缀传递时,您的路径仍受
最大路径
长度限制的约束

下面的示例程序演示了这一点。
TestNestedDir
函数只是尝试创建(然后删除)长度大于
MAX\u 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;
}
#包括
#包括
#包括
#包括
#包括
常量wchar_t*pszLongPath=
L“C:\\”
L“12345678901213456789012345678901234567890123456789012345678901234567890\\”
L“12345678901213456789012345678901234567890123456789012345678901234567890\\”
L“12345678901213456789012345678901234567890123456789012345678901234567890\\”
L“12345678901213456789012345678901234567890123456789012345678901234567890\\”
L“12345678901213456789012345678901234567890123456789012345678901234567890\\”
L“12345678901213456789012345678901234567890123456789012345678901234567890”;
bool TestCreateNestedDir(LPCWSTR pszPath)
{
std::wstring strPath=pszPath;
std::wstring::size_type pos=0,first=std::wstring::npos;
bool-fDirs=false,fResult=false;
//逐步浏览路径中的每个级别,但仅尝试开始创建目录
//看完一个:角色
while((pos=strPath.find_first_of(L'\\',pos))!=std::wstring::npos)
{
如果(联邦国税局)
{
//获取此级别路径的子字符串
std::wstring strSub=strPath.substr(0,位置);
//检查该级别是否因某种原因已存在
DWORD dwAttr=::GetFileAttributesW(strSub.c_str());
if(dwAttr!=-1&(dwAttr&FILE\u属性\u目录))
{
++pos;
继续;
}
//尝试创建目录。如果它存在,请记住我们成功创建的第一个目录,以便以后进行清理
if(!::CreateDirectoryW(strSub.c_str(),nullptr))
打破
if(first==std::wstring::npos)first=pos;
}
其他的
如果(pos>0&&strPath[pos-1]==L':')
fDirs=真;
++pos;
}
if(pos==std::wstring::npos)
{
//尝试创建路径的最后一级(我们假设这一级不存在)
if(::CreateDirectoryW(pszPath,nullptr))
{
fResult=真;
::RemoveDirectoryW(pszPath);
}
}
其他的
--pos;
//现在删除我们成功创建的所有目录
while((pos=strPath.find_last_of(L'\\',pos))!=std::wstring::npos)
{
::RemoveDirectoryW(strPath.substr(0,pos.c_str());
如果(位置==首次)中断;
--pos;
}
返回fResult;
}
int _tmain(int argc,_TCHAR*argv[]
{
断言(wcslen(pszLongPath)>最大路径);
printf(“CreateDir,无前缀=%ld\n”,TestCreateNestedDir(pszLongPath));
std::wstring strPrefix=L“\\\\\?\\”+std::wstring(pszLongPath);
printf(“CreateDir,前缀\\\\?\\=%ld\n”,TestCreateNestedDir(strPrefix.c_str());
strPrefix[1]=L'?';
printf(“CreateDir,前缀\??\=%ld\n”,TestCreateNestedDir(strPrefix.c_str());
返回0;
}

实际路径和带有转义反斜杠的C样式字符串文字混淆在一起。没有
\\\.\
前缀
“\\\\.\\”
是如何在C字符串文本中编写
\\.\
前缀的。
\.\
前缀在同一MSDN页面的“Win32 Device Namespaces”部分中进行了描述,可能与我删除的\\.\n问题完全相同。集中我的问题