Security Exception while use FileSystemObject in C

2019-07-25 11:50发布

问题:

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:

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;
}