static void Main(string[] args)
        {
            try
            {
                // DeleteFilesWithProgress.exe d:\home\logfiles\SalesForceIntegration
                if (args.Length == 0)
                {
                    Console.WriteLine(@"Usage: DeleteFilesWithProgress.exe d:\home\logfiles\SalesForceIntegration");
                    return;
                }

                // delete file older than 10 mins
                FileExtensions.DeleteIfOlderTime = DateTime.UtcNow.AddMinutes(-10);

                var    path = new DirectoryInfo(args[0]);
                int    folderCount;
                int    filesCount;
                string largestFile;
                long   largestFileSizeInBytes;
                string largestFolder;
                int    largestFolderChildrenCount;
                int    deletedFileCount = 1;
                bool   incompleteScan;
                var    size = FileExtensions.GetDirectorySizeInBytes(
                    path: path.FullName,
                    skipSymLink: true,
                    continueOnError: false,
                    cancellationToken: CancellationToken.None,
                    maxItemCount: 10000,
                    pauseInterval: 100,
                    folderCount: out folderCount,
                    filesCount: out filesCount,
                    largestFile: out largestFile,
                    largestFileSizeInBytes: out largestFileSizeInBytes,
                    largestFolder: out largestFolder,
                    largestFolderChildrenCount: out largestFolderChildrenCount,
                    deletedFileCount: out deletedFileCount,
                    incompleteScan: out incompleteScan);

                Console.WriteLine("size: {0:n0} bytes", size);
                Console.WriteLine("folderCount: {0:n0} folders", folderCount);
                Console.WriteLine("filesCount: {0:n0} files", filesCount);
                Console.WriteLine("largestFile: {0}", largestFile);
                Console.WriteLine("largestFileSize: {0:n0} bytes", largestFileSizeInBytes);
                Console.WriteLine("largestFolder: {0}", largestFolder);
                Console.WriteLine("largestFolderChildrenCount: {0:n0} items", largestFolderChildrenCount);
                Console.WriteLine("deletedFileCount: {0:n0} files", deletedFileCount);
                Console.WriteLine("incompleteScan: {0}", incompleteScan);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        //public static long GetDirectorySizeInBytes(string path, out int folderCount, out int filesCount, bool continueOnError)
        //{
        //    return GetDirectorySizeInBytes(path, skipSymLink: false, continueOnError: continueOnError, folderCount: out folderCount, filesCount: out filesCount);
        //}

        //public static long GetDirectorySizeInBytes(string path, bool skipSymLink, bool continueOnError, out int folderCount, out int filesCount)
        //{
        //    return GetDirectorySizeInBytes(
        //        path, skipSymLink, continueOnError, CancellationToken.None,
        //        maxItemCount: -1, pauseInterval: 0,
        //        folderCount: out folderCount, filesCount: out filesCount,
        //        largestFile: out _, largestFileSizeInBytes: out _, largestFolder: out _, largestFolderChildrenCount: out _,
        //        incompleteScan: out _);
        //}

        /// <summary>
        /// This method is optimized to avoid recursion and we use pinvoke to retrieve the file attributes in the same call since
        /// performance is critical for this routine.
        /// </summary>
        public static long GetDirectorySizeInBytes(
            string path, bool skipSymLink, bool continueOnError, CancellationToken cancellationToken,
            int maxItemCount, int pauseInterval, out int folderCount, out int filesCount,
            out string largestFile, out long largestFileSizeInBytes,
            out string largestFolder, out int largestFolderChildrenCount,
            out int deletedFileCount, out bool incompleteScan)
        {
            Console.Write($"{DateTime.UtcNow:s}");
            IntPtr INVALID_HANDLE_VALUE    = new IntPtr(-1);
            int    currentDeletedFileCount = 0;

            largestFile                = null;
            largestFileSizeInBytes     = 0;
            largestFolder              = null;
            largestFolderChildrenCount = 0;
            incompleteScan             = true;
            deletedFileCount           = 0;

            List <string> toDeletes = new List <string>();

            Queue <string> foldersQueue = new Queue <string>();

            foldersQueue.Enqueue(path);

            folderCount = filesCount = 0;
            long size = 0;

            while (foldersQueue.Count > 0)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(size);
                }

                string currentFolder = foldersQueue.Dequeue();

                int childrenCount = 0;
                NativeMethods.WIN32_FIND_DATAW findData;
                IntPtr handle = IntPtr.Zero;

                try
                {
                    // Uses a larger buffer for directory queries, which can increase performance of the find operation.
                    handle = NativeMethods.FindFirstFileExW(ToLongPath(currentFolder).TrimEnd('\\') + @"\*",
                                                            NativeMethods.FINDEX_INFO_LEVELS.FindExInfoBasic, // does not query the short file name, improving overall enumeration speed
                                                            out findData,
                                                            NativeMethods.FINDEX_SEARCH_OPS.FindExSearchNameMatch,
                                                            IntPtr.Zero,
                                                            NativeMethods.FIND_FIRST_EX_LARGE_FETCH);

                    if (handle == INVALID_HANDLE_VALUE)
                    {
                        int    error   = Marshal.GetLastWin32Error();
                        string message = string.Format("{0}. {1}", new Win32Exception(error).Message, currentFolder);

                        Win32Exception exception = new Win32Exception(error, message);
                        exception.Data.Add("Method", "FindFirstFile");
                        exception.Data.Add("Error", "INVALID_HANDLE_VALUE");
                        exception.Data.Add("Path", currentFolder);

                        throw exception;
                    }

                    do
                    {
                        // Pause for a little while to avoid spinning 100% CPU when enumerating a large directory, if requested.
                        if (pauseInterval > 1 && (folderCount + filesCount + 1) % pauseInterval == 0)
                        {
                            if ((folderCount + filesCount + 1) % (pauseInterval * 5) == 0)
                            {
                                Console.WriteLine();
                                Console.Write($"{DateTime.UtcNow:s}({deletedFileCount - currentDeletedFileCount} files deleted)");
                                currentDeletedFileCount = deletedFileCount;
                            }
                            Console.Write('.');
                            Thread.Sleep(100);
                        }

                        if (cancellationToken.IsCancellationRequested)
                        {
                            return(size);
                        }

                        if ((findData.dwFileAttributes & FileAttributes.Directory) != 0)
                        {
                            // this is a directory
                            if (skipSymLink && (findData.dwFileAttributes & FileAttributes.ReparsePoint) != 0)
                            {
                                // We want to skip symlinks when directed to
                                continue;
                            }

                            if (findData.cFileName != "." && findData.cFileName != "..")
                            {
                                // count the folder
                                folderCount++;
                                childrenCount++;

                                // Every folder will be charged an extra 1K for file system metadata.
                                // REMARKS: FSRM uses the same logic for calculating usage which
                                // helps blocking tenants to store a bunch of folders since
                                // they can still fill their quota.
                                size += FileSystemMetadataSize;

                                foldersQueue.Enqueue(currentFolder.TrimEnd('\\') + "\\" + findData.cFileName);

                                // Impose a limit on the total number of items we will go through.
                                if (maxItemCount != -1 && (folderCount + filesCount > maxItemCount))
                                {
                                    Console.WriteLine("Hit max limit " + maxItemCount + " items!");
                                    return(size);
                                }
                            }

                            continue;
                        }

                        var ftLastWriteTime = ((long)findData.ftLastWriteTime.dwHighDateTime << 32) + findData.ftLastWriteTime.dwLowDateTime;
                        var lastWriteTime   = DateTime.FromFileTimeUtc(ftLastWriteTime);
                        var toDelete        = lastWriteTime < DeleteIfOlderTime;
                        if (toDelete)
                        {
                            toDeletes.Add(currentFolder.TrimEnd('\\') + "\\" + findData.cFileName);
                            if (toDeletes.Count >= 25)
                            {
                                var deleted = 0;
                                Parallel.ForEach(toDeletes, f =>
                                {
                                    if (FileExtensions.DeleteFileInternal(f, throwIfError: false))
                                    {
                                        Interlocked.Increment(ref deleted);
                                    }
                                });

                                deletedFileCount += deleted;

                                toDeletes.Clear();
                            }
                        }

                        // count the file
                        filesCount++;
                        childrenCount++;

                        // get file size and aggregate the value
                        long filesize = (long)findData.nFileSizeHigh << 32 | (long)findData.nFileSizeLow;
                        size += filesize;

                        // Track file with the largest size.
                        if (filesize > largestFileSizeInBytes)
                        {
                            largestFile            = Path.Combine(currentFolder, findData.cFileName);
                            largestFileSizeInBytes = filesize;
                        }

                        // Every file will be charged an extra 1K for file system metadata.
                        // REMARKS: FSRM uses the same logic for calculating usage which
                        // helps blocking tenants to store a bunch of files with no data since
                        // they can still fill their quota.
                        size += FileSystemMetadataSize;

                        // Impose a limit on the total number of items we will go through.
                        if (maxItemCount != -1 && (folderCount + filesCount > maxItemCount))
                        {
                            return(size);
                        }
                    }while (NativeMethods.FindNextFileW(handle, out findData));

                    // Track folder with the most items.
                    if (childrenCount > largestFolderChildrenCount)
                    {
                        largestFolder = currentFolder;
                        largestFolderChildrenCount = childrenCount;
                    }
                }
                catch (Exception ex)
                {
                    if (continueOnError)
                    {
                        // log and continue
                        Console.WriteLine(ex);
                    }
                    else
                    {
                        throw;
                    }
                }
                finally
                {
                    if (handle != IntPtr.Zero && handle != INVALID_HANDLE_VALUE)
                    {
                        NativeMethods.FindClose(handle);
                    }

                    handle = IntPtr.Zero;
                }
            }

            incompleteScan = false;
            return(size);
        }