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
Accessing UI Controls in Task.Run with Async/Await on Winforms
How to Have a Variable Number of Generic Parameters
How to Create a New Operator in C#
Convert Anonymous Type to Class
Encoding Trouble with Httpwebresponse
Invoking Powershell Cmdlets from C#
C#: How to Take a Screenshot of a Portion of Screen
Dictionary <String,String> Map to an Object Using Automapper
Update App.Config System.Net Setting at Runtime
Detect Swipe Gesture Direction
Why Is Double.Nan Not Equal to Itself
What Is the Easiest Way to Do Inter Process Communication in C#
Questions About Entity Framework Context Lifetime
String Interpolation with Variable Content in C#
Synchronizationlockexception on Monitor.Exit When Using Await