I am trying to retrieve the subdirectories of a path I pass in. It proccess it and gives me half of the subdirectories but for the other half, it returns a "?" when debugging. I do not know what is causing this
Here is what I have:
string root = #"C:\Users\Documents\Meta Consumer";
string[] subDir = Directory.GetDirectories(root);
When Debugging:
1: (good)
2: (good)
3: (good)
.. ..
?: (this is where 14 is)
?: (15 is here)
.. ..
?: ?
I'm not sure the entire goal, if you intend to specifically Search for a specific item or intend to manipulate the Directory at all. One thing that I do see is you haven't specified any additional search for your array. This can be hindered I believe through deep nesting or permission issues.
Resolution One: Ensure that you have valid permission to do recursive searches within the specified directory.
Resolution Two: You can attempt to run a search for all items with a wildcard then force it to search all directories. This may help solve potential deep nesting issues you may encounter.
Resolution Three: Try the below code; see if it solves the issue.
string root = Environment.GetFolderPath(Environment.SpecialFolder.Documents);
string[] subDir = Directories.GetDirectories(root, "*", SearchOption.AllDirectories);
foreach (string s in subDir)
{
Console.WriteLine(s);
}
See if that returns the proper information that it wasn't previously. There are folders located in your Library that though are considered public to the user are still locked as they reside in the User Profile so permissions will be a good check.
Running Visual Studio as an Administrator will also help in your troubleshooting. Also you should see if there are any Inner Exceptions to help identify it as well.
Related
I am testing software in C# and must ensure proper behavior (graceful failure) occurs when a program is given an invalid full path. Initially this is trivial,as I give something like "Q:\\fakepath" and since there is no Q drive mounted on the system, the program fails as expected.
However, I would like my test to be robust and want a way to generate a path that is guaranteed to not exist and to not be able to exist. The path must be full since if it doesn't start with a drive letter it will be treated relative to some directory, resulting in no failure.
Some approaches I have though of are to search for local drives that are mounted and then pick a drive letter that does not appear. This would work fine and I might end up using this, but I would prefer a more elegant solution such as using a drive letter that could not possibly exist.
Another (potential) option is to use invalid characters in the path name. However, using invalid characters is not preferred as it actually results in a different failure mode of the program.
So formally: How can I most elegantly generate a full path that is guaranteed not be invalid?
EDIT: The program I am testing will go ahead and create a directory (including parent directories) if it is on a valid drive but in a location that does not already exist. Hence, this path needs to be something that couldn't be created with something like Directory.CreateDirectory(<path>), not just something that doesn't already exist.
One method would be to use the Windows API to create a temporary folder. This might sound counterintuitive, but now you have a known empty folder, any path you specify inside it is guaranteed to not exist. For example:
//From https://stackoverflow.com/a/278457/1663001:
public string GetTemporaryDirectory()
{
string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDirectory);
return tempDirectory;
}
public string GetNonexistantPath()
{
return Path.Combine(GetTemporaryDirectory(), "no-such-file");
}
One way to get a guaranteed invalid folder path is have a file that exists with the same name as part of the directory path.
public string Example()
{
string filePath = Path.GetTempFileName(); //Creates a uniquely named, zero-byte temporary file on disk.
var invalidDirectoryPath = Path.Combine(filePath, "CanNotExist");
Directory.CreateDirectory(invalidDirectoryPath); //throws a IOException
}
You could try using one of the reserved words, for instance C:\NUL (case-sensitive). Trying to create such directory will cause a DirectoryNotFoundException. More details here.
You can use some really long path (say a thousand characters). Your program won't probably be able to create it as it is invalid.
You can try this approach. Not sure though it would work or not but a worth try.
use path: Q:\asddsafkdjfkasjdfklahsjfhskdjfladjfhsafjklasdjfkashfkajsdfhasdklfjashdfkljasdhfklajdfajsdfklajsfkjasjfhadkfjasflhakldfjashdfklajsdjfhaksldjfahsdkljadfklajfkjlkajfkljagkjklfdjgklajdkfljgskljgklfjskgjfkljdsgkfsdgsfgsdfgsfggsdfgsfdgsgwesdfgdgjgfadfsfgffgfsdghijklm
Don't bother about counting the total number of letters, you can do the same using http://www.lettercount.com/
The trick is the max length of windows folder can be 260.
Though I tried in on Windows 10 and the max length allowed to me is 247.
Source_MAX_Length_Of_Folder_On_Windows
So, this folder is guaranteed to be never found. Cheers :)
Although, I think the most elegant solution is checking the mounted drives and generate a path afterwards that you have already mentioned and decided to keep it as a last option.
DirectorySecurity fs;
string FolderPath = "C:/Program Files";
fs = Directory.GetAccessControl(FolderPath, AccessControlSections.All);
foreach (FileSystemAccessRule fileSystemAccessRule in fs.GetAccessRules(true,true,typeof(System.Security.Principal.NTAccount)))
{
userNameDomain = fileSystemAccessRule.IdentityReference.Value;
userRights = fileSystemAccessRule.FileSystemRights.ToString();
string[] row = { userNameDomain, userRights };
var listViewItem = new ListViewItem(row);
lv_perm.Items.Add(listViewItem);
}
When doing this on "regular" files and folders (ex: C:/Users/Julien/Desktop/New folder), everything seems good:
ListView:
But, when I'm doing this on a folder with "special" rights (ex: C:/Program Files), I got duplicate IdentityReference.Value associated to strange numbers for rights:
Listview not good:
I don't have as many rights entries with strange numbers when I open the Permissions tab in C:/Program Files properties.
Maybe I'm doing something bad?
EDIT: From that page Here:
Using .NET you may think that determining which permissions are
assigned to a directory/file should be quite easy, as there is a
FileSystemRights Enum defined that seems to contain every possible
permission that a file/directory can have and calling
AccessRule.FileSystemRights returns a combination of these values.
However, you will soon come across some permissions where the value in
this property does not match any of the values in the FileSystemRights
Enum (I do wish they wouldn’t name some properties with the same name
as a Type but hey).
The end result of this is that for some files/directories you simply
cannot determine which permissions are assigned to them. If you do
AccessRule.FileSystemRights.ToString then for these values all you see
is a number rather than a description (e.g Modify, Delete, FullControl
etc). Common numbers you might see are:
-1610612736, –536805376, and 268435456
To work out what these permissions actually are, you need to look at
which bits are set when you treat that number as 32 separate bits
rather than as an Integer (as Integers are 32 bits long), and compare
them to this diagram:
msdn.microsoft.com/en-us/library/aa374896(v=vs.85).aspx
So for example, -1610612736 has the first bit and the third bit set,
which means it is GENERIC_READ combined with GENERIC_EXECUTE. So now
you can convert these generic permissions into the specific file
system permissions that they correspond to.
You can see which permissions each generic permission maps to here:
msdn.microsoft.com/en-us/library/aa364399.aspx. Just be aware
that STANDARD_RIGHTS_READ, STANDARD_RIGHTS_EXECUTE and
STANDARD_RIGHTS_WRITE are all the same thing (no idea why, seems
strange to me) and actually all equal the
FileSystemRights.ReadPermissions value.
So I think, because GetAccessRules are unable to group for ex:
NT SERVICE\TrustedInstaller FullControl
and
NT SERVICE\TrustedInstaller 268435456
He create 2 distinct entry.
I have to correct the FileSystemRights so they fit to the enumeration.
268435456 - FullControl
-536805376 - Modify, Synchronize
-1610612736 - ReadAndExecute, Synchronize
This issue exist since 2014. And still exist today.
You're not doing anything bad. The access mask is actually represented as an int internally (and on the file system), but the enum FileSystemRights is incomplete.
Therefore the visualizers and those functions converting a FileSystemRights value to a string will be at a loss and simply give you the numeric value (but as a string).
This means in order to make sense of them you'd have to inspect WinNT.h (from the Windows SDK) and look up all the possible access mask values - including generic ones - and provide a manual way of converting the numeric representation to a more human-readable string.
What you encountered are two distinct ACEs with different (but semantically equivalent) access masks, it seems. This is perfectly fine and happens in the wild (as your screenshot proves!). The .NET framework, however, chooses to ignore the issue.
If you look at the ACL with Powershell's Get-Acl or with icacls (a tool which has been on board since Windows 2000), you can see that there are potentially differences in how the individual ACEs are inherited or propagate down the filesystem hierarchy.
Another alternative would be to call the MapGenericMask() and have it perform the mapping. So when you would give it GENERIC_ALL (== 0x10000000) it would return 0x001f01ff, which corresponds to FileSystemRights.FullControl. This way you could "bend" the generic access mask into a form understood by the .NET framework.
Any idea how to easily support file search patterns in your software, like **, *, ?
For example subfolder/**/?svn - search in all levels of subfolder for files/folders ending in "svn" 4 characters in total.
full description: http://nant.sourceforge.net/release/latest/help/types/fileset.html
If you load the directory as a directory info e.g.
DirectoryInfo directory = new DirectoryInfo(folder);
then do a search for files like this
IEnumerable<FileInfo> fileInfo = directory.GetFiles("*.svn", SearchOption.AllDirectories);
this should get you a list of fileInfo which you can manipulate
to get all subdirectories you can do the same e.g
IEnumerable<DirectoryInfo> dirInfo = directory.GetDirectories("*svn", SearchOption.AllDirectories);
anyway that should give a idea of how i'd do it. Also because fileInfo and dirInfo are IEnumerable you can add linq where queries etc. to filter results
A mix of regex and recursion should do the trick.
Another trick might be to spawn a thread for every folder or set of folders and have the thread proceed checking one more level down. This could be beneficial to speed up the process a bit.
The reason I say this is because that is highly io bound process to check folders etc. So many threads will allow you to submit more disk requests faster thus improving the speed.
This might sound silly, but have you considered downloading the nant source code to see how they did it?
I decided not to keep the reports in the application as embedded resources anymore, and move them to the server locally. The advantages are obvious, but considering that I want to organize the directories (common reports, letterheads, etc.) in a legible way. I need a way to load reports only by name, calling a recursive search method. My plan is to use a cache that can be built at start of the application and then search in a list OR use a method that seeks report name each time I need... Any ideas and an example eventually with code(c#) are very welcomed!
Folder Structure can be like:
\\webserver\Reports(report files with unique names)
\\webserver\Reports\Common(report files with unique names)
\\webserver\Reports\Manager1(report files with unique names)
\\webserver\Reports\Manager1\Invoices(report files with unique names)
\\webserver\Reports\ManagerN(report files with unique names)
Hope to be useful this exemplification! Thank you!
If you want to search directories recursivly for a certain extension:
var d = new DirectoryInfo("\\\\webserver\\Reports");
var files = d.GetFiles(".rpt", SearchOption.AllDirectories);
How about instead of searching every time, you record the exact location of the reports in a section of your application's configuration file? Maybe that is what you mean by building a cache.
please refer to this article by Microsoft How to recursively search directories by using Visual C#
It explains how to write your recursive function (which is very simple in c#)
If your directory tree is not too huge (less than 100 dirs and less than, say, 50 files in each dir), then a cache is not necessary (in my humble opinion). If you build a cache, you have to take care of it (ie. handle the case when it has to be updated etc)
void DirSearch(string sDir) {
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d, txtFile.Text))
{
lstFilesFound.Items.Add(f);
}
DirSearch(d); /// Recursive Call !!
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}}
I was just wondering how do you display the conetnts of a chosen folder on a ListView or something for example so the files can be individually be selected (and multiple files)
At the moment i have a folder dialog where the user chooses their desired path and yeah have stopped there :S
Given the string path you can use
Directory.GetDirectories
and
Directory.GetFiles
to retrieve the contents of a folder.
I'm going to focus on your statement : "a Listview or something," and talk about the "something" scenario :)
Why aren't you using the built-in control 'OpenFileDialog : you can set the 'MultiSelect property to 'true and select all the files you like, you can filter the files that appear in complex ways, etc. : it's there, it's "free," it works.
If you specifically do not want to use this control for reasons like, for example, you want the list files to remain visible (i.e., not to be a modal interface) at all times, I suggest you clarify your original question to reflect that. The more you tell us exactly what you want, the more focused the answers you can get.
regards Bill,
System.IO.Directory.GetFiles(<filepath>)
will return a string array which you can iterate through and display file names. It can be passed a true boolean value as well if you wish to do a recursive directory search.
If you wish to display directories as well, you will need to use
System.IO.Directory.GetDirectories(<filepath>)
If you just call ListView.Items.AddRange(Directory.GetFiles(#"c:\temp"); the names of all files in c:\temp will be shown in the ListView.
All the cool kids use Linq :)
var fileList = new DirectoryInfo(#"C:\").GetFiles().Where(file => file.Extension == ".txt");
foreach (var file in fileList)
{
// Do what you will here
// listView1.Items.Add(
}
This just gets text files in the C:\ drive, but you can tweak as necessary