2011-05-25 72 views
5

因此,我正在編寫一個程序,用於掃描計算機上的重複文件,因爲我看過的程序非常慢,並且/或者內存耗盡,但是當我嘗試整個驅動器時遇到了PathTooLongException。在閱讀PathTooLongException in C# code後,我對以下兩個問題感到好奇。如何克服PathTooLongException?

  1. 如果我每次更改關卡時切換當前目錄,會傷害我的表現嗎?

  2. 有沒有更好的方式來獲得所有文件的目錄結構(也許通過調用像tree.exe然後解析)?

+0

Downvoter照顧對此有何評論? – soandos 2011-05-27 14:01:28

+0

掃描整個文件系統的最佳實踐似乎是單獨的上下文切換 - 在.NET中,這可能看起來像一個新的線程 - 至少在第一個級別,如果不是第二個甚至第三個。也就是說,爲每個根文件夾旋轉一個線程,在該線程中更改一次目錄,然後繼續。如果你很聰明,只有在路徑長度超過閾值時才能自適應地旋轉線程,然後在那裏改變目錄並有效地重置路徑長度容差。 – ssamuel 2011-08-26 13:29:44

+0

如何:通過目錄樹迭代(C#編程指南) http://msdn.microsoft.com/en-us/library/bb513869.aspx – JamieSee 2011-12-27 23:25:30

回答

3

看到這個圖書館!

.NET Base Class Libraries : Long Path

+0

The Kim Hamilton Long Path博客系列也值得一看,以便更好地理解問題:http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx – AFract 2014-11-18 15:18:08

1

或自己做,

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

我還沒有證實此代碼,顯然不是所有的類型定義,但它應該爲我們指出了正確的方向。

0

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