1

I want to write a fast disk usage detect program, and found that FileSystemObject is the fastest way to do this. And FileSystemObject is in COM -> Microsoft Scripting Runtime.

All the code is simple, and I parsed here.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DiskUsage
{
    class Program
    {
        const string FolderPath = @"C:\Windows\System32";

        static void Main(string[] args)
        {
            var startTime = DateTime.Now;

            Scripting.FileSystemObject fso = new Scripting.FileSystemObject();
            Scripting.Folder folder = fso.GetFolder(FolderPath);
            Int64 dirSize = (Int64)folder.Size;

            TimeSpan span = DateTime.Now.Subtract(startTime);

            System.Console.WriteLine("{1} size: {0}", dirSize, FolderPath);
            System.Console.WriteLine("use time: {0}", span);
            System.Console.ReadKey();
        }
    }
}

And I setup the app.manifest to

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

When I test my program, Security exception come at this line

Int64 dirSize = (Int64)folder.Size;

The Exception Info( I translate manually, sorry for my poor English. ) is

Unhandled exception type “System.Security.SecurityException” occurred in DiskUsage.exe 
Other message: exception from HRESULT:0x800A0046 (CTL_E_PERMISSIONDENIED)

If I change FolderPath to @"D:\Codes". It works fine. So I think the security setting in manifest is not effect to COM -> Microsoft Scripting Runtime. Anyone know how to fix this? Please help, thanks.

1 Answer 1

2

There is magic happening when that Size property is called. Internally it still traverses the full folder tree and it's files to determine the size of the folder. If your current identity, elevated or not, doesn't have permission to list the files in any subfolder you're out of luck, the call to Size will fail.

I've instead re-implemented the folder traversal using the Directory class found in System.IO but added some logging and exception handling so it will tell you the size of the folder, at least for the files you've access to.

Your main method needs to have this change:

Int64 dirSize;
try { 
    dirSize = (Int64)folder.Size;
}
catch(SecurityException sec)
{
    dirSize = FolderSize(FolderPath);
}

If you run into the exception we make a call to the c# implementation of FolderSize but this time it will compensate for the Unauthorized exceptions.

static Int64 FolderSize(string path)
{
    long sum =0;
    try
    {
        // scan folders
        foreach(var dir in Directory.EnumerateDirectories(path))
        {
            //recursive call
            sum += FolderSize( dir);
        }
    } 
    catch(UnauthorizedAccessException unath)
    {
        Console.WriteLine("{0} for folder {1}", unath.Message, path);
    }
    try
    {
        // scan files
        foreach (var file in Directory.EnumerateFiles(path))
        {
            sum += new FileInfo(file).Length;
        }
    }
    catch(UnauthorizedAccessException filesec)
    {
        Console.WriteLine("{0} for file {1}", filesec.Message, path);
    }
    return sum;
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for your answer. It would work, but I think the core issue is how to gain the permission to access all the files?
You can't have guaranteed permission to all files @Jennal you might elevate your process and/or find the folders that lack permission, try to take ownership of them, assign permissions etc. but that isn't fail safe. My solution offers you the foldersize for the files you have access to.
Is there a way to get administrator permission while runtime? I think it would be the best solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.