There are some times when you need to get the disk information in your system, to check what's happening, for inventory check, or even to know the free and available space. Getting the free and available space is fairly easy in .NET, just use the GetDrives method of the DriveInfo class:
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo drive in drives)
{
Console.WriteLine($"Name: {drive.Name}");
Console.WriteLine($"VolumeLabel: {drive.VolumeLabel}");
Console.WriteLine($"RootDirectory: {drive.RootDirectory}");
Console.WriteLine($"DriveType: {drive.DriveType}");
Console.WriteLine($"DriveFormat: {drive.DriveFormat}");
Console.WriteLine($"IsReady: {drive.IsReady}");
Console.WriteLine($"TotalSize: {drive.TotalSize}");
Console.WriteLine($"TotalFreeSpace: {drive.TotalFreeSpace}");
Console.WriteLine($"AvailableFreeSpace: {drive.AvailableFreeSpace}");
Console.WriteLine();
}
If you run this code, you will get the info for all drives in your system:
This is ok for most apps, but sometimes you want more than that. If, for example, you want an inventory of your physical disks (not the logical disks, like DriveInfo gives) ?
The physical and logical structure of your disks can differ a lot. For example, if you have a disk with three partitions, you will have one physical disk and three logical disks.
On the other side, you can mount a drive in an empty folder and have two physical disks and one logical disk. And we are not considering multiple OSs, here.
.NET doesn't have any dedicated class for the physical disk structure, so we should find another way to do it. In fact, there is a way to get this information in Windows, called WMI (Windows Management Instrumentation).
It's the infrastructure for data management in Windows and you can use it to manage your local and remote systems. You can access it with C++, C# or even PowerShell, to get info and manage your computer.
With WMI, you can use SQL-like queries to query information in your system, using the available classes. For example, to get all the drives in your system, you would use:
SELECT * FROM Win32_DiskDrive
That would return a key-value set with all the properties of the system disks, and you can check the values you want. You can even filter the properties and drives that you want. Something like:
SELECT DeviceID, Model, Name, Size, Status, Partitions,
TotalTracks, TotalSectors, BytesPerSector, SectorsPerTrack, TotalCylinders, TotalHeads,
TracksPerCylinder, CapabilityDescriptions
FROM Win32_DiskDrive WHERE MediaType = 'Fixed hard disk media'
Will get only the device data and geometry for the fixed disks in your system (it won't get info for the external HDs).
Now that we already know how to get the disk information, let's see how to do it in C#.
To use WMI in a C# program, we must add the package System.Management to the project and start using it.
Open a new terminal window and create a new console application with
dotnet new console -o DiskInfoCSharp
Once you do that, a new console project will be created in the DiskInfoCSharp folder. You can cd to it and add the new package to the project with
dotnet add package System.Management
This will add the package to the project and will allow you to use WMI in it. You can open VS Code with code .
and start editing the project.
The first step is to create a scope and connect to it:
using System.Management;
ManagementScope scope = new ManagementScope("\\\\.\\root\\cimv2");
scope.Connect();
This will allow you to query data in the local computer. If you want to connect to a remote computer, just change the \. to the name of the remote computer.
Then, you must create the query for getting the disk information:
ObjectQuery query = new ObjectQuery(@"SELECT DeviceID, Model, Name, Size, Status, Partitions,
TotalTracks, TotalSectors, BytesPerSector, SectorsPerTrack, TotalCylinders, TotalHeads,
TracksPerCylinder, CapabilityDescriptions
FROM Win32_DiskDrive WHERE MediaType = 'Fixed hard disk media'");
As you can see, the query has a syntax similar to SQL, you can change it to get different fields (or use * to get all fields) or change (or remove) the WHERE clause. This query will only return the selected fields for the fixed hard disks.
Then, we should instantiate a ManagementObjectSearcher object and start iterating on all instances of fixed disks in your system:
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
foreach (ManagementObject wmi_HD in searcher.Get())
{
foreach (PropertyData property in wmi_HD.Properties)
Console.WriteLine($"{property.Name} = {property.Value}");
var capabilities = wmi_HD["CapabilityDescriptions"] as string[];
if (capabilities != null)
{
Console.WriteLine("Capabilities");
foreach (var capability in capabilities)
Console.WriteLine($" {capability}");
}
Console.WriteLine("-----------------------------------");
}
For each disk, we get all the properties and then we iterate on the capability descriptions, as this is a property that returns an array of strings.
If we run this program, we will get something like
As you can see, it's very easy to get disk information using WMI in C#. There are some caveats in using this method:
- You don't have the properties in advance. Yes, you could create a class with the properties and populate it, but it's not something that is built in
- This method only works in Windows. If you are creating a multi-platform program, this method isn't for you
On the other side, this is a very easy method to get the information in local and remote computers, you can easily filter data and use it as you want.
If you go to the Using WMI page, you will see a note:
As you can see, the System.Management namespace is not recommended anymore, and it must be replaced by the Microsoft.Management.Infrastructure, especially for .NET Core apps.
To port our app, the first thing to do is to ass the package Microsoft.Management.Infrastructure with
dotnet add package Microsoft.Management.Infrastructure
Once you do that, you should change the classes you are using to access the data. We create a CimSession and query the data with QueryInstances:
using Microsoft.Management.Infrastructure;
var instances = CimSession.Create(null)
.QueryInstances(@"root\cimv2", "WQL", @"SELECT DeviceID, Model, Name, Size, Status, Partitions,
TotalTracks, TotalSectors, BytesPerSector, SectorsPerTrack, TotalCylinders, TotalHeads,
TracksPerCylinder, CapabilityDescriptions
FROM Win32_DiskDrive WHERE MediaType = 'Fixed hard disk media'");
Once you do that, you can iterate on the instances and get the properties with:
foreach (var instance in instances)
{
foreach (var property in instance.CimInstanceProperties)
Console.WriteLine($"{property.Name} = {property.Value}");
var capabilities = instance.CimInstanceProperties["CapabilityDescriptions"].Value as string[];
if (capabilities != null)
{
Console.WriteLine("Capabilities");
foreach (var capability in capabilities)
Console.WriteLine($" {capability}");
}
Console.WriteLine("-----------------------------------");
}
If you run the program, you will get properties similar to the ones in the previous figure.
As you can see, WMI can ease the retrieval of information in a Windows machine. Unfortunately, that can't be used in a Linux machine, that may be a subject for another article!
All the code for this article is at https://github.com/bsonnino/WMICsharp