Windows的MAX_PATH

在公司做项目的时候遇到    #define MAX_PATH          260    ,一直有点疑问MAX_PATH 只有260够不够长等问题,上网找的解释如下。

 

MAX_PATH的解释: 文件名最长256(ANSI),加上盘符(X:\)3字节,259字节,再加上结束符1字节,共260

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
http://support.microsoft.com/kb/320081


windows下,全文件名的字符长度是有限制的,众所周知,是MAX_PATH,260。对于这个,几点具体解释:
1. 这个260指的是包含目录到文件名的全路径字符长度。
2. 实际上,测试可以发现:
--加上null,你在资源管理器里只能创建259个字符的全文件名,即实际上只能创建MAX_PATH -2 = 258字符的路径。
--你可以创建c:\长目录\abc.txt <= 258,也可以创建c:\abc\长文件名.txt <= 258。
--创建每一级目录的时候,可以输入的目录名字符是有限制的,规则就是:至少保留下了 11个字符(8.3规则?)使得最内层目录仍然可以创建出文件。
--但是,比如你创建了C:\abc\长文件名.txt,然后呢,重命名目录abc,这时候,你可以创建出一个总长度超过MAX_PATH的全文件名。相当tricky。

参考: http://blog.163.com/wangdong_mail@126/blog/static/3606944620105109561679/


------------------------------ 解决方案一 ------------------------------ 
http://www.debugease.com/csharp/1202792.html
那你不要用它的全路径来创建,用某个文件夹的DirectoryInfo来创建不就好了
多谢提醒,取得父文件夹路径CreateSubdirectory就可以了。试了好多方法,怎么把这个忘了,汗一下自己。

让我们从BCL中的一个有趣的异常开始今天的话题:[PathTooLongException]: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.我们的客户在bug报告里说: “路径最多只有260个字符? MS搞笑的吧. 把这个限制搞得更长一些!”. 在这里我将会对这些提交bug报告的人(很抱歉你们的bug被关成了”won’t fix”)详细解释这个问题并告诉你们我们对此所作出的努力.让我们先来澄清一些术语:Path: 一个文件的全路径. 比如你又一个文件: c:\temp\fileA.txt, 那么通常你会叫这个文件fileA.txt, 但它的全路径应该是c:\temp\fileA.txt. 
MAX_PATH: Windows API定义的路径的最大长度, 260个字符. 
Long path: 一个长度超过了MAX_PATH的路径. 
Long file name: 跟long path还不一样. 这个其实是用来跟短文件名作对比的, 就是以前我们说的那个8.3格式的文件名.
众所周知.NET API是依赖于Windows API的, 从这一点上看, 上面的这个异常就没有什么问题了. 然而Windows API还提供了一个方法来绕过这个MAX_PATH的限制. 如果你在你的文件路径前面加上”\\?\”的前缀, 然后调用unicode版本的Windows API, 那么你的path的最大长度就可以达到32k了. 也就是说你只要加上前缀”\\?\”就可以在Windows API中使用long path了.没有人会抱怨32k的长度限制了, 那么是不是就可以说问题解决了呢? 也不完全是. 过去我们不愿意支持long path是有原因的, 而且现在我们还会考虑这些原因. 第一个原因就是安全. 前缀”\\?\”并不仅仅是打破的long path的限制, 它还能让path在到达文件系统之前只受到Windows API的最小的修正. 这样做的结果就是”\\?\”规避了Windows API对于path的一系列的标准化的操作: 去掉path后面的空格, 把’.’和’..’扩展为相应的内容, 以及把相对路径转换成全路径等等. 在.Net中的如果用FileIOPermission attribute来保证安全, 我们就不得不使用标准化后的路径. 而不用FileIOPermission就会有安全隐患. 现在我们明白了如果我们用前缀”\\?\”来解决long path的问题的话, 我们就必须能像Windows API那样把路径标准化.第二个原因是支持long path可能导致的不一致行为. 很多操作文件的Windows API都支持以”\\?\” 作为前缀的long path, 但仅仅是很多而不是全部. 比如LoadLibrary, 它的功能是将一个module映射到调用者的地址空间, 在文件路径超过MAX_PATH的时候就会失败. 这就意味着你可以调用MoveFile把一个DLL放到一个路径长度超过MAX_PATH的地方, 但是当你想加载这个DLL的时候却失败了. 在Windows API里面有很多这样的例子, 虽然有一些权宜之计, 但都是针对特殊问题的, 没有一个通用的解决方案.另外一个因素, 也是最痛苦的一个, 是Windows Application和Windows shell本身在long path上的兼容性. 因为Windows shell本身只支持长度小于260的路径 (下面会讲到Vista shell弱化了这个限制). 就是说如果.NET支持了long path, 那么你就可以通过你的.NET App创建一些在Explorer或是命令行中不能访问的文件了. J我们已经意识到了260个字符的限制并不是很合理. 我们的客户并不经常碰到这个问题, 但是一旦需要一个超出MAX_PATH的路径, 就会觉得很不方便. 一个权宜之计是P/Invoking Windows API并使用”\\?\”前缀, 但是这样就不得不写一大坨跟System.IO重复的code. 所以为了解决这个问题, 我们的客户常常会重新设计目录结构, 绞尽脑汁的缩短目录名. 因为这个问题已经逐渐变得普遍, 所以无论是.NET framework还是别的领域, MS都已经开始着手解决这个问题. 实际上在vista中你应该已经可以看到我们为了减少出现MAX_PATH的问题的几率所作出的改动: 很多特定的目录名已经被缩短 (译注: \Documents and Settings à \Users, 实际上, 在MS有一个专门的alias叫longpath来谈论这个问题), shell还有一个auto-path shrinking的功能, 它会用比较短的别名来表示路径以把那些long path压缩在260个字符以内.


------------------------------ 解决方案二 ------------------------------ 
http://express.ruanko.com/ruanko-express_11/webpage/tech-overnight_1.html

上层逻辑控制和事务处理使用 Java 开发,而底层核心功能使用 C/C++ 实现,这已经成为一种较为通用的开发模式。但由于 Windows 操作系统的默认设置,上述语言在对长路径名(>260 字符)文件的处理时会遇到一些问题。本文列出了不同的 JDK 版本在 Windows 操作系统上对于长路径名文件处理的区别,给出了两种支持长路径名文件的 C/C++ 编程方法,同时还指出了从 JDK 5.0 开始才完全支持长路径名。使用本文的方法,可以解决在 Windows 平台上标准 API 函数对长路径名文件支持的局限性问题,给开发测试工作带来方便。

Windows 对长路径名文件的限制

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

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

#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 编译环境:JDK 1.4.2 以及 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 实验代码:

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. 使用jdk1.42 的结果

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. 使用jdk5.0 的结果

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)

{
FILE *from, *to;
char filename[1024];
strcpy(filename,"\\\\?\\E:\\VerylongpathVerylongpath
VerylongpathVerylongpathVerylongpathV erylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
\\VerylongpathVeryl ongpathVerylongpathVerylongpathVerylongpathVerylongpath
VerylongpathVerylongpathVerylongpat 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,依然可以实现上述功能。

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

{
char pathName [1024];
strcpy(pathName,"\\\\?\\E:\\VerylongpathVerylongpathVerylongpath
VerylongpathVerylongpathV erylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
\\VerylongpathVeryl ongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
VerylongpathVerylongpat 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 函数对长路径名文件支持的局限性问题。

 

转载于:https://www.cnblogs.com/findumars/p/3817091.html

posted @ 2019-09-16 13:31  KwinWei  阅读(1218)  评论(0编辑  收藏  举报