Getting Bytes Per Sector using WMI in C# by only knowing the Partition Letter

22 Views Asked by At

For my application, I need to determine the best drive / partition. The requirements are as follows:

  1. 20GB or more free
  2. The drive must be fixed (as in, it mustn't be a detachable USB Drive, etc)
  3. The underlying disk must have a BytesPerSector value of 512 OR 4096.

Points 1 and 2 can be easily parsed with the DriveInfo class in C#. However I get stuck at point 3...

Getting the BytesPerSector value from the Win32_DiskDrive class is done with an ManagementObjectSearch. However I can't parse a specific drive.

For example. Let's say I want to find the BytesPerSector value of C:\

            // Specify the drive letter (change it to the desired letter)
            string driveLetter = "C:";

            // Get DriveInfo for the specified drive letter
            DriveInfo driveInfo = new(driveLetter);

            // Check if the drive is ready (mounted)
            Assert.IsTrue(driveInfo.IsReady, $"Expected drive to be ready but was not");
            Console.WriteLine("Format: " + driveInfo.Name);
            // Get the disk drive associated with the partition
            var diskDrive = new System.Management.ManagementObjectSearcher(
                "SELECT * FROM Win32_DiskDrive WHERE DeviceID = '" + driveInfo.Name + "'"
            ).Get().OfType<System.Management.ManagementObject>().FirstOrDefault();

            // Check if the disk drive is found
            Assert.IsNotNull(diskDrive, $"Expected disk drive to be found but was null");

            // Get the BytesPerSector information
            uint bytesPerSector = Convert.ToUInt32(diskDrive["BytesPerSector"]);

            // Print out the BytesPerSector information
            Assert.IsTrue(bytesPerSector == 512 || bytesPerSector == 4096, $"Expected 512 or 4096 but was {bytesPerSector}");

This fails with the exception:

Message:  Test method TestSectorInfo threw exception: System.Management.ManagementException: Invalid query

Some digging around led me to the discovery that the DeviceID expected by Win32_DiskDrive looks like so:

\\.\PHYSICALDRIVE0 \\.\PHYSICALDRIVE1 etc.

But I can't know for certain whether C:\ is on DRIVE0 or DRIVE1 so I first have to somehow translate the partition letter to its underlying disk... How do I even do that?

I've also tried passing the letter of the volume to the query which doesn't work, and looked for any c# classes that might do what I need to no avail.

1

There are 1 best solutions below

0
João Gervas On

The issue you’re encountering is due to the fact that the DeviceID for the Win32_DiskDrive WMI class is not the same as the drive letter. The DeviceID is a unique identifier for the physical drive, not the partition or volume.

To get the BytesPerSector for a specific drive letter, you’ll need to go through several steps:

Get the Win32_Volume instance that corresponds to the drive letter. Get the associated Win32_DiskPartition instance. Get the associated Win32_DiskDrive instance. Here’s how you can do it:

// Specify the drive letter (change it to the desired letter)
string driveLetter = "C:";

// Get the Win32_Volume instance for the drive letter
var volume = new System.Management.ManagementObjectSearcher(
    "SELECT * FROM Win32_Volume WHERE Name = '" + driveLetter + "\\'"
).Get().OfType<System.Management.ManagementObject>().FirstOrDefault();

// Check if the volume is found
Assert.IsNotNull(volume, $"Expected volume to be found but was null");

// Get the associated Win32_DiskPartition instance
var partition = new System.Management.ManagementObjectSearcher(
    "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + volume["DeviceID"] + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition"
).Get().OfType<System.Management.ManagementObject>().FirstOrDefault();

// Check if the partition is found
Assert.IsNotNull(partition, $"Expected partition to be found but was null");

// Get the associated Win32_DiskDrive instance
var diskDrive = new System.Management.ManagementObjectSearcher(
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + partition["DeviceID"] + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition"
).Get().OfType<System.Management.ManagementObject>().FirstOrDefault();

// Check if the disk drive is found
Assert.IsNotNull(diskDrive, $"Expected disk drive to be found but was null");

// Get the BytesPerSector information
uint bytesPerSector = Convert.ToUInt32(diskDrive["BytesPerSector"]);

// Print out the BytesPerSector information
Assert.IsTrue(bytesPerSector == 512 || bytesPerSector == 4096, $"Expected 512 or 4096 but was {bytesPerSector}");

This code first gets the Win32_Volume instance for the specified drive letter. It then uses the ASSOCIATORS OF WQL query to get the associated Win32_DiskPartition and Win32_DiskDrive instances. The BytesPerSector information is then retrieved from the Win32_DiskDrive instance.

You might need to run your application with administrative privileges to access some WMI classes. Also, remember to add a reference to the System.Management namespace and the System.Management assembly to your project.