2011-05-25 12 views
5

así que estoy escribiendo un programa que analiza la existencia de archivos duplicados en un equipo como los programas que he visto son muy lento, y/o la memoria cerdos, pero yo estaba corriendo en una PathTooLongException cuando traté de toda la unidad. Después de leer PathTooLongException in C# code comencé a sentir curiosidad por las siguientes dos preguntas.Cómo superar PathTooLongException?

  1. ¿Lastimaría mi rendimiento si tuviera que cambiar mi directorio actual cada vez que cambiara de nivel?

  2. ¿Existe alguna forma mejor de obtener la estructura de directorios de todos los archivos (quizás llamando a algo como tree.exe y luego analizando eso)?

+0

cuidado Downvoter comentar? – soandos

+0

Las mejores prácticas para la digitalización de los sistemas de ficheros enteros parece ser interruptores contexto individual - en .NET, que probablemente va a parecer un nuevo hilo - por lo menos el primer nivel si no es segunda o incluso tercera. Es decir, gire un hilo por cada carpeta raíz, cambie el directorio una vez en ese hilo, luego continúe. Si eres inteligente, puede girar de forma adaptativa un hilo sólo cuando la longitud del trayecto cruza un umbral, a continuación, cambiar el directorio allí y efectivamente restablecer su tolerancia a la longitud del trayecto. – ssamuel

+0

Cómo iterar a través de un árbol de directorios (Guía de programación de C#) http://msdn.microsoft.com/en-us/library/bb513869.aspx – JamieSee

Respuesta

3

¡Vea esta biblioteca!

.NET Base Class Libraries : Long Path

+0

La serie del blog Path Kim Hamilton Long también Worths un vistazo a comprender mejor la tema: http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx – AFract

1

o hágalo usted mismo,

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

no he verificado el código y, obviamente, no están definidos todos los tipos pero nos debería apuntar en la dirección correcta.

0

pura C#, pero necesita la optimización dará a la gente una ventaja inicial sin necesidad de utilizar una biblioteca externa o p/invocando ..

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); 
     } 
    } 
} 
Cuestiones relacionadas