/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
namespace Alphaleonis.Win32.Filesystem
{
/// Retrieves information about the amount of space that is available on a disk volume, which is the total amount of space,
/// the total amount of free space, and the total amount of free space available to the user that is associated with the calling thread.
/// This class cannot be inherited.
///
[SerializableAttribute]
[SecurityCritical]
public sealed class DiskSpaceInfo
{
#region Constructor
/// Initializes a DiskSpaceInfo instance.
/// A valid drive path or drive letter. This can be either uppercase or lowercase, 'a' to 'z' or a network share in the format: \\server\share
/// This is a Lazyloading object; call to populate all properties first before accessing.
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")]
[SecurityCritical]
public DiskSpaceInfo(string drivePath)
{
if (Utils.IsNullOrWhiteSpace(drivePath))
throw new ArgumentNullException("drivePath");
if (drivePath.Length == 1)
DriveName += Path.VolumeSeparatorChar;
else
DriveName = Path.GetPathRoot(drivePath, false);
if (Utils.IsNullOrWhiteSpace(DriveName))
throw new ArgumentException("Argument must be a drive letter (\"C\"), RootDir (\"C:\\\") or UNC path (\"\\\\server\\share\")");
// MSDN:
// If this parameter is a UNC name, it must include a trailing backslash (for example, "\\MyServer\MyShare\").
// Furthermore, a drive specification must have a trailing backslash (for example, "C:\").
// The calling application must have FILE_LIST_DIRECTORY access rights for this directory.
DriveName = Path.AddTrailingDirectorySeparator(DriveName, false);
}
/// Initializes a DiskSpaceInfo instance.
/// A valid drive path or drive letter. This can be either uppercase or lowercase, 'a' to 'z' or a network share in the format: \\server\share
/// gets both size- and disk cluster information. Get only disk cluster information, Get only size information.
/// Refreshes the state of the object.
/// suppress any Exception that might be thrown as a result from a failure, such as unavailable resources.
[SecurityCritical]
public DiskSpaceInfo(string drivePath, bool? spaceInfoType, bool refresh, bool continueOnException) : this(drivePath)
{
if (spaceInfoType == null)
_initGetSpaceInfo = _initGetClusterInfo = true;
else
{
_initGetSpaceInfo = (bool) !spaceInfoType;
_initGetClusterInfo = (bool) spaceInfoType;
}
_continueOnAccessError = continueOnException;
if (refresh)
Refresh();
}
#endregion // Constructor
#region Fields
private readonly bool _initGetClusterInfo = true;
private readonly bool _initGetSpaceInfo = true;
private readonly bool _continueOnAccessError;
#endregion // Fields
#region Methods
#region Refresh
/// Refreshes the state of the object.
public void Refresh()
{
Reset();
using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
{
int lastError = (int) Win32Errors.NO_ERROR;
#region Get size information.
if (_initGetSpaceInfo)
{
long freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
if (!NativeMethods.GetDiskFreeSpaceEx(DriveName, out freeBytesAvailable, out totalNumberOfBytes, out totalNumberOfFreeBytes))
lastError = Marshal.GetLastWin32Error();
else
{
FreeBytesAvailable = freeBytesAvailable;
TotalNumberOfBytes = totalNumberOfBytes;
TotalNumberOfFreeBytes = totalNumberOfFreeBytes;
}
if (!_continueOnAccessError && (lastError != Win32Errors.NO_ERROR && lastError != Win32Errors.ERROR_NOT_READY))
NativeError.ThrowException(DriveName);
}
#endregion // Get size information.
#region Get cluster information.
if (_initGetClusterInfo)
{
int sectorsPerCluster, bytesPerSector, numberOfFreeClusters;
uint totalNumberOfClusters;
if (!NativeMethods.GetDiskFreeSpace(DriveName, out sectorsPerCluster, out bytesPerSector, out numberOfFreeClusters, out totalNumberOfClusters))
lastError = Marshal.GetLastWin32Error();
else
{
BytesPerSector = bytesPerSector;
NumberOfFreeClusters = numberOfFreeClusters;
SectorsPerCluster = sectorsPerCluster;
TotalNumberOfClusters = totalNumberOfClusters;
}
if (!_continueOnAccessError && (lastError != Win32Errors.NO_ERROR && lastError != Win32Errors.ERROR_NOT_READY))
NativeError.ThrowException(DriveName);
}
#endregion // Get cluster information.
}
}
#endregion // Refresh
#region Reset
/// Initializes all properties to 0.
private void Reset()
{
if (_initGetSpaceInfo)
FreeBytesAvailable = TotalNumberOfBytes = TotalNumberOfFreeBytes = 0;
if (_initGetClusterInfo)
{
BytesPerSector = NumberOfFreeClusters = SectorsPerCluster = 0;
TotalNumberOfClusters = 0;
}
}
#endregion // Reset
#region ToString
/// Returns the drive name.
/// A string that represents this object.
public override string ToString()
{
return DriveName;
}
#endregion // ToString
#endregion // Methods
#region Properties
/// Indicates the amount of available free space on a drive, formatted as percentage.
public string AvailableFreeSpacePercent
{
get
{
return string.Format(CultureInfo.CurrentCulture, "{0:0.00}%", Utils.PercentCalculate(TotalNumberOfBytes - (TotalNumberOfBytes - TotalNumberOfFreeBytes), 0, TotalNumberOfBytes));
}
}
/// Indicates the amount of available free space on a drive, formatted as a unit size.
public string AvailableFreeSpaceUnitSize
{
get { return Utils.UnitSizeToText(TotalNumberOfFreeBytes); }
}
/// Returns the Clusters size.
public long ClusterSize
{
get { return SectorsPerCluster * BytesPerSector; }
}
/// Gets the name of a drive.
/// The name of the drive.
/// This property is the name assigned to the drive, such as C:\ or E:\
public string DriveName { get; private set; }
/// The total number of bytes on a disk that are available to the user who is associated with the calling thread, formatted as a unit size.
public string TotalSizeUnitSize
{
get { return Utils.UnitSizeToText(TotalNumberOfBytes); }
}
/// Indicates the amount of used space on a drive, formatted as percentage.
public string UsedSpacePercent
{
get
{
return string.Format(CultureInfo.CurrentCulture, "{0:0.00}%", Utils.PercentCalculate(TotalNumberOfBytes - FreeBytesAvailable, 0, TotalNumberOfBytes));
}
}
/// Indicates the amount of used space on a drive, formatted as a unit size.
public string UsedSpaceUnitSize
{
get { return Utils.UnitSizeToText(TotalNumberOfBytes - FreeBytesAvailable); }
}
/// The total number of free bytes on a disk that are available to the user who is associated with the calling thread.
public long FreeBytesAvailable { get; private set; }
/// The total number of bytes on a disk that are available to the user who is associated with the calling thread.
public long TotalNumberOfBytes { get; private set; }
/// The total number of free bytes on a disk.
public long TotalNumberOfFreeBytes { get; private set; }
/// The number of bytes per sector.
public int BytesPerSector { get; private set; }
/// The total number of free clusters on the disk that are available to the user who is associated with the calling thread.
public int NumberOfFreeClusters { get; private set; }
/// The number of sectors per cluster.
public int SectorsPerCluster { get; private set; }
/// The total number of clusters on the disk that are available to the user who is associated with the calling thread.
/// If per-user disk quotas are in use, this value may be less than the total number of clusters on the disk.
///
public long TotalNumberOfClusters { get; private set; }
#endregion // Properties
}
}