Access to the path is denied when using Directory.GetFiles(...) [duplicate] - c#

This question already has answers here:
Ignore folders/files when Directory.GetFiles() is denied access
(8 answers)
UnauthorizedAccessException cannot resolve Directory.GetFiles failure [duplicate]
(6 answers)
Closed 2 years ago.
I'm running the code below and getting exception below. Am I forced to put this function in try catch or is there other way to get all directories recursively?
I could write my own recursive function to get files and directory. But I wonder if there is a better way.
// get all files in folder and sub-folders
var d = Directory.GetFiles(#"C:\", "*", SearchOption.AllDirectories);
// get all sub-directories
var dirs = Directory.GetDirectories(#"C:\", "*", SearchOption.AllDirectories);
"Access to the path 'C:\Documents and Settings\' is denied."

If you want to continue with the next folder after a fail, then yea; you'll have to do it yourself. I would recommend a Stack<T> (depth first) or Queue<T> (bredth first) rather than recursion, and an iterator block (yield return); then you avoid both stack-overflow and memory usage issues.
Example:
public static IEnumerable<string> GetFiles(string root, string searchPattern)
{
Stack<string> pending = new Stack<string>();
pending.Push(root);
while (pending.Count != 0)
{
var path = pending.Pop();
string[] next = null;
try
{
next = Directory.GetFiles(path, searchPattern);
}
catch { }
if(next != null && next.Length != 0)
foreach (var file in next) yield return file;
try
{
next = Directory.GetDirectories(path);
foreach (var subdir in next) pending.Push(subdir);
}
catch { }
}
}

You can set the program so you can only run as administrator.
In Visual Studio:
Right click on the Project -> Properties -> Security -> Enable ClickOnce Security Settings
After you clicked it, a file will be created under the Project's properties folder called app.manifest once this is created, you can uncheck the Enable ClickOnce Security Settings option
Open that file and change this line :
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
to:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
This will make the program require administrator privileges, and it will guarantee you have access to that folder.

It has already been pointed out that you need to do it yourself so I thought I'd share my solution which avoids collections along the way. It should be noted that this will ignore all errors not just AccessDenied. To change that you can just make the catch blocks more specific.
IEnumerable<string> GetFiles(string folder, string filter, bool recursive)
{
string [] found = null;
try
{
found = Directory.GetFiles(folder, filter);
}
catch { }
if (found!=null)
foreach (var x in found)
yield return x;
if (recursive)
{
found = null;
try
{
found = Directory.GetDirectories(folder);
}
catch { }
if (found != null)
foreach (var x in found)
foreach (var y in GetFiles(x, filter, recursive))
yield return y;
}
}

Well, you either avoid the directories for which you don't have permissions, or you don't but then respond gracefully when access is denied.
If you choose the first option, you will need to make sure that you know what directories they are, and also that the permissions for the thread's identity do not change. This is tricky and prone to error; I wouldn't recommend it for a production-quality system.
The second option looks more appropriate. Use a try/catch block and skip any "forbidden" directories.

You can achieve this by using EnumerationOptions for the third argument. This class provides a property called IgnoreInaccessible which toggles whether an exception will be thrown if an inaccessbile file/folder is encountered.
Other properties related to searching are available too, see: EnumerationOptions Class (System.IO)
Example:
var options = new EnumerationOptions()
{
IgnoreInaccessible = true
};
var files = Directory.GetFiles("mypath", "*.*", options);
foreach (var file in files)
{
// File related activities
}
Note: IgnoreAccessible is set to true by default, but I've included it in the example above for visibility purposes.

I know this question is somewhat old, but I had this same problem today and I found the following article that explains a 'folder recursion' solution in detail.
The article acknowledges the flaws of the GetDirectories() method... :
Unfortunately, this [using the GetDirectories() method] has problems. Key amongst these is that some of
the folders that you attempt to read could be configured so that the
current user may not access them. Rather than ignoring folders to
which you have restricted access, the method throws an
UnauthorizedAccessException. However, we can circumvent this problem
by creating our own recursive folder search code.
... and then introduces the solution in detail:
http://www.blackwasp.co.uk/FolderRecursion.aspx

This recursive method will return list of all files that are accessible in the folder.
static List<string> getFilesInDir(string dirPath)
{
List<string> retVal = new List<string>();
try
{
retVal = IO.Directory.GetFiles(dirPath, "*.*", IO.SearchOption.TopDirectoryOnly).ToList();
foreach (IO.DirectoryInfo d in new IO.DirectoryInfo(dirPath).GetDirectories("*", IO.SearchOption.TopDirectoryOnly))
{
retVal.AddRange(getFilesInDir(d.FullName));
}
}
catch (Exception ex)
{
//Console.WriteLine(dirPath);
}
return retVal;
}

Related

Directory.EnumerateDirectories : Only get back Directores with a special file in it [duplicate]

I wish to get list of all the folders/directories that has a particular file in it. How do I do this using C# code.
Eg: Consider I have 20 folders of which 7 of them have a file named "abc.txt". I wish to know all folders that has the file "abc.txt".
I know that we can do this by looking thru all the folders in the path and for each check if the File.Exists(filename); But I wish to know if there is any other way of doing the same rather than looping through all the folder (which may me little time consuming in the case when there are many folders).
Thanks
-Nayan
I would use the method EnumerateFiles of the Directory class with a search pattern and the SearchOption to include AllDirectories. This will return all files (full filename including directory) that match the pattern.
Using the Path class you get the directory of the file.
string rootDirectory = //your root directory;
var foundFiles = Directory.EnumerateFiles(rootDirectory , "abc.txt", SearchOption.AllDirectories);
foreach (var file in foundFiles){
Console.WriteLine(System.IO.Path.GetDirectoryName(file));
}
EnumerateFiles is only available since .NET Framework 4. If you are working with an older version of the .NET Framework then you could use GetFiles of the Directory class.
Update (see comment from PLB):
The code above will fail if the access to a directory in denied. In this case you will need to search each directory one after one to handle exceptions.
public static void SearchFilesRecursivAndPrintOut(string root, string pattern)
{
//Console.WriteLine(root);
try
{
var childDireactory = Directory.EnumerateDirectories(root);
var files = Directory.EnumerateFiles(root, pattern);
foreach (var file in files)
{
Console.WriteLine(System.IO.Path.GetDirectoryName(file));
}
foreach (var dir in childDireactory)
{
SearchRecursiv(dir, pattern);
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
The following shows how to narrow down your search by specific criteria (i.e. include only DLLs that contain "Microsoft", "IBM" or "nHibernate" in its name).
var filez = Directory.EnumerateFiles(#"c:\MLBWRT", "*.dll", SearchOption.AllDirectories)
.Where(
s => s.ToLower().Contains("microsoft")
&& s.ToLower().Contains("ibm")
&& s.ToLower().Contains("nhibernate"));
string[] allFiles = filez.ToArray<string>();
for (int i = 0; i < allFiles.Length; i++) {
FileInfo fInfo = new FileInfo(allFiles[i]);
Console.WriteLine(fInfo.Name);
}

Ignore folder C# (System.UnauthorizedAccessException)

A quick question. I'm executing this code:
listBox1.Items.AddRange( Directory.GetDirectories("C:\\Users\\", "*" ,SearchOption.AllDirectories));
It list all directories and subdirectories in C:\Users\ (yes I know, it maybe blow up my pc)
Anyways, I am getting this error (System.UnauthorizedAccessException)
This error comes from the special folders "C:\Users\All Users\" and "C:\Users\USER\AppData\"
How can I ignore this folders to program keeping listing all dir and subd without Exceptions?
Unfortunately it's not possible to filter out all directories without the required permissions. You need to implement your own recursive function to deal with the problem by catching the UnauthorizedAccessException. Since there could be many exceptions the way is not very fast but reliable like explained in this question:
[...] permissions (even file existence) are volatile — they can change at any time [...]
Here is my possible solution:
public static void GetDirectories(string path, Action<string> foundDirectory)
{
string[] dirs;
try
{
dirs = Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly);
}
catch (UnauthorizedAccessException)
{
//Ignore a directory if an unauthorized access occured
return;
}
foreach (string dir in dirs)
{
foundDirectory(dir);
//Recursive call to get all subdirectories
GetDirectories(dir, foundDirectory);
}
}
Simply call the function like
List<string> allDirectories = new List<string>();
GetDirectories(#"C:\Users\", d => allDirectories.Add(d));

How to check if a specific file exists in directory or any of its subdirectories

In C#, how do I check if a specific file exists in a directory or any of its subdirectories?
System.IO.File.Exists only seems to accept a single parameter with no overloads to search subdirectories.
I can do it with LINQ and System.IO.Directory.GetFiles using the SearchOption.AllDirectories overload, but that seems a bit heavy handed.
var MyList = from f in Directory.GetFiles(tempScanStorage, "foo.txt", SearchOption.AllDirectories)
where System.IO.Path.GetFileName(f).ToUpper().Contains(foo)
select f;
foreach (var x in MyList)
{
returnVal = x.ToString();
}
If you're looking for a single specific filename, using *.* is indeed heavy handed. Try this:
var file = Directory.GetFiles(tempScanStorage, foo, SearchOption.AllDirectories)
.FirstOrDefault();
if (file == null)
{
// Handle the file not being found
}
else
{
// The file variable has the *first* occurrence of that filename
}
Note that this isn't quite what your current query does - because your current query would find "xbary.txt" if you foo was just bar. I don't know whether that's intentional or not.
If you want to know about multiple matches, you shouldn't use FirstOrDefault() of course. It's not clear exactly what you're trying to do, which makes it hard to give more concrete advice.
Note that in .NET 4 there's also Directory.EnumerateFiles which may or may not perform better for you. I highly doubt that it'll make a difference when you're searching for a specific file (instead of all files in the directory and subdirectories) but it's worth at least knowing about. EDIT: As noted in comments, it can make a difference if you don't have permission to see all the files in a directory.
The alternative is to write the search function yourself, one of these should work:
private bool FileExists(string rootpath, string filename)
{
if(File.Exists(Path.Combine(rootpath, filename)))
return true;
foreach(string subDir in Directory.GetDirectories(rootpath, "*", SearchOption.AllDirectories))
{
if(File.Exists(Path.Combine(subDir, filename)))
return true;
}
return false;
}
private bool FileExistsRecursive(string rootPath, string filename)
{
if(File.Exists(Path.Combine(rootPath, filename)))
return true;
foreach (string subDir in Directory.GetDirectories(rootPath))
{
if(FileExistsRecursive(subDir, filename))
return true;
}
return false;
}
The first method still extracts all of the directory names and would be slower when there are many subdirs but the file is close to the top.
The second is recursive which would be slower in 'worst case' scenarios but faster when there are many nested subdirs but the file is in a top level dir.
To Check for file existing in any specific directory do the following
Note: "UploadedFiles" is name of the folder.
File.Exists(Server.MapPath("UploadedFiles/"))
Enjoy Coding
It is a recursive search on the filesystem. You have some functional examples in CodeProject:
Simple File Search Class (by jabit)
Scan directories using recursion using events (by Jan Schreuder)
This is a recursive search function that will break out as soon as finds the file you've specified. Please note the parameters should be specified as fileName (eg. testdb.bak) and directory (eg. c:\test).
Be aware that this can be quite slow if you do this in a directory with a large quantity of subdirecories and files.
private static bool CheckIfFileExists(string fileName, string directory) {
var exists = false;
var fileNameToCheck = Path.Combine(directory, fileName);
if (Directory.Exists(directory)) {
//check directory for file
exists = Directory.GetFiles(directory).Any(x => x.Equals(fileNameToCheck, StringComparison.OrdinalIgnoreCase));
//check subdirectories for file
if (!exists) {
foreach (var dir in Directory.GetDirectories(directory)) {
exists = CheckIfFileExists(fileName, dir);
if (exists) break;
}
}
}
return exists;
}

C#: How to check if I can read and/or delete a directory

I loop through a bunch of directories recursively. Some of them (like D:\$RECYCLE.BIN\S-1-5-20) give me a System.UnauthorizedAccessException. I suppose that I can just catch it and move on, but I would rather figure that out in advance.
So, when I have a DirectoryInfo object. How can I see if I am allowed to GetDirectories() and possibly Delete() it?
If you intend to delete it, try to delete it and then proceed (handling the exception as appropriate).
If you perform a check-and-then-delete-if-should-be-able-to-delete there is the chance of a race condition on the filesystem, however slight. This applies to most all file/directory access operations. Most filesystem operations are designed to be atomic and moving this logic into user code conflicts this atomicity and one would still need to handle a possible exception being raised.
I built following code. Please, see if it helps:
//using System.IO;
//using System.Security.AccessControl;
//using System.Security.Principal;
string[] directories = Directory.GetDirectories(
Path.Combine(Environment.CurrentDirectory, #"..\.."),
"*", SearchOption.AllDirectories);
foreach (string directory in directories)
{
DirectoryInfo info = new DirectoryInfo(directory);
DirectorySecurity security = info.GetAccessControl();
Console.WriteLine(info.FullName);
foreach (FileSystemAccessRule rule in
security.GetAccessRules(true, true, typeof(NTAccount)))
{
Console.WriteLine("\tIdentityReference = {0}", rule.IdentityReference);
Console.WriteLine("\tInheritanceFlags = {0}", rule.InheritanceFlags );
Console.WriteLine("\tPropagationFlags = {0}", rule.PropagationFlags );
Console.WriteLine("\tAccessControlType = {0}", rule.AccessControlType);
Console.WriteLine("\tFileSystemRights = {0}", rule.FileSystemRights );
Console.WriteLine();
}
}
Result:
D:\Projects\ConsoleApplication1\bin
IdentityReference = BUILTIN\Administrators
InheritanceFlags = ContainerInherit, ObjectInherit
PropagationFlags = None
AccessControlType = Allow
FileSystemRights = FullControl
Note that IdentityReference and FileSystemRights properties; probably you should test your current ACL against them before trying to delete a directory.
I believe you will need to write your own GetDirectories() method; that recursivly gets the ones inside of it.
This Microsoft Article has a good article on how to do it, with a bit of work you can clean it up to use Generic Lists and make it fit your solution.
Simply put, System.IO.Directory.GetDirectories() will fail every time it gets one of those exceptions.
Code roughly like this (copied from above) should get you started
List<String> directories = new List<String>();
void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
//foreach (string f in Directory.GetFiles(d, txtFile.Text))
//{
//
//}
// use this to search for files recursivly.
directories.Add(d);
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
Once you have your list of directories, you can then perform operations on them, with some mods the above method should ensure you have read permissions on anything in the list.

How do I get a directory size (files in the directory) in C#?

I want to be able to get the size of one of the local directories using C#. I'm trying to avoid the following (pseudo like code), although in the worst case scenario I will have to settle for this:
int GetSize(Directory)
{
int Size = 0;
foreach ( File in Directory )
{
FileInfo fInfo of File;
Size += fInfo.Size;
}
foreach ( SubDirectory in Directory )
{
Size += GetSize(SubDirectory);
}
return Size;
}
Basically, is there a Walk() available somewhere so that I can walk through the directory tree? Which would save the recursion of going through each sub-directory.
A very succinct way to get a folder size in .net 4.0 is below. It still suffers from the limitation of having to traverse all files recursively, but it doesn't load a potentially huge array of filenames, and it's only two lines of code. Make sure to use the namespaces System.IO and System.Linq.
private static long GetDirectorySize(string folderPath)
{
DirectoryInfo di = new DirectoryInfo(folderPath);
return di.EnumerateFiles("*.*", SearchOption.AllDirectories).Sum(fi => fi.Length);
}
If you use Directory.GetFiles you can do a recursive seach (using SearchOption.AllDirectories), but this is a bit flaky anyway (especially if you don't have access to one of the sub-directories) - and might involve a huge single array coming back (warning klaxon...).
I'd be happy with the recursion approach unless I could show (via profiling) a bottleneck; and then I'd probably switch to (single-level) Directory.GetFiles, using a Queue<string> to emulate recursion.
Note that .NET 4.0 introduces some enumerator-based file/directory listing methods which save on the big arrays.
Here my .NET 4.0 approach
public static long GetFileSizeSumFromDirectory(string searchDirectory)
{
var files = Directory.EnumerateFiles(searchDirectory);
// get the sizeof all files in the current directory
var currentSize = (from file in files let fileInfo = new FileInfo(file) select fileInfo.Length).Sum();
var directories = Directory.EnumerateDirectories(searchDirectory);
// get the size of all files in all subdirectories
var subDirSize = (from directory in directories select GetFileSizeSumFromDirectory(directory)).Sum();
return currentSize + subDirSize;
}
Or even nicer:
// get IEnumerable from all files in the current dir and all sub dirs
var files = Directory.EnumerateFiles(searchDirectory,"*",SearchOption.AllDirectories);
// get the size of all files
long sum = (from file in files let fileInfo = new FileInfo(file) select fileInfo .Length).Sum();
As Gabriel pointed out this will fail if you have a restricted directory under the searchDirectory!
You could hide your recursion behind an extension method (to avoid the issues Marc has highlighted with the GetFiles() method):
public static class UserExtension
{
public static IEnumerable<FileInfo> Walk(this DirectoryInfo directory)
{
foreach(FileInfo file in directory.GetFiles())
{
yield return file;
}
foreach(DirectoryInfo subDirectory in directory.GetDirectories())
{
foreach(FileInfo file in subDirectory.Walk())
{
yield return file;
}
}
}
}
(You probably want to add some exception handling to this for protected folders etc.)
Then:
using static UserExtension;
long totalSize = 0L;
var startFolder = new DirectoryInfo("<path to folder>");
// iteration
foreach(FileInfo file in startFolder.Walk())
{
totalSize += file.Length;
}
// linq
totalSize = di.Walk().Sum(s => s.Length);
Basically the same code, but maybe a little neater...
First, forgive my poor english ;o)
I had a problem that took me to this page : enumerate files of a directory and his subdirectories without blocking on an UnauthorizedAccessException, and, like the new method of .Net 4 DirectoryInfo.Enumerate..., get the first result before the end of the entire query.
With the help of various examples found here and there on the web, I finally write this method :
public static IEnumerable<FileInfo> EnumerateFiles_Recursive(this DirectoryInfo directory, string searchPattern, SearchOption searchOption, Func<DirectoryInfo, Exception, bool> handleExceptionAccess)
{
Queue<DirectoryInfo> subDirectories = new Queue<DirectoryInfo>();
IEnumerable<FileSystemInfo> entries = null;
// Try to get an enumerator on fileSystemInfos of directory
try
{
entries = directory.EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
catch (Exception e)
{
// If there's a callback delegate and this delegate return true, we don't throw the exception
if (handleExceptionAccess == null || !handleExceptionAccess(directory, e))
throw;
// If the exception wasn't throw, we make entries reference an empty collection
entries = EmptyFileSystemInfos;
}
// Yield return file entries of the directory and enqueue the subdirectories
foreach (FileSystemInfo entrie in entries)
{
if (entrie is FileInfo)
yield return (FileInfo)entrie;
else if (entrie is DirectoryInfo)
subDirectories.Enqueue((DirectoryInfo)entrie);
}
// If recursive search, we make recursive call on the method to yield return entries of the subdirectories.
if (searchOption == SearchOption.AllDirectories)
{
DirectoryInfo subDir = null;
while (subDirectories.Count > 0)
{
subDir = subDirectories.Dequeue();
foreach (FileInfo file in subDir.EnumerateFiles_Recursive(searchPattern, searchOption, handleExceptionAccess))
{
yield return file;
}
}
}
else
subDirectories.Clear();
}
I use a Queue and a recursive method to keep traditional order (content of directory and then content of first subdirectory and his own subdirectories and then content of the second...). The parameter "handleExceptionAccess" is just a function call when an exception is thrown with a directory; the function must return true to indicate that the exception must be ignored.
With this methode, you can write :
DirectoryInfo dir = new DirectoryInfo("c:\\temp");
long size = dir.EnumerateFiles_Recursive("*", SearchOption.AllDirectories, (d, ex) => true).Sum(f => f.Length);
And here we are : all exception when trying to enumerate a directory will be ignore !
Hope this help
Lionel
PS : for a reason I can't explain, my method is more quick than the framework 4 one...
PPS : you can get my test solutions with source for those methods : here TestDirEnumerate. I write EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (use a queue to avoid recursion) and EnumerateFiles_NonRecursive_TraditionalOrder (use a stack of queue to avoid recursion and keep traditional order). Keep those 3 methods has no interest, I write them only for test the best one. I think to keep only the last one.
I also wrote the equivalent for EnumerateFileSystemInfos and EnumerateDirectories.
Have a look at this post:
http://social.msdn.microsoft.com/forums/en-US/vbgeneral/thread/eed54ebe-facd-4305-b64b-9dbdc65df04e
Basically there is no clean .NET way, but there is a quite straightforward COM approach so if you're happy with using COM interop and being tied to Windows, this could work for you.
the solution is already here https://stackoverflow.com/a/12665904/1498669
as in the duplicate How do I Get Folder Size in C#? shown -> you can do this also in c#
first, add the COM reference "Microsoft Scripting Runtime" to your project and use:
var fso = new Scripting.FileSystemObject();
var folder = fso.GetFolder(#"C:\Windows");
double sizeInBytes = folder.Size;
// cleanup COM
System.Runtime.InteropServices.Marshal.ReleaseComObject(folder);
System.Runtime.InteropServices.Marshal.ReleaseComObject(fso);
remember to cleanup the COM references
I've been looking some time ago for a function like the one you ask for and from what I've found on the Internet and in MSDN forums, there is no such function.
The recursive way is the only I found to obtain the size of a Folder considering all the files and subfolders that contains.
You should make it easy on yourself. Make a method and passthrough the location of the directory.
private static long GetDirectorySize(string location) {
return new DirectoryInfo(location).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length);
}
-G

Categories

Resources