.Net DriveInfo() with UNC paths?

2019-01-28 11:38发布

问题:

Good morning,

is there a way to get a DriveInfo instance for UNC paths (e.g. "\fors343a.ww123.somedomain.net\folder\1\") because for example...

var driveInfo = new System.IO.DriveInfo(drive);

... throws an ArgumentException ("Object must be a root directory (\"C:\\") or a drive letter (\"C\").") when using that UNC path above.

What would I use in order to retrieve information about that or e.g. how would I check whether a given folder resides on a local drive or an unc path?

回答1:

The Remarks section for the DriveInfo constructor says:

The drive name must be either an uppercase or lowercase letter from 'a' to 'z'. You cannot use this method to obtain information on drive names that are nullNothingnullptra null reference (Nothing in Visual Basic) or use UNC (\server\share) paths.

I was able to make it work by mapping a network drive in Windows Explorer. That is, I mapped "\server\share" to drive Z, and then DriveInfo("Z:\\"); gave me what I expected.

Unfortunately, there's no simple way to map a network drive from C#. You'll either have to execute an external command (i.e. "net use z: \server\share"), or call the Windows WNetAddConnection2 API function to do it. Either way you go, you'll need to remove the drive mapping when you're done.



回答2:

On Windows, the following works great in C# (at least to get the sizes, which is what is most commonly needed):

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, out ulong lpFreeBytesAvailable, out ulong lpTotalNumberOfBytes, out ulong lpTotalNumberOfFreeBytes);

Here is some sample code (this hasn't actually been compiled in this form, but is bits and pieces from working code scattered across multiple files):

/// <summary>
/// A compilation of the properties of folders and files in a file system.
/// </summary>
public struct FileSystemProperties
{
    private FileSystemProperties(long? totalBytes, long? freeBytes, long? availableBytes)
        : this()
    {
        TotalBytes = totalBytes;
        FreeBytes = freeBytes;
        AvailableBytes = availableBytes;
    }
    /// <summary>
    /// Gets the total number of bytes on the drive.
    /// </summary>
    public long? TotalBytes { get; private set; }
    /// <summary>
    /// Gets the number of bytes free on the drive.
    /// </summary>
    public long? FreeBytes { get; private set; }
    /// <summary>
    /// Gets the number of bytes available on the drive (counts disk quotas).
    /// </summary>
    public long? AvailableBytes { get; private set; }

    /// <summary>
    /// Gets the properties for this file system.
    /// </summary>
    /// <param name="volumeIdentifier">The path whose volume properties are to be queried.</param>
    /// <param name="cancel">An optional <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
    /// <returns>A <see cref="FileSystemProperties"/> containing the properties for the specified file system.</returns>
    public static FileSystemProperties GetProperties(string volumeIdentifier)
    {
        ulong available;
        ulong total;
        ulong free;
        if (GetDiskFreeSpaceEx(volumeIdentifier, out available, out total, out free))
        {
            return new FileSystemProperties((long)total, (long)free, (long)available);
        }
        return new FileSystemProperties(null, null, null);
    }
    /// <summary>
    /// Asynchronously gets the properties for this file system.
    /// </summary>
    /// <param name="volumeIdentifier">The path whose volume properties are to be queried.</param>
    /// <param name="cancel">An optional <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
    /// <returns>A <see cref="Task"/> containing the <see cref="FileSystemProperties"/> for this entry.</returns>
    public static async Task<FileSystemProperties> GetPropertiesAsync(string volumeIdentifier, CancellationToken cancel = default(CancellationToken))
    {
        return await Task.Run(() =>
        {
            ulong available;
            ulong total;
            ulong free;
            if (GetDiskFreeSpaceEx(volumeIdentifier, out available, out total, out free))
            {
                return new FileSystemProperties((long)total, (long)free, (long)available);
            }
            return new FileSystemProperties(null, null, null);
        }, cancel);
    }
}

Don't try to use this on Linux or Mac--it would have to be rewritten for those (and I'd be interested to see that).