Click here to Skip to main content
Click here to Skip to main content

Flattening a Hierarchy – a producer thread to get all files in a folder and subfolders

, 21 Aug 2003 CPOL
Rate this:
Please Sign up or sign in to vote.
Flattening a Hierarchy using a producer thread to get all files in a folder and subfolders.
<!-- Add the rest of your HTML here -->

Introduction

This article demonstrates a technique to "flatten" a hierarchy to make it appear to be a simple list. Several interesting coding techniques are demonstrated, including threads and how to make WIN32 calls.

I was developing an image viewer when I started thinking about hierarchies. Walking thru a hierarchy, in my case a folder with subfolders and then files inside them, is quite easy using recursion, but often recursion doesn’t quite fit with the design of other parts of the software you are writing. Anyone who has written code to print things will know what I mean.

Lets say you are printing an org-chart of all the people in your company, starting with the CEO, then the line managers, and for each line manager, the people that work for her and so on. Now Windows calls you to print a page at a time. On the second page, you need to remember where you were up to, and if you try to use recursion to walk a hierarchy like this, you are in trouble because you would need to try to recreate the state of the stack at the end of the first page!

So you could try putting all the people into a simple list then use that list to print your report. But in cases where the hierarchy is very large, putting the items in the hierarchy into a list is not always possible due to lack of memory. You only have three hundred people work for your organisation? OK smarty, I'll make it harder - print an org-chart, and for every person, print every project they worked on, and for every project print all the customers, and for every customer... OK you get the picture!

So I developed a technique using a thread that can do the job.

Required model

The following code snippet shows the kind of code I wanted to be able to write

void showFiles(string path)
{
    Flattener f(path);

    foreach (string fn in f)
    {
        Console.WriteLine(fn);
    }
}
Not only is the code made easier to read, by hiding the complexity in the Flattener class, it is more versatile.

Threads to the Rescue

Well just one thread actually.

Take a look at the following code snippet

void showFiles(string path)
{
    foreach (string fn in Directory.GetFiles(path))
    {
        Console.WriteLine(fn);
    }

    foreach (string folder in Directory.GetFolders(path))
    {
        showFiles(folder);
    }
}
Standard stuff really.

Now suppose we make it operate on a separate thread, and change it ever so slightly.

void showFiles(string path)
{
    foreach (string fn in Directory.GetFiles(path))
    {
        m_filename = fn;
        wait();
    }

    foreach (string folder in Directory.GetFolders(path))
    {
        showFiles (folder);
    }
}
All we need to do now is to write the wait() function to wait on a Monitor until the main thread wants the next item.

QED!

Stopping the Thread Prematurely

In the event that the user wants to close down before exhausting all the files, I needed a way to signal the thread to stop, so I wrote an endThread function.
public void endThread()
{
    lock(this)
    {
        m_askedToClose = true;
        Monitor.Pulse(this);
        return;
    }
}
When the thread wakes up it can check the boolean. I found throwing an exception was the easiest way to unwind the recursion.

Enumerators

You may notice that I also derive my enumerator from IDisposable. I did this because I needed to end the thread when someone is finished with the enumerator. I ended up with some ugly code though.
FolderFlattener ff = new FolderFlattener(m_initialFolder);

using (IDisposable d = ff.GetEnumerator() as IDisposable)
{
    IEnumerator en = d as IEnumerator;
    
    etc...
}
I didnt like this all that much, so instead I created my own interface that derived from both
public interface ImyOwnEnumerator : IEnumerator, IDisposable { }
and used it as follows
FolderFlattener ff = new FolderFlattener(m_initialFolder);

using (ImyOwnEnumerator me = ff.GetEnumerator() as ImyOwnEnumerator)
{
    etc...
}
which I think looks a little nicer.

WIN32 calls

I got greatly carried away, and started to dislike the way Directory.GetFiles works. I thought it might be interesting to call the WIN32 FindFirstFile etc. After getting it working, I wondered what possessed me to get started in the first place but couldn’t remember. Anyway the code is still there so ignore it if you are disinterested in how to call WIN32 functions.

Acknowledgements

Thanks to Wesner Moyse for portions of the code - see "A Win32 Library for .Net" www.codeproject.com/csharp/Win32.asp.

Frank Eden

Frank has been in computing since 1973 and sometimes pretends to be an owner builder. For the last 15 years, he has worked for TOWER Software developing TRIM, (http://www.towersoft.com.au/) the worlds premier Document Management Software.

You can contact Frank by email frank.eden a@t towersoft.com.au. <!------------------------------- That's it! --------------------------->

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Frank Eden
Web Developer
Australia Australia
Frank has been in computing since the age of the dinosaurs. He prefers playing table tennis to writing C#, C++, VB, or any of the other dozen or more languages he has written since the Jurasic.

Comments and Discussions

 
GeneralQuestion Pinmember_Random_22-Sep-04 19:04 
GeneralRe: Question PinmemberFrank Eden22-Sep-04 19:27 
GeneralRe: Question Pinmember_Random_23-Sep-04 4:32 
GeneralRe: Question PinmemberFrank Eden5-Oct-04 19:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.2 | Last Updated 22 Aug 2003
Article Copyright 2003 by Frank Eden
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid