10101
10101

Reputation: 2402

Calculate used disk space on network drive

I am trying to create a small tool for inspecting used disk space on network drive. Windows folder property window behaves very strange. While inspecting top level folder, it shows about 300 MB for size, while there are sub folders larger than 50 Gb.

Folder structure:

I have started to search for some other way and found this code:

    public static long DirSize(DirectoryInfo d)
    {

        long size = 0;
        // Add file sizes.
        FileInfo[] fis = d.GetFiles();
        foreach (FileInfo fi in fis)
        {
            size += fi.Length;
        }
        // Add subdirectory sizes.
        DirectoryInfo[] dis = d.GetDirectories();
        foreach (DirectoryInfo di in dis)
        {
            size += DirSize(di);
        }
        return size;
    }

and tried to implement it in WPF form:

using System.IO;
using System.Windows;

namespace Disk_space_inspector
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string targetFolder = PathSrc.Text;

            MessageBox.Show ("The size is " + DirSize(new DirectoryInfo(targetFolder)) / 1000000 + " MB.");
        }

        public static long DirSize(DirectoryInfo d)
        {

            long size = 0;
            // Add file sizes.
            FileInfo[] fis = d.GetFiles();
            foreach (FileInfo fi in fis)
            {
                size += fi.Length;
            }
            // Add subdirectory sizes.
            DirectoryInfo[] dis = d.GetDirectories();
            foreach (DirectoryInfo di in dis)
            {
                size += DirSize(di);
            }
            return size;
        }
    }
}

This works fine for some folders, but it is throwing an error while trying to run it on top level folder.

Managed Debugging Assistant 'ContextSwitchDeadlock' Message=Managed Debugging Assistant 'ContextSwitchDeadlock' : 'The CLR has been unable to transition from COM context 0x134c510 to COM context 0x134c458 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.'

I have tried to add without success:

try
{
}
catch()
{
}

Is there any way to improve it by:

Also if I perform this for each sub folder separately (for example Administration, Projects, Database etc.), everything works. However running current code on top level folder (Company) throws an error.

Upvotes: 0

Views: 1228

Answers (2)

Matthew Watson
Matthew Watson

Reputation: 109537

If all you want to do is to determine the amount of disk space used on the entire network drive for the current user, you can use the Windows API function GetDiskFreeSpaceEx().

First, use P/Invoke to declare it:

/// <summary>Gets the disk free space on a particular volume.</summary>
/// <param name="lpszPath">Full path of folder on required volume. Must end with a backslash!</param>
/// <param name="lpFreeBytesAvailable">Free bytes available on the volume for the current user.</param>
/// <param name="lpTotalNumberOfBytes">Total number of bytes available on the volume for the current user.</param>
/// <param name="lpTotalNumberOfFreeBytes">Total number of bytes available on the volume for all users.</param>
/// <returns>True if successful, false on error.</returns>

[SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), SuppressUnmanagedCodeSecurity]
[DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool GetDiskFreeSpaceEx
(
    string   lpszPath, // Must name a folder.
    out long lpFreeBytesAvailable,
    out long lpTotalNumberOfBytes,
    out long lpTotalNumberOfFreeBytes
);

Then call it. In the example below, "M:\" is the root of one of my network drives; obviously you would use the appropriate path for you:

GetDiskFreeSpaceEx("M:\\", out var freeUser, out var totalUser, out _);

Console.WriteLine($"Used = {(totalUser - freeUser)}");

You can of course also use a UNC path rather than a mapped drive:

GetDiskFreeSpaceEx(@"\\Share\root", out var freeUser, out var totalUser, out _);

This is an extremely fast operation compared to traversing all the files.

Upvotes: 1

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131180

Adding a catch{} won't fix any errors, it will only hides them. They're still there. In this case, the application complains because the code froze the UI for over 60 seconds. Using a catch{} inside the already frozen UI thread won't fix the problem

For starters, use Directory.EnumerateFiles(string searchPattern, System.IO.SearchOption searchOption) with the AllDirectories option to list all files, not just the top directory. Run that in the background so the UI doesn't freeze, eg:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    string targetFolder = PathSrc.Text;
    var dir=new DirectoryInfo(targetFolder);

    var size=await Task.Run(()=> 
            dir.EnumerateFiles("*",SearchOption.AllDirectories)
               .Sum(fi=>fi.Length));

    MessageBox.Show ("The size is " + size / 1000000 + " MB.");
}

Upvotes: 1

Related Questions