Working with Windows Management Instrumentation (WMI) Technology in C#

October 27th, 2022

In this article, we will talk about Windows Management Instrumentation (WMI) technology. Also, we will discuss how to work with it in C# and store the hardware details of your computer in a PostgreSQL database. It will take advantage of Devart for PostgreSQL to communicate with the database.

The Windows Management Instrumentation (WMI) technology is built into the Windows operating system and allows you to query and manage data about the hardware on your computer. You can use WMI to monitor your system’s performance and get information about its hardware.

In this article, I’ll show you how to manage and query data in C# using WMI.

Pre-requisites

You’ll need the following tools to deal with code examples:

What Is WMI and Why do we Need it?

WMI is an abbreviation for Windows Management Instrumentation, a Microsoft technology based on COM used to obtain system-related information. You may use this technology to get your system’s CPU ID, MAC ID, etc. It consists of types that operate as wrappers over native types to get hardware-related information. WMI enables low-level interaction with the host operating system.

You can take advantage of WMI to manipulate performance counters and collect hardware information from a machine. WMI can query and modify the state of many different aspects of Windows systems that include: Applications, Hardware devices, and Software services.

What Are WMI Providers?

You can work with WMI through a set of objects known as the WMI providers. A WMI Provider interfaces between managed objects and CIM Object Manager and oversees managed objects for WMI. These providers can send data to the CIM Object Manager from managed objects, as well as process requests on behalf of management applications and also produce alerts.

These providers expose data and allow you to manipulate it using WMI’s query language. The WMI providers are written in C++ and are COM components. You can use WMI in any programming language that provides support for working with COM objects. Incidentally, the C# programming language has built-in support for working with WMI.

Create a New ASP.NET 6 Core Web API Project

In this section, we’ll learn how to create a new ASP.NET 6 Core Web API project in Visual Studio 2022.

Now, follow the steps outlined below:

  1. Open Visual Studio 2022.
  2. Click Create a new project.
  3. Select ASP.NET Core Web API and click Next.
  4. Specify the project name and location to store that project in your system. Optionally, checkmark the Place solution and project in the same directory checkbox.
  5. Click Next.
  6. In the Additional information window, select .NET 6.0 (Long-term support) as the project version.
  7. Disable the Configure for HTTPS and Enable Docker Support options (uncheck them).
  8. Since we’ll not be using authentication in this example, select the Authentication type as None.
  9. Since we won’t use Open API in this example, deselect the Enable OpenAPI support checkbox.
  10. Since we’ll not be using minimal APIs in this example, ensure that the Use controllers (uncheck to use minimal APIs) is checked.
  11. Leave the Do not use top-level statements checkbox unchecked.
  12. Click Create to finish the process.

So, in this article, we will use this project.

Install NuGet Packages

Before you get started implementing WMI, you should install the Devart.Data.PostgreSQL and the System.Management packages into your project. You can install them either from the NuGet Package Manager tool inside Visual Studio or, from the NuGet Package Manager console using the following command:

PM> Install-Package Devart.Data.PostgreSQL
PM> Install-Package System.Management

The Windows Management Instrumentation Query Language (WQL)

The Windows Management Instrumentation Query Language (WQL) is a query language that allows you to obtain information from the WMI repository of a local or remote computer. You can use WQL to retrieve information from classes and instances in WMI. You can obtain information about a computer’s hardware and software through a WQL query. WQL queries can retrieve information from the WMI repository, including information about the operating system, hardware and software components, applications, services, and events. You can also take advantage of WMI to start or stop services, restart or shut down a computer, etc.

Programming WMI in C#

To work with WMI in .NET Core, you should include the following namespaces:

  • System.Diagnostics
  • System.Management

The following code snippet can be used to store the process Ids and process names of all running processes on your computer:

var mngtObjectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Process");
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (ManagementObject mngtObject in mngtObjectSearcher.Get())
{
dictionary.Add(mngtObject["ProcessId"].ToString(), mngtObject["Name"].ToString());
}

In the preceding code snippet, an instance of ManagementObjectSearcher is created. Next, the Get method is called on the instance to retrieve a collection of ManagementObject objects. You can then iterate over this collection to access the information related to the running processes in the system.

Use WMI to Retrieve Hardware Information

You can use the following piece of code to get the names of the logical disks on your computer.

public static List <string> GetLogicalDiskNames() {
List <string> diskNames = new List <string> ();
string queryName = "Win32_LogicalDisk";
SelectQuery selectQuery = new SelectQuery(queryName);
ManagementObjectSearcher mngtObjectSearcher = new ManagementObjectSearcher(selectQuery);
foreach(ManagementObject mngtObject in mngtObjectSearcher.Get()) {
diskNames.Add(mngtObject.ToString());
}
return diskNames;
}

The following code snippet shows how you can retrieve information related to the logical disks in your computer.

public static List<string> GetLogicalDiskMetadata()
{
    string query = "Select Name,Size,FreeSpace from Win32_LogicalDisk";
    System.Management.ManagementScope mngtScope = new
    System.Management.ManagementScope();
    System.Management.ObjectQuery objectQuery = new
    System.Management.ObjectQuery("select FreeSpace,Size,Name from" + "Win32_LogicalDisk");
    ManagementObjectSearcher mngtObjectSearcher = new
    ManagementObjectSearcher(mngtScope, objectQuery);
    ManagementObjectCollection mngtObjectCollection = mngtObjectSearcher.Get();
    List<string> diskInformation = new List<string>();
    foreach (ManagementObject mngtObject in mngtObjectCollection)
    {
       string name = string.Format("Name : {0}",
       mngtObject["Name"].ToString());
       string size = string.Format("Size : {0}",
       mngtObject["Size"].ToString());
       string freeSpace = string.Format("FreeSpace : {0}",
       mngtObject["FreeSpace"].ToString());
       diskInformation.Add(string.Concat(name, " ", size, " ", freeSpace));
    }
    return diskInformation;
}

Persist Hardware Information into the Database

You can take advantage of WMI to retrieve much different hardware information from your system that includes, the serial number, free space, size of hard disks, serial number, clock speed or CPUs, MAC address, etc. of the network adapter in use. Since MACAddress is unique, we’ll use it in this section to implement a feature known as node locking. We’ll come back to this shortly.

Create a new class named WMIHelper in a file named WMIHelper.cs in your project. We’ll write a few methods to work with WMI in this class. The following code snippet can be used to retrieve the MAC address of your computer.

public static string GetMACAddress()
{
   ManagementClass mngtClass = new
   ManagementClass("Win32_NetworkAdapterConfiguration");
   ManagementObjectCollection mngtObjectCollection = mngtClass.GetInstances();
   string MACAddress = String.Empty;

   foreach (var mngtObject in mngtObjectCollection)
   {
        if (bool.Parse(mngtObject["IPEnabled"].ToString()))
        {
             MACAddress = mngtObject["MacAddress"].ToString();
             break;
        }
   }

   MACAddress = MACAddress.Replace(":", "");
   return MACAddress;
}

You can call this method from Program.cs file as shown below:

var macAddress = PersistHardwareInformation.GetMACAddress();

Create another static method named WriteMacAddress in the WMIHelper class and write the following code there:

public static void WriteMacAddress(string macaddress)
{
       try
       {

           using (
               PgSqlConnection pgSqlConnection = new PgSqlConnection(
                   "User Id = postgres; Password = sa123#;" +
                    "host=localhost;database=postgres;"))
           {
               using (PgSqlCommand cmd = new PgSqlCommand())
               {
                   cmd.CommandText =
                       "INSERT INTO public.demo (id, macaddress) " +
                   "VALUES(@id, @macaddress)";

                   cmd.Connection = pgSqlConnection;
                   cmd.Parameters.AddWithValue("id", Guid.NewGuid().ToString());
                   cmd.Parameters.AddWithValue("macaddress", macaddress);
                   if (pgSqlConnection.State != System.Data.ConnectionState.Open)
                       pgSqlConnection.Open();
                   cmd.ExecuteNonQuery();
               }
           }
      }
      catch
      {
          throw;
      }
}

You should store the MACAddress of your computer in which the application is running only once – at the time the application is first loaded. Once the MACAddress has been stored in the database, you can check if the MACAddress of the computer system from which each request has originated is identical to the MACAddress stored in the database.

If the MACAddresses don’t match, the application has been copied to some other computer after it was first installed. In other words, suppose the application was executed in Computer A where the MACAddress of Computer A was captured and stored in the database. Then, the application was copied to another system Computer B.

When you attempt to run the application in Computer B, it will disallow execution since the MACAddress of Computer A is already stored in the database. This concept is known as Node Locking, a feature that ensures that an application cannot be copied to a different system and executed thereafter it has already been installed and executed in a system. You can take advantage of WMI to implement Node Locking easily.

Conclusion

This article presented a discussion on how to work with WMI in C#. Using WMI you can retrieve the MACAddress of your computer. Since MACAddress is unique, it can be used to copy-protect your application. It should be noted that WMI is only supported on Windows Operating Systems.

Comments are closed.