13

I am trying to get all files from folder this way:

try
{
    string[] files = Directory.GetFiles(folderBrowserDialog1.SelectedPath, "*.*", SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException)
{
    throw;
}

If my root folder contains a folder for which the user doesn't have permission to access, the UnauthorizedAccessException is caught and my array is empty and all the recursion failed.

How can I handle this case and insure that my code ignore locations without permission but add the files from location with permissions?

Ivaylo Slavov
  • 8,839
  • 12
  • 65
  • 108
user1710944
  • 1,419
  • 4
  • 16
  • 17

2 Answers2

17

see SafeFileEnumerator on this other post. I have used the SafeFileEnumerator code in the past with success. It prevents loosing the entire enumeration when you simply don't have access to a single file so you are still able to iterate through the files that you can access.

EDIT: The version I have is slightly different from the one I linked to so let me share the version I have.

public static class SafeFileEnumerator
{
    public static IEnumerable<string> EnumerateDirectories(string parentDirectory, string searchPattern, SearchOption searchOpt)
    {
        try
        {
            var directories = Enumerable.Empty<string>();
            if (searchOpt == SearchOption.AllDirectories)
            {
                directories = Directory.EnumerateDirectories(parentDirectory)
                    .SelectMany(x => EnumerateDirectories(x, searchPattern, searchOpt));
            }
            return directories.Concat(Directory.EnumerateDirectories(parentDirectory, searchPattern));
        }
        catch (UnauthorizedAccessException ex)
        {
            return Enumerable.Empty<string>();
        }
    }

    public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, SearchOption searchOpt)
    {
        try
        {
            var dirFiles = Enumerable.Empty<string>();
            if (searchOpt == SearchOption.AllDirectories)
            {
                dirFiles = Directory.EnumerateDirectories(path)
                                    .SelectMany(x => EnumerateFiles(x, searchPattern, searchOpt));
            }
            return dirFiles.Concat(Directory.EnumerateFiles(path, searchPattern));
        }
        catch (UnauthorizedAccessException ex)
        {
            return Enumerable.Empty<string>();
        }
    }
}

Example Usage:

foreach(string fileName in SafeFileEnumerator.EnumerateFiles(folderPath, "*" + extension, SearchOption.AllDirectories))
{
    //Do something with filename, store into an array or whatever you want to do.
}
Community
  • 1
  • 1
Aaron Hawkins
  • 2,611
  • 1
  • 20
  • 24
  • can you show me please an example how to use this class in order to get all files from dir with out errors ? – user1710944 Dec 19 '12 at 14:56
  • This example will skip any files that you do not have access to, but should return a list of files that are available to be read. Do not, however, make the assumption that you have write permissions. – Aaron Hawkins Dec 19 '12 at 15:15
  • 1
    This works perfectly, even in cases where inaccessible nested subdirectories in accessible parent subdirectories are. Which seems to screw most other solutions I found to the original question. – Lennart Oct 18 '13 at 15:43
  • When I call either EnumerateDirectories(@"C:\Windows", "*", SearchOption.AllDirectories) or EnumerateFiles(@"C:\Windows", "*", SearchOption.AllDirectories), the enumeration starts at "C:\" and not "C:\Windows". – Mort Aug 26 '19 at 18:08
3

You can use FileSystemInfo objects and recursion to accomplish this:

static List<string> files = new List<string>();

static void MyMethod() {
    DirectoryInfo dir = new DirectoryInfo(folderBrowserDialog1.SelectedPath);
    ProcessFolder(dir.GetFileSystemInfos());
}

static void ProcessFolder(IEnumerable<FileSystemInfo> fsi) {
    foreach (FileSystemInfo info in fsi) {

        // We skip reparse points 
        if ((info.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) {
            Debug.WriteLine("Skipping reparse point '{0}'", info.FullName);
            return;
        }

        if ((info.Attributes & FileAttributes.Directory) == FileAttributes.Directory) {
            // If our FileSystemInfo object is a directory, we call this method again on the
            // new directory.
            try {
                DirectoryInfo dirInfo = (DirectoryInfo)info;
                ProcessFolder(dirInfo.GetFileSystemInfos());
            }
            catch (Exception ex) {
                // Skipping any errors
                // Really, we should catch each type of Exception - 
                // this will catch -any- exception that occurs, 
                // which may not be the behavior we want.
                Debug.WriteLine("{0}", ex.Message);
                break;
            }
        } else {
            // If our FileSystemInfo object isn't a directory, we cast it as a FileInfo object, 
            // make sure it's not null, and add it to the list.
            var file = info as FileInfo;
            if (file != null) {
                files.Add(file.FullName);
            }
        }
    }
}

MyMethod takes your selected path and creates a DirectoryInfo object with it, and then calls the GetFileSystemInfos() method and passes that to the ProcessFolder method.

The ProcessFolder method looks at each FileSystemInfo object, skips the reparse points, and if the FileSystemInfo object is a directory, calls the ProcessFolder method again - otherwise, it casts the FileSystemInfo object as a FileInfo object, makes sure it's not null, and then adds the filename to the list.

More reading:

Jared Harley
  • 8,219
  • 4
  • 39
  • 48
  • 1
    +1 This is the only code sample that worked for me on an iOS device (Xamarin/Mono), to avoid the app crashing completely on an UnauthorizedAccessException for the Snapshots directory. – Nate Cook Feb 25 '16 at 20:16