이름이 259 자보다 긴 파일을 처리하는 방법은 무엇입니까?


82

일부 디렉토리의 모든 파일을 살펴보고 해당 파일에 대해 몇 가지 작업을 수행하는 응용 프로그램에서 작업 중입니다. 무엇보다도 파일 크기와이 파일이 수정 된 날짜를 검색해야합니다.

(디렉토리 + 파일 이름) 너무 오래있는 일부 파일 전체 이름, 나는 .NET 프레임 워크 사용할 수없는 FileInfo제한됩니다,MAX_PATH (260 자). 많은 웹 소스는 이름이 너무 긴 파일에 액세스하기 위해 P / Invoke를 통해 기본 Win32 기능을 사용하도록 권장했습니다.

현재 Win32 기능에서도 똑같은 문제가 발생하는 것 같습니다. 예를 들면GetFileAttributesEx (kernel32.dll)은 270 바이트 경로에 대해 Win32 오류 3 ERROR_PATH_NOT_FOUND와 함께 실패합니다.

Notepad2에서 동일한 파일을 성공적으로 열고 Windows 탐색기에 성공적으로 표시 할 수 있습니다 (그러나 Visual Studio 2010은 259 자 제한 ¹으로 인해 파일을 열지 못함).

파일 경로가 270 자일 때 파일에 액세스하려면 어떻게해야합니까?

메모:

  • 파일 경로 길이가 259 자보다 긴 파일을 제거하거나 무시하는 것은 해결책이 아닙니다.

  • 유니 코드 호환 솔루션 만 찾고 있습니다.

  • 이 응용 프로그램은 .NET Framework 4가 설치된 Windows 2008 / Vista 이상에서 실행됩니다.


¹ 놀랍게도 Microsoft Word 2007이 실패하여 플로피 드라이브가없는 컴퓨터에서 "플로피 디스크가 너무 작습니다"또는 4GB의 RAM이 남아있을 때 "RAM 메모리가 부족합니다"라고 불평합니다. "바이러스 백신 소프트웨어 [...]를 업데이트해야합니다." 언젠가는 마이크로 소프트 오피스와 같은 주요 제품에서 어리석은 의미없는 오류를 표시하는 것을 멈출까요?


1
요즘에도 모든 파일 이름이 8.3 형식 파일 이름에 매핑된다고 생각합니다. 사용할 수 없습니까? en.wikipedia.org/wiki/...
그랜트 토마스에게

6
8.3 형식 파일 이름도 260자를 초과 할 수 있으며 깊은 폴더 중첩 만 있으면됩니다.
David Heffernan 2011 년

1
8.3 이름 생성을 비활성화 할 수 있습니다 (I / O 오버 헤드를 추가하기 때문에 원할 수 있음). 따라서 8.3이 존재하는지 확신 할 수 없습니다. 을 참조하십시오 fsutil.exe 8dot3name.
Bacon Bits

답변:


81

.NET 4.6.2 솔루션

여기에\\?\C:\Verrrrrrrrrrrry long path 설명 된 구문을 사용 하십시오. .

.NET Core 솔루션

프레임 워크가 긴 경로 구문을 추가하기 때문에 작동합니다.

.NET 4.6.2 이전 솔루션

또한 P / Invoke와 함께 긴 경로 구문 과 Win32 API 함수의 유니 코드 버전을 사용하십시오. 에서 파일, 경로 및 네임 스페이스 명명 :

Windows API에는 최대 총 경로 길이가 32,767자인 확장 길이 경로를 허용하는 유니 코드 버전도있는 많은 함수가 있습니다. 이 유형의 경로는 각각 GetVolumeInformation 함수의 lpMaximumComponentLength 매개 변수에 반환 된 값까지 백 슬래시로 구분 된 구성 요소로 구성됩니다 (이 값은 일반적으로 255 자). 확장 길이 경로를 지정하려면 \\?\접두사를 사용하십시오 . 예 : \\?\D:\very long path.

이 Microsoft 지원 페이지를 읽는 것도 흥미로울 수 있습니다.

BCL 팀 블로그의 Kim Hamilton이 .NET의 Long Paths에 대한 매우 광범위한 설명 에는 이러한 경로를 처리하는 데 몇 가지 장애가 나열되어 있는데, 이것이이 구문이 여전히 .NET에서 직접 지원되지 않는 이유입니다.

우리가 과거에 긴 경로를 추가하기를 꺼려했던 몇 가지 이유와 우리가 여전히 그것에 대해주의하는 이유가 있습니다 <...>.

<...> \\?\접두사는 긴 경로를 가능하게 할뿐만 아니라; Windows API에 의한 최소한의 수정으로 경로가 파일 시스템에 전달되도록합니다. 결과적으로 \\?\후행 공백 제거, '.'확장을 포함하여 Windows API에서 수행하는 파일 이름 정규화를 해제합니다. 및 '..', 상대 경로를 전체 경로로 변환하는 등. <...>

<...> \\?\접두사가있는 긴 경로 는 대부분의 파일 관련 Windows API에서 사용할 수 있지만 모든 Windows API는 사용할 수 없습니다. 예를 들어, 파일 이름이 MAX_PATH보다 길면 LoadLibrary <...>가 실패합니다. <...> Windows API 전체에 유사한 예가 있습니다. 일부 해결 방법이 있지만 사례별로 적용됩니다.

또 다른 요소 <...>는 다른 Windows 기반 응용 프로그램 및 Windows 셸 자체 <...>와의 호환성입니다.

이 문제가 점점 보편화되고 있기 때문에 <...> Microsoft 전체에서이 문제를 해결하기위한 노력이 있습니다. 사실 적시에 Vista 플러그를 사용하면 MAX_PATH 제한에 도달 할 가능성을 줄이는 몇 가지 변경 사항을 알 수 있습니다. 많은 특수 폴더 이름이 단축되었으며 더 흥미롭게도 셸은 자동 경로 축소 기능을 사용하고 있습니다. <...>을 사용하여 260 자로 압축합니다.


경고 : .NET Framework가 이러한 종류의 경로 구문을 지원하지 않을 수도 있으므로 Windows API를 직접 호출해야 할 수도 있습니다.


예, 3.5는 이런 종류의 경로를 지원하지 않았습니다. 4.0이 추가 한 것 같지 않습니다.
Jamie Penney 2011 년

3
예, 당신은 것입니다 P / 호출은 Win32 API 함수를 필요로하고 .NET 응용 프로그램에서 직접 호출합니다. .NET의 내부 배관 (특히 PathHelper클래스)은 경로의 유효성을 검사하고 MAX_PATH(260) 자 이상이면 예외를 throw합니다 .
Cody Gray

12
@AmaniKilumanga : 그럼 당신의 길은 기본적으로 6000 단어 에세이이고 시스템은 그것을 처리 할 수 ​​없습니다.
user541686

1
@denahiro : 아마 당신은 ... 그것은 거기에 넣어 이전의 사람이 (내가하지 않았다)했다 ... 편집 그것에 자유를 취할 수
user541686

1
매우 정확함-\\? \는 4.6.1에서는 작동하지 않지만 4.6.2에서는 작동합니다.
jw_

33

나는 그 문제를 해결하기 위해 내 자신 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에 대한 해당 링크를 포함 할 수 있습니다.
Arseni Mourzenko 16.16.16

13
이것은 내 개인 TFS 저장소 (visualstudio.com)에있는 더 큰 프로젝트의 몇 가지 클래스에 불과합니다. 긴 경로에 대한 지원이 누락 된 소프트웨어 문제를 자주 볼 수 있다고 생각했습니다 (길이가 259 개를 넘으면 TFS 2013도 실패합니다 ...). 하지만 그렇습니다. 앞으로 이와 같은 작업을 수행 할 수 있습니다 (예 :이 게시물이 많은 표를 얻은 경우 :)).
Wolf5

이 솔루션은 나를 위해 작동하지만 InternalGetDirectories 기능에서 AllDirectories 옵션이 선택되고 검색 패턴이 하위 디렉토리 목록에서 찾을 수없는 경우 재귀가 작동하지 않습니다 . ThrowWin32Exception (); 줄을 바꿔야했습니다 . 의 종류에 의해 (긴 여기에 포함 할 수에 코드,하지만 매우 유사하기 패턴으로 "*"사전을 검색 기능의 코드를).
Alex

ZetaLongPaths nuget / github 프로젝트를 시도했지만 파일을 복사하면 어떤 이유로 든 손상된 파일이 생성되어 지금 시도했습니다. 나는 또한 이러한 pinvoke 패턴을 사용한다고 생각하지만 당신은 결코 알지 못합니다!
fartwhif

감사합니다. 그러나 참고로 대상 경로가 상대 경로 ( ".. \ Fu \ Bar.txt")이면 작동하지 않습니다. 나는 그것을 절대 경로로 강제하여 내 편에서 고쳤다.
Tipx

22

긴 파일 이름 문제를 극복하기 위해 Microsoft TechNet의 .NET Framework 4 기반 라이브러리 인 Delimon 라이브러리를 사용해 볼 수 있습니다.

Delimon.Win32.I O 라이브러리 (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 자로 구성됨). 일반적으로 표준 .NET 라이브러리 에서 System.IO.PathTooLongException 오류가 발생합니다.


4
260자를 초과하는 경로에 대한 AlphaFS 라이브러리 도 있습니다 .
Mark G


5

내가 작성중인 응용 프로그램에서이 문제가 한 번 발생했습니다. 260 자 제한에 가까워 졌을 때 네트워크 드라이브를 전체 경로의 일부 세그먼트에 즉시 매핑하여 전체 경로 + 파일 이름의 길이를 크게 줄였습니다. 정말 우아한 솔루션은 아니지만 작업을 완료했습니다.



2

GetFileAttributesEx에 대한 MSDN 참조는 말합니다 :

이 함수의 ANSI 버전에서 이름은 MAX_PATH 문자로 제한됩니다. 이 제한을 32,767 와이드 문자로 확장하려면 함수의 유니 코드 버전을 호출하고 경로 앞에 "\\? \"를 추가하십시오. 자세한 내용 은 파일 이름 지정을 참조하십시오 .

따라서 GetFileAttributesExW를 사용하고 경로 앞에 "\\? \"를 붙여야합니다.


귀하의 인용문은 정확하지만 약간 오해의 소지가 있습니다.이 제한은 ANSI 버전과 관련이 없습니다 (유니 코드 버전에서도 제한됨).
user541686 2011-04-04

1
제한을 확장하려면 유니 코드 버전과 접두사를 모두 사용해야 함을 매우 명확하게 설명합니다.
lunixbochs 2011 년

2

다음과 같이 구성 파일을 업데이트하십시오.

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

0

Robocopy를 사용하는 별도의 프로세스를 만드는 것도 여기에 설명 된 솔루션 입니다. Windows 8.1에서 경로 이름이 255 자 이상인 폴더 / 파일을 이동하는 방법은 무엇입니까?

  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();
        }

에서 보는 바와 같이 : 파일 로보 복사 및 프로세스를 사용하여 복사

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.