【问题标题】:How to deal with files with a name longer than 259 characters?如何处理名称超过 259 个字符的文件?
【发布时间】:2011-07-08 11:51:36
【问题描述】:

我正在开发一个应用程序,它遍历某些目录中的每个文件并对这些文件执行一些操作。其中,我必须检索文件大小和修改该文件的日期。

某些文件全名(目录+文件名)太长,我无法使用 .NET Framework FileInfo,限制为 MAX_PATH(260 个字符)。许多网络资源建议通过 P/Invoke 使用原生 Win32 函数来访问名称过长的文件。

目前,Win32 函数似乎也出现了完全相同的问题。例如,GetFileAttributesEx (kernel32.dll) 失败,出现 Win32 错误 3 ERROR_PATH_NOT_FOUND,路径为 270 字节。

可以从 Notepad2 成功打开相同的文件,并使用 Windows 资源管理器成功显示(但例如 Visual Studio 2010 由于 259 个字符的限制而无法打开它¹)。

当文件路径长度为 270 个字符时,我该怎么做才能访问文件?

注意事项:

  • 删除或忽略文件路径长度超过 259 个字符的文件不是解决方案。

  • 我正在寻找仅与 Unicode 兼容的解决方案。

  • 应用程序将在安装了 .NET Framework 4 的 Windows 2008/Vista 或更高版本下运行。


¹ 令人惊讶的是,Microsoft Word 2007 失败了,在没有软驱的计算机上抱怨“软盘太小”,或者当有 4 GB 的内存时“RAM 内存不足” RAM 离开了,或者最后“防病毒软件 [...] 需要更新”。至少有一天他们会停止在 Microsoft Office 等关键产品中显示这些愚蠢无意义的错误吗?

【问题讨论】:

  • 我相信,即使是现在,每个文件名都映射到一个 8.3 格式的文件名,你不能使用它吗? en.wikipedia.org/wiki/…
  • 即使是 8.3 格式的文件名也可以超过 260 个字符,您只需要深层文件夹嵌套即可。
  • 请注意,您可以(并且可能希望这样做,因为它会增加 I/O 开销)禁用 8.3 名称创建,因此,不,您不能确定是否存在 8.3。见fsutil.exe 8dot3name

标签: c# windows file-io interop pinvoke


【解决方案1】:

.NET 4.6.2 解决方案

使用 \\?\C:\Verrrrrrrrrrrry long path 语法,如 here 所述。

.NET Core 解决方案

它之所以有效,是因为框架为您添加了长路径语法。

Pre .NET 4.6.2 解决方案

还可以使用长路径语法和带有 P/Invoke 的 Win32 API 函数的 Unicode 版本。 来自Naming Files, Paths, and Namespaces

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

阅读this Microsoft Support page 可能也很有趣。

Long Paths in .NET by Kim Hamilton at the BCL Team blog 中的一个非常广泛的解释列出了处理这些路径的一些问题,他声称这是 .NET 中仍然不直接支持这种语法的原因:

过去我们不愿意添加长路径有几个原因,以及为什么我们仍然对此保持谨慎<...>。

<...>\\?\ 前缀不仅支持长路径;它会导致路径被传递到文件系统,而 Windows API 的修改最少。结果是\\?\ 关闭了由 Windows API 执行的文件名规范化,包括删除尾随空格、扩展“.”和“..”、将相对路径转换为完整路径等等。<...>

<...> 带有\\?\ 前缀的长路径可用于大多数文件相关 Windows API,但并非所有Windows API。例如,如果文件名长于 MAX_PATH,则 LoadLibrary<...> 失败。 <...> 在整个 Windows API 中都有类似的示例;存在一些解决方法,但它们是根据具体情况而定的。

另一个因素<...>是与其他基于Windows的应用程序和Windows shell本身的兼容性<...>

因为这个问题变得越来越普遍<...>整个微软都在努力解决这个问题。事实上,作为一个及时的 Vista 插件,您会注意到一些减少达到 MAX_PATH 限制的机会的更改:许多特殊文件夹名称已缩短,更有趣的是,shell 正在使用自动路径缩小功能<...> 尝试将它们压缩成 260 个字符。


警告:您可能需要直接调用 Windows API,因为我认为 .NET Framework 可能不支持这种路径语法。

【讨论】:

  • 如果你需要访问一个长路径的服务器共享,你需要这样写:\\?\UNC\Server\Share,换句话说,添加 \\?\UNC\ 作为字首。更多信息:installsetupconfig.com/win32programming/windowsfileapis4_2.html
  • 是的,您需要 P/Invoke Win32 API 函数并直接从 .NET 应用程序调用它们。 .NET 的内部管道(特别是 PathHelper 类)验证路径并在超过 MAX_PATH (260) 个字符时引发异常。
  • @AmaniKilumanga:那你的路径基本上是6000字的作文,系统处理不了。
  • @denahiro:也许你可以冒昧地编辑它......就像之前把它放在那里的人一样(我没有)......
  • 非常精确 - \\?\ 不适用于 4.6.1,但适用于 4.6.2
【解决方案2】:

我创建了自己的 LongFileLongDirectory 类来解决这个问题。每当我通常使用System.IO.File 时,我都会使用它。

可能会对其进行优化等,但多年来一直运行良好。

public static class LongFile
{
    private const int MAX_PATH = 260;

    public static bool Exists(string path)
    {
        if (path.Length < MAX_PATH) return System.IO.File.Exists(path);
        var attr = NativeMethods.GetFileAttributesW(GetWin32LongPath(path));
        return (attr != NativeMethods.INVALID_FILE_ATTRIBUTES && ((attr & NativeMethods.FILE_ATTRIBUTE_ARCHIVE) == NativeMethods.FILE_ATTRIBUTE_ARCHIVE));
    }

    public static void Delete(string path)
    {
        if (path.Length < MAX_PATH) System.IO.File.Delete(path);
        else
        {
            bool ok = NativeMethods.DeleteFileW(GetWin32LongPath(path));
            if (!ok) ThrowWin32Exception();
        }
    }

    public static void AppendAllText(string path, string contents)
    {
        AppendAllText(path, contents, Encoding.Default);
    }

    public static void AppendAllText(string path, string contents, Encoding encoding)
    {
        if (path.Length < MAX_PATH)
        {
            System.IO.File.AppendAllText(path, contents, encoding);
        }
        else
        {
            var fileHandle = CreateFileForAppend(GetWin32LongPath(path));
            using (var fs = new System.IO.FileStream(fileHandle, System.IO.FileAccess.Write))
            {
                var bytes = encoding.GetBytes(contents);
                fs.Position = fs.Length;
                fs.Write(bytes, 0, bytes.Length);
            }
        }
    }

    public static void WriteAllText(string path, string contents)
    {
        WriteAllText(path, contents, Encoding.Default);
    }

    public static void WriteAllText(string path, string contents, Encoding encoding)
    {
        if (path.Length < MAX_PATH)
        {
            System.IO.File.WriteAllText(path, contents, encoding);
        }
        else
        {
            var fileHandle = CreateFileForWrite(GetWin32LongPath(path));

            using (var fs = new System.IO.FileStream(fileHandle, System.IO.FileAccess.Write))
            {
                var bytes = encoding.GetBytes(contents);
                fs.Write(bytes, 0, bytes.Length);
            }
        }
    }

    public static void WriteAllBytes(string path, byte[] bytes)
    {
        if (path.Length < MAX_PATH)
        {
            System.IO.File.WriteAllBytes(path, bytes);
        }
        else
        {
            var fileHandle = CreateFileForWrite(GetWin32LongPath(path));

            using (var fs = new System.IO.FileStream(fileHandle, System.IO.FileAccess.Write))
            {
                fs.Write(bytes, 0, bytes.Length);
            }
        }
    }

    public static void Copy(string sourceFileName, string destFileName)
    {
        Copy(sourceFileName, destFileName, false);
    }

    public static void Copy(string sourceFileName, string destFileName, bool overwrite)
    {
        if (sourceFileName.Length < MAX_PATH && (destFileName.Length < MAX_PATH)) System.IO.File.Copy(sourceFileName, destFileName, overwrite);
        else
        {
            var ok = NativeMethods.CopyFileW(GetWin32LongPath(sourceFileName), GetWin32LongPath(destFileName), !overwrite);
            if (!ok) ThrowWin32Exception();
        }
    }

    public static void Move(string sourceFileName, string destFileName)
    {
        if (sourceFileName.Length < MAX_PATH && (destFileName.Length < MAX_PATH)) System.IO.File.Move(sourceFileName, destFileName);
        else
        {
            var ok = NativeMethods.MoveFileW(GetWin32LongPath(sourceFileName), GetWin32LongPath(destFileName));
            if (!ok) ThrowWin32Exception();
        }
    }

    public static string ReadAllText(string path)
    {
        return ReadAllText(path, Encoding.Default);
    }

    public static string ReadAllText(string path, Encoding encoding)
    {
        if (path.Length < MAX_PATH) { return System.IO.File.ReadAllText(path, encoding); }
        var fileHandle = GetFileHandle(GetWin32LongPath(path));

        using (var fs = new System.IO.FileStream(fileHandle, System.IO.FileAccess.Read))
        {
            var data = new byte[fs.Length];
            fs.Read(data, 0, data.Length);
            return encoding.GetString(data);
        }
    }

    public static string[] ReadAllLines(string path)
    {
        return ReadAllLines(path, Encoding.Default);
    }

    public static string[] ReadAllLines(string path, Encoding encoding)
    {
        if (path.Length < MAX_PATH) { return System.IO.File.ReadAllLines(path, encoding); }
        var fileHandle = GetFileHandle(GetWin32LongPath(path));

        using (var fs = new System.IO.FileStream(fileHandle, System.IO.FileAccess.Read))
        {
            var data = new byte[fs.Length];
            fs.Read(data, 0, data.Length);
            var str = encoding.GetString(data);
            if (str.Contains("\r")) return str.Split(new[] { "\r\n" }, StringSplitOptions.None);
            return str.Split('\n');
        }
    }
    public static byte[] ReadAllBytes(string path)
    {
        if (path.Length < MAX_PATH) return System.IO.File.ReadAllBytes(path);
        var fileHandle = GetFileHandle(GetWin32LongPath(path));

        using (var fs = new System.IO.FileStream(fileHandle, System.IO.FileAccess.Read))
        {
            var data = new byte[fs.Length];
            fs.Read(data, 0, data.Length);
            return data;
        }
    }


    public static void SetAttributes(string path, FileAttributes attributes)
    {
        if (path.Length < MAX_PATH)
        {
            System.IO.File.SetAttributes(path, attributes);
        }
        else
        {
            var longFilename = GetWin32LongPath(path);
            NativeMethods.SetFileAttributesW(longFilename, (int)attributes);
        }
    }

    #region Helper methods

    private static SafeFileHandle CreateFileForWrite(string filename)
    {
        if (filename.Length >= MAX_PATH) filename = GetWin32LongPath(filename);
        SafeFileHandle hfile = NativeMethods.CreateFile(filename, (int)NativeMethods.FILE_GENERIC_WRITE, NativeMethods.FILE_SHARE_NONE, IntPtr.Zero, NativeMethods.CREATE_ALWAYS, 0, IntPtr.Zero);
        if (hfile.IsInvalid) ThrowWin32Exception();
        return hfile;
    }

    private static SafeFileHandle CreateFileForAppend(string filename)
    {
        if (filename.Length >= MAX_PATH) filename = GetWin32LongPath(filename);
        SafeFileHandle hfile = NativeMethods.CreateFile(filename, (int)NativeMethods.FILE_GENERIC_WRITE, NativeMethods.FILE_SHARE_NONE, IntPtr.Zero, NativeMethods.CREATE_NEW, 0, IntPtr.Zero);
        if (hfile.IsInvalid)
        {
            hfile = NativeMethods.CreateFile(filename, (int)NativeMethods.FILE_GENERIC_WRITE, NativeMethods.FILE_SHARE_NONE, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
            if (hfile.IsInvalid) ThrowWin32Exception();
        }
        return hfile;
    }

    internal static SafeFileHandle GetFileHandle(string filename)
    {
        if (filename.Length >= MAX_PATH) filename = GetWin32LongPath(filename);
        SafeFileHandle hfile = NativeMethods.CreateFile(filename, (int)NativeMethods.FILE_GENERIC_READ, NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
        if (hfile.IsInvalid) ThrowWin32Exception();
        return hfile;
    }

    internal static SafeFileHandle GetFileHandleWithWrite(string filename)
    {
        if (filename.Length >= MAX_PATH) filename = GetWin32LongPath(filename);
        SafeFileHandle hfile = NativeMethods.CreateFile(filename, (int)(NativeMethods.FILE_GENERIC_READ | NativeMethods.FILE_GENERIC_WRITE | NativeMethods.FILE_WRITE_ATTRIBUTES), NativeMethods.FILE_SHARE_NONE, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
        if (hfile.IsInvalid) ThrowWin32Exception();
        return hfile;
    }

    public static System.IO.FileStream GetFileStream(string filename, FileAccess access = FileAccess.Read)
    {
        var longFilename = GetWin32LongPath(filename);
        SafeFileHandle hfile;
        if (access == FileAccess.Write)
        {
            hfile = NativeMethods.CreateFile(longFilename, (int)(NativeMethods.FILE_GENERIC_READ | NativeMethods.FILE_GENERIC_WRITE | NativeMethods.FILE_WRITE_ATTRIBUTES), NativeMethods.FILE_SHARE_NONE, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
        }
        else
        {
            hfile = NativeMethods.CreateFile(longFilename, (int)NativeMethods.FILE_GENERIC_READ, NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
        }

        if (hfile.IsInvalid) ThrowWin32Exception();

        return new System.IO.FileStream(hfile, access);
    }


    [DebuggerStepThrough]
    public static void ThrowWin32Exception()
    {
        int code = Marshal.GetLastWin32Error();
        if (code != 0)
        {
            throw new System.ComponentModel.Win32Exception(code);
        }
    }

    public static string GetWin32LongPath(string path)
    {
        if (path.StartsWith(@"\\?\")) return path;

        if (path.StartsWith("\\"))
        {
            path = @"\\?\UNC\" + path.Substring(2);
        }
        else if (path.Contains(":"))
        {
            path = @"\\?\" + path;
        }
        else
        {
            var currdir = Environment.CurrentDirectory;
            path = Combine(currdir, path);
            while (path.Contains("\\.\\")) path = path.Replace("\\.\\", "\\");
            path = @"\\?\" + path;
        }
        return path.TrimEnd('.'); ;
    }

    private static string Combine(string path1, string path2)
    {
        return path1.TrimEnd('\\') + "\\" + path2.TrimStart('\\').TrimEnd('.'); ;
    }


    #endregion

    public static void SetCreationTime(string path, DateTime creationTime)
    {
        long cTime = 0;
        long aTime = 0;
        long wTime = 0;

        using (var handle = GetFileHandleWithWrite(path))
        {
            NativeMethods.GetFileTime(handle, ref cTime, ref aTime, ref wTime);
            var fileTime = creationTime.ToFileTimeUtc();
            if (!NativeMethods.SetFileTime(handle, ref fileTime, ref aTime, ref wTime))
            {
                throw new Win32Exception();
            }
        }
    }

    public static void SetLastAccessTime(string path, DateTime lastAccessTime)
    {
        long cTime = 0;
        long aTime = 0;
        long wTime = 0;

        using (var handle = GetFileHandleWithWrite(path))
        {
            NativeMethods.GetFileTime(handle, ref cTime, ref aTime, ref wTime);

            var fileTime = lastAccessTime.ToFileTimeUtc();
            if (!NativeMethods.SetFileTime(handle, ref cTime, ref fileTime, ref wTime))
            {
                throw new Win32Exception();
            }
        }
    }

    public static void SetLastWriteTime(string path, DateTime lastWriteTime)
    {
        long cTime = 0;
        long aTime = 0;
        long wTime = 0;

        using (var handle = GetFileHandleWithWrite(path))
        {
            NativeMethods.GetFileTime(handle, ref cTime, ref aTime, ref wTime);

            var fileTime = lastWriteTime.ToFileTimeUtc();
            if (!NativeMethods.SetFileTime(handle, ref cTime, ref aTime, ref fileTime))
            {
                throw new Win32Exception();
            }
        }
    }

    public static DateTime GetLastWriteTime(string path)
    {
        long cTime = 0;
        long aTime = 0;
        long wTime = 0;

        using (var handle = GetFileHandleWithWrite(path))
        {
            NativeMethods.GetFileTime(handle, ref cTime, ref aTime, ref wTime);

            return DateTime.FromFileTimeUtc(wTime);
        }
    }

}

还有一个对应的LongDirectory

public class LongDirectory
{
    private const int MAX_PATH = 260;

    public static void CreateDirectory(string path)
    {
        if (string.IsNullOrWhiteSpace(path)) return;
        if (path.Length < MAX_PATH)
        {
            System.IO.Directory.CreateDirectory(path);
        }
        else
        {
            var paths = GetAllPathsFromPath(GetWin32LongPath(path));
            foreach (var item in paths)
            {
                if (!LongExists(item))
                {
                    var ok = NativeMethods.CreateDirectory(item, IntPtr.Zero);
                    if (!ok)
                    {
                        ThrowWin32Exception();
                    }
                }
            }
        }
    }

    public static void Delete(string path)
    {
        Delete(path, false);
    }

    public static void Delete(string path, bool recursive)
    {
        if (path.Length < MAX_PATH && !recursive)
        {
            System.IO.Directory.Delete(path, false);
        }
        else
        {
            if (!recursive)
            {
                bool ok = NativeMethods.RemoveDirectory(GetWin32LongPath(path));
                if (!ok) ThrowWin32Exception();
            }
            else
            {
                DeleteDirectories(new string[] { GetWin32LongPath(path) });
            }
        }
    }


    private static void DeleteDirectories(string[] directories)
    {
        foreach (string directory in directories)
        {
            string[] files = LongDirectory.GetFiles(directory, null, System.IO.SearchOption.TopDirectoryOnly);
            foreach (string file in files)
            {
                LongFile.Delete(file);
            }
            directories = LongDirectory.GetDirectories(directory, null, System.IO.SearchOption.TopDirectoryOnly);
            DeleteDirectories(directories);
            bool ok = NativeMethods.RemoveDirectory(GetWin32LongPath(directory));
            if (!ok) ThrowWin32Exception();
        }
    }

    public static bool Exists(string path)
    {
        if (path.Length < MAX_PATH) return System.IO.Directory.Exists(path);
        return LongExists(GetWin32LongPath(path));
    }

    private static bool LongExists(string path)
    {
        var attr = NativeMethods.GetFileAttributesW(path);
        return (attr != NativeMethods.INVALID_FILE_ATTRIBUTES && ((attr & NativeMethods.FILE_ATTRIBUTE_DIRECTORY) == NativeMethods.FILE_ATTRIBUTE_DIRECTORY));
    }


    public static string[] GetDirectories(string path)
    {
        return GetDirectories(path, null, SearchOption.TopDirectoryOnly);
    }

    public static string[] GetDirectories(string path, string searchPattern)
    {
        return GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
    }

    public static string[] GetDirectories(string path, string searchPattern, System.IO.SearchOption searchOption)
    {
        searchPattern = searchPattern ?? "*";
        var dirs = new List<string>();
        InternalGetDirectories(path, searchPattern, searchOption, ref dirs);
        return dirs.ToArray();
    }

    private static void InternalGetDirectories(string path, string searchPattern, System.IO.SearchOption searchOption, ref List<string> dirs)
    {
        NativeMethods.WIN32_FIND_DATA findData;
        IntPtr findHandle = NativeMethods.FindFirstFile(System.IO.Path.Combine(GetWin32LongPath(path), searchPattern), out findData);

        try
        {
            if (findHandle != new IntPtr(-1))
            {

                do
                {
                    if ((findData.dwFileAttributes & System.IO.FileAttributes.Directory) != 0)
                    {
                        if (findData.cFileName != "." && findData.cFileName != "..")
                        {
                            string subdirectory = System.IO.Path.Combine(path, findData.cFileName);
                            dirs.Add(GetCleanPath(subdirectory));
                            if (searchOption == SearchOption.AllDirectories)
                            {
                                InternalGetDirectories(subdirectory, searchPattern, searchOption, ref dirs);
                            }
                        }
                    }
                } while (NativeMethods.FindNextFile(findHandle, out findData));
                NativeMethods.FindClose(findHandle);
            }
            else
            {
                ThrowWin32Exception();
            }
        }
        catch (Exception)
        {
            NativeMethods.FindClose(findHandle);
            throw;
        }
    }

    public static string[] GetFiles(string path)
    {
        return GetFiles(path, null, SearchOption.TopDirectoryOnly);
    }

    public static string[] GetFiles(string path, string searchPattern)
    {
        return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
    }


    public static string[] GetFiles(string path, string searchPattern, System.IO.SearchOption searchOption)
    {
        searchPattern = searchPattern ?? "*";

        var files = new List<string>();
        var dirs = new List<string> { path };

        if (searchOption == SearchOption.AllDirectories)
        {
            //Add all the subpaths
            dirs.AddRange(LongDirectory.GetDirectories(path, null, SearchOption.AllDirectories));
        }

        foreach (var dir in dirs)
        {
            NativeMethods.WIN32_FIND_DATA findData;
            IntPtr findHandle = NativeMethods.FindFirstFile(System.IO.Path.Combine(GetWin32LongPath(dir), searchPattern), out findData);

            try
            {
                if (findHandle != new IntPtr(-1))
                {

                    do
                    {
                        if ((findData.dwFileAttributes & System.IO.FileAttributes.Directory) == 0)
                        {
                            string filename = System.IO.Path.Combine(dir, findData.cFileName);
                            files.Add(GetCleanPath(filename));
                        }
                    } while (NativeMethods.FindNextFile(findHandle, out findData));
                    NativeMethods.FindClose(findHandle);
                }
            }
            catch (Exception)
            {
                NativeMethods.FindClose(findHandle);
                throw;
            }
        }

        return files.ToArray();
    }



    public static void Move(string sourceDirName, string destDirName)
    {
        if (sourceDirName.Length < MAX_PATH || destDirName.Length < MAX_PATH)
        {
            System.IO.Directory.Move(sourceDirName, destDirName);
        }
        else
        {
            var ok = NativeMethods.MoveFileW(GetWin32LongPath(sourceDirName), GetWin32LongPath(destDirName));
            if (!ok) ThrowWin32Exception();
        }
    }

    #region Helper methods



    [DebuggerStepThrough]
    public static void ThrowWin32Exception()
    {
        int code = Marshal.GetLastWin32Error();
        if (code != 0)
        {
            throw new System.ComponentModel.Win32Exception(code);
        }
    }

    public static string GetWin32LongPath(string path)
    {

        if (path.StartsWith(@"\\?\")) return path;

        var newpath = path;
        if (newpath.StartsWith("\\"))
        {
            newpath = @"\\?\UNC\" + newpath.Substring(2);
        }
        else if (newpath.Contains(":"))
        {
            newpath = @"\\?\" + newpath;
        }
        else
        {
            var currdir = Environment.CurrentDirectory;
            newpath = Combine(currdir, newpath);
            while (newpath.Contains("\\.\\")) newpath = newpath.Replace("\\.\\", "\\");
            newpath = @"\\?\" + newpath;
        }
        return newpath.TrimEnd('.');
    }

    private static string GetCleanPath(string path)
    {
        if (path.StartsWith(@"\\?\UNC\")) return @"\\" + path.Substring(8);
        if (path.StartsWith(@"\\?\")) return path.Substring(4);
        return path;
    }

    private static List<string> GetAllPathsFromPath(string path)
    {
        bool unc = false;
        var prefix = @"\\?\";
        if (path.StartsWith(prefix + @"UNC\"))
        {
            prefix += @"UNC\";
            unc = true;
        }
        var split = path.Split('\\');
        int i = unc ? 6 : 4;
        var list = new List<string>();
        var txt = "";

        for (int a = 0; a < i; a++)
        {
            if (a > 0) txt += "\\";
            txt += split[a];
        }
        for (; i < split.Length; i++)
        {
            txt = Combine(txt, split[i]);
            list.Add(txt);
        }

        return list;
    }

    private static string Combine(string path1, string path2)
    {
        return path1.TrimEnd('\\') + "\\" + path2.TrimStart('\\').TrimEnd('.');
    }


    #endregion
}

NativeMethods:

internal static class NativeMethods
{
    internal const int FILE_ATTRIBUTE_ARCHIVE = 0x20;
    internal const int INVALID_FILE_ATTRIBUTES = -1;

    internal const int FILE_READ_DATA = 0x0001;
    internal const int FILE_WRITE_DATA = 0x0002;
    internal const int FILE_APPEND_DATA = 0x0004;
    internal const int FILE_READ_EA = 0x0008;
    internal const int FILE_WRITE_EA = 0x0010;

    internal const int FILE_READ_ATTRIBUTES = 0x0080;
    internal const int FILE_WRITE_ATTRIBUTES = 0x0100;

    internal const int FILE_SHARE_NONE = 0x00000000;
    internal const int FILE_SHARE_READ = 0x00000001;

    internal const int FILE_ATTRIBUTE_DIRECTORY = 0x10;

    internal const long FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE |
                                                FILE_WRITE_DATA |
                                                FILE_WRITE_ATTRIBUTES |
                                                FILE_WRITE_EA |
                                                FILE_APPEND_DATA |
                                                SYNCHRONIZE;

    internal const long FILE_GENERIC_READ = STANDARD_RIGHTS_READ |
                                            FILE_READ_DATA |
                                            FILE_READ_ATTRIBUTES |
                                            FILE_READ_EA |
                                            SYNCHRONIZE;



    internal const long READ_CONTROL = 0x00020000L;
    internal const long STANDARD_RIGHTS_READ = READ_CONTROL;
    internal const long STANDARD_RIGHTS_WRITE = READ_CONTROL;

    internal const long SYNCHRONIZE = 0x00100000L;

    internal const int CREATE_NEW = 1;
    internal const int CREATE_ALWAYS = 2;
    internal const int OPEN_EXISTING = 3;

    internal const int MAX_PATH = 260;
    internal const int MAX_ALTERNATE = 14;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct WIN32_FIND_DATA
    {
        public System.IO.FileAttributes dwFileAttributes;
        public FILETIME ftCreationTime;
        public FILETIME ftLastAccessTime;
        public FILETIME ftLastWriteTime;
        public uint nFileSizeHigh; //changed all to uint, otherwise you run into unexpected overflow
        public uint nFileSizeLow;  //|
        public uint dwReserved0;   //|
        public uint dwReserved1;   //v
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]
        public string cAlternate;
    }


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool CopyFileW(string lpExistingFileName, string lpNewFileName, bool bFailIfExists);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern int GetFileAttributesW(string lpFileName);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool DeleteFileW(string lpFileName);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool MoveFileW(string lpExistingFileName, string lpNewFileName);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool SetFileTime(SafeFileHandle hFile, ref long lpCreationTime, ref long lpLastAccessTime, ref long lpLastWriteTime);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool GetFileTime(SafeFileHandle hFile, ref long lpCreationTime, ref long lpLastAccessTime, ref long lpLastWriteTime);


    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);


    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool FindClose(IntPtr hFindFile);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool RemoveDirectory(string path);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern bool CreateDirectory(string lpPathName, IntPtr lpSecurityAttributes);


    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern int SetFileAttributesW(string lpFileName, int fileAttributes);
}

【讨论】:

  • StackOverflow 不是共享库源代码的好地方。如果你想让它真正被其他开发者使用,我相信你应该(1)将它发布到 GitHub 或类似的服务上,(2)包含单元测试,以及(3)将它发布为 NuGet 包;可选地,如果您想鼓励其他人为您的图书馆做出贡献,您应该考虑添加文档。然后,您可以编辑此答案以解释您所做的以及该库如何回答原始问题(因为它确实如此!),并包含指向 GitHub 和 NuGet 的相应链接。
  • 这只是我个人 TFS 存储库 (visualstudio.com) 上一个较大项目中的几个类。想我分享它,因为我经常看到缺少对长路径支持的软件问题(如果长度超过 259,即使 TFS 2013 也会失败......)。但是,是的。将来可能会做类似的事情(例如,如果这篇文章得到很多投票:))。
  • 此解决方案适用于我,但在 InternalGetDirectories 函数中,如果选择了 AllDirectories 选项并且搜索模式为'在子目录列表中找不到。我不得不用“*”作为模式的一种预搜索替换行 ThrowWin32Exception(); (代码很长要包含在这里,但非常类似于 do 函数中的代码)。
  • 试过 ZetaLongPaths nuget/github 项目,由于某种原因,复制文件会产生损坏的文件,现在试试这个。我认为它也使用了这些 pinvoke 模式,但你永远不知道!
  • 谢谢这位先生,但仅供参考,如果目标路径是相对的(“..\Fu\Bar.txt”),它将不起作用。我通过将其强制为绝对路径来将其固定在我身边。
【解决方案3】:

您可以尝试使用 Delimon 库,它是 Microsoft TechNet 上基于 .NET Framework 4 的库,用于解决长文件名问题:

Delimon.Win32.I​O Library (V4.0).

它有自己的 System.IO 中的关键方法版本。例如,您将替换:

System.IO.Directory.GetFiles

Delimon.Win32.IO.Directory.GetFiles

这将让您处理长文件和文件夹。

来自网站:

Delimon.Win32.IO 替换了 System.IO 的基本文件功能和 支持最多 32,767 个字符的文件和文件夹名称。

这个库是在 .NET Framework 4.0 上编写的,可以使用 在 x86 和 x64 系统上。标准的文件和文件夹限制 System.IO 命名空间可以处理包含 260 个字符的文件 文件名和文件夹名称中的 240 个字符(MAX_PATH 通常为 配置为 260 个字符)。通常你会遇到 System.IO.PathTooLongException 标准 .NET 库出错。

【讨论】:

  • 还有AlphaFS库用于超过260个字符的路径。
【解决方案4】:

【讨论】:

    【解决方案5】:

    我曾经在编写应用程序时遇到过这个问题。当我接近达到 260 个字符的限制时,我会动态地将网络驱动器映射到完整路径的某个部分,从而显着缩短完整路径 + 文件名的长度。这并不是一个真正优雅的解决方案,但它完成了工作。

    【讨论】:

      【解决方案6】:

      请像这样更新您的配置文件:

      <configuration>
        <runtime>
          <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" />
        </runtime>
      </configuration>
      

      【讨论】:

      • 最佳解决方案!!
      【解决方案7】:

      对于共享文件夹 (\\ServerName\PathThatIsVeryLong\),我们可以使用“\\?\UNC\ServerName\PathThatIsVeryLong\”,其中“ServerName”是计算机的名称,“PathThatIsVeryLong”是计算机的名称共享文件夹。

      参考:https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN

      【讨论】:

        【解决方案8】:

        MSDN reference for GetFileAttributesEx 说:

        在此函数的 ANSI 版本中,名称仅限于 MAX_PATH 个字符。要将此限制扩展到 32,767 个宽字符,请调用函数的 Unicode 版本并在路径前添加“\\?\”。如需更多信息,请参阅Naming a File

        所以你想使用 GetFileAttributesExW 并在你的路径前加上“\\?\”

        【讨论】:

        • 您的引用是正确的,但有点误导:此限制与 ANSI 版本无关(在 Unicode 版本中也有限制)。
        • 它非常清楚地表明您需要同时使用 Unicode 版本和前缀来扩展限制。
        【解决方案9】:

        创建一个使用 Robocopy 的单独进程也是一种解决方案,如下所述: How to move folder/files with path names > 255 characters in Windows 8.1?

          public static void RoboCopy(string src, string dst)
                {
                    Process p = new Process();
                    p.StartInfo.Arguments = string.Format("/C Robocopy {0} {1}", src, dst);
                    p.StartInfo.FileName = "CMD.EXE";
                    p.StartInfo.CreateNoWindow = true;
                    p.StartInfo.UseShellExecute = false;
                    p.Start();
                    p.WaitForExit();
                }
        

        见于:File copy using robo copy and process

        【讨论】:

          猜你喜欢
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          • 2017-05-18
          • 1970-01-01
          • 2011-06-28
          • 1970-01-01
          • 2021-03-20
          相关资源
          最近更新 更多