Sample C# code for using the latest WMI classes to manage Windows Storage

 

This blog post shows a bit of C# code to use the Windows Storage Management API (SM-API) classes that were introduced in Windows Server 2012 and Windows 8.

You can find a list of these classes at class described at https://msdn.microsoft.com/en-us/library/hh830612.aspx, including MSFT_PhysicalDisk, MSFT_StoragePool or MSFT_VirtualDisk.

I found a number of examples with the old interface using the old classes like Win32_Volume, but few good ones with the new classes like MSFT_Volume.

This is some simple C# code using console output. The main details to highlight here are the use of System.Management and how to specify the scope, which allows you to manage a remote computer.

Please note that you might need to enable WMI on the computer, which can be easily done with the command line “winrm quickconfig”.

 

Here is my first attempt is below, using just only System.Management and the ManagementObject class.

It’s implemented in a simple console application, which lists information about volumes and physical disks on the local machine.

 

using System;
using System.Text;
using System.Threading;
using System.Management;

namespace SMAPIQuery
{
class Program
{
static void Main(string[] args)
{
// Use the Storage management scope
ManagementScope scope = new ManagementScope("\\localhost\ROOT\Microsoft\Windows\Storage");
// Define the query for volumes
ObjectQuery query = new ObjectQuery("SELECT * FROM MSFT_Volume");

            // create the search for volumes
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
// Get the volumes
ManagementObjectCollection allVolumes = searcher.Get();
// Loop through all volumes
foreach (ManagementObject oneVolume in allVolumes)
{
// Show volume information
if (oneVolume["DriveLetter"].ToString()[0] > ' ' )
{
Console.WriteLine("Volume '{0}' has {1} bytes total, {2} bytes available", oneVolume["DriveLetter"], oneVolume["Size"], oneVolume["SizeRemaining"]);
}
}

            // Define the query for physical disks
query = new ObjectQuery("SELECT * FROM MSFT_PhysicalDisk");

            // create the search for physical disks
searcher = new ManagementObjectSearcher(scope, query);

            // Get the physical disks
ManagementObjectCollection allPDisks = searcher.Get();

            // Loop through all physical disks
foreach (ManagementObject onePDisk in allPDisks)
{
// Show physical disk information
Console.WriteLine("Disk {0} is model {1}, serial number {2}", onePDisk["DeviceId"], onePDisk["Model"], onePDisk["SerialNumber"]);
}

            Console.ReadLine();
}
}
}

 

Here is some sample output from this application:

 

Volume 'D' has 500104687616 bytes total, 430712184832 bytes available
Volume 'E' has 132018860032 bytes total, 110077665280 bytes available
Volume 'F' has 500105216000 bytes total, 356260683776 bytes available
Volume 'C' has 255690010624 bytes total, 71789502464 bytes available

Disk 2 is model SD , serial number
Disk 0 is model MTFDDAK256MAM-1K12, serial number 131109303905
Disk 3 is model 5AS , serial number 00000000e45ca01b30c1
Disk 1 is model ST9500325AS, serial number 6VEK9B89

 

Next, I got some help from other folks from Microsoft, including Cosmos Darwin (PM Intern) and Gustavo Franco (Senior Developer).

I wanted to use the more modern CimInstance objects, which offer more flexibility. Here’s the same code as above, but now using the Microsoft.Management.Insfrastructure namespace:

 

using System;
using System.Text;
using System.Threading;
using Microsoft.Management.Infrastructure;

namespace SMAPIQuery
{
class Program
{
static void Main(string[] args)
{

            string computer = "localhost";

            // Create CIM session
CimSession Session = CimSession.Create(computer);

            // Query Volumes, returns CimInstances
var allVolumes = Session.QueryInstances(@"rootmicrosoftwindowsstorage", "WQL", "SELECT * FROM MSFT_Volume");

            // Loop through all volumes
foreach (CimInstance oneVolume in allVolumes)
{
// Show volume information

                if (oneVolume.CimInstanceProperties["DriveLetter"].ToString()[0] > ' ' )
{

                Console.WriteLine("Volume '{0}' has {1} bytes total, {2} bytes available", oneVolume.CimInstanceProperties["DriveLetter"], oneVolume.CimInstanceProperties["Size"], oneVolume.CimInstanceProperties["SizeRemaining"]);
}

            }

            // Query Physical Disks, returns CimInstances
var allPDisks = Session.QueryInstances(@"rootmicrosoftwindowsstorage", "WQL", "SELECT * FROM MSFT_PhysicalDisk");

            // Loop through all physical disks
foreach (CimInstance onePDisk in allPDisks)
{
// Show physical disk information
Console.WriteLine("Disk {0} is model {1}, serial number {2}", onePDisk.CimInstanceProperties["DeviceId"], onePDisk.CimInstanceProperties["Model"].ToString().TrimEnd(), onePDisk.CimInstanceProperties["SerialNumber"]);
}

            Console.ReadLine();
}
}
}

 

The output is the same as before, but you can see that the top portion of the code uses a completely different set of classes to create a CimSession, then query the objects, which return in the form of CimInstance objects.

That code works well if the user running it has the right credentials to access the objects. If you want to provide specific credentials for your CimSession, that’s also possible with a bit more code. Here’s what it would look like:

 

using System;
using System.Text;
using System.Threading;
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Options;
using System.Security;

namespace SMAPIQuery
{
class Program
{
static void Main(string[] args)
{

            string computer = "10.1.1.1";
string domain = "Domain1";
string username = "User1";

            string plaintextpassword;

            Console.WriteLine("Enter password:");
plaintextpassword = Console.ReadLine();

            SecureString securepassword = new SecureString();
foreach (char c in plaintextpassword)
{
securepassword.AppendChar(c);
}

            // create Credentials
CimCredential Credentials = new CimCredential(PasswordAuthenticationMechanism.Default, domain, username, securepassword);

            // create SessionOptions using Credentials
WSManSessionOptions SessionOptions = new WSManSessionOptions();
SessionOptions.AddDestinationCredentials(Credentials);

            // create Session using computer, SessionOptions
CimSession Session = CimSession.Create(computer, SessionOptions);

            var allVolumes = Session.QueryInstances(@"rootmicrosoftwindowsstorage", "WQL", "SELECT * FROM MSFT_Volume");
var allPDisks = Session.QueryInstances(@"rootmicrosoftwindowsstorage", "WQL", "SELECT * FROM MSFT_PhysicalDisk");

            // Loop through all volumes
foreach (CimInstance oneVolume in allVolumes)
{
// Show volume information

                if (oneVolume.CimInstanceProperties["DriveLetter"].ToString()[0] > ' ' )
{

                Console.WriteLine("Volume '{0}' has {1} bytes total, {2} bytes available", oneVolume.CimInstanceProperties["DriveLetter"], oneVolume.CimInstanceProperties["Size"], oneVolume.CimInstanceProperties["SizeRemaining"]);
}

            }

            // Loop through all physical disks
foreach (CimInstance onePDisk in allPDisks)
{
// Show physical disk information
Console.WriteLine("Disk {0} is model {1}, serial number {2}", onePDisk.CimInstanceProperties["DeviceId"], onePDisk.CimInstanceProperties["Model"].ToString().TrimEnd(), onePDisk.CimInstanceProperties["SerialNumber"]);
}

            Console.ReadLine();
}
}
}

 

Notice that the only thing that changed is how you create the CimSession object, using the SessionOptions to provide explicit credentials.

This sample code if focused on the storage side of things, so I am letting the user enter a visible plaintext password here. It should go without saying that you should provide a better mechanism to enter passwords.

You can get all the details on the other classes in the Microsoft.Management.Infrastructure namespace at https://msdn.microsoft.com/en-us/library/microsoft.management.infrastructure(v=vs.85).aspx.

All right, that should give you plenty of food for thought. Now go write some code!