1

Ok solved a bit my old problem thx to @sircodesalot

Public Sub AppendFilesFromDirectory(ByVal path As DirectoryInfo, ByVal files As List(Of FileInfo)) For Each file As FileInfo In CurrentFolder.GetFiles() files.Add(file) Next For Each subfolder As DirectoryInfo In CurrentFolder.GetDirectories() AppendFilesFromDirectory(subfolder, files) Next End Sub

Dim files As New List(Of FileInfo) AppendFilesFromDirectory(New DirectoryInfo(FolderBrowserDialog1.SelectedPath), files) For Each file As FileInfo In files ListBox1.Items.Add(file.FullName) Next


And it works well for folders which have subfolders in it, but if a folder has only files then it becomes a loop adding continuously the file to myList.

Any suggestions how to avoid it? @sircodesalot kindly tried to explain me but I'm not able to do wathever I try..

Help is really appreciated!

fulmix
  • 15
  • 1
  • 8
  • I don't think you realize how huge that list will be. – the_lotus Sep 08 '14 at 13:27
  • It was an example with C:\, ofcourse I have a variable with my path to list. – fulmix Sep 08 '14 at 13:29
  • Note that `My.Computer.FileSystem.GetDirectories` should be empty if there are no subfolders, so the recursion should stop. – sircodesalot Sep 08 '14 at 15:25
  • And how can insert the if inside it and check it? Tried several times but doesn't work.. – fulmix Sep 08 '14 at 15:32
  • This looks pretty close to right. What's not working about it? – sircodesalot Sep 08 '14 at 15:39
  • I tried adding an if , if "path" parameter does not contains any directory use only first "for" and skip second but it doesn't work.. where I should put the if statement and what I have to check exactly? Thanks man in advance for your time. – fulmix Sep 08 '14 at 16:03
  • You shouldn't have to add anything though, because (implicitly) 'if `My.Computer.FileSystem.GetDirectories` is empty' the recursion will stop anyway. – sircodesalot Sep 08 '14 at 19:26
  • Yep, that's right. Though I tested it with a folder with only one file inside it and it continuously add it to the listbox without stopping. Really weird.. Using a folder with at least one subdirectory works as it should. – fulmix Sep 08 '14 at 19:30
  • Updated with example – sircodesalot Sep 08 '14 at 19:58

1 Answers1

1

First get your head around a simple recursive function like factorial. For example:

int factorial(int number) {
    if (number < 1)
        return 1;
    else
        return factorial(number - 1) * number;
}

Basically, if we want to calculate factorial(5), then it's just:

 factorial(5) * factorial(4) * factorial (3) * factorial(2) * factorial(1)

or more abstractly:

 factorial(n) * factorial(n - 1) * factorial (n - 2) * ... * factorial (1)

So we make the function call itself with a diminishing value to compute the result.

The same applies do your problem above. If we want to get all the sub directories, then all we have to do is:

(1) List all the files in the current folder
(2) For the files that are directories, repeat step one.

In other words:

List<Folder> readAllFiles() {
    List<Folder> folders = new List<Folder>();

    readAllFilesRecursively("C:/", folders);
}

List<Folder> readAllFilesRecursively(String directory, List<Folder> folders) {
     Folder currentDirectory = castTheStringToADirectorySomehow(directory);

     // Add the current folder to the list.
     folders.add(currentDirectory);


     // ** HERE IS WHAT YOU'RE MISSING ** //
     // Re-call the same function on the sub folder, passing in our list
     // so that the subfolder items can be added to the list.
     foreach (Folder subfolder in currentDirectory.subFolders()) {
          readAllFilesRecursively(subFolder.pathString(), folders);
     }

     // Return the folder list.
     return folders;
}

Edit:

So this works for me, but you'll have to change it to VB obviously. Also, I'm on a mac so you'll notice that the path format is a little different:

class Program {
    public static void Main() {

        List<FileInfo> files = new List<FileInfo> ();
        AppendFilesFromDirectory (new DirectoryInfo("/Users/sircodesalot/Desktop/Dev"), files);

        foreach (FileInfo file in files) {
            Console.WriteLine (file.FullName);
        }
    }

    public static void AppendFilesFromDirectory(DirectoryInfo currentFolder, List<FileInfo> files) {

        foreach (FileInfo file in currentFolder.GetFiles()) {
            files.Add(file);
        }

        // This if statement is unneccesary, but I'll add it for clarity 
        // to explain the concept of a base case. We don't really need it though
        // since the 'foreach' statement won't execute if there aren't any items.

        if (currentFolder.GetDirectories().Count() > 0) {

            // Process the subfolders
            foreach (DirectoryInfo subfolder in currentFolder.GetDirectories()) {
                AppendFilesFromDirectory (subfolder, files);
            }
        }
    }
}
sircodesalot
  • 11,231
  • 8
  • 50
  • 83
  • I forgot to say that I do it in vb.net, can you please write it in vb.net? Very much appreciated! First post edited too. – fulmix Sep 08 '14 at 12:32
  • Yeah, I noticed that. I don't really know vb.net unfortunately, but the solution is basically the same. Basically you just need to write a function that captures all the folders in the current directory, then call that function again (from within that function) for each subfolder in that directory. – sircodesalot Sep 08 '14 at 12:46
  • Ok. I think I got it. But it comes another problem.. if the directory has only files and no directories.. it becomes a loop while adding every file found to list.. – fulmix Sep 08 '14 at 12:50
  • That's the zen part. You only call the function again *if* one of the files is a directory. Just like in the factorial function above, we stop when we reach 1, because at that point there's nothing left to do. If there's no more folders, then we're done, and we let the function exit (returning us to the parent directory). – sircodesalot Sep 08 '14 at 12:51
  • In other words, think of the function as listing all items in a directory. When you meet up with a sub directory, you call the same exact function again, but this time on the sub directory. If the sub directory has a sub directory, then you do that as well. When we've finished, then we let the function exit, and it rolls us back up to the parent directory, and so on until we've exhausted all of the files in all directories. Recursive programming is really odd at first, but it's really the solution to your problem here. – sircodesalot Sep 08 '14 at 12:57
  • Yeah it's really confusing.. though you're the first person explaining me so well. Well I made a function with two "for" the first one checks for each file found add it to listbox, the second for each directory found recalls the function with the subdirectory found. And until now it's all good and I understood why the function it's called again. But now I don't know where to check if there aren't folders inside the for and exit as you said.. Sorry if I make such stupid questions. – fulmix Sep 08 '14 at 13:04
  • No you're fine, recursion takes a few tries before you get familiar with it. That said, if there are no subfolders, your second loop shouldn't run -- and the function should just exit normally. – sircodesalot Sep 08 '14 at 15:18
  • I do recommend taking a couple of minutes to implement factorial, just so you can get the basic concepts down. In particular the concept of a 'base case', that is the point at which you stop recursing. For factorial it's `1` because you can compute that directly. For your situation, the base case is 'no folders'. Basically you keep calling the function over and over again until you reach the base case (no folders). In recursion, you recurse indefinitely until the base case `if` statement is met. Once that if statement is met then you stop (i.e. let the function exit normally). – sircodesalot Sep 08 '14 at 15:22
  • Updated code. I'm getting MissingMemberException cannot find GetFiles in type Integer..? Weird.. – fulmix Sep 09 '14 at 01:55
  • http://stackoverflow.com/questions/17683271/system-missingmemberexception-was-unhandled – sircodesalot Sep 09 '14 at 03:06
  • I cheated a bit and used the LINQ `Count` method in the optional if statement. Linq does a lot of wizardry to compile that I'm familiar with in C#, but I don't know how it works in VB.net, so you may opt to ignore that altogether. Basically the (again, entirely optional) if statement needs to check if there is any reason to run the `foreach` loop to see if there are any subfolders to process. – sircodesalot Sep 09 '14 at 03:07
  • Also, this is good programming experience for you :), you understand what needs to be done, you just need to struggle through the implementation. – sircodesalot Sep 09 '14 at 03:09
  • My mistake in code.. :) now it works as it should. Thank you very much for this, really helped me out understand things better! :) Much appreciated! – fulmix Sep 09 '14 at 11:42
  • Np sir, glad to hear! – sircodesalot Sep 09 '14 at 12:14
  • Just a doubt got me.. if I'd like to list al C:\ files into a ListBox the program almost crash.. is there any way to make it faster? I mean I heard about multi-threading thought I don't if it's this case. A quick theoric explanation would be awesome, thanks in advance! – fulmix Sep 10 '14 at 11:15
  • What most file browsers do is just-in-time loading. There should be an event you can attach to (especially if you're using a treeview or something to that affect) that allows you to load the next directory when the user selects a node. In most situations, there are better options than up-front full system scans. That said, you should never use the UI thread for long processing. You should be using a background thread (like a `BackgroundWorker`, or a `Task`) and then using `Control.Invoke` to marshall (send) the results back to the main thread. – sircodesalot Sep 10 '14 at 12:14
  • If you're not familiar with `Invoke`, now is probably a good time to learn it (http://stackoverflow.com/questions/1423446/thread-control-invoke). Basically UI controls are thread-aware, in that they log what thread created them, and they verify that only that thread ever accesses them. In order to update the UI, you have to use `Invoke` to schedule a callback/event function to perform the actual screen update on the UI thread. Invoke is a bit tricky to get your head around the first time, but its another invaluable technique to have under your belt. – sircodesalot Sep 10 '14 at 12:18
  • Oh that's really interesting, never thought about using a background thread and never knew about Invoke to invoke it in my current thread. I will learn that as it seems really interesting. Thank you again! Much appreciated :) – fulmix Sep 10 '14 at 16:07
  • Ok, so with ButtonClick I used to invoke Timer1() to start AppendFilesFromDirectory().. now I made a BackgroundWorker, and this time ButtonClick invoked RunWorkerAsync() and then DoWork() uses Control.Invoke to invoke the Timer1().. the result is that is the same thing.. it almost crashes when scanning C:\ – fulmix Sep 15 '14 at 11:36
  • When you say 'crash' do you mean an exception is thrown, or it freezes? – sircodesalot Sep 15 '14 at 13:06
  • Nono I mean it freezes because of too many files on whole hard disk :) – fulmix Sep 15 '14 at 14:22
  • Oh yeah, thats what a `BackgroundWorker` is for. But you shouldn't update the screen for *every* new file added, otherwise you don't really gain anything since the UI thread will have to refresh for every new item added. (I'm assuming that's what the issue is). – sircodesalot Sep 15 '14 at 14:30
  • Yeah but how should I proceed then? Sorry for my dumbness – fulmix Sep 15 '14 at 14:32
  • Just update it periodically (once every half second or something to that effect). Also if you are clearing and repopulating a list or something, recognize that that will incur overhead, especially if the list is large. If you're populating the user's entire HDD then there really is no good solution for this because at a certain point the list is too long to put into the box efficiently anyway. So you may consider just rethinking your approach. You have the mental tools you need to get the data, now it's just a matter of designing a user-friendly interface. – sircodesalot Sep 15 '14 at 14:59
  • I think it will never do the job as what I want to do is to display all files of whole HDD into listbox the faster way possible.. so I guess there is nothing to speed it. – fulmix Sep 15 '14 at 15:23
  • If you're going that route, it probably makes more sense to just have a progress bar, and then have the user wait for it to be done. Although I can't really think of a scenario where I would want to *see* all the files on my hard drive -- *search* maybe, but not **see**. – sircodesalot Sep 15 '14 at 15:31
  • Hehe I understand your point. Let me explain you my motivation: if I want to scan all HDD to search if a file has same md5 as a constant md5 string given by me, what I do is list all files in a ListBox and then if ListBox.Contains() my md5 string (after calculating it of course) display the name of the file found. So that is my idea of how I could do.. maybe there are more ways to do so I don't know. – fulmix Sep 15 '14 at 15:36
  • If that's the case, you should only update the list box if it matches your hash (which should be very infrequent). – sircodesalot Sep 15 '14 at 15:37
  • yeah but first I invoke recursive function to add all files to listbox then compares the hash of ListBox.SelectedItem() to the hash given by me. – fulmix Sep 16 '14 at 11:13
  • Right, but think about the overhead of displaying something, and then undisplaying it. Both of those things take additional time. It's far cheaper to filter the item before it gets to the listbox. – sircodesalot Sep 16 '14 at 12:50
  • You mean filter by extension? If so I need all files to be listed into listbox not just some extensions... – fulmix Sep 16 '14 at 13:00
  • No I mean if you only want to show items that have a certain hash, then you should do that filtering separately, and then only display that content to the user when you're sure it needs to be presented. Graphics are very computationally expensive, so you should only show stuff if you're absolutely sure you want to show it. – sircodesalot Sep 16 '14 at 13:24
  • I think I understand that way and it should work fine. Though I'm thinking then how could I know the name of the current file being scanned, with the fullName added to ListBox I could see it... – fulmix Sep 16 '14 at 13:59