You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

266 lines
11 KiB

  1. /* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. using System;
  22. using System.Diagnostics.CodeAnalysis;
  23. using System.Globalization;
  24. using System.Runtime.InteropServices;
  25. using System.Security;
  26. namespace Alphaleonis.Win32.Filesystem
  27. {
  28. /// <summary>Retrieves information about the amount of space that is available on a disk volume, which is the total amount of space,
  29. /// the total amount of free space, and the total amount of free space available to the user that is associated with the calling thread.
  30. /// <para>This class cannot be inherited.</para>
  31. /// </summary>
  32. [SerializableAttribute]
  33. [SecurityCritical]
  34. public sealed class DiskSpaceInfo
  35. {
  36. #region Constructor
  37. /// <summary>Initializes a DiskSpaceInfo instance.</summary>
  38. /// <param name="drivePath">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</param>
  39. /// <Remark>This is a Lazyloading object; call <see cref="Refresh()"/> to populate all properties first before accessing.</Remark>
  40. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")]
  41. [SecurityCritical]
  42. public DiskSpaceInfo(string drivePath)
  43. {
  44. if (Utils.IsNullOrWhiteSpace(drivePath))
  45. throw new ArgumentNullException("drivePath");
  46. if (drivePath.Length == 1)
  47. DriveName += Path.VolumeSeparatorChar;
  48. else
  49. DriveName = Path.GetPathRoot(drivePath, false);
  50. if (Utils.IsNullOrWhiteSpace(DriveName))
  51. throw new ArgumentException("Argument must be a drive letter (\"C\"), RootDir (\"C:\\\") or UNC path (\"\\\\server\\share\")");
  52. // MSDN:
  53. // If this parameter is a UNC name, it must include a trailing backslash (for example, "\\MyServer\MyShare\").
  54. // Furthermore, a drive specification must have a trailing backslash (for example, "C:\").
  55. // The calling application must have FILE_LIST_DIRECTORY access rights for this directory.
  56. DriveName = Path.AddTrailingDirectorySeparator(DriveName, false);
  57. }
  58. /// <summary>Initializes a DiskSpaceInfo instance.</summary>
  59. /// <param name="drivePath">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</param>
  60. /// <param name="spaceInfoType"><see langword="null"/> gets both size- and disk cluster information. <see langword="true"/> Get only disk cluster information, <see langword="false"/> Get only size information.</param>
  61. /// <param name="refresh">Refreshes the state of the object.</param>
  62. /// <param name="continueOnException"><see langword="true"/> suppress any Exception that might be thrown as a result from a failure, such as unavailable resources.</param>
  63. [SecurityCritical]
  64. public DiskSpaceInfo(string drivePath, bool? spaceInfoType, bool refresh, bool continueOnException) : this(drivePath)
  65. {
  66. if (spaceInfoType == null)
  67. _initGetSpaceInfo = _initGetClusterInfo = true;
  68. else
  69. {
  70. _initGetSpaceInfo = (bool) !spaceInfoType;
  71. _initGetClusterInfo = (bool) spaceInfoType;
  72. }
  73. _continueOnAccessError = continueOnException;
  74. if (refresh)
  75. Refresh();
  76. }
  77. #endregion // Constructor
  78. #region Fields
  79. private readonly bool _initGetClusterInfo = true;
  80. private readonly bool _initGetSpaceInfo = true;
  81. private readonly bool _continueOnAccessError;
  82. #endregion // Fields
  83. #region Methods
  84. #region Refresh
  85. /// <summary>Refreshes the state of the object.</summary>
  86. public void Refresh()
  87. {
  88. Reset();
  89. using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
  90. {
  91. int lastError = (int) Win32Errors.NO_ERROR;
  92. #region Get size information.
  93. if (_initGetSpaceInfo)
  94. {
  95. long freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
  96. if (!NativeMethods.GetDiskFreeSpaceEx(DriveName, out freeBytesAvailable, out totalNumberOfBytes, out totalNumberOfFreeBytes))
  97. lastError = Marshal.GetLastWin32Error();
  98. else
  99. {
  100. FreeBytesAvailable = freeBytesAvailable;
  101. TotalNumberOfBytes = totalNumberOfBytes;
  102. TotalNumberOfFreeBytes = totalNumberOfFreeBytes;
  103. }
  104. if (!_continueOnAccessError && (lastError != Win32Errors.NO_ERROR && lastError != Win32Errors.ERROR_NOT_READY))
  105. NativeError.ThrowException(DriveName);
  106. }
  107. #endregion // Get size information.
  108. #region Get cluster information.
  109. if (_initGetClusterInfo)
  110. {
  111. int sectorsPerCluster, bytesPerSector, numberOfFreeClusters;
  112. uint totalNumberOfClusters;
  113. if (!NativeMethods.GetDiskFreeSpace(DriveName, out sectorsPerCluster, out bytesPerSector, out numberOfFreeClusters, out totalNumberOfClusters))
  114. lastError = Marshal.GetLastWin32Error();
  115. else
  116. {
  117. BytesPerSector = bytesPerSector;
  118. NumberOfFreeClusters = numberOfFreeClusters;
  119. SectorsPerCluster = sectorsPerCluster;
  120. TotalNumberOfClusters = totalNumberOfClusters;
  121. }
  122. if (!_continueOnAccessError && (lastError != Win32Errors.NO_ERROR && lastError != Win32Errors.ERROR_NOT_READY))
  123. NativeError.ThrowException(DriveName);
  124. }
  125. #endregion // Get cluster information.
  126. }
  127. }
  128. #endregion // Refresh
  129. #region Reset
  130. /// <summary>Initializes all <see ref="Alphaleonis.Win32.Filesystem.DiskSpaceInfo"/> properties to 0.</summary>
  131. private void Reset()
  132. {
  133. if (_initGetSpaceInfo)
  134. FreeBytesAvailable = TotalNumberOfBytes = TotalNumberOfFreeBytes = 0;
  135. if (_initGetClusterInfo)
  136. {
  137. BytesPerSector = NumberOfFreeClusters = SectorsPerCluster = 0;
  138. TotalNumberOfClusters = 0;
  139. }
  140. }
  141. #endregion // Reset
  142. #region ToString
  143. /// <summary>Returns the drive name.</summary>
  144. /// <returns>A string that represents this object.</returns>
  145. public override string ToString()
  146. {
  147. return DriveName;
  148. }
  149. #endregion // ToString
  150. #endregion // Methods
  151. #region Properties
  152. /// <summary>Indicates the amount of available free space on a drive, formatted as percentage.</summary>
  153. public string AvailableFreeSpacePercent
  154. {
  155. get
  156. {
  157. return string.Format(CultureInfo.CurrentCulture, "{0:0.00}%", Utils.PercentCalculate(TotalNumberOfBytes - (TotalNumberOfBytes - TotalNumberOfFreeBytes), 0, TotalNumberOfBytes));
  158. }
  159. }
  160. /// <summary>Indicates the amount of available free space on a drive, formatted as a unit size.</summary>
  161. public string AvailableFreeSpaceUnitSize
  162. {
  163. get { return Utils.UnitSizeToText(TotalNumberOfFreeBytes); }
  164. }
  165. /// <summary>Returns the Clusters size.</summary>
  166. public long ClusterSize
  167. {
  168. get { return SectorsPerCluster * BytesPerSector; }
  169. }
  170. /// <summary>Gets the name of a drive.</summary>
  171. /// <returns>The name of the drive.</returns>
  172. /// <remarks>This property is the name assigned to the drive, such as C:\ or E:\</remarks>
  173. public string DriveName { get; private set; }
  174. /// <summary>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.</summary>
  175. public string TotalSizeUnitSize
  176. {
  177. get { return Utils.UnitSizeToText(TotalNumberOfBytes); }
  178. }
  179. /// <summary>Indicates the amount of used space on a drive, formatted as percentage.</summary>
  180. public string UsedSpacePercent
  181. {
  182. get
  183. {
  184. return string.Format(CultureInfo.CurrentCulture, "{0:0.00}%", Utils.PercentCalculate(TotalNumberOfBytes - FreeBytesAvailable, 0, TotalNumberOfBytes));
  185. }
  186. }
  187. /// <summary>Indicates the amount of used space on a drive, formatted as a unit size.</summary>
  188. public string UsedSpaceUnitSize
  189. {
  190. get { return Utils.UnitSizeToText(TotalNumberOfBytes - FreeBytesAvailable); }
  191. }
  192. /// <summary>The total number of free bytes on a disk that are available to the user who is associated with the calling thread.</summary>
  193. public long FreeBytesAvailable { get; private set; }
  194. /// <summary>The total number of bytes on a disk that are available to the user who is associated with the calling thread.</summary>
  195. public long TotalNumberOfBytes { get; private set; }
  196. /// <summary>The total number of free bytes on a disk.</summary>
  197. public long TotalNumberOfFreeBytes { get; private set; }
  198. /// <summary>The number of bytes per sector.</summary>
  199. public int BytesPerSector { get; private set; }
  200. /// <summary>The total number of free clusters on the disk that are available to the user who is associated with the calling thread.</summary>
  201. public int NumberOfFreeClusters { get; private set; }
  202. /// <summary>The number of sectors per cluster.</summary>
  203. public int SectorsPerCluster { get; private set; }
  204. /// <summary>The total number of clusters on the disk that are available to the user who is associated with the calling thread.
  205. /// If per-user disk quotas are in use, this value may be less than the total number of clusters on the disk.
  206. /// </summary>
  207. public long TotalNumberOfClusters { get; private set; }
  208. #endregion // Properties
  209. }
  210. }