DriveInfoInternal.Windows.cs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. #nullable enable
  5. using System.Diagnostics;
  6. using System.Text;
  7. namespace System.IO
  8. {
  9. /// <summary>Contains internal volume helpers that are shared between many projects.</summary>
  10. internal static partial class DriveInfoInternal
  11. {
  12. public static string[] GetLogicalDrives()
  13. {
  14. int drives = Interop.Kernel32.GetLogicalDrives();
  15. if (drives == 0)
  16. {
  17. throw Win32Marshal.GetExceptionForLastWin32Error();
  18. }
  19. // GetLogicalDrives returns a bitmask starting from
  20. // position 0 "A" indicating whether a drive is present.
  21. // Loop over each bit, creating a string for each one
  22. // that is set.
  23. uint d = (uint)drives;
  24. int count = 0;
  25. while (d != 0)
  26. {
  27. if (((int)d & 1) != 0) count++;
  28. d >>= 1;
  29. }
  30. string[] result = new string[count];
  31. Span<char> root = stackalloc char[] { 'A', ':', '\\' };
  32. d = (uint)drives;
  33. count = 0;
  34. while (d != 0)
  35. {
  36. if (((int)d & 1) != 0)
  37. {
  38. result[count++] = root.ToString();
  39. }
  40. d >>= 1;
  41. root[0]++;
  42. }
  43. return result;
  44. }
  45. public static string NormalizeDriveName(string driveName)
  46. {
  47. Debug.Assert(driveName != null);
  48. string? name;
  49. if (driveName.Length == 1)
  50. {
  51. name = driveName + ":\\";
  52. }
  53. else
  54. {
  55. name = Path.GetPathRoot(driveName);
  56. // Disallow null or empty drive letters and UNC paths
  57. if (string.IsNullOrEmpty(name) || name.StartsWith("\\\\", StringComparison.Ordinal))
  58. {
  59. throw new ArgumentException(SR.Arg_MustBeDriveLetterOrRootDir, nameof(driveName));
  60. }
  61. }
  62. // We want to normalize to have a trailing backslash so we don't have two equivalent forms and
  63. // because some Win32 API don't work without it.
  64. if (name.Length == 2 && name[1] == ':')
  65. {
  66. name = name + "\\";
  67. }
  68. // Now verify that the drive letter could be a real drive name.
  69. // On Windows this means it's between A and Z, ignoring case.
  70. char letter = driveName[0];
  71. if (!((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z')))
  72. {
  73. throw new ArgumentException(SR.Arg_MustBeDriveLetterOrRootDir, nameof(driveName));
  74. }
  75. return name;
  76. }
  77. }
  78. }