I needed an efficient and performant way to keep track of the current number of files/folders in a folder. Directory.GetFiles() was out of the question for a number of reasons. So I ended up using a two step process. First grab the current number of files/folders in the folder using the FindFirstFile() and FindNextFile() Win32 API calls (Which is what Directory.GetFiles() uses under the covers.). These increment the file counter. Then immediately after that completes, activate a FileSystemWatcher to monitor file creation and deletion. These events respectively increment or decrement the counter. I have been putting this through the ringer and it seems to do the job superbly.

Interesting observation about the FindNextFile() method; this method will continue to grab files as long as they exist in the directory, it's not like it's working from some sort of static snapshot of the files created by FindFirstFile() when it is called. So in other words if other files are added to the folder that match the criteria of your filter while you are in the process of iterating with FindNextFile(), FindNextFile() will eventually "find" them. Although this seems implied by the name of both the method calls, you never know what might actually be going on under the hood...

The following code illustrates how to do this. Note that there can potentially be a significant performance hit when a folder is first enumerated. This is especially true when the file/folder count gets past the hundreds of thousands. But once the folder is first enumerated the FSW will keep track of the additional creates/deletes, no need to re-enumerate.

class Program
{

   static FileSystemWatcher _Watcher = new FileSystemWatcher();
   static int _Count = 0;

   static void Main(string[] args)
   {

      _Watcher.Filter = "*.*";
      _Watcher.Path = @"D:\Temp";

      _Watcher.Created += new FileSystemEventHandler(OnCreated);
      _Watcher.Deleted += new FileSystemEventHandler(OnDeleted);

      NativeMethods.FindData FileInfo = new NativeMethods.FindData();

      IntPtr FileHandle = NativeMethods.FindFirstFile(@"D:\Temp\*.*", FileInfo);

      do
      {
         if (FileInfo.fileName != "." && FileInfo.fileName != "..") { _Count++; }
      } while (NativeMethods.FindNextFile(FileHandle, FileInfo) == true);

      NativeMethods.FindClose(FileHandle);
      _Watcher.EnableRaisingEvents = true;

      do
      {
         Console.WriteLine("Total Files: " + _Count.ToString());
      } while (Console.ReadLine() != "q");

   }

   private static void OnCreated(object source, FileSystemEventArgs e)
   {
      _Count++;
   }

   private static void OnDeleted(object source, FileSystemEventArgs e)
   {
      _Count--;
   }

}

class NativeMethods
{
   // Declares a class member for structure element.
   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
   public class FindData
   {
      public int fileAttributes = 0;
      // creationTime was an embedded FILETIME structure.
      public int creationTime_lowDateTime = 0;
      public int creationTime_highDateTime = 0;
      // lastAccessTime was an embedded FILETIME structure.
      public int lastAccessTime_lowDateTime = 0;
      public int lastAccessTime_highDateTime = 0;
      // lastWriteTime was an embedded FILETIME structure.
      public int lastWriteTime_lowDateTime = 0;
      public int lastWriteTime_highDateTime = 0;
      public int nFileSizeHigh = 0;
      public int nFileSizeLow = 0;
      public int dwReserved0 = 0;
      public int dwReserved1 = 0;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
      public String fileName = null;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
      public String alternateFileName = null;
   }

   [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
   public static extern IntPtr FindFirstFile(String fileName, [In, Out] 
   FindData findFileData);

   [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
   public static extern bool FindNextFile(IntPtr hFindFile, [In, Out] 
   FindData findFileData);

   [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
   public static extern bool FindClose(IntPtr hndFindFile);

}