在 NT 中,“\??\”是一个路径前缀,代表为用户设备保留的对象目录,或者更具体地说,是设备别名。设备别名在对象命名空间中实现为符号链接,通常解析为“\Device”目录中的设备对象。有时在文档中这些设备别名在对象命名空间中被称为“联结”,不要与文件系统中的“目录联结”(或挂载点)混淆。
使用“\??\”前缀指示对象管理器在调用者的本地设备目录中搜索,“\Sessions\0\DosDevices\[Logon Authentication ID]”,它耦合到(即影子)全局设备目录,“\Global??”。为了提高效率,这两个目录都由访问令牌的关联登录会话记录以及每个进程对象缓存。 SYSTEM 登录 (ID 0x3E7) 使用“\Global??”作为其本地设备目录。请注意,本地目录有一个“全局”链接以允许在本地设备隐藏全局设备时访问全局设备(例如“\\?\Global\Z:”),或者允许设备驱动程序在以下情况下创建全局设备不在系统线程中执行。 NT 最初使用单个“\DosDevices”目录,而与调用者无关。随着 NT 5 中终端服务和快速用户切换的引入,他们不得不将其推广到当前的本地和全局设备系统。如今,为了向后兼容,“\DosDevices”是指向“\??”的链接。
将 DOS 路径转换为原生 NT 路径是由 NT 的用户模式运行时库(即“ntdll.dll”导出的 Rtl 前缀函数)实现的。
直截了当的情况是路径以“\\.\”或“\\?\”为前缀。这是本地设备路径,而不是 UNC 路径。 (严格来说是 UNC 路径的形式,但“.”和“?”是保留的设备域。)对于这种情况,前缀简单地替换为 NT“\??\”。两个 WINAPI 设备路径前缀之间的区别在于“\\?\”路径(所有反斜杠,没有正斜杠)是所谓的“扩展”路径,它绕过所有规范化,而“\\.\” " 路径被规范化。
设备路径规范化解析“.”和“..”组件,用反斜杠替换正斜杠,并从最终路径组件中去除尾随空格和点。因为正斜杠被转换为反斜杠,所以规范化设备路径的前缀可以是“//./”或“//?/”或斜杠和反斜杠的任意组合,除了“\\?\”。请注意,如果进程不支持长路径,则规范化路径限制为少于 MAX_PATH (260) 个字符。 (可以通过注册表和应用程序清单设置的组合在 Windows 10 中启用长路径支持;请参阅相关文档。)GetFullPathNameW 等效地处理这两个前缀,甚至规范化以“\\?\”开头的扩展路径。
UNC 路径也不足为奇。运行时库简单地将规范化路径中的前导“\\”替换为对“UNC”设备的显式引用,即“\??\UNC\”(例如“\\server\share”->“\?? \UNC\服务器\共享”)。请注意,“\Global??\UNC”是指向“\Device\Mup”的符号链接,多 UNC 提供程序设备负责将“服务器\共享”映射到正确的 UNC 提供程序(例如,映射到 LanmanWorkstation 重定向器)对于 SMB 共享)。
DOS 驱动器路径(即那些以“[A-Z]:”驱动器开头的路径)在某些情况下很有趣。首先是运行时库支持使用常规“隐藏”环境变量(例如“=C:”)的每个驱动器工作目录。例如,如果“=C:”环境变量设置为“C:\Windows”,“C:System32”将解析为“C:\Windows\System32”。此外,如果路径的最后一个组成部分是保留的 DOS 设备名称,包括名称结尾有冒号、空格、点,甚至是文件扩展名,则路径将被转换为设备路径(例如“C:\Windows\ nul: .txt" -> "\??\nul")。 (DOS 设备也保留在没有驱动器的相对路径的最终组件中。)否则,运行时库只需将“\??\”添加到规范化路径(例如“C:/Windows”->“\?? \C:\Windows")。
诸如“C:”(即“\Global??\C:”)之类的 DOS 驱动器是 NT 卷设备(即对象符号链接)的别名。 NT 设备名称不是持久的,通常是枚举的,因此最终目标取决于添加卷的相对顺序,如果删除卷并随后恢复它,它甚至可能会发生变化。例如,可移动驱动器上“E:\Temp”的最终 NT 路径可能以“\Device\HarddiskVolume8\Temp”开头,然后在删除并重新插入后,新的最终路径为“\Device\HarddiskVolume10\温度”。挂载点管理器使用卷的唯一 ID 实现持久性,它与卷 GUID 名称(例如“卷{00000000-0000-0000-0000-000000000000}”)和可选的(通常)DOS 驱动器号相关联。 GUID 名称用于在支持联结的文件系统中实现卷挂载点(即IO_REPARSE_TAG_MOUNT_POINT 重解析点),例如 NTFS 和 ReFS。