Looping through directories not behaving as expected - c#

directory is a path, ie L:\\H435431 Project Name which may or may not contain a sub directory named 2-Storage, or 2-STORAGE. If this directory is discovered, I'd like to get a list of its sub directories and test to see if it contains ! Issued Packages.
When I step through this code, subdirectories returns the expected array of folders and containsStorage is correctly set to true. In turn, d correctly reflects the directory name i'd like to search next, L:\\H435431 Project Name\\2-Storage. ad never gets assigned the value of Directory.GetDirectories(d) and the directory is never searched. Why is this?
string[] subirectories = Directory.GetDirectories(directory);
foreach (string d in subirectories)
{
if (Regex.IsMatch(d, #"2-Storage", RegexOptions.IgnoreCase))
{
containsStorage = true;
foreach (string ad in Directory.GetDirectories(d))
{
if (Regex.IsMatch(ad, #"! Issued Packages", RegexOptions.IgnoreCase))
{
containsIssued = true;
}
}
}
}

Check to see if SubSubDirectories is empty for us if there is nothing in there then there is nothing to iterate through.
string[] subirectories = Directory.GetDirectories(directory);
foreach (string d in subirectories)
{
if (Regex.IsMatch(d, #"2-Storage", RegexOptions.IgnoreCase))
{
containsStorage = true;
string[] subSubDirectories = Directory.GetDirectories(d);
foreach (string ad in subSubDirectories)
{
if (Regex.IsMatch(ad, #"! Issued Packages", RegexOptions.IgnoreCase))
{
containsIssued = true;
}
}
}
}

Related

Error message comes up telling me it cannot find the folder after it has deleted it

What I'm trying to do is move the location of a folder, then copy the contents of another folder and move it to the parent directory and delete the original folder. All of that works great but after the process is done I get a weird error telling me it cannot find the folder it deleted and I can't figure out what's causing it look for the folder after it had already deleted it.
I had an exception that told me the error isDirectory.Move(FoundFolder, Path.GetFullPath(Setup.SelectedPath) + #"\" + Path.GetFileName(FoundFolder));
This is the error message:
Here's the code I tried:
string[] SetupFolderKeywords = { "Setup", "Installed" }; //keywords
string[] roots = Directory.GetDirectories(Game.SelectedPath, "*", SearchOption.TopDirectoryOnly); //gets all directories in Game.SelectedP
foreach (var item in roots) //foreach directory found
{
string? FoundFolder = Path.GetFullPath(item); //We get each path for each directory found
if (FoundFolder.Contains(SetupFolderKeywords[1])) //if any of the found directories have any of the keywords
{
CopyDeleteFolder();
}
else if (FoundFolder.Contains(SetupFolderKeywords[0]))
{
CopyDeleteFolder();
//unneccessary second pop up appears due to line 100
}
}
public void CopyDeleteFolder()
{
DirectoryInfo GameDirectory = new DirectoryInfo(Game.SelectedPath);
string GameContentsPath = Game.SelectedPath + #"\" + GameDirectory.Name; //inside of the game folder
string[] roots = Directory.GetDirectories(Game.SelectedPath, "*", SearchOption.TopDirectoryOnly);
foreach (var item in roots)
{
string? FoundFolder = Path.GetFullPath(item);
Directory.Move(FoundFolder, Path.GetFullPath(Setup.SelectedPath) + #"\" + Path.GetFileName(FoundFolder));
if (Directory.Exists(GameContentsPath))
{
string[] GameFiles = Directory.GetFiles(GameContentsPath);
foreach (string GameFile in GameFiles)
{
FileInfo CopyGameFile = new FileInfo(GameFile);
if (new FileInfo(GameDirectory + #"\" + CopyGameFile.Name).Exists == false)
{
CopyGameFile.MoveTo(GameDirectory + #"\" + CopyGameFile.Name);
}
}
Directory.Delete(GameContentsPath);
}
}
}
I'm guessing it's roots in foreach var item in roots but I'm not sure how to get around it.
`
It would appear that the 'Test Game' folder is being successfully moved on the first call to CopyDeleteFolder but (according to your error message) there is a second attempt to move it and predictably this will fail and throw the exception. It's easy to see why this would be the case if you make a minimal reproducible example and simulate the Test Game folder as containing:
string[] roots = new[] { "MockGameSetup1", "MockGameInstalled" };
This simplified loop captures the essence of your original code and makes it easier to see that one condition will be true on the first loop iteration and the other will be true on the second loop iteration resulting in two calls to CopyDeleteFolder.
foreach (var item in roots)
{
if (item.Contains(SetupFolderKeywords[1]))
{
CopyDeleteFolder();
}
else if (item.Contains(SetupFolderKeywords[0]))
{
CopyDeleteFolder();
}
}
void CopyDeleteFolder()
{
// Move the 'Test Game' directory but only ONE time
}
One way to express If any of the found directories have any of the keywords is to use a System.Linq expression resulting in a single call to CopyDeleteFolder:
// Simulate names of folders contained.
string[] roots = new[] { "MockGameSetup1", "MockGameInstalled" };
string[] SetupFolderKeywords = { "Setup", "Installed" };
// If any of the found directories have any of the keywords
if(roots.Any(path=>SetupFolderKeywords.Any(keyword=>path.Contains(keyword))))
{
CopyDeleteFolder();
}

List all files on all disks and all directories except the files in system directories in C# .Net

I have a program written in C# which lists all files in all directories except that ones where you need admin rights. It has no errors but always gives me an access denied exception for a directory that I already ignore. The access denied exception is for "C:$Recycle.Bin\S-1-5-18".
Actually, I ignrore this directory AND run the program with admin rights but the exception is still here.Can anyone help me? Thanks
static void List()
{
List<string> files = new List<string>();
List<string> nofiles = new List<string>();
foreach (var drives in DriveInfo.GetDrives())
{
var filez = Directory.GetFiles(drives.Name,"*",SearchOption.AllDirectories);
foreach (string f in filez)
{
if (f.StartsWith(#"C:\Windows"))
{
}
else if (f.StartsWith(#"C:\Config.Msi"))
{
}
else if (f.StartsWith(#"C:\Program Files"))
{
}
else if (f.StartsWith(#"C:\Program Files (x86)"))
{
}
else if (f.StartsWith(#"C:\DumpStack.log"))
{
}
else if(f.StartsWith(#"C:\$Recycle.Bin\S-1-5-18"))
{
}
else if(f.StartsWith(#"C:\Documents and Settings"))
{
}
else
{
files.Add(f);
}
}
foreach (string fl in files)
{
var c = Path.GetFullPath(fl);
Console.WriteLine(c);
Console.ReadKey();
}
Console.ReadKey();
}
}
Direct access almost always ends with access denied somewhere you should specify the search rules more precisely with the enumeration options, like below:
foreach (var drive in DriveInfo.GetDrives())
{
if (drive.IsReady == false)
{
continue;
}
var filez = Directory.GetFiles(drive.Name, "*", new EnumerationOptions
{
AttributesToSkip = FileAttributes.Hidden | FileAttributes.System,
IgnoreInaccessible = true,
RecurseSubdirectories = true,
ReturnSpecialDirectories = true,
});
// TODO: implement the rest of your code hier ...
}
where the FileAttributes.Hidden is not realy required.
... hope that helps!
With Directory.GetFiles(drives.Name,"*",SearchOption.AllDirectories); you already iterate trough all the directories. The filtering is done afterwards and only used to decide whether you add the directory name (that you already have!) to your list.
To filter the way you want, you need to implement the recursion yourself and catch the exception on each step.

C# Recursive folder search does not work properly

I need help, because I've written a method that should find a special directory on a computer that definitely exists.
First I've written a method that will be go through every drive on a computer and open up for every drive the recursive method.
private string LocateOnDrives()
{
string result = string.Empty;
string[] drives = Directory.GetLogicalDrives();
foreach (string drive in drives)
{
string buffer = this.Locate(drive);
if (buffer.EndsWith(SEARCH_PATTERN))
{
return buffer;
}
}
return result;
}
And this is the method that is called for every drive:
private string Locate(string directory)
{
string result = string.Empty;
try
{
string[] dirs = Directory.GetDirectories(directory);
foreach (string dir in dirs)
{
if (dir.EndsWith(SEARCH_PATTERN))
{
return dir;
}
else
{
result = this.Locate(dir);
}
}
}
catch { }
return result;
}
The try catch is necessary, because there are some directories with no permission. I have the permission for the sought folder and when i debug this, it will jump into the if condition that it has found and set the local 'result' to this sought folder. Up to this point it really makes that what was my intention. But the recursive method will search further and the overall return is string.Empty!
I already did something link this:
private string tragetDir;
private string Locate(string directory)
{
string result = string.Empty;
try
{
string[] dirs = Directory.GetDirectories(directory);
foreach (string dir in dirs)
{
if (dir.EndsWith(DEFAULT_GTAV_DIRECTORY_NAME))
{
targetDir = dir;
}
else
{
result = this.Locate(dir);
}
}
}
catch { }
return result;
This is working for me, but not what I wanted to have, because it should be possible that the recursive method will return this wanted folder…
It is late for me and I just want to fix this little mistake!
Can someone help me out, because I am desperate, THANKS!
When you find a match and return it, then unwind once in your nested calls to Locate(), you assign the match to result but then keep progressing with the loop, when you actually want to break out of it.
result = this.Locate(dir, SEARCH_PATTERN);
if (result.EndsWith(SEARCH_PATTERN))
{
break;
}
Also, you might consider just catching the UnauthorizedAccessException since that's the one it'll throw if you don't have permission to a particular directory.
This is a solution I tried and it worked for me now:
private string Locate(string directory)
{
string result = string.Empty;
string[] dirs = new string[0];
try
{
dirs = Directory.GetDirectories(directory);
}
catch { /* Ignore */ }
foreach (string dir in dirs)
{
if (dir.EndsWith(SEARCH_PATTERN))
{
result = dir;
break;
}
else
{
result = this.Locate(dir);
if (result.EndsWith(SEARCH_PATTERN))
{
break;
}
}
}
return result;
}
First I had to check if the current "dir" in the loop was already the sought folder. If not, the loop had to browse inside this folder and if the result inside this folder isn't the sought folder the loop had to going on and search on or in the next folder in loop.
In any case that the right directory was found, the loop will "break" and return the result!
This is it!

Access to the path 'd:\$RECYCLE.BIN\S-1-5-21-494745725-312220573-749543506-41600' is denied

I am new to C# . I have a text box where i enter the file to search and a 'search' button. on clock of search i want it to populate the files in the folder but i get the above error. Below is my code:
string[] directories = Directory.GetDirectories(#"d:\",
"*",
SearchOption.AllDirectories);
string file = textBox1.Text;
DataGrid dg = new DataGrid();
{
var files = new List<string>();
foreach (DriveInfo d in DriveInfo.GetDrives().Where(x => x.IsReady))
{
try
{
files.AddRange(Directory.GetFiles(d.RootDirectory.FullName, file , SearchOption.AllDirectories));
}
catch(Exception ex)
{
MessageBox.Show("the exception is " + ex.ToString());
//Logger.Log(e.Message); // Log it and move on
}
}
Please help me resolve it . Thanks
The most important rule when searching on a folder which potentially contains inaccessible subfolder is:
Do NOT use SearchOption.AllDirectories!
Use SearchOption.TopDirectoryOnly instead, combined with recursive search for all the accessible directories.
Using SearchOption.AllDirectories, one access violation will break your entire loop even before any file/directory is processed. But if you use SearchOption.TopDirectoryOnly, you only skip what is inaccessible.
There is more difficult way to use Directory.GetAccessControl() per child directory check to see if you have an access to a Directory before hand (this option is rather hard though - I don't really recommend this unless you know exactly how the access system works).
For recursive search, I have this code implemented for my own use:
public static List<string> GetAllAccessibleDirectories(string path, string searchPattern) {
List<string> dirPathList = new List<string>();
try {
List<string> childDirPathList = Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList(); //use TopDirectoryOnly
if (childDirPathList == null || childDirPathList.Count <= 0) //this directory has no child
return null;
foreach (string childDirPath in childDirPathList) { //foreach child directory, do recursive search
dirPathList.Add(childDirPath); //add the path
List<string> grandChildDirPath = GetAllAccessibleDirectories(childDirPath, searchPattern);
if (grandChildDirPath != null && grandChildDirPath.Count > 0) //this child directory has children and nothing has gone wrong
dirPathList.AddRange(grandChildDirPath.ToArray()); //add the grandchildren to the list
}
return dirPathList; //return the whole list found at this level
} catch {
return null; //something has gone wrong, return null
}
}
This is how you call it
List<string> accessibleDirs = GetAllAccessibleDirectories(myrootpath, "*");
Then, you only need to search/add the files among all accessible directories.
Note: this question is quite classical though. I believe there are some other better solutions out there too.
And in case there are some directories which you particularly want to avoid after you get all your accessible directories, you could also filter the List result by LINQ using part of the directory's name as keyword (i.e. Recycle.Bins).
As Ian has specified in his post, do not use recursive file listing (Directory.GetFiles(path, searchPattern, SearchOption.AllDirectories)) in case like yours, since the first exception will stop further processing.
Also, to somewhat alleviate such issues and for better results in general, you should run this program as an Administrator. This can be done by right-clicking your application in windows explorer, and then checking Run this program as an administrator option on Compatibility tab.
Also, you should use code like below to do your search, so the intermediate exceptions do not stop further searching.
static void Main(string[] args) {
string fileToFind = "*.jpg";
var files = new List<string>();
foreach (DriveInfo d in DriveInfo.GetDrives().Where(x => x.IsReady))
files.AddRange(FindDirectory(fileToFind, d.RootDirectory.FullName));
}
/// <summary>
/// This function returns the full file path of the matches it finds.
/// 1. It does not do any parameter validation
/// 2. It searches recursively
/// 3. It eats up any error that occurs when requesting files and directories within the specified path
/// 4. Supports specifying wildcards in the fileToFind parameter.
/// </summary>
/// <param name="fileToFind">Name of the file to search, without the path</param>
/// <param name="path">The path under which the file needs to be searched</param>
/// <returns>Enumeration of all valid full file paths matching the file</returns>
public static IEnumerable<string> FindDirectory(string fileToFind, string path) {
// Check if "path" directly contains "fileToFind"
string[] files = null;
try {
files = Directory.GetFiles(path, fileToFind);
} catch { }
if (files != null) {
foreach (var file in files)
yield return file;
}
// Check all sub-directories of "path" to see if they contain "fileToFInd"
string[] subDirs = null;
try {
subDirs = Directory.GetDirectories(path);
} catch { }
if (subDirs == null)
yield break;
foreach (var subDir in subDirs)
foreach (var foundFile in FindDirectory(fileToFind, subDir))
yield return foundFile;
}

Scanning for Java executable

I'd like my program to scan the user's hard drive for java.exe.
I was considering using a for-loop and then going through each directory and then check if the directory names match the ones I would set up and then check in those for the java exe, but I'm sure there is a much more efficient way of doing so.
Any ideas on how to approach this?
Edit:
I've gone ahead and done some foreach loops, but it isn't going too well. I'm probably missing something:
// Scan for Java executable (java.exe)
foreach (String dir in Directory.GetDirectories("C:/"))
{
if (dir == "Program Files")
{
foreach (String _dir in Directory.GetDirectories(dir)) {
if (_dir == "Java")
{
foreach (String javaDir in Directory.GetDirectories(_dir))
{
if (javaDir == "jre7")
{
foreach (String binDir in Directory.GetDirectories(javaDir)) {
if (binDir == "bin")
{
foreach (String file in Directory.GetFiles(binDir))
{
if (file == "java.exe")
{
javaExe = file;
}
}
}
}
}
}
}
}
}
}
You are looking for java.exe. So
Firstly you should look for directory where oracle always install it's java.exe
which should be at
C:\Windows\System32
to limit your search and then search in other directories.
var paths = new List<string>
{
Environment.SystemDirectory,
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)+#"\java",
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)+#"\java",
Environment.GetFolderPath(Environment.SpecialFolder.Programs)+#"\java"
//C:\
//rest
};
foreach (string path in paths)
{
var ser = Search("java.exe", path);
if (!string.IsNullOrEmpty(ser))
{
if (File.Exists(ser))
{
javaExe = ser;
break;
}
}
}
Search function:
private static string Search(string pattern, string root)
{
if (!Directory.Exists(root))
{
return string.Empty;
}
var paths = new Queue<string>();
paths.Enqueue(root);
while (paths.Count > 0)
{
root = paths.Dequeue();
string[] temp = Directory.GetFiles(root, pattern);
foreach (string t in temp)
{
return t;
}
temp = Directory.GetDirectories(root);
foreach (string t in temp)
{
paths.Enqueue(t);
}
}
return string.Empty;
}
Also it's preferred to use this search method to avoid Access permissions.
Note:
You could find java.exe in more than one place.
Do you only want to see if java.exe is there?
File.Exists(#"C:\Program Files\Java\jre7\bin\java.exe")
You might need additional logic to look in the Program Files (x86) folder if needed. Also this will obviously fail if the user installed java somewhere else.
If you want to know where is java, take a look at this other Stack Overflow post.

Categories