Windows Management Instrumentation

I’ve been programming a fair bit of C# lately and one of the things I needed to do was find a list of all the COM ports used by NXTs that had been paired with the currently connected Bluetooth controller on the laptop.

WMI, or Windows Management Instrumentation, is a mechanism in Microsoft Windows that allows you to execute SQL-like queries to get and set parameters in the configuration of many aspects of the operating system and devices.  The COM ports on a computer are no exception to this and can easily be retrieved if you know what you’re looking for.  How can this be done?

PowerShell

Windows_PowerShell_iconPowerShell is Microsoft’s answer to the UNIX shell.  It is based on the Korn Shell and offers virtually unlimited access to all levels of the Operating System and many application and services, depending on your current privileges, of course.  PowerShell makes it super easy to try out WMI queries without needing to make an actual program.

PowerShell is part of Windows Server 2008R2  and Windows 7 and can be downloaded for many other versions of Windows.

Querying the serial ports

There are many roads that lead to Rome, but some take a little longer than others.  One of the first queries I found on the Net was a very simple looking one.  I like simple.

PS C:\> Get-WmiObject -query "select DeviceID,PNPDeviceID from Win32_SerialPort"

DeviceID         : COM1
PNPDeviceID      : ACPI\PNP0501\1

DeviceID         : COM14
PNPDeviceID      : BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000
                   \8&1D08283&0&000000000000_00000025

DeviceID         : COM11
PNPDeviceID      : BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000
                   \8&1D08283&0&000000000000_00000029

This returns a list of all the serial ports that the system knows about.  However, there’s a BIG problem with this.  It seems that querying the SerialPort object “tickles” the Bluetooth virtual COM ports.  It attempts to open them and if the NXTs are not switched on, this takes a while to time-out.  I’ve also found that if the NXT is switched on and you attempt to connect to it after this query, you’ll get some really weird system errors.

A much faster approach, which doesn’t seem as nice at first glance, is to query all the PnP devices that are currently connected and functioning well.

PS C:\> Get-WmiObject -query "SELECT Caption FROM Win32_PnPEntity WHERE
ConfigManagerErrorCode = 0"

Caption          : Intel(R) Core(TM) i5 CPU       M 520  @ 2.40GHz

Caption          : Generic USB Hub

Caption          : VMware Bridge Protocol

Caption          : Standard Serial over Bluetooth link (COM32)

<list goes on and on and on>
A little refinement

The list the last query returns is freaking huge, though, so some additional filtering needs to be done.  The virtual COM ports have a very helpful fixed string in their “Caption” field, namely “Standard Serial over Bluetooth link (COM<NUMBER>)”,  The following query gets us all of those.

PS C:\> Get-WmiObject -query "SELECT Caption FROM Win32_PnPEntity WHERE
ConfigManagerErrorCode = 0 and Caption like
'Standard Serial over Bluetooth link (COM%'"

Caption          : Standard Serial over Bluetooth link (COM13)

Caption          : Standard Serial over Bluetooth link (COM14)

Caption          : Standard Serial over Bluetooth link (COM32)

Caption          : Standard Serial over Bluetooth link (COM33)

Now we’re almost there.  The last two issues we need to tackle is that every NXT creates two virtual COM ports and not all of them belong to an NXT.

Every Bluetooth device has a unique identifier, or MAC address, that is 6 bytes long. An example of this is 00:16:53:87:FA:12 The 3 most significant bytes (MSB) of this address identifies the manufacturer of this device.  LEGO owns the entire 00:16:53:X:X:X range.  With this knowledge, an NXT address is very easily filtered, just adjust your query to return only information on devices that have this MAC address pattern.  The MAC address of the device that “owns” a particular virtual COM port is kept as part of a long string in the “PNPDeviceID”.  Our query now looks like this:

PS C:\> Get-WmiObject -query "SELECT Caption FROM Win32_PnPEntity WHERE
ConfigManagerErrorCode = 0 and Caption like
'Standard Serial over Bluetooth link (COM%'
and PNPDeviceID like '%&001653%'"

Caption : Standard Serial over Bluetooth link (COM13)

Caption : Standard Serial over Bluetooth link (COM32)

Just two COM ports left.  These ports just happened to correspond to the ones listed in the properties of this specific Bluetooth device in the Bluetooth Devices folder in the Control Panel. Great!  Now that we have our final WMI query, let’s turn this into an actual program.

Coding it up in C#

I’ve made a quick little program to demonstrate what a complete program in C# looks like.  It merely runs the query and prints out the COM ports and associated BT address.  This is just a small snippet of it.  You can download the complete program and project files here: [LINK].

// The WMI query that makes it all happen
const string QueryString = "SELECT Caption,PNPDeviceID FROM Win32_PnPEntity " +
                            "WHERE ConfigManagerErrorCode = 0 AND " +
                            "Caption LIKE 'Standard Serial over Bluetooth link " +
                            "(COM%' AND PNPDeviceID LIKE '%&001653%'";

SelectQuery WMIquery = new SelectQuery(QueryString);
ManagementObjectSearcher WMIqueryResults = new ManagementObjectSearcher(WMIquery);
if (WMIqueryResults != null)
{
    Console.WriteLine("The following NXTs were found on your system:");
    foreach (object result in WMIqueryResults.Get())
    {
        ManagementObject mo = (ManagementObject)result;
        object captionObject = mo.GetPropertyValue("Caption");
        object pnpIdObject = mo.GetPropertyValue("PNPDeviceID");

        // Get the COM port name out of the Caption, requires a little work.
        string caption = captionObject.ToString();
        string comPort = caption.Substring(caption.LastIndexOf("(COM")).
                            Replace("(", string.Empty).Replace(")", string.Empty);

        // Extract the BT address from the PNPObjectID property
        string BTaddress = pnpIdObject.ToString().Split('&')[4].Substring(0, 12);

        Console.WriteLine("COM Port: {0} ", comPort);
        Console.WriteLine("BT Addr:  {0} ", BTaddress);
        Console.WriteLine("");
    }
}
else
{
    Console.WriteLine("Error executing query");
}
Conclusions

WMI is a very powerful tool to get information from your system.  However, it’s really important to make sure you apply the right filters on your queries or you’ll end up with an overload of information that will not help you at all.  Also keep in mind that querying some objects may have unwanted side-effects like the ones I experienced when querying the Win32_SerialPort object.

Happy coding!

Tags