Reputation: 2057
While enumerating I would like to Skip/Ignore exception.
I try to add a try catch in the selector:
static IEnumerable<string> GetSafeAllFiles
(string path, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
{
return Directory.EnumerateFiles(path, searchPattern, searchOption)
.Select(f =>
{
try
{
return f;
}
catch (Exception e)
{
return string.Empty;
}
});
}
I try using a solution from an accepted answer:
var test23 = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
.SkipExceptions().Take(100);
With no result, as it will stop after the first error. So I try to implement my own :
static IEnumerable<string> test12(string path, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
if (string.IsNullOrEmpty(searchPattern))
{
throw new ArgumentNullException("searchPattern");
}
Queue<string> stillToProcess = new Queue<string>(new[] { path });
foreach (var dir in Directory.EnumerateDirectories(path))
{
stillToProcess.Enqueue(dir);
}
while (stillToProcess.Count > 0)
{
string currentPath = stillToProcess.Dequeue();
IEnumerable<string> ret = Enumerable.Empty<string>();
try
{
ret = Directory.EnumerateFiles(currentPath, searchPattern);
}
catch (UnauthorizedAccessException e)
{ }
// yield! keyword
foreach (var i in ret) { yield return i; }
}
yield break;
}
But it skip directory if there is one error. When I want to skip only the error file.
In order to test, possible solution please do it on c:\$Recycle.Bin
, as it's the easiest source of UnauthorizedAccessException
.
Upvotes: 5
Views: 1988
Reputation: 3061
Update: In newer DotNet Versions you can specify the Enumeration Options like this:
// SearchOption.TopDirectoryOnly
public static EnumerationOptions Compatible => new EnumerationOptions
{
MatchType = MatchType.Win32,
AttributesToSkip = (FileAttributes)0,
IgnoreInaccessible = false
};
// SearchOption.AllDirectories
public static EnumerationOptions CompatibleRecursive => new EnumerationOptions
{
RecurseSubdirectories = true,
MatchType = MatchType.Win32,
AttributesToSkip = (FileAttributes)0,
IgnoreInaccessible = false
};
Here you can set IgnoreInaccessible
for the EnumerationOptions.
Pass the EnumerationOptions to EnumerateFiles
as the 2nd Parameter to ignore errors.
Original Solution:
public static IEnumerable<FileInfo> EnumerateFilesIgnoreErrors(IEnumerable<FileInfo> files)
{
using (var e1 = files.GetEnumerator())
{
while (true)
{
FileInfo cur = null;
try
{
// MoveNext() can throw an Exception
if (! e1.MoveNext())
break;
cur = e1.Current;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
if (cur != null)
{
yield return cur;
}
}
}
}
Use like this:
var myFiles = new DirectoryInfo(@"C:\")
.EnumerateFiles("*", SearchOption.AllDirectories);
foreach (FileInfo fi in EnumerateFilesIgnoreErrors(myFiles))
{
Debug.WriteLine(fi.Name);
}
You can also use it like this:
var myList = EnumerateFilesIgnoreErrors(myFiles).ToList();
Works on "c:\$Recycle.Bin" etc
Ignores UnauthorizedAccessException
Upvotes: 3
Reputation: 249
Some times ago, I wrote this piece of code, to get all files in a directory and all subdirectories. I would go this way to achieve your goal. You have to customize it to work with search pattern and search options. Otherwise, the CanRead method could be solve your problem, by preventing the code to throw exceptions overall.
public class DirectoryAnalyser
{
private List<string> _files;
private int _directoryCounter;
public async Task<List<string>> GetFilesAsync(string directory, CancellationTokenSource cancellationToken, IProgress<DirectoryAnalyserProgress> progress)
{
this._files = new List<string>();
this._directoryCounter = 0;
await this.GetFilesInternalAsync(directory, cancellationToken, progress);
return this._files;
}
private async Task GetFilesInternalAsync(string directory, CancellationTokenSource cancellationToken, IProgress<DirectoryAnalyserProgress> progress)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
if (!this.CanRead(directory))
{
return;
}
this._files.AddRange(Directory.GetFiles(directory));
this._directoryCounter++;
progress?.Report(new DirectoryAnalyserProgress()
{
DirectoriesSearched = this._directoryCounter,
FilesFound = this._files.Count
});
foreach (var subDirectory in Directory.GetDirectories(directory))
{
await this.GetFilesInternalAsync(subDirectory, cancellationToken, progress);
}
}
public bool CanRead(string path)
{
var readAllow = false;
var readDeny = false;
var accessControlList = Directory.GetAccessControl(path);
if (accessControlList == null)
{
return false;
}
var accessRules = accessControlList.GetAccessRules(true, true, typeof(SecurityIdentifier));
if (accessRules == null)
{
return false;
}
foreach (FileSystemAccessRule rule in accessRules)
{
if ((FileSystemRights.Read & rule.FileSystemRights) != FileSystemRights.Read)
{
continue;
}
if (rule.AccessControlType == AccessControlType.Allow)
{
readAllow = true;
}
else if (rule.AccessControlType == AccessControlType.Deny)
{
readDeny = true;
}
}
return readAllow && !readDeny;
}
}
public class DirectoryAnalyserProgress
{
public int FilesFound { get; set; }
public int DirectoriesSearched { get; set; }
}
Upvotes: 0