4

Coming from the VB6 era, I was able to use the "on error resume next" in a relatively appropriate manner when recursing through directories on my system. If my "foreach" loop encountered a Permission Denied or Access Denied error, all I had to do was call the "resume next" statement.

In C# however, this does not exist and I appreciate why. However, it boggles my mind to figure out how this is possible in C#.

I am trying to recurse through the directories on my hard drive and populate a TreeView control.

private void PopulateTree(string dir, TreeNode node)
    {
        try
        {
            // get the information of the directory
            DirectoryInfo directory = new DirectoryInfo(dir);

            // loop through each subdirectory
            foreach (DirectoryInfo d in directory.GetDirectories("*", SearchOption.AllDirectories))
            {
                // create a new node
                TreeNode t = new TreeNode(d.Name);
                // populate the new node recursively
                PopulateTree(d.FullName, t);
                node.Nodes.Add(t); // add the node to the "master" node
            }

            // lastly, loop through each file in the directory, and add these as nodes
            foreach (FileInfo f in directory.GetFiles())
            {
                // create a new node
                TreeNode t = new TreeNode(f.Name);
                // add it to the "master"
                node.Nodes.Add(t);
            }
        }
        catch (System.Exception e)
        {
            MessageBox.Show(e.Message, "Error Loading Directories", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

    }

The code is expected to work. However, the very moment it reaches the "C:\\Documents and Settings" folder on a Windows 7 machine, the catch block traps the "Access Denied" error (which I expect). What I would like to do is CONTINUE on with the next folder in series.

So the question is, how can I make this possible in C#?

My research shows a common opinion to use a TRY...CATCH block, but it doesn't show me how to do something so simple as what I am wanting to do above.

NOTE: I also try modifying the code to check the attributes as follows but it too fails.

private void PopulateTree(string dir, TreeNode node)
    {
        try
        {
            // get the information of the directory
            DirectoryInfo directory = new DirectoryInfo(dir);

            if (directory.Attributes == FileAttributes.ReparsePoint || directory.Attributes == FileAttributes.System)
            {
                Console.Write("Access denied to folder.");
            }
            else
            {

                // loop through each subdirectory
                foreach (DirectoryInfo d in directory.GetDirectories("*", SearchOption.AllDirectories))
                {
                    // create a new node
                    TreeNode t = new TreeNode(d.Name);
                    // populate the new node recursively
                    PopulateTree(d.FullName, t);
                    node.Nodes.Add(t); // add the node to the "master" node
                }

                // lastly, loop through each file in the directory, and add these as nodes
                foreach (FileInfo f in directory.GetFiles())
                {
                    // create a new node
                    TreeNode t = new TreeNode(f.Name);
                    // add it to the "master"
                    node.Nodes.Add(t);
                }
            }
        }
        catch (System.Exception e)
        {
            MessageBox.Show(e.Message, "Error Loading Directories", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

    }
J Bradley
  • 109
  • 1
  • 3
  • 12

4 Answers4

7

Move the try/catch block into the foreach loop, so you have the populating code in the try block. That way, you don't drop out of the loop when an exception is encountered.

foreach(var item in col)
{
  try
  {
     //do stuff with item
  }
  catch 
  {
     //yes, this is empty, but in this case it is okay as there is no other way
  }
}
Femaref
  • 60,705
  • 7
  • 138
  • 176
  • I'm afraid I tried that and sinch the "foreach" statement executes first, the inner try block is never reached and therefore it hits the outter catch block. – J Bradley Mar 13 '11 at 15:43
4

I think your method of choosing sub-directories is flawed, that's why you get the access exceptions - you have to exclude system directories, so something like this should work:

var subDirectories = directory.GetDirectories()
                              .Where(d => (d.Attributes & FileAttributes.ReparsePoint) ==0  
                                     && (d.Attributes & FileAttributes.System) == 0);
foreach (DirectoryInfo d in subDirectories)
{
  //...
}   

In your version using directory.GetDirectories("*", SearchOption.AllDirectories) you specifically ask for system directories to be included - SearchOption.AllDirectories will include system directories and reparse points. From MSDN:

The weakness in this approach is that if any one of the subdirectories under the specified root causes a DirectoryNotFoundException or UnauthorizedAccessException, the whole method fails and returns no directories. The same is true when you use the GetFiles method. If you have to handle these exceptions on specific subfolders, you must manually walk the directory tree, as shown in the following examples.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • I believe you are right and this is sort of what I believed. Unfortunately I am new to C# so even learning about LINQ is new too. From your code, I follow you up until the argument "d->" and then (d.Attributes....). I don't understand the "d->". This looks somewhat like a pointer in C++ but you have a double arrow. I also don't see where or how "d" by itself is defined. – J Bradley Mar 17 '11 at 23:44
  • Thanks very much for your reply as well as the others. I believe you are onto the right answer here. I look forward to your reply. – J Bradley Mar 17 '11 at 23:47
  • I'm looking into LINQ and operator overloading which is where I think I might find the answer to my question. If I am on the right track, perhaps you can articulate how I am to interpret everything in the Where statement. I appreciate your help!!!! – J Bradley Mar 17 '11 at 23:56
  • @J D Bradley: The `Where(..)` part is a predicate (a form of a lambda expression commonly used in LINQ that returns a boolean), this filters out all directories from the `GetDirectories()` call that do not pass its condition, in this case the condition is that the directory must not be a reparsepoint and it must not be a marked as System. The end result will be all directories that pass these conditions, so you execute your `foreach` only on those - so you won't get an exception. – BrokenGlass Mar 18 '11 at 00:42
0

For this particular instance (the find files case), I have a work around that avoids the resume after exception problem (see below).

The problem is that GetFiles()/GetDirectories() or EnumerateFiles().GetEnumerator() forces evaluation of the Enumerable, and thus causes the exception to be thrown at that point.

see https://stackoverflow.com/a/9831340/89584 for a code example.

Community
  • 1
  • 1
Malcolm
  • 1,239
  • 1
  • 14
  • 25
0

Instead of Try-Catching around the whole thing why not do it just where you need it to be.

private void PopulateTree(string dir, TreeNode node)
    {
        // get the information of the directory
        DirectoryInfo directory = new DirectoryInfo(dir);

    // loop through each subdirectory
    foreach (DirectoryInfo d in directory.GetDirectories("*", SearchOption.AllDirectories))
    {
       try
       {
            // create a new node
            TreeNode t = new TreeNode(d.Name);
            // populate the new node recursively
            PopulateTree(d.FullName, t);
            node.Nodes.Add(t); // add the node to the "master" node
        }
        catch (System.Exception e)
        {
            MessageBox.Show(e.Message, "Error Loading Directories", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
    }

    // lastly, loop through each file in the directory, and add these as nodes
    foreach (FileInfo f in directory.GetFiles())
    {
        // create a new node
        TreeNode t = new TreeNode(f.Name);
        // add it to the "master"
        node.Nodes.Add(t);
    }
}
msarchet
  • 15,104
  • 2
  • 43
  • 66
  • 1
    on your example he would need try and catch on both foreach as both have possibility of trying to access a folder with denied permission as well as at the `DirectoryInfo directory = new DirectoryInfo(dir);` as you don't know wether you can or cannot access it. – Prix Mar 13 '11 at 15:58