在Windows中(假设从2000年开始),文件path的长度最多可以是大约32767个字符。 这个限制的存在是由于UNICODE_STRING在本地API中的内部处理(也在内核端,驱动等)。 到现在为止还挺好。 我知道这部分背后的理论。
限制的原因是UNICODE_STRING的Length和MaximumLength成员计算了Buffer的字节数,但它们本身是16位无符号整数。
我也知道为什么限制是一个近似而不是一个限制。 这主要是由于你的文件名(例如\\.\C:\boot.ini )被parsing为它的本机forms(例如\??\C:\boot.ini ),然后被parsing为前缀实际卷设备名称,然后是相对于该卷的path,例如\Device\HarddiskVolume2\boot.ini 。
此外,从Windows资源pipe理器遇到(“ANSI”) MAX_PATH限制时的已知症状是假装某些版本的Windows中不存在的文件或文件夹(可能会在某些点上得到修复)。
但是,当我调用CreateFile() ,path看起来像\\.\C:\...\filename.ext ,而整个path没有这个对象pipe理器,I / Opipe理器和文件系统驱动程序级别会发生什么超出限制,但达到它,在我的电话kernel32.dll的CreateFile() , 然后得到扩大? …
对于这个话题,SDK和WDK都没有特别的唠叨。 还是我看错了部分?
因为我很懒,所以我没有编写测试程序,而是使用优秀的远程管理器(它处理诸如长路径(超过MAX_PATH )或特殊文件名( con , prn等))来测试它。
我做了一个正好255个字符的字符串(“12345678901234 … 012345”),并开始创建嵌套的目录。 幸运的是,Far的“Make Directory”函数需要一个斜杠分隔的字符串来表示“创建嵌套的目录”,所以我可以通过在内部编辑器中准备一个字符串并进行一些复制和粘贴操作来完成。
我能够创建的最长路径长度为32739个字符,从“C:\”开始计数(即不包括“Far”添加的“\\?\”)。 尝试使用一个附加字符创建目录或文件时遇到的错误是“ 文件名或扩展名太长 ”。 如果我尝试进入该目录,我得到相同的错误。
编辑 :花了一些时间在调试器,这里是什么发生在Win32 API级别:
CreateFileW ,这个字符串长32744个字节(不包括终止零)。 CreateFileW执行一些额外的检查,将以null结尾的字符串转换为UNICODE_STRING (Length = 65488,MaximumLength = 65490),并准备一个OBJECT_ATTRIBUTES结构体。 CreateFileW然后在ntdll.dll调用NtCreateFile ,这只是syscall指令的一个包装。 NtCreateFile返回0xC0000106 ( STATUS_NAME_TOO_LONG )。 RtlNtStatusToDosError )到Win32错误206( ERROR_FILENAME_EXCED_RANGE )。 我没有检查内核中发生了什么,但我想我也可以看看 。
编辑2 :我运行WinObj ,发现在我的系统C:是符号链接\Device\HarddiskVolume1 。 该字符串长23个字符 。 如果我们用它替换传递给NtCreateFile的字符串\C:得到32744 – 3 + 23 = 32764个字符。 加上终止零,这需要65530字节。 仍然缺少限制(0xFFFF = 65535),所以我猜想还有一些额外的东西被添加,比如会话或者命名空间的名字。
编辑3 :通过内核后:
NtCreateFile调用IopCreateFile IopCreateFile调用ObOpenObjectByName ObOpenObjectByName调用ObpLookupObjectName ObpLookupObjectName检查ObpDosDevicesShortNamePrefix ( "\??\" ) – >成功 "C:"和"\1234..." ObpLookupDirectoryEntry来解析"C:" ObpParseSymbolicLink传递查找的目录条目( _OBJECT_SYMBOLIC_LINK与LinkTarget == "\Device\HarddiskVolume1"和DosDeviceDriveIndex == 3)和名称的其余部分。 然后它做了这样的事情(忠实地由ReactOS复制 ):
TargetPath = &SymlinkObject->LinkTarget; TempLength = TargetPath->Length; TotalLength = TempLength + RemainingName->Length; if (LengthUsed > 0xFFF0) return STATUS_NAME_TOO_LONG;
在我们的例子中,46 + 65476 = 65522(0xfff2),它刚好在极限之上。
那么,神秘解决了(我希望!)。
PS在Windows 7 x64 SP1下测试的一切。