Как преодолеть PathTooLongException?

Поэтому я пишу программу, которая будет сканировать дубликаты файлов на компьютере, поскольку программы, которые я видел, работают очень медленно и / или занимают много памяти, но я столкнулся с PathTooLongException когда я пытался весь диск. После прочтения PathTooLongException в коде C# мне стало интересно узнать о следующих двух вопросах.

  1. Повредит ли это моей производительности, если я буду переключать свой текущий каталог каждый раз, когда меняю уровни?

  2. Есть ли лучший способ получить структуру каталогов всех файлов (возможно, вызвав что-то вроде tree.exe, а затем проанализировав его)?

3 ответа

Решение

Смотрите эту библиотеку!

.NET Base Class Libraries : Long Path

Или сделай это сам,

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

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

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

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FindClose(IntPtr hFindFile);

// Assume dirName passed in is already prefixed with \\?\
public static IEnumerable<string> EnumerateEntries(string directory)
{ 
    WIN32_FIND_DATA findData;
    IntPtr findHandle = FindFirstFile(dirName + @"\*", out findData);

    try
    {
        if (findHandle != INVALID_HANDLE_VALUE)
        {
            bool found;
            do
            {
                string currentFileName = findData.cFileName;

                // if this is a directory, find its contents
                if (((int)findData.dwFileAttributes &
                                FILE_ATTRIBUTE_DIRECTORY) != 0)
                {
                    if (currentFileName != "." && currentFileName != "..")
                    {
                        foreach(var child in FindFilesAndDirs(
                                Path.Combine(dirName, currentFileName))
                        {
                            yield return child;
                        }
                    }
                }

                yield return Path.Combine(dirName, currentFileName);

                // find next
                found = FindNextFile(findHandle, out findData);
            }
            while (found);
        }

    }
    finally
    {
        // close the find handle
        FindClose(findHandle);
    }
}

Я не проверял этот код, и, очевидно, не все типы определены, но он должен указывать нам правильное направление.

Чистый C#, нуждается в оптимизации, но даст людям преимущество без использования внешней библиотеки или вызова p/.

public static class DirectoryEx
{
    static char driveLetter;
    static string longPath;
    static List<string> directories;

    static DirectoryEx()
    {
        longPath = String.Empty;
    }

    private static char GetAvailableDrive()
    {
        var all = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().Reverse();
        var occupied = DriveInfo.GetDrives()
            .OrderByDescending(d => d.Name)
            .Select(d => (char)d.Name.ToUpper().First());

        var free = all.Except(occupied).First();

        return free;
    }

    public static List<string> GetDirectories(string path)
    {
        directories = new List<string>();

        // recursive call
        FindDirectories(path);

        return directories;
    }

    static void FindDirectories(string path)
    {
        try
        {
            foreach (var directory in Directory.GetDirectories(path))
            {
                var di = new DirectoryInfo(directory);

                if(!String.IsNullOrEmpty(longPath))
                    directories.Add(di.FullName.Replace(driveLetter + ":\\", longPath + "\\"));
                else
                    directories.Add(di.FullName);

                FindDirectories(di.FullName);
            }
        }
        catch (UnauthorizedAccessException uaex) { Debug.WriteLine(uaex.Message); }
        catch (PathTooLongException ptlex)
        {
            Debug.WriteLine(ptlex.Message);

            longPath = path;

            Task t = new Task(new Action(() =>
            {
                CreateVirtualDrive(longPath);
                FindDirectories(driveLetter + ":\\");
                DeleteVirtualDrive();

                longPath = String.Empty;
            }));

            if (!String.IsNullOrEmpty(longPath))
                t.RunSynchronously();
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message); 
        }
    }

    static void CreateVirtualDrive(string path)
    {
        driveLetter = GetAvailableDrive();

        Process.Start(new ProcessStartInfo() {
            FileName = "cmd.exe",
            WindowStyle = ProcessWindowStyle.Hidden,
            Arguments = String.Format("/c subst {0}: {1}", driveLetter.ToString(), path)
        });

        while (!DriveInfo.GetDrives().Select(d => d.Name.ToUpper().First()).Contains(driveLetter))
        {
            System.Threading.Thread.Sleep(1);
        }
    }

    static void DeleteVirtualDrive()
    {
        Process.Start(new ProcessStartInfo()
        {
            FileName = "cmd.exe",
            WindowStyle = ProcessWindowStyle.Hidden,
            Arguments = String.Format("/c subst {0}: /D", driveLetter.ToString())
        });

        while (DriveInfo.GetDrives().Select(d => d.Name.ToUpper().First()).Contains(driveLetter))
        {
            System.Threading.Thread.Sleep(1);
        }
    }
}
Другие вопросы по тегам