【问题标题】:Is there a difference between \??\ and \\?\ paths?\??\ 和 \\?\ 路径之间有区别吗?
【发布时间】:2014-09-25 05:45:23
【问题描述】:

MSDN 文档Naming Files, Paths, and Namespaces 谈到了\\?\ 前缀。引用:

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

实验表明\??\ 前缀具有相同的效果,既禁用路径解析(.. 处理),又启用比MAX_PATH 更长的路径。

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

【问题讨论】:

  • 实际路径和带有转义反斜杠的 C 风格字符串文字令人困惑。没有\\\\.\\ 前缀。 "\\\\.\\" 是您在 C 字符串文字中编写 \\.\ 前缀的方式。 \\.\ 前缀在同一 MSDN 页面中的“Win32 设备命名空间”部分中进行了描述
  • 我完全删除了 \\.\ 问题。聚焦我的问题。
  • @HarryJohnston 那里的答案很可能回答了我的问题,谢谢
  • @HarryJohnston:这个问题的范围有点不同:这是关于文件名的,另一个问题是关于 GUID 的。但是,另一个问题的答案也应该适用于文件名。

标签: winapi internals


【解决方案1】:

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

在内部,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;
}

【讨论】:

  • Win32 似乎不太可能将 \\?\ 替换为 \??\,因为它们在内核中似乎具有不同的含义;第一个是(或应该是)本地 DOS 命名空间,而第二个是全局 DOS 命名空间。你有参考吗? (我的参考是 Windows Research Kernel 源代码中的注释;但是,我没有通过实验验证这一点,并且由于 WRK 是基于 Windows 2003 的,它可能在以后的版本中有所更改。)
  • 另一方面,我看到你描述的替代品in this question。因此,也许 Win32 确实忽略了本地和全局命名空间之间的区别,或者可能出于某种原因故意更改对本地命名空间的引用以使用全局命名空间。
  • 在 Windows 7 上,CreateFileW 将打开一个比 MAX_PATH 更长的路径,并带有 \??\ 前缀。不过,CreateDirectoryW 确实失败了。
  • 另外,\??\不关闭“自动扩展”是什么意思?当我尝试使用 CreateFileW 打开“\??\C:\Windows\..\Windows\notepad.exe”时,它不起作用。
猜你喜欢
  • 1970-01-01
  • 2018-06-17
  • 2015-12-18
  • 1970-01-01
  • 2012-02-16
  • 2019-02-21
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
相关资源
最近更新 更多