2009-09-08 11 views
22

Directory.GetFiles method schlägt bei der ersten Begegnung mit einem Ordner fehl, zu dem es keine Zugriffsrechte hat.UnauthorizedAccessException kann Directory.GetFiles-Fehler nicht beheben

Die Methode löst eine UnauthorizedAccessException aus (die abgefangen werden kann), aber zu dem Zeitpunkt ist die Methode bereits fehlgeschlagen/beendet.

Der Code I bin mit unten aufgeführt:

try 
{ 
    // looks in stated directory and returns the path of all files found     
    getFiles = Directory.GetFiles(
     @directoryToSearch, 
     filetype, 
     SearchOption.AllDirectories);    
} 
catch (UnauthorizedAccessException) 
{ 
} 

Soweit ich weiß, gibt es keine Möglichkeit vorher zu prüfen, ob ein bestimmte Ordner hat Zugriffsrechte definiert.

In meinem Beispiel suche ich auf einer Festplatte über ein Netzwerk und wenn ich auf einen Root-Zugriff nur Ordner stößt, schlägt mein Programm fehl.

Antwort

17

Um die Kontrolle über die gewünschte Ebene zu erlangen, sollten Sie wahrscheinlich ein Verzeichnis nach dem anderen durchsuchen, anstatt einen ganzen Baum. Die folgende Methode Füllt mit allen Dateien die angegebene IList<string> im Verzeichnisbaum gefunden, mit Ausnahme derjenigen, bei denen der Benutzer keinen Zugriff hat:

// using System.Linq 
private static void AddFiles(string path, IList<string> files) 
{ 
    try 
    { 
     Directory.GetFiles(path) 
      .ToList() 
      .ForEach(s => files.Add(s)); 

     Directory.GetDirectories(path) 
      .ToList() 
      .ForEach(s => AddFiles(s, files)); 
    } 
    catch (UnauthorizedAccessException ex) 
    { 
     // ok, so we are not allowed to dig into that directory. Move on. 
    } 
} 
+0

Ich werde dies auszuprobieren und zu Ihnen zurück. Können Sie bitte erklären, was der '=> Operator tut? Danke – Ric

+3

@Ric: '=>' ist der Lambda-Operator. Sie können über Lambda-Ausdrücke in C# hier lesen: http://msdn.microsoft.com/en-us/library/bb397687.aspx –

+0

Perfekt! Cheers Fredrik – Ric

3

In .NET 4 dies viel einfacher wird, sehen http://msdn.microsoft.com/en-us/library/dd997370.aspx

+2

Ich bin mir nicht sicher, das ist genau richtig - Directory.EnumerateFiles verursacht immer noch die gleiche Ausnahme ausgelöst. –

+5

Aber Sie können es fangen und es pro Stück behandeln. –

4

.Net 4's Directory.EnumerateFiles funktioniert, aber Sie müssen vorsichtig sein, wie Sie das Aufzählbare evaluieren und diesen Teil im try-catch-Block tun. Das größte Problem ist sicherzustellen, dass Sie die Verarbeitung bei der ersten Ausnahme nicht stoppen (was ich denke, Antwort https://stackoverflow.com/a/1393219/89584 oben hat dieses Problem, bitte korrigieren Sie mich, wenn ich falsch liege).

folgende Arbeiten und gibt Ihnen einen Enumerable, so dass Sie die gesamte Datei bewerten müssen Baum nicht, wenn Sie für das erste Spiel suchen, usw.

private IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse) 
{ 
    IEnumerable<String> emptyList = new string[0]; 

    if (File.Exists(path)) 
    return new string[] { path }; 

    if (!Directory.Exists(path)) 
    return emptyList; 

    var top_directory = new DirectoryInfo(path); 

    // Enumerate the files just in the top directory. 
    var files = top_directory.EnumerateFiles(file_pattern); 
    var filesLength = files.Count(); 
    var filesList = Enumerable 
      .Range(0, filesLength) 
      .Select(i => 
      { 
       string filename = null; 
       try 
       { 
       var file = files.ElementAt(i); 
       filename = file.FullName; 
       } 
       catch (UnauthorizedAccessException) 
       { 
       } 
       catch (InvalidOperationException) 
       { 
        // ran out of entries 
       } 
       return filename; 
      }) 
      .Where(i => null != i); 

     if (!recurse) 
      return filesList; 

     var dirs = top_directory.EnumerateDirectories("*"); 
     var dirsLength = dirs.Count(); 
     var dirsList = Enumerable 
      .Range(0, dirsLength) 
      .SelectMany(i => 
      { 
       string dirname = null; 
       try 
       { 
       var dir = dirs.ElementAt(i); 
       dirname = dir.FullName; 
       return FindAccessableFiles(dirname, file_pattern, required_extension, recurse); 
       } 
       catch (UnauthorizedAccessException) 
       { 
       } 
       catch (InvalidOperationException) 
       { 
       // ran out of entries 
       } 

       return emptyList; 
      }) 

    return Enumerable.Concat(filesList, dirsList); 
} 

Verbesserungen der oben willkommen.

+0

Dieses Beispiel funktioniert nicht (var files = dirInfo.EnumerateFiles(). GetEnumerator();) löst den Fehler aus –

+0

@sjorsmiltenburg - behoben und aktualisiert. Ich habe das Problem gelöst und einen Workaround gefunden. Dann habe ich vergessen, meine Antwort zu aktualisieren. – Malcolm

+0

Dieser Code wird nicht wie vorgesehen funktionieren. Die 'EnumerateFiles' erzeugen ein 'FileSystemEnumerableIterator'-Objekt. Wenn die MoveNext-Methode beim Iterieren des Enumerators aufgerufen wird, wird der Iterator bei einer beliebigen 'IOException' entsorgt. '.ElementAt' klont Ihren Iterator und iteriert über alle vorherigen Elemente, bis der angeforderte Index erreicht ist. Dies bedeutet, dass alle diese Iterator-Klone in genau derselben unzugänglichen Datei fehlschlagen werden. Mit anderen Worten, der 'Enumerable.Range.Select {.ElementAt}' Trick macht nichts. – Paya

4

Dies ist eine Verbesserung zu Malcolms Antwort (http://stackoverflow.com/a/9831340/226181). Dies durchsucht alle logischen Laufwerke nach einem Dateivergleichsmuster und ignoriert die Verzeichnisse, auf die nicht zugegriffen werden kann.

static List<string> SearchFiles(string pattern) 
    { 
     var result = new List<string>(); 

     foreach (string drive in Directory.GetLogicalDrives()) 
     { 
      Console.WriteLine("searching " + drive); 
      var files = FindAccessableFiles(drive, pattern, true); 
      Console.WriteLine(files.Count().ToString() + " files found."); 
      result.AddRange(files); 
     } 

     return result; 
    } 

    private static IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse) 
    { 
     Console.WriteLine(path); 
     var list = new List<string>(); 
     var required_extension = "mp4"; 

     if (File.Exists(path)) 
     { 
      yield return path; 
      yield break; 
     } 

     if (!Directory.Exists(path)) 
     { 
      yield break; 
     } 

     if (null == file_pattern) 
      file_pattern = "*." + required_extension; 

     var top_directory = new DirectoryInfo(path); 

     // Enumerate the files just in the top directory. 
     IEnumerator<FileInfo> files; 
     try 
     { 
      files = top_directory.EnumerateFiles(file_pattern).GetEnumerator(); 
     } 
     catch (Exception ex) 
     { 
      files = null; 
     } 

     while (true) 
     { 
      FileInfo file = null; 
      try 
      { 
       if (files != null && files.MoveNext()) 
        file = files.Current; 
       else 
        break; 
      } 
      catch (UnauthorizedAccessException) 
      { 
       continue; 
      } 
      catch (PathTooLongException) 
      { 
       continue; 
      } 

      yield return file.FullName; 
     } 

     if (!recurse) 
      yield break; 

     IEnumerator<DirectoryInfo> dirs; 
     try 
     { 
      dirs = top_directory.EnumerateDirectories("*").GetEnumerator(); 
     } 
     catch (Exception ex) 
     { 
      dirs = null; 
     } 


     while (true) 
     { 
      DirectoryInfo dir = null; 
      try 
      { 
       if (dirs != null && dirs.MoveNext()) 
        dir = dirs.Current; 
       else 
        break; 
      } 
      catch (UnauthorizedAccessException) 
      { 
       continue; 
      } 
      catch (PathTooLongException) 
      { 
       continue; 
      } 

      foreach (var subpath in FindAccessableFiles(dir.FullName, file_pattern, recurse)) 
       yield return subpath; 
     } 
    } 
4

Ich weiß, dass dieses Themas alt ist, aber falls jemand stolpert dies und muss eine Antwort, ich erhielt eine rekursive Lösung hier:

public static List<string> GetAllAccessibleFiles(string rootPath, List<string> alreadyFound = null) 
    { 
     if (alreadyFound == null) 
      alreadyFound = new List<string>(); 
     DirectoryInfo di = new DirectoryInfo(rootPath); 
     var dirs = di.EnumerateDirectories(); 
     foreach (DirectoryInfo dir in dirs) 
     { 
      if (!((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)) 
      { 
       alreadyFound = GetAllAccessibleFiles(dir.FullName, alreadyFound); 
      } 
     } 

     var files = Directory.GetFiles(rootPath); 
     foreach (string s in files) 
     { 
      alreadyFound.Add(s);     
     } 

     return alreadyFound; 
    } 

Es gibt ein List<string> den vollständigen Pfad für alle enthalten Dateien, die sich in zugänglichen Verzeichnissen unterhalb des angegebenen Stammverzeichnisses befinden. Nennen Sie es wie folgt aus:

var files = GetAllAccessibleFiles(@"C:\myDirectory"); 

So ein Ergebnis wie folgt sein könnte:

C:\myDirectory\a\a.txt 
C:\myDirectory\a\b.mp3 
C:\myDirectory\b\a\a\foo.txt 
C:\myDirectory\b\b\b\hello.exe 
C:\myDirectory\b\c\bar.jpg 
C:\myDirectory\and\so\on.bar 
C:\myDirectory\a_file_in_root.bmp 

Hoffe, dass es jemand hilft!

+1

das spart meinen Tag! Danke, mein Herr! –

1
public string[] GetFilesFrom(string dir, string search_pattern, bool recursive) 
{ 
    List<string> files = new List<string>(); 

    string[] temp_files = new string[0]; 

    try { temp_files = Directory.GetFiles(dir, search_pattern, SearchOption.TopDirectoryOnly); } 
    catch { } 

    files.AddRange(temp_files); 

    if (recursive) 
    { 
     string[] temp_dirs = new string[0]; 

     try { temp_dirs = Directory.GetDirectories(dir, search_pattern, SearchOption.TopDirectoryOnly); } 
     catch { } 

     for (int i = 0; i < temp_dirs.Length; i++) 
      files.AddRange(GetFilesFrom(temp_dirs[i], search_pattern, recursive)); 
    } 

    return files.ToArray(); 
} 

Dies ist meine Lösung für dieses Problem. Einfach und ausfallsicher.

+0

In der Directory.GetDirectories-Zeile musste ich search_pattern durch "*" ersetzen, was sinnvoll ist, wenn man bedenkt, dass der Filter generell in den Dateien sein soll, nicht sowohl in den Dateien als auch in den Ordnern. –

0

Die einfachste Version:

IEnumerable<String> GetAllFiles(string path, string searchPattern) 
{ 
    return System.IO.Directory.EnumerateFiles(path, searchPattern).Union(
     System.IO.Directory.EnumerateDirectories(path).SelectMany(d => 
     { 
      try 
      { 
       return GetAllFiles(d, searchPattern); 
      } 
      catch (UnauthorizedAccessException e) 
      { 
       return Enumerable.Empty<String>(); 
      } 
     })); 
} 
Verwandte Themen