windows下的 长路径

https://docs.microsoft.com/zh-cn/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN

https://docs.microsoft.com/zh-cn/windows/win32/fileio/maximum-file-path-limitation

https://docs.microsoft.com/zh-cn/troubleshoot/windows-server/backup-and-storage/cannot-delete-file-folder-on-ntfs-file-system

Windows支持的所有文件系统都使用文件和目录的概念来访问存储在磁盘或设备上的数据。使用Windows API进行文件和设备I / O的Windows开发人员应了解文件和目录名称的各种规则,约定和限制。

可以使用文件I / O API从磁盘,设备和网络共享访问数据。文件和目录以及名称空间是路径概念的一部分,路径是对数据获取位置的字符串表示,无论数据是来自磁盘,设备还是用于特定操作的网络连接。

某些文件系统(例如NTFS)支持链接的文件和目录,它们也遵循文件命名约定和规则,就像常规文件或目录一样。有关更多信息,请参见“硬链接和连接点以及重新解析点和文件操作”。

有关其他信息,请参见以下小节:

文件名和目录名
命名约定
短名与长名
路径
完全合格与相对路径
最大路径长度限制
命名空间
Win32文件命名空间
Win32设备命名空间
NT命名空间
相关话题
要了解有关配置Windows 10以支持长文件路径的信息,请参阅最大路径长度限制。

文件名和目录名
对于单个文件,所有文件系统都遵循相同的通用命名约定:基本文件名和可选的扩展名,以句点分隔。但是,每个文件系统(例如NTFS,CDFS,exFAT,UDFS,FAT和FAT32)对于在目录或文件路径中各个组件的形成都可以有特定且不同的规则。请注意,目录只是具有特殊属性的文件,将其指定为目录,但否则必须遵循与常规文件相同的所有命名规则。因为就文件系统而言,目录一词仅指一种特殊类型的文件,所以某些参考资料将使用通用术语“文件”。包含目录和数据文件本身的概念。因此,除非另有说明,否则文件的任何命名或使用规则或示例也应适用于目录。术语路径是指一个或多个目录,反斜杠以及可能的卷名。有关更多信息,请参见路径部分。

字符数限制也可以不同,并且可以根据文件系统和使用的路径名前缀格式而有所不同。对后向兼容机制的支持使情况更加复杂。例如,较旧的MS-DOS FAT文件系统最多支持8个字符的基本文件名和3个扩展名的字符,包括点分隔符在内总共12个字符。这通常称为8.3文件名。Windows FAT和NTFS文件系统不限于8.3文件名,因为它们支持长文件名,但它们仍支持8.3版本的长文件名。

命名约定
以下基本规则使应用程序可以创建和处理文件和目录的有效名称,而与文件系统无关:

使用句点将基本文件名与目录或文件名中的扩展名分开。

使用反斜杠(\)到分离的部件a的路径。反斜杠将文件名从路径分开,并将一个目录名与路径中的另一个目录名分开。您不能在实际文件或目录的名称中使用反斜杠,因为它是一个保留字符,将名称分隔为多个部分。

作为卷名的一部分,请根据需要使用反斜杠,例如,“ C:\ path \ file”中的“ C:\”或“ \\ server \ share \ path \ file”中的“ \\ server \ share”通用命名约定(UNC)名称。有关UNC名称的更多信息,请参见最大路径长度限制部分。

不要假设大小写敏感。例如,即使某些文件系统(例如POSIX兼容文件系统)可能认为它们不同,也应将名称OSCAR,Oscar和oscar视为相同。请注意,NTFS支持POSIX语义以区分大小写,但这不是默认行为。有关更多信息,请参见CreateFile。

卷号(驱动器号)类似地不区分大小写。例如,“ D:\”和“ d:\”指的是同一卷。

使用当前代码页中的任何字符作为名称,包括Unicode字符和扩展字符集(128–255)中的字符,但以下各项除外:

以下保留字符:

<(小于)
>(大于)
:(冒号)
”(双引号)
/(正斜杠)
\(反斜杠)
| (垂直杆或管)
?(问号)
*(星号)
整数值零,有时也称为ASCII NUL字符。

整数表示形式在1到31之间的字符,但允许这些字符的备用数据流除外。有关文件流的更多信息,请参见文件流。

目标文件系统不允许的任何其他字符。

使用句点作为路径中代表当前目录的目录组件,例如“。\ temp.txt”。有关更多信息,请参见路径。

使用两个连续的句点(..)作为路径中的目录组成部分,以表示当前目录的父目录,例如“ .. \ temp.txt”。有关更多信息,请参见路径。

不要将以下保留名称用作文件名:

CON,PRN,AUX,NUL,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8和LPT9。也要避免在这些名称后面加上扩展名;例如,不建议使用NUL.txt。有关更多信息,请参见命名空间。

文件名或目录名不要以空格或句号结尾。尽管基础文件系统可能支持此类名称,但Windows Shell和用户界面不支持。但是,可以将句点指定为名称的第一个字符。例如,“。temp”。

短名与长名
长文件名被认为是任何超出短MS-DOS(也称为8.3)样式命名约定的文件名。当您创建一个长文件名时,Windows可能还会创建一个简短的8.3形式的名称(称为8.3别名或简称),并将其也存储在磁盘上。出于性能原因,可以在系统范围内或为指定的卷禁用此8.3别名,具体取决于特定的文件系统。

Windows Server 2008,Windows Vista,Windows Server 2003和Windows XP:在Windows 7和Windows Server 2008 R2之前,不能为指定的卷禁用8.3别名。

在许多文件系统上,文件名的每个组成部分中都将包含波浪号(〜),该波浪号太长而无法遵守8.3命名规则。

备注

并非所有文件系统都遵循波浪号替换约定,并且可以将系统配置为禁用8.3别名生成,即使它们通常支持它也是如此。因此,请勿假设磁盘上已经存在8.3别名。

 

要从系统请求8.3文件名,长文件名或文件的完整路径,请考虑以下选项:

要获取8.3格式的长文件名,请使用GetShortPathName函数。
要获取短名称的长文件名版本,请使用GetLongPathName函数。
要获取文件的完整路径,请使用GetFullPathName函数。
在较新的文件系统(例如NTFS,exFAT,UDFS和FAT32)上,Windows使用Unicode将长文件名存储在磁盘上,这意味着始终保留原始长文件名。即使长文件名包含扩展字符也是如此,无论在磁盘读取或写入操作期间处于活动状态的代码页如何。

可以在NTFS文件系统分区和Windows FAT文件系统分区之间复制使用长文件名的文件,而不会丢失任何文件名信息。对于较早的MS-DOS FAT和某些类型的CDFS(CD-ROM)文件系统,这可能不是正确的,具体取决于实际的文件名。在这种情况下,如果可能,将替换短文件名。

路径
指定文件的路径由一个或多个组件组成,并用特殊字符(反斜杠)分隔,每个组件通常是目录名或文件名,但下面将讨论一些值得注意的例外。对于系统对路径的解释,通常至关重要的是该路径的开始或前缀是什么样的。该前缀确定路径正在使用的名称空间,另外还确定在路径中哪个位置使用哪些特殊字符,包括最后一个字符。

如果路径的组成部分是文件名,则它必须是最后一个组成部分。

路径的每个组成部分还将受到为特定文件系统指定的最大长度的约束。通常,这些规则分为两类:short和long。请注意,目录名由文件系统存储为特殊类型的文件,但是文件的命名规则也适用于目录名。总而言之,路径只是针对特定文件或目录名称存在的所有目录之间的层次结构的字符串表示形式。

完全合格与相对路径
对于操作文件的Windows API函数,文件名通常可以相对于当前目录,而某些API需要完全限定的路径。如果文件名不是以下列之一开头,则它是相对于当前目录的:

任意格式的UNC名称,始终以两个反斜杠字符(“ \\”)开头。有关更多信息,请参见下一节。
带有反斜杠的磁盘标识符,例如“ C:\”或“ d:\”。
单个反斜杠,例如“ \ directory”或“ \ file.txt”。这也称为绝对路径。
如果文件名仅以磁盘标识符开头,而不以冒号后的反斜杠开头,则将其解释为驱动器上具有指定字母的当前目录的相对路径。请注意,当前目录可能是根目录,也可能不是根目录,这取决于在该磁盘上进行最新“更改目录”操作期间设置的根目录。此格式的示例如下:

“ C:tmp.txt”是指驱动器C上当前目录中名为“ tmp.txt”的文件。
“ C:tempdir \ tmp.txt”是指驱动器C上当前目录的子目录中的文件。
如果路径包含“双点”,则也称为相对路径。也就是说,在路径的一个分量中两个周期在一起。该特殊说明符用于表示当前目录上方的目录,否则称为“父目录”。此格式的示例如下:

“ .. \ tmp.txt”指定位于当前目录的父目录中的名为tmp.txt的文件。
“ .. \ .. \ tmp.txt”指定一个文件,该文件位于当前目录的上方两个目录中。
“ .. \ tempdir \ tmp.txt”指定位于名为tempdir的目录中的名为tmp.txt的文件,该目录是当前目录的对等目录。
相对路径可以合并两个示例类型,例如“ C:.. \ tmp.txt”。这很有用,因为尽管系统会跟踪当前驱动器以及该驱动器的当前目录,但它也会跟踪每个不同驱动器号(如果您的系统有多个)中的当前目录,无论哪个驱动器指示符被设置为当前驱动器。

最大路径长度限制
在Windows 10版本1607之前的Windows版本中,路径的最大长度是MAX_PATH,它定义为260个字符。在Windows的更高版本中,需要更改注册表项或使用组策略工具才能删除限制。有关完整详细信息,请参见最大路径长度限制。

命名空间
Windows API中使用的命名空间约定主要有两类,通常称为NT命名空间和Win32命名空间。NT名称空间被设计为可能存在其他子系统和名称空间的最低级别名称空间,包括Win32子系统,以及扩展的Win32名称空间。POSIX是Windows中基于NT名称空间构建的子系统的另一个示例。Windows的早期版本还为某些特殊设备(如通信(串行和并行)端口和默认显示控制台)定义了一些预定义或保留的名称,这些名称现在称为NT设备名称空间,在当前版本中仍受支持。 Windows的向后兼容性。

Win32文件命名空间
本节和下一节总结了Win32名称空间前缀和约定,并说明了如何使用它们。请注意,这些示例旨在与Windows API函数一起使用,并不一定都与Windows Shell应用程序(例如Windows Explorer)一起使用。因此,与Windows Shell应用程序相比,可能的路径范围更广,可以使用这些命名空间约定来开发利用此功能的Windows应用程序。

对于文件I / O,路径字符串的前缀“ \\?\”告诉Windows API禁用所有字符串解析,并将其后的字符串直接发送到文件系统。例如,如果文件系统支持大路径和文件名,则可以超过Windows API强制实施的MAX_PATH限制。有关正常最大路径限制的更多信息,请参见上一节“最大路径长度限制”。

因为它关闭了路径字符串的自动扩展,所以“ \\?\”前缀也允许使用“ ..”和“。”。在路径名中,如果您尝试使用这些保留的相对路径说明符作为标准路径的一部分来对文件执行操作,这将很有用。

许多但不是全部文件I / O API支持“ \\?\”;您应该查看每个API的参考主题以确保。

请注意,应使用Unicode API来确保前缀“ \\?\”允许您超出MAX_PATH

Win32设备命名空间
“ \\。\”前缀将访问Win32设备名称空间,而不是Win32文件名称空间。如果API支持这种访问方式,则可以直接完成对物理磁盘和卷的访问,而无需通过文件系统。您可以通过这种方式访问磁盘以外的许多设备(例如,使用CreateFile和DefineDosDevice函数)。

例如,如果要打开系统的串行通信端口1,则可以在对CreateFile函数的调用中使用“ COM1” 。这是可行的,因为COM1-COM9是NT名称空间中保留名称的一部分,尽管使用“ \\。\”前缀也可以与这些设备名称一起使用。相比之下,如果您安装了100端口串行扩展板并想打开COM56,则不能使用“ COM56”打开它,因为没有针对COM56的预定义NT名称空间。您将需要使用“ \\。\ COM56”打开它,因为“ \\。\”直接转到设备名称空间,而不会尝试找到预定义的别名。

使用Win32设备名称空间的另一个示例是将CreateFile函数与“ \\。\ PhysicalDisk X ”(其中X是有效的整数值)或“ \\。\ CdRom X ”一起使用。这样,您可以绕过文件系统直接访问那些设备。之所以可行,是因为这些设备名称是由系统在枚举这些设备时创建的,并且某些驱动程序还将在系统中创建其他别名。例如,实现名称“ C:\”的设备驱动程序具有自己的名称空间,该名称空间也恰好是文件系统。

通过CreateFile函数的API通常使用“ \\。\”前缀,因为CreateFile是用于打开文件和设备的函数,具体取决于您使用的参数。

如果使用Windows API函数,则应使用“ \\。\”前缀仅访问设备,而不访问文件。

大多数API不支持“ \\。\”;只有那些旨在与设备名称空间一起使用的设备才能识别它。请务必检查每个API的参考主题,以确保正确。

NT命名空间
还有一些API允许使用NT名称空间约定,但是Windows Object Manager在大多数情况下都不需要这样做。为了说明这一点,使用Windows Sysinternals WinObj在系统对象浏览器中浏览Windows命名空间非常有用。工具。运行此工具时,您看到的是从根(或“ \”)开始的NT名称空间。子文件夹称为“ Global ??” 是Win32名称空间所在的位置。命名的设备对象位于“设备”子目录中的NT名称空间中。在这里,您还可以找到Serial0和Serial1,它们是表示系统上存在的前两个COM端口的设备对象。代表卷的设备对象将类似于“ HarddiskVolume1”,尽管数字后缀可能有所不同。子目录“ Harddisk0”下的名称“ DR0”是代表磁盘的设备对象的示例,依此类推。

为了使Windows设备可以访问这些设备对象,设备驱动程序在Win32命名空间“ Global ??”中创建指向其各自设备对象的符号链接(symlink)。例如,“ Global ??”下的COM0和COM1 子目录只是指向Serial0和Serial1的符号链接,“ C:”是指向HarddiskVolume1的符号链接,“ Physicaldrive0”是指向DR0的符号链接,依此类推。如果没有符号链接,则如上所述,使用Win32名称空间约定的Windows应用程序将无法使用指定的设备“ Xxx”。但是,可以使用任何支持“ \ Device \ Xxx”格式的NT命名空间绝对路径的API对该设备打开句柄。

通过终端服务和虚拟机增加了多用户支持,进一步有必要在Win32名称空间内虚拟化系统范围的根设备。这是通过在Win32名称空间中添加名为“ GLOBALROOT”的符号链接来完成的,您可以在“ Global ??”中看到该符号链接。先前讨论的WinObj浏览器工具的子目录,并且可以通过路径“ \\?\ GLOBALROOT”进行访问。此前缀确保其后的路径显示在系统对象管理器的真实根路径中,而不是会话相关的路径中。

最大路径长度限制

在Windows API(以下段落讨论了一些例外)中,路径的最大长度为MAX_PATH,它定义为260个字符。本地路径按以下顺序构造:驱动器号,冒号,反斜杠,用反斜杠分隔的名称组件以及终止的空字符。例如,驱动器D上的最大路径是“ D:\ 256个字符的字符串<NUL>”,其中“ <NUL>”代表当前系统代码页的不可见终止空字符。(此处使用字符<>是为了清晰起见,并且不能成为有效路径字符串的一部分。)

例如,如果将具有长文件名的git repo克隆到本身具有长名的文件夹中,则可能会遇到此限制。

备注

Windows API中的文件I / O函数在将名称转换为NT样式名称的过程中将“ /”转换为“ \”,除非使用“ \\?\”前缀(如以下各节所述)。

Windows API具有许多功能,这些功能还具有Unicode版本,以允许使用扩展长度的路径,最大总路径长度为32,767个字符。这种类型的路径由用反斜杠分隔的组件组成,每个反斜杠都取决于GetVolumeInformation函数的lpMaximumComponentLength参数中返回的值(该值通常为255个字符)。要指定扩展长度的路径,请使用“ \\?\”前缀。例如,“ \\?\ D:\非常长的路径”。

备注

最大路径为32,767个字符,这是近似值,因为系统可能会在运行时将“ \\?\”前缀扩展为更长的字符串,并且这种扩展适用于总长度。

“ \\?\”前缀也可以与根据通用命名约定(UNC)构造的路径一起使用。要使用UNC指定这样的路径,请使用“ \\?\ UNC \”前缀。例如,“ \\?\ UNC \ server \ share”,其中“ server”是计算机的名称,“ share”是共享文件夹的名称。这些前缀不用作路径本身的一部分。它们指示路径应该以最小的修改传递给系统,这意味着您不能使用斜杠来表示路径分隔符,也不能使用句号来表示当前目录,也不能使用双点号来表示父目录。因为您不能在相对路径中使用“ \\?\”前缀,所以相对路径始终限制为总共MAX_PATH个字符。

无需对Windows文件I / O API函数使用的路径和文件名字符串执行任何Unicode规范化,因为文件系统将路径和文件名视为WCHAR的不透明序列。请记住,在对相关Windows文件I / O API函数的任何调用之外,应执行应用程序所需的任何规范化。

使用API创建目录时,指定的路径不能太长,以至于不能追加8.3文件名(即,目录名不能超过MAX_PATH减12)。

外壳和文件系统有不同的要求。可以使用Windows API创建Shell用户界面无法正确解释的路径。

在Windows 10版本1607和更高版本中启用长路径
从Windows 10版本1607开始,MAX_PATH限制已从常见的Win32文件和目录功能中删除。但是,您必须选择加入新行为。

要启用新的长路径行为,必须满足以下两个条件:

注册表项Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled (Type: REG_DWORD)必须存在并设置为1。在首次调用受影响的Win32文件或目录函数(请参阅下面的函数列表)后,注册表值将由系统(每个进程)缓存。在该过程的生存期内,将不会重新加载注册表项。为了使系统上的所有应用都能识别密钥的值,可能需要重新启动,因为在设置密钥之前可能已经启动了某些进程。

备注

也可以通过位于的组策略来控制此注册表项Computer Configuration > Administrative Templates > System > Filesystem > Enable Win32 long paths。

该应用程序清单还必须包括longPathAware的元素。

XML格式

复制中
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
如果您选择采用长路径行为,这些目录管理功能将不再具有MAX_PATH限制:CreateDirectoryW,CreateDirectoryExW GetCurrentDirectoryW RemoveDirectoryW SetCurrentDirectoryW。

如果您选择采用长路径行为,这些文件管理功能将不再具有MAX_PATH限制:CopyFileW,CopyFile2,CopyFileExW,CreateFileW,CreateFile2,CreateHardLinkW,CreateSymbolicLinkW,DeleteFileW,FindFirstFileW,FindFirstFileExW,FindNextFileW,GetFileAttributesW,GetFileAttributesExW,SetFileAttributesW GetFullPathNameW,GetLongPathNameW,MoveFileW,MoveFileExW,MoveFileWithProgressW,ReplaceFileW,SearchPathW,FindFirstFileNameW,FindNextFileNameW,FindFirstStreamW,FindNextStreamW,GetCompressedFileSizeW,GetFinalPathNameByHandleW。

无法删除 NTFS 文件系统卷上的文件或文件夹

内部,NTFS 将文件夹视为一种特殊类型的文件。 因此,本文中的 word 文件 表示文件或文件夹。

原因1:文件使用 ACL

如果文件使用 (ACL) 的访问控制列表,则可能无法删除该文件。 若要解决此问题,请更改文件的权限。 您可能需要取得文件的所有权才能更改权限。

管理员具有获得任何文件所有权的隐式能力,即使尚未明确向其授予对该文件的任何权限也是如此。 文件所有者具有修改文件权限的隐式能力,即使没有明确向用户授予对文件的任何权限也是如此。 因此,您可能必须取得文件的所有权,授予自己删除文件的权限,然后再删除该文件。

您不能使用某些安全工具来显示或修改权限,因为文件具有不规范的 ACL

若要解决此问题,请使用另一个工具 (例如,Cacls.exe) 的更高版本。

ACL 中 (Ace) 的访问控制项具有特定的首选顺序,具体取决于它们的类型。 例如,拒绝访问的 Ace 通常位于授予访问权限的 Ace 之前。 但是,任何操作都不会阻止程序以任意顺序写入具有 Ace 的 ACL。 在某些早期版本的 Windows 中,Microsoft Windows 尝试读取这些不规范的 Acl 时出现问题。 有时,不能使用 Microsoft Windows 资源管理器图形安全编辑器正确修改这些 Acl。 此问题已在 Windows 的更高版本中得到更正。 如果遇到此问题,请使用 Cacls.exe 的最新版本。 即使无法就地显示或编辑 ACL,也可以编写一个新的 ACL,让您获取对文件的访问权限。

原因2:文件正在使用

如果文件正在使用,您可能无法删除该文件。 若要解决此问题,请确定具有打开句柄的进程,然后关闭该进程。

根据文件的打开方式 (例如,以独占访问方式打开(而不是共享访问) )时,您可能无法删除正在使用的文件。 您可以使用多种工具来帮助您根据需要随时确定具有打开的文件句柄的进程。

此问题的症状可能有所不同。 您可以使用 Delete 命令删除文件,但在打开文件的进程释放文件之前,不会删除该文件。 此外,您可能无法访问挂起删除的文件的安全性对话框。 若要解决此问题,请确定具有打开句柄的进程,然后关闭该进程。

原因3:文件系统损坏阻止对文件的访问

如果文件系统已损坏,则可能无法删除该文件。 若要解决此问题,请在磁盘卷上运行 Chkdsk 实用工具以更正任何错误。

磁盘上的坏扇区、其他有故障的硬件或软件错误可能会损坏文件系统并使文件处于有问题的状态。 典型操作可能会以多种方式失败。 当文件系统检测到损坏时,它会将事件记录到事件日志,并且您通常会收到一条消息,提示您运行 Chkdsk。 根据损坏的性质,Chkdsk 可能无法恢复文件数据,也可能无法恢复。但是,Chkdsk 会将文件系统返回到内部一致的状态。

原因4:文件存在于 MAX_PATH 个字符更深层的路径中

如果文件路径有问题,您可能无法打开、编辑或删除文件。

解决方法1:使用自动生成的8.3 名称访问文件

若要解决此问题,您可能需要使用自动生成的8.3 名称来访问该文件。 如果路径深度太长,则此解决方案可能是最简单的解决方案,因为文件夹名称太长。 如果8.3 路径也太长,或者如果在卷上禁用了8.3 名称,请转到 " 解决方法 2"。 有关在 NTFS 卷上禁用8.3 文件名称的详细信息,请参阅 如何禁用 ntfs 分区上的8.3 名称创建

解决方案2:重命名或移动深层文件夹

重命名文件夹,使目标文件的深度大于不再存在的目标文件 MAX_PATH 。 如果这样做,请从根文件夹 (或任何其他方便的位置) ,然后重命名文件夹,以便它们的名称较短。 如果此步骤不能解决此问题 (例如,如果文件的文件夹深度) 超过128,请转到 " 解决方法 4"。

解决方案3:将驱动器映射到路径结构中的文件夹

将驱动器映射到目标文件或文件夹的路径结构中的文件夹。 此方法缩短了虚拟路径。

例如,假设您有一个路径,其结构如下所示:

\\ServerName\SubfolderName1\SubfolderName2\SubfolderName3\SubfolderName4\...

在此路径中,总字符数超过255个字符。 若要将此路径的长度缩短到73个字符,请将驱动器映射到 SubfolderName4。

解决方案4:使用作为文件夹的深度的网络共享

如果解决办法1、2和3不太方便或不能解决问题,请创建一个在文件夹树中的深度更深的网络共享,然后通过访问共享来重命名这些文件夹。

解决方案5:使用可遍历深度路径的工具

许多 Windows 程序预计最大路径长度应少于255个字符。 因此,这些程序只分配足够的内部存储来处理这些典型的路径。 NTFS 不具有此限制,可以保留更长的路径。

如果您在文件夹结构中已有相当深的某个点创建共享,然后使用该共享在该点下面创建一个深层结构,则可能会遇到此问题。 在文件夹树本地运行的某些工具可能无法遍历从根开始的整个树。 您可能需要以特殊方式使用这些工具,以便它们可以遍历共享。 (CreateFile API 文档介绍了在这种情况下遍历整个树的方法。 )

通常情况下,可以使用创建文件的软件来管理文件。 如果您有一个可以创建更早文件的程序 MAX_PATH ,则通常可以使用相同的程序来删除或管理这些文件。 您通常可以使用相同的共享删除在共享上创建的文件。

原因5:文件名包含 Win32 名称空间中的保留名称

如果文件名包含保留名称 (例如,lpt1) 在 Win32 名称空间中,则可能无法删除该文件。 若要解决此问题,请使用非 Win32 程序重命名该文件。 您可以使用 POSIX 工具或使用适当的内部语法的任何其他工具来使用该文件。

此外,如果您使用特定语法来指定文件的路径,则可以使用某些内置命令绕过典型的 Win32 保留名称检查。

如果使用典型的 Win32 CreateFile 机制打开文件句柄,则会为旧样式的 DOS 设备保留某些文件名。 为了实现向后兼容,不允许使用这些文件名,也不能使用典型的 Win32 文件调用来创建这些文件名。 但是,此问题并不是 NTFS 的限制。

您可以使用 Win32 程序绕过在创建文件 (或) 删除文件时执行的典型名称检查,方法是使用用于遍历更早的文件夹的相同技术 MAX_PATH 。 此外,某些 POSIX 工具不受这些名称检查的制约。

原因6:文件名包含 Win32 命名空间中的无效名称

如果文件名中包含无效的名称,则可能无法删除文件 (例如,文件名的尾部空格或尾随句点,或者文件名仅由一个空格) 组成。 若要解决此问题,请使用适当的内部语法的工具删除该文件。 您可以使用 "\\?\" 某些工具的语法对这些文件进行操作,例如:

控制台
del "\\?\c:\<path_to_file_that contains a trailing space.txt>"

此问题的原因与 原因 4类似。 但是,如果使用典型的 Win32 语法打开名称中有尾部空格或尾部句点的文件,则在打开实际文件之前,将去除尾部空格或句点。 因此,如果在名为 AFile.txt 的同一文件夹中有两个文件,并且 AFile.txt (请注意文件名后面的空格) 之后,如果您尝试使用标准 Win32 调用打开第二个文件,则打开的是第一个文件。 同样,如果文件的名称只是一个空格字符,并且您尝试使用标准 Win32 调用打开它,则打开的是文件的父文件夹。 在这种情况下,如果您尝试更改这些文件的安全设置,则可能无法执行此操作,否则可能会意外更改不同文件的设置。 如果出现这种情况,您可能会认为您对实际具有限制性 ACL 的文件具有权限。

原因的组合

有时,您可能会遇到这些原因的组合,这会使删除文件的过程更加复杂。 例如,如果以计算机管理员的身份登录,则可能会遇到 原因 1 的组合 (您没有删除文件的权限) 并 导致 5 (文件名包含一个尾部字符,该字符会导致文件访问被重定向到其他或不存在的文件) 并且您可能无法删除该文件。 如果尝试通过获取文件所有权并添加权限来解决 原因 1 ,则仍可能无法删除该文件,因为由于 原因 6,用户界面中的 ACL 编辑器无法访问相应的文件。

在这种情况下,可以将 Subinacl 实用程序与 (交换机结合使用,该 /onlyfile 实用程序将包含在资源工具包中,) 用于更改在其他情况下无法访问的文件的所有权和权限,例如:

控制台
subinacl /onlyfile "\\?\c:\<path_to_problem_file>" /setowner= domain\administrator /grant= domain\administrator=F

 备注

此命令是一个命令行,为便于阅读,已将其换行。

此示例命令行修改 C:\<path_to_problem_file> 包含尾随空格的文件,以便 domain\administrator 帐户是文件的所有者,并且此帐户对该文件具有完全控制。 您现在可以使用具有相同语法的 Del 命令删除此文件 "\\?\" 。

 

 

Windows 对长路径名文件的限制

众所周知,微软的文件系统经历了 fat->fat32->NTFS 的技术变革。且不论安全和文件组织方式上的革新,单就文件名而言,已经从古老的 DOS 8.3 文件格式(仅支持最长 8 个字符的文件名和 3 个字符的后缀名)转变为可以支持长达 255 个字符的文件名。而对于路径长度,NTFS 也已经支持长达 32768 个字符的路径名。

然而,Windows 操作系统并没有完全放开路径名长度的限制,在 windef.h 中,可以找到如下的宏:

1

#define MAX_PATH 260

事实上,所有的 Windows API 都遵循这个限制。因此,每当我们试图更改某一文件的文件名时,当输入的文件名长度 ( 全路径 ) 到达一定限度时,虽然文件名本身还未达到 255 个字符的限制,但是任何输入将不再被接受,这其实正是由于操作系统不允许 260 个字符(byte)的文件全路径。

实际应用中,这种 260 个字符的全路径的限制给应用开发带来了很大的不便。试想如下应用:我们希望给应用服务器增加一个本地 cache 的功能,该功能可以把远程服务器上的文件留下一个本地的副本。一个合理的实现可以把 url 映射为文件名,当 url 很长时,cache 文件的长度也会很长。当文件名长度超过 255,我们可以把映射文件名的前 255 个字符作为目录名称。但是,我们仍然无法解决 260 个字符的全路径限制。另外,如果一个应用软件的目录结构过深,很容易出现某些文件名长度(含路径)超过 260 个字符,并因此造成安装或删除的失败。总而言之,该限制给我们的开发测试工作带来了诸多不便。

对于一些网络服务器,往往需要将 Java 代码用于上层逻辑控制 / 事务处理的开发,同时将 C/C++ 用于底层核心功能的实现。为此,我们研究了这两种程序语言对长路径名文件的支持情况。其中,对于 Java,比较了两个常用版本 1.4 和 5.0 对长路径支持的差异性;对于 C/C++ 语言的局限性,提出了我们的解决方法。

实验环境 :

操作系统: Windows xp

文件系统: NTFS 文件系统

Java 编译环境: IBM JDK 1.4.2 以及 IBM JDK 5.0

C++ 编译环境: VC.net

在 Java 中使用长路径名文件

Java 语言并不需要对长路径名文件进行特殊的处理,就可以支持长路径名文件的创建、读写和删除操作等基本操作。但是,JDK 1.4.2 和 JDK 5.0 在长路径的支持上是不同的,JDK 1.4.2 并不是完全支持所有的长路径名文件操作,比如访问文件属性的操作是不支持的。我们设计了如下代码来验证 JDK 1.4.2 和 JDK 5.0 对长路径名文件支持的区别。

清单 1. 对长路径名文件操作的 Java 实验代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

try {

    String fileName = "E:\\VerylongpathVerylongpathVerylongpath

        VerylongpathVerylongpathVerylongpathVerylongpath

        VerylongpathVerylongpathVerylongpathVerylongpath\\

    VerylongpathVerylongpathVerylongpathVery

        longpathVerylongpathVerylongpathVerylongpath

    VerylongpathVerylongpathVerylongpathVerylongpa

        th.txt";

    System.out.println("Filename: " + fileName);

    System.out.println("File path length: " + fileName.length());

    String renameFileName = "E:\\VerylongpathVerylongpathVerylongpath

        VerylongpathVerylongpathVerylongpathVerylongpath

        VerylongpathVerylongpathVerylongpathVerylongpath\\Short.txt";

     

    //Create the file.

    File file = new File(fileName);

    if (!file.exists())

        file.createNewFile();

    if (file.exists())

        System.out.println("The file exists!");

    if (file.canRead())

        System.out.println("The file can be read!");

    if (file.canWrite())

        System.out.println("The file can be written!");

    if (file.isFile())

        System.out.println("It's a file!");

 

    //Write to the created file.

    FileOutputStream out = new FileOutputStream(file);

    PrintStream p = new PrintStream(out);

    p.println("This is only a test!");

    p.close();

 

    //Read the information from that file.

    BufferedReader br = new BufferedReader(new FileReader(file));

    StringBuffer sb = new StringBuffer();

    while (true) {

        String sl = br.readLine();

        if (sl == null) {

            break;

        } else {

            sb.append(sl + "\n");

        }

    }

    br.close();

    System.out.println("The content in the file:");

    System.out.print("\t" + sb.toString());

 

    //File rename

    File newfile = new File(renameFileName);

    if (newfile.exists())

        System.out.println(renameFileName + "exsited");

    else {

        if (file.renameTo(newfile)){

            System.out.println("Rename sucessful!");

        } else {

            System.out.println("Rename failed!");

        }  

    }

 

    //delete file

    if (file.delete())

        System.out.println("The old file deleted!");

    if (newfile.delete())

        System.out.println("The renamed file deleted!");

    }  catch (IOException e) {

        //Error happened

        e.printStackTrace();

        System.out.println("Error occurs in writing to the file.");

    }

}

清单 2. 使用 ibm-java2-sdk-142 的结果

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

Filename: E:\VerylongpathVerylongpathVerylongpath

VerylongpathVerylongpathVerylongpathVerylongpathVer

ylongpathVerylongpathVerylongpathVerylongpath\

VerylongpathVerylongpathVerylongpathVerylong

pathVerylongpathVerylongpathVerylongpath

VerylongpathVerylongpathVerylongpathVerylongpath.t

xt

 

File path length: 272

 

The content in the file:

 

This is only a test!

 

Rename failed!

 

The old file deleted!

从实验结果来看,JDK 1.4.2 得到了该长路径名文件的内容,因此,对于该长路径名文件的创建以及读写操作都是支持的。但是对比下文使用 JDK 5.0 的结果,可以看到,所有对于文件属性的判断都是错误的,同时,重命名的操作也无法实现。更为重要的是,JDK 1.4.2 存在着一个很致命的问题,即方法 File.exists() 是失效的。通常,在删除文件前,需要调用该方法判断文件是否存在,对于 JDK 1.4.2,如果直接去删除一个不知道是否存在的文件,就会存在比较大的风险。因此,JDK 1.4.2 在 Windows 平台对长路径名文件的操作只是有限的支持,使用的时候,一定要注意。

清单 3. 使用 ibm-java2-sdk-50 的结果

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

Filename: E:\VerylongpathVerylongpathVerylongpath

VerylongpathVerylongpathVerylongpathVerylongpathVer

ylongpathVerylongpathVerylongpathVerylongpath\

VerylongpathVerylongpathVerylongpathVerylong

pathVerylongpathVerylongpathVerylongpath

VerylongpathVerylongpathVerylongpathVerylongpath.t

xt

File path length: 272

The file exists!

The file can be read!

The file can be written!

It's a file!

The content in the file:

    This is only a test!

Rename sucessful!

The renamed file deleted!

从实验中可以清楚的看到,在版本 JDK 5.0 中,所有的文件操作(新建、读写、属性操作、重命名、删除等)都能够得到正确的处理。使用 JDK 5.0 就可以完全不用担心长路径名文件的使用问题。

在 C/C++ 中使用长路径名文件

相对于 JDK 5.0 不需要任何改动就可以支持长路径名文件,在 C/C++ 中使用超过 260 个字符的路径长度的文件,会复杂得多。下面介绍两种支持长路径名文件的方法。

方法一:使用 Unicode 版本的 API

从微软官方网站 Path Field Limits,可以查到,使用 Unicode 版本的 API,对于使用 NTFS 文件系统的 Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional 和 Windows Server 2003 操作系统,可以支持 32768 字节的文件路径长度。同时,路径名必须使用 \\?\ 的前缀。依照这个思路,我们设计了实验。

清单 4. 对长路径名文件操作的 C 的示例代码(Unicode API)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

{

FILE *from, *to;

char filename[1024];

strcpy(filename,"\\\\?\\E:\\VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathV

erylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath\\VerylongpathVeryl

ongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpat

hVerylongpathVerylongpath.txt");

int iL1=MultiByteToWideChar(CP_ACP, 0, filename, strlen(filename), NULL, 0);

WCHAR* wfilename=new WCHAR[iL1+1];

wfilename[iL1] = '\0';

int iL2=MultiByteToWideChar(CP_ACP, 0, filename, strlen(filename), wfilename, iL1);

from = _wfopen( wfilename ,L"rb");

to = fopen(".\\longpath.txt", "wb");

if((from ==NULL)||(to==NULL))

    return -1;

char buffer[1024];

int count = 0;

while ( (count = fread(buffer, sizeof(char), 1024, from)) != 0)

    fwrite( buffer, sizeof(char), count, to);

delete []wfilename;

fclose (from); fclose(to);

}

使用如上的方法,我们可以拷贝某长路径名的文件到当前文件夹中。从试验结果看,该方法是有效的。但是,由于该方法要求系统使用 Unicode 的 API,同时需要更改路径名称以及编码方式。因此,对于一个已经存在的系统,由于需要改变所有文件操作相关的 API,因此改动将会很大。

方法二:创建 8.3 格式的短路径名

对于每一个长路径名,都有一个 8.3 格式(8 个字符的文件名和 3 个字符的后缀名)的短路径名与其相对应,任意的文件夹或者文件名都可以映射成一个 8 字符的文件名(A~B),其中 A 是文件名前缀,B 是表示字母序的顺序。操作系统可以保证这样的映射是一对一的,只要使用 GetShortPathName() 将长路径名转成相应的短路径名,就可以进行对该文件进行普通的文件操作。同时,在任何时候都可以用函数 GetLongPathName() 把 8.3 格式的短路径名恢复成初始的长路径名。

如 GetShortPathName Function 叙述,我们需要一个 Unicode 版本的 API,同时在路径名前加上 \\?\ 的前缀,才能实现长短路径名间的切换。但从实验来看,即使不使用 Unicode 的 API,依然可以实现上述功能。

清单 4. 对长路径名文件操作的 c 的示例代码(ShortPath)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

{

char pathName [1024];

strcpy(pathName,"\\\\?\\E:\\VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathV

erylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath\\VerylongpathVeryl

ongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpat

hVerylongpathVerylongpath.txt");

 

const int MaxPathLength = 2048;

char shortPath[MaxPathLength];

     

if (strlen(pathName) >= MAX_PATH)

{

    char prePath[] = "\\\\?\\";

    if (strlen(pathName) >= MaxPathLength - strlen(pathName))

        return false;

 

    sprintf(shortPath, "%s%s", prePath, pathName);

 

    for (int iPathIndex = 0; iPathIndex < strlen(shortPath); iPathIndex++)

        if (shortPath[iPathIndex] == '/')

            shortPath[iPathIndex] = '\\';

 

    int dwlen = GetShortPathName(shortPath, shortPath, MaxPathLength);

    if (dwlen <= 0)

        return false;

}

}

经过上述的代码,超过 MAX_PATH 限制的路径名都可以转变成一个 8.3 格式的短路径名,可以把这个文件名 (shortPath)作为后续文件操作函数的参数。这种情况下,对于该文件的所有操作都可以被支持了。我们用这种缩短路径名长度的方式解决了长路径名文件的操作问题。

结束语

本文首先列出了不同的 JDK 版本在 Windows 操作系统上对于长路径名文件处理的区别,同时指出了 JDK 5.0 开始才完全支持长路径名;在第二部分中给出了两种支持长路径名文件的 C/C++ 编程方法。使用上文中的任一方法,我们都可以实现对长路径名文件的操作,这将在很大程度上方便我们的开发工作,解决在 Windows 平台上标准 API 函数对长路径名文件支持的局限性问题。

声明

以上实验代码仅在 Windows XP 操作系统和 VC.NET 编译环境中测试通过 , 作者不对其提供任何种类的保证。如果有任何问题 , 欢迎来信与作者讨论。

 
 
posted @ 2020-12-08 14:29  CharyGao  阅读(2626)  评论(0编辑  收藏  举报