Multicast Delegate of Type Func (With Return Value)

DirectoryInfo.EnumerateFiles(...) causes UnauthorizedAccessException (and other exceptions)

In order to solve this problem, I have created a replacement File System Enumerator. Although it may not be perfect, it performs fairly quickly and traps the two exceptions that I have run into. It will find any directories or files that match the search pattern passed to it.

// This code is public domain
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using log4net;

public class FileSystemEnumerable : IEnumerable<FileSystemInfo>
{
private ILog _logger = LogManager.GetLogger(typeof(FileSystemEnumerable));

private readonly DirectoryInfo _root;
private readonly IList<string> _patterns;
private readonly SearchOption _option;

public FileSystemEnumerable(DirectoryInfo root, string pattern, SearchOption option)
{
_root = root;
_patterns = new List<string> { pattern };
_option = option;
}

public FileSystemEnumerable(DirectoryInfo root, IList<string> patterns, SearchOption option)
{
_root = root;
_patterns = patterns;
_option = option;
}

public IEnumerator<FileSystemInfo> GetEnumerator()
{
if (_root == null || !_root.Exists) yield break;

IEnumerable<FileSystemInfo> matches = new List<FileSystemInfo>();
try
{
_logger.DebugFormat("Attempting to enumerate '{0}'", _root.FullName);
foreach (var pattern in _patterns)
{
_logger.DebugFormat("Using pattern '{0}'", pattern);
matches = matches.Concat(_root.EnumerateDirectories(pattern, SearchOption.TopDirectoryOnly))
.Concat(_root.EnumerateFiles(pattern, SearchOption.TopDirectoryOnly));
}
}
catch (UnauthorizedAccessException)
{
_logger.WarnFormat("Unable to access '{0}'. Skipping...", _root.FullName);
yield break;
}
catch (PathTooLongException ptle)
{
_logger.Warn(string.Format(@"Could not process path '{0}\{1}'.", _root.Parent.FullName, _root.Name), ptle);
yield break;
} catch (System.IO.IOException e)
{
// "The symbolic link cannot be followed because its type is disabled."
// "The specified network name is no longer available."
_logger.Warn(string.Format(@"Could not process path (check SymlinkEvaluation rules)'{0}\{1}'.", _root.Parent.FullName, _root.Name), e);
yield break;
}

_logger.DebugFormat("Returning all objects that match the pattern(s) '{0}'", string.Join(",", _patterns));
foreach (var file in matches)
{
yield return file;
}

if (_option == SearchOption.AllDirectories)
{
_logger.DebugFormat("Enumerating all child directories.");
foreach (var dir in _root.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
{
_logger.DebugFormat("Enumerating '{0}'", dir.FullName);
var fileSystemInfos = new FileSystemEnumerable(dir, _patterns, _option);
foreach (var match in fileSystemInfos)
{
yield return match;
}
}
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

The usage is fairly simple.

//This code is public domain
var root = new DirectoryInfo(@"c:\wherever");
var searchPattern = @"*.txt";
var searchOption = SearchOption.AllDirectories;
var enumerable = new FileSystemEnumerable(root, searchPattern, searchOption);

People are free to use it if they find it useful.

Directory.EnumerateFiles = UnauthorizedAccessException

Ths issue with the above answer is that is does not take care of exception in sub directories. This would be a better way to handling those exceptions so you get ALL files from ALL subdirectories except those with threw an access exception:

    /// <summary>
/// A safe way to get all the files in a directory and sub directory without crashing on UnauthorizedException or PathTooLongException
/// </summary>
/// <param name="rootPath">Starting directory</param>
/// <param name="patternMatch">Filename pattern match</param>
/// <param name="searchOption">Search subdirectories or only top level directory for files</param>
/// <returns>List of files</returns>
public static IEnumerable<string> GetDirectoryFiles(string rootPath, string patternMatch, SearchOption searchOption)
{
var foundFiles = Enumerable.Empty<string>();

if (searchOption == SearchOption.AllDirectories)
{
try
{
IEnumerable<string> subDirs = Directory.EnumerateDirectories(rootPath);
foreach (string dir in subDirs)
{
foundFiles = foundFiles.Concat(GetDirectoryFiles(dir, patternMatch, searchOption)); // Add files in subdirectories recursively to the list
}
}
catch (UnauthorizedAccessException) { }
catch (PathTooLongException) {}
}

try
{
foundFiles = foundFiles.Concat(Directory.EnumerateFiles(rootPath, patternMatch)); // Add files from the current directory
}
catch (UnauthorizedAccessException) { }

return foundFiles;
}

Excepting and ignoring system files isn't working for me

Continuing execution after catching an exception

The problem ultimately is that the program closes after excepting the error.

So your real problem is that the program exits after the catch-block?

Well, the reason your problem does not continue execution if an exception is caught is that you don't have any code after the last catch-block, so the program exits because there is nothing more to do.

See this simplified example of your code

public static void Main(string[] args)
{
try
{
throw new UnauthorizedAccessException("Cannot access this path.");
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine($"I am not continuing. Reason: {e.Message}.");
}

// Important, otherwise we would simply exit.
Console.WriteLine("Or look, I am in fact continuing :)"); }

which prints

I am not continuing. Reason: Cannot access this path.

Or look, I am in fact continuing :)

so, in order for your program to not exit after the catch-block(s) and continue execution, you need to put some code after them.



Getting all files in a folder and its subfolders

As the OP has explained in a comment, the real real problem is they want to iterate over every file in a particular folder and its subfolders, but don't want to stop if they are not authorized to access a file or folder.

Therefore, I came up with the following method

public static IEnumerable<string> GetAllAccessibleFilesIn(string rootDirectory, string searchPattern = "*.*")
{
List<string> files = new List<string>();

try
{
files.AddRange(Directory.GetFiles(rootDirectory, searchPattern, SearchOption.TopDirectoryOnly));

foreach (string directory in Directory.GetDirectories(rootDirectory))
{
files.AddRange(GetAllAccessibleFilesIn(directory, searchPattern));
}
}
catch (UnauthorizedAccessException)
{
// Don't do anything if we cannot access a file.
}

return files;
}

which, when used like

IEnumerable<string> allFiles = GetAllAccessibleFilesIn(@"C:\");

yields every accessible file in either C:\ or any of its subfolders.

UnauthorizedAccessException cannot resolve Directory.GetFiles failure

In order to gain control on the level that you want, you should probably probe one directory at a time, instead of a whole tree. The following method populates the given IList<string> with all files found in the directory tree, except those where the user doesn't have access:

// 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.
}
}

VB.NET directory.EnumerateFiles: Access to path X is denied

I tried posting this as a comment but it's pretty messy. This should work, basically it tries to create a directory out of every subdirectory in the C drive, and if it fails with the unauthorized access exception, it moves on to the next subdirectory.

For Each directory In New DirectoryInfo("C:\").GetDirectories()
Try
For Each file In directory.GetFiles("*", SearchOption.AllDirectories)
Try
TextBox1.Text += Environment.NewLine + file.Name
Catch ex As Exception
'MsgBox(ex.Message)
Continue For
End Try
Next
Catch ex As Exception
'MsgBox(ex.Message)
Continue For
End Try
Next

System.UnauthorizedAccessException when getting files

Your app could not have access rights to some folders, for others you can use the following code:

void DiscoverDirs(string where, List<string> files, Func<FileInfo, bool> filter)
{
try
{
var di = new DirectoryInfo(where);
files.AddRange(di.EnumerateFiles().Where(filter).Select(x => x.FullName));

foreach (var dir in Directory.GetDirectories(where))
{
DiscoverDirs(dir, files, filter);
}
}
catch
{
// no access fo this dir, ignore
}
}

Usage:

DateTime From = DateTime.Now.AddHours(-24);
DateTime To = DateTime.Now;
var ScanSize = 5*1024*1024;

var list = new List<string>();
DiscoverDirs(@"C:\", list,
file => file.LastWriteTime >= From & file.LastWriteTime <= To && file.Length >= ScanSize);

foreach (string name in list)
{
FileInfo file = new FileInfo(name);
string fullname = file.FullName;

Console.WriteLine(file.FullName + " ; " + "last changed at " + " ; " + file.LastWriteTime.ToString());
}


Related Topics



Leave a reply



Submit