KnowDotNet NetRefactor

Examining Special Folders

by William Ryan
Print this Article Discuss in Forums

Normally, when .NET Programmers manipulate files and folders, we use System.IO and perform tasks like creating a directory, deleting a file etc.  Well, Windows has some folders known as Special Folders.  'Special Folders is an enumeration for many well, Special Folders like History, Desktop, History, Startup, Program Files and many more.  If you aren't familiar with them, I'd encourage you to visit the link I provided and look through them...I'm sure it will open your eyes to some possible shortcuts and timesavers.

Ok, so how do we use these things and how do they fit into the System.IO namespace?  As I mentioned, special folders are merely an enumeration and they belong to the Environment class.  As such, you can access them just like you would any other directory or folder.  

To find out about a folder or directory (like if it exists, when it was created etc.), you can use either the Static/Shared Directory class or you can use the instance counterpart, DirectoryInfo.  Similarly, with files you can use the Shared/Static File or the instance counterpart, FileInfo.  A few articles back , I show you how to enumerate a directory.  I mention that you could make it a lot more powerful using Recursion.  So, let's walk through this:

First, include your namespace references (in this case, System.IO):

using System.IO;


To find out about a Special Folder, you can interrogate the Environment class. In this case, since I know that their is a 'Favorties' special folder that contains all of my Internet Explorer favorites and that's what I want to access, I'll reference it explicitly:

DirectoryInfo di = new DirectoryInfo(System.Environment.GetFolderPath(Environment.SpecialFolder.Favorites));

All I'm doing here is declaring and instantiating an new instance of a DirectoryInfo class and passing a reference to the Favorites Special Folder via the constructor.  At this point, assuming their is a Favorites folder and we have permission to access it, our 'di' will know all about it.  But now that I know the Path to it, what good does that do me?  How can I extract any of the favorites or links from it?  To do that, we'll need to walk the folder and each of its folders and list the files.  So, I'll drag a TreeView control on to a blank form and insert a button onto it.  This probably isn't a realistic example but it should server to illustrate how things work:

Behind the button's click event, I'm going to use the 'di' declaration above and then pass it to a function I called EnumerateFavorites().  Hence, the call will look like this:

DirectoryInfo di =
new DirectoryInfo(System.Environment.GetFolderPath(Environment.SpecialFolder.Favorites));
EnumerateFavorites(di);      

All the work however, is done in EnumerateFavorites:


private void EnumerateFavorites(DirectoryInfo dii)
{
    tv.Nodes.Add(dii.FullName);
  
int i = tv.Nodes.Count-1;
  
foreach(DirectoryInfo dI in dii.GetDirectories())
   {            
       EnumerateFavorites(dI);
   }

  
foreach(FileInfo fi in dii.GetFiles())
   {
           tv.Nodes[i].Nodes.Add(fi.FullName);
   }
}


To see what's going on behind the scenes in the IL Instructions, the disassembly code is provided as well (Yes, it's amazing how the .NET Languages encapsulate IL Instructions):

ILDASM.exe Output for the EnumerateFavorites Method:

.method private hidebysig instance void  EnumerateFavorites(class [mscorlib]System.IO.DirectoryInfo dii) cil managed
{
  // Code size       206 (0xce)
  .maxstack  4
  .locals init ([0] int32 i,
           [1] class [mscorlib]System.IO.DirectoryInfo dI,
           [2] class [mscorlib]System.IO.FileInfo fi,
           [3] class [mscorlib]System.IO.DirectoryInfo[] CS$00000007$00000000,
           [4] int32 CS$00000008$00000001,
           [5] class [mscorlib]System.IO.FileInfo[] CS$00000007$00000002,
           [6] int32 CS$00000008$00000003)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TreeView WindowsApplication6.Form1::tv
  IL_0006:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection [System.Windows.Forms]System.Windows.Forms.TreeView::get_Nodes()
  IL_000b:  ldarg.1
  IL_000c:  callvirt   instance string [mscorlib]System.IO.FileSystemInfo::get_FullName()
  IL_0011:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNode [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection::Add(string)
  IL_0016:  pop
  IL_0017:  ldarg.0
  IL_0018:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TreeView WindowsApplication6.Form1::tv
  IL_001d:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection [System.Windows.Forms]System.Windows.Forms.TreeView::get_Nodes()
  IL_0022:  callvirt   instance int32 [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection::get_Count()
  IL_0027:  ldc.i4.1
  IL_0028:  sub
  IL_0029:  stloc.0
  IL_002a:  ldarg.1
  IL_002b:  callvirt   instance class [mscorlib]System.IO.DirectoryInfo[] [mscorlib]System.IO.DirectoryInfo::GetDirectories()
  IL_0030:  stloc.3
  IL_0031:  ldc.i4.0
  IL_0032:  stloc.s    CS$00000008$00000001
  IL_0034:  br.s       IL_0048
  IL_0036:  ldloc.3
  IL_0037:  ldloc.s    CS$00000008$00000001
  IL_0039:  ldelem.ref
  IL_003a:  stloc.1
  IL_003b:  ldarg.0
  IL_003c:  ldloc.1
  IL_003d:  call       instance void WindowsApplication6.Form1::EnumerateFavorites(class [mscorlib]System.IO.DirectoryInfo)
  IL_0042:  ldloc.s    CS$00000008$00000001
  IL_0044:  ldc.i4.1
  IL_0045:  add
  IL_0046:  stloc.s    CS$00000008$00000001
  IL_0048:  ldloc.s    CS$00000008$00000001
  IL_004a:  ldloc.3
  IL_004b:  ldlen
  IL_004c:  conv.i4
  IL_004d:  blt.s      IL_0036
  IL_004f:  ldarg.1
  IL_0050:  callvirt   instance class [mscorlib]System.IO.FileInfo[] [mscorlib]System.IO.DirectoryInfo::GetFiles()
  IL_0055:  stloc.s    CS$00000007$00000002
  IL_0057:  ldc.i4.0
  IL_0058:  stloc.s    CS$00000008$00000003
  IL_005a:  br.s       IL_00c5
  IL_005c:  ldloc.s    CS$00000007$00000002
  IL_005e:  ldloc.s    CS$00000008$00000003
  IL_0060:  ldelem.ref
  IL_0061:  stloc.2
  IL_0062:  ldloc.2
  IL_0063:  callvirt   instance valuetype [mscorlib]System.IO.FileAttributes [mscorlib]System.IO.FileSystemInfo::get_Attributes()
  IL_0068:  box        [mscorlib]System.IO.FileAttributes
  IL_006d:  call       instance string [mscorlib]System.Enum::ToString()
  IL_0072:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_0077:  ldarg.0
  IL_0078:  ldfld      class [System.Windows.Forms]System.Windows.Forms.TreeView WindowsApplication6.Form1::tv
  IL_007d:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection [System.Windows.Forms]System.Windows.Forms.TreeView::get_Nodes()
  IL_0082:  ldloc.0
  IL_0083:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNode [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection::get_Item(int32)
  IL_0088:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection [System.Windows.Forms]System.Windows.Forms.TreeNode::get_Nodes()
  IL_008d:  ldloc.2
  IL_008e:  callvirt   instance string [mscorlib]System.IO.FileSystemInfo::get_FullName()
  IL_0093:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TreeNode [System.Windows.Forms]System.Windows.Forms.TreeNodeCollection::Add(string)
  IL_0098:  pop
  IL_0099:  ldarg.0
  IL_009a:  ldfld      class [System.Windows.Forms]System.Windows.Forms.RichTextBox WindowsApplication6.Form1::rtb
  IL_009f:  dup
  IL_00a0:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text()
  IL_00a5:  ldloc.2
  IL_00a6:  callvirt   instance string [mscorlib]System.IO.FileSystemInfo::get_FullName()
  IL_00ab:  callvirt   instance string [mscorlib]System.String::ToString()
  IL_00b0:  ldstr      "\r\n"
  IL_00b5:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)
  IL_00ba:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
  IL_00bf:  ldloc.s    CS$00000008$00000003
  IL_00c1:  ldc.i4.1
  IL_00c2:  add
  IL_00c3:  stloc.s    CS$00000008$00000003
  IL_00c5:  ldloc.s    CS$00000008$00000003
  IL_00c7:  ldloc.s    CS$00000007$00000002
  IL_00c9:  ldlen
  IL_00ca:  conv.i4
  IL_00cb:  blt.s      IL_005c
  IL_00cd:  ret
} // end of method Form1::EnumerateFavorites


ILDASM.exe Output And the button Click that starts everything:


.method private hidebysig instance void  button4_Click(object sender,
                                                       class [mscorlib]System.EventArgs e) cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Collections.ArrayList all,
           [1] class [mscorlib]System.IO.DirectoryInfo di)
  IL_0000:  newobj     instance void [mscorlib]System.Collections.ArrayList::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldc.i4.6
  IL_0007:  call       string [mscorlib]System.Environment::GetFolderPath(valuetype [mscorlib]System.Environment/SpecialFolder)
  IL_000c:  newobj     instance void [mscorlib]System.IO.DirectoryInfo::.ctor(string)
  IL_0011:  stloc.1
  IL_0012:  ldarg.0
  IL_0013:  ldloc.1
  IL_0014:  call       instance void WindowsApplication6.Form1::EnumerateFavorites(class [mscorlib]System.IO.DirectoryInfo)
  IL_0019:  ret
} // end of method Form1::button4_Click

Basically I'm just passing in a DirectoryInfo object which corresponds to a directory or folder in the Favorites folder (I could just as easily have used the some other method like GetLogicalDrives to get some directories and pass these into a function to enumerate them.  Anyway, each time a DirectoryInfo is passed to the function, it's FullName property is added as a root node to the TreeView.  In order to add child nodes to a TreeView node, you need to know its numeric Index, which I'm keeping track of with the variable 'i'.  Each time through I add a node, and then set i equal to the count of nodes in the Nodes collection less one (it's zero based).  That way, when I add child nodes to it, I know where I am.  There are other ways to get here, but as far as examples go, this makes what I'm doing pretty easy to follow.

Now, after I add the node I use the GetDirectories() method of the DirectoryInfo class.  If I don't find any (which means that a directory doesn't have any subdirectories), than I walk through it, adding each FileInfo's .FullName property as a child node.  However, if a directory does have subdirectories, I call the EnumerateFavorite method, passing it in the currect DirectoryInfo (which starts the process all over again until we no longer have any directories.  At that point, we fill the TreeView with that names of each file and go back up the stack).  I'd like to point out though that using this methodology will yield a Treeivew that's not exactly heirachical b/c every directory will be inserted at the same level.  So a subdirectory will appear in the same place as a root directory. The purpose of this article was to illustrate using simple recursion and using the SpecialFolders enumeration of the Environment class.    If you want to enumerate directories and create a Windows Explorer like interface  this tutorial shows you how to do it.

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing