I have often wondered what my hard drive space is getting used for. My little 10GB hard drive hardly seems big enough so I'm constantly searching for directories that have forgotten big files. This simple C# tool will recursively search the directories and print a simple HTML report detailing the results.

The main method of this C# console program is pretty straightforward. It starts by examing the command line arguments. These are the possibilities:
-d<depth> : max directory depth to include in report
-s<directory>: directory in which to begin search
-h or -? : help, presents a list of the possible arguments

So if the user enters DirectorySize -d2 -sc:\windows, then the report will start searching in c:\windows and only show directories less that 2 levels below c:\windows (altough all the directories below windows are searched for their sizes.)

After the command line has been parsed, the beginning of the HTML file is written which simply sets up a table. Next, the recursive method is called to start the process. After the program returns from the method, the end of the HTML file is written, the file is closed and the program is finished.
 
using System;
using System.IO;

namespace DirectorySize
{
 class DirectorySize
 {
  static FileStream fs = new FileStream("report.html", 
     FileMode.Create, FileAccess.Write);
  static StreamWriter writer = new StreamWriter(fs);
  static int maxReportDepth  = -1;
  static int currentDepth = 0;

  static void Main(string[] args)
  {
    // set default search location to c:
    String searchDir = "c:\\";

    // parse the command line arguments
    if (args.Length > 0)
    {
      for (int i = 0; i < args.Length; i++)
      {
       switch(args[i][1])
       {
        case 'd':    // specify max depth for report            
         maxReportDepth = Int32.Parse(args[i].Substring(2));
         break;
        case 's':    // beginning search directory
         // if a directory is specified, we need to make sure it is valid
         try
         {
          searchDir = args[i].Substring(2);
          Directory.SetCurrentDirectory(searchDir);
         }
         catch
         {
          Console.WriteLine(args[0] + " is not a valid directory");
         }
         break;
        case '?':
         goto case 'h';
        case 'h':
         Console.WriteLine("usage: DirectorySize " +
              "[-d&ltdepth>] [-s&ltdirectory>]");
         Console.WriteLine(" -d&ltdepth>: max directory " +
              "depth to include in report");
         Console.WriteLine(" -s&ltdirectory>: directory " +
              "in which to begin search");
         return;
        default:
         Console.WriteLine(args[i] + " - unrecognized argument");
         Console.WriteLine("Use -h for help");
         return;
        }
       }
      }

      // write the beginning of the HTML file
      // this will be a simple table
      writer.WriteLine("&lthtml>");
      writer.WriteLine("&lthead>");
      writer.WriteLine("&lttitle>DirectorySize Report</title>");
      writer.WriteLine("</head>");
      writer.WriteLine("&ltbody>");
      writer.WriteLine("&ltfont face=\"Courier New\" size=\"2\">");
      writer.WriteLine("&lttable border=\"1\" "
       + "bordercolor=\"#111111\" "
       + "width=\"100%\" cellpadding=\"2\" cellspacing=\"0\" "
       + "style=\"border-collapse: collapse\">");
      writer.WriteLine("  &lttr>");
      writer.WriteLine("    &lttd align=\"left\">" +
         "&ltb>Directory</b></td>");
      writer.WriteLine("    &lttd width=\"100\" " +
         "align=\"right\">&ltb>MB</b></td>");
      writer.WriteLine("  </tr>");
      // make the first call to the recursive searching method
      analyzeDirectory(searchDir);

      // close the file off
      writer.WriteLine("</table>");
      writer.WriteLine("</font>");
      writer.WriteLine("</body>");
      writer.WriteLine("</html>");
      writer.Close();
    }

      


The idea of the recursive method analyzeDirectory() is fairly simple. First it gets a list of all the immediate subdirectories. Then it calls analyzeDirectory on each of those subdirectories. After it finishes anazlying the subdirectories, the method gets a list of all the files in the directory. The length (in bytes) of each of these files is added to a running sum. The sum is then convereted to megabytes (by dividing by 1024 twice) and a new row is printed for the table. Finally, the size of the subdirectories plus the size of the files in this directory is returned to the calling method. This completes the recursion.

There is also a variable called currentDepth. This is incremented when a subdirectory is entered and decremented when a directory is left. The point of this is to make sure that only the directory level that the user specified (with the -d option) is included in the report.

  static long analyzeDirectory(string path)
  {

   // first recurse into all subdirectories
   long sizeOfDirectories = 0;
   try
   {
    String[] subdirectories = Directory.GetDirectories(path);
    foreach (String current in subdirectories)
    {
     // moving down one level in the directory structure
     currentDepth++;
     sizeOfDirectories += analyzeDirectory(current);
    }
   }
   catch
   {
    // most likely a security error
    // i.e. the current user doesn't have access to this directory
    Console.WriteLine(path + "   Access denied.");
    currentDepth--;
    return 0;
   }
   // once the subdirectories are searched, get the total
   // size (int bytes) of all files in this path
   long fileBytes = 0;
   foreach (String current in Directory.GetFiles(path))
   {
    FileInfo info = new FileInfo(current);
    fileBytes += info.Length;
   }

   // now write a line indicating the size of the directories added
   // to the size of the files in that directory if this level is
   // included in the report
   if (currentDepth <= maxReportDepth || maxReportDepth == -1)
   {
    writer.WriteLine("  &lttr>");
    writer.WriteLine("    &lttd align=\"left\">" + path + "</td>");
    double total = (double)(sizeOfDirectories + 
          fileBytes)/(1024.0*1024.0);
    writer.WriteLine("    &lttd width=\"100\" align=\"right\">"
          + total.ToString("n") + "</td>");
    writer.WriteLine("  </tr>");
   }

   // moving up one level in the directory structure
   currentDepth--;
   return sizeOfDirectories + fileBytes;
  }
 }
}

      


That's all there is to it. This program obviously takes a couple minutes to run as it has to touch every file on your system. However, a few minutes of CPU chunking is preferrable to manually looking through directories!

This code was accepted as a tutorial on Devhood!

Disclaimer:
By installing this software, you assume complete responsibility for any damage that may occur to your system or your oldest child.  I assume no liability.  Besides, if you tried to sue me you wouldn't get much.  May cause cancer in pregnant lab rats on rainy days in Arkansas. Violators will be persecuted. Do not pass Go, do not collect $200, go directly to jail. An apple a day keeps the doctor away. Caffeine-free (although I am probably not). Look both ways before crossing the road. Don't put that quarter in your mouth, you don't know where it's been. Do not use in areas of high radiation. Do not use while being chased by a rabid African tiger (a healthy one maybe, but not a rabid one). I am not responsible for any lost or stolen brain cells. And last, but not least, have fun!