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
PowerShell 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!
So Mac copied the eye candy of Linux, Windows copied the terminal, and yet somehow the masses are unaware of how beautifully Linux works. I’ve never had any Bluetooth problems. It’s like the Bluetooth connection between any non Windows machine and another non Windows machine. For example, an NXT to another NXT, or a Wii to a Wiimote.
How is this related to this article in any way?
Linux is always related to everything. 😉
Linux has nothing that even comes close to the power of WMI. Anyone who disputes this, has either:
No clue about Linux or how to manage large numbers of them (by large I mean 100+ hosts, not just your workstation and the aging Pentium3 in the closet).
Never used WMI
All of the above
Linux is a lot of things, related to everything is not one of those.
Do you always need to run this via PowerShell, or is it also possible to check connectivity via wmic at the commandline? Looks like this is not possible via the commandline, since you query WMIC first, then do a little filtering and then 🙂
Just a question, I expect no answer, thinking out loud
I have no idea, tbh. I don’t think filtering is all that easy on a normal command line in any case 🙂
Is it pretty standard for the PNPDeviceID’s address to be delimited by &’s in this manner? I ask because I used your code snippet above to grab the address from the PNPDeviceId, however, my device’s address is retrieved from position 5, not 4 like you specified:
BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_VID&00010047_PID&F000\8&22F39DE3&0&00078094330D_C00000003
Just wondering if a standard exists for these strings…
Thanks!
It was in my case. I am using Windows 7 64bit. What are you using? Is it a USB BT dongle or built-in?
Thank you for your article. Do you have any suggestions on how to get the ‘Active’ Bluetooth connections from the WMI? I have gone through all the fields of the Win32_PnPEntity, and there isn’t any indications of a active connection condition. Win32_SerialPort is basically useless; doesn’t even list the Bluetooth ports. Any help would be very much appreciated!
Thanks,
Rick
Hi Rick,
I’m afraid my knowledge of WMI is not nearly as extensive as you might hope. You might want to conder asking on http://www.codeproject.com/, that’s where I got most of my info.
– Xander
Thank you for your reply. There’s so much information in the WMI, learning it could be a full-time job.
Rick
Not sure it would be a job I’d like to do, it’s simply too much 🙂
This article helped me, and was easily refound thanks to the use of a rather unique “tickle” verb as applied to SerialPort class querying.
I am developing a Delphi Serial port app, and Bluetooth dongle / devices were severely damaging the startup ime due to the very “tickling” phenomenon mentioned above. Essentially, you try to open the device to see if it’s in use. This works for “normal” serial ports, but not BT.
And as you noted, once you’ve tickled the BT device, subsequent attempts to connect to it fail dismally.
In this instance, I am working towards interfacing my PC to a microcontroller (Arduino) wirelessly using the inexpensive (and up till now frustratingly uncooperative) HC06 BT modules available on Ebay.
Using Creatfile to open a BT virtual comport, or querying “Win32_SerialPort” via WMI most definitely renders said BT devices inoperable. The outward sign being the “SPP” Service from the Properties –> Services tab becomes blank.
Querying the PnPEntity WMI instead, has no such effect, and returns sufficient information to interact successfully with the COM ports retrieved.
I realise your code works for you, but felt it mentionable that
1. (ConfigManagerErrorCode = 0) is true for every device on my computer (and hence redundant in the code above)
2. (Caption LIKE ”%(COM%)”) will filter out the connected devices
3. (NOT PnPDeviceID LIKE ”%&000000000000_00%”) will filter out the MAC address-less “phantom” BT COM ports noted above.
Thanks again for the article, was very helpful in my hair-depleting, BT detecting, non-tickle code seeking quest.
Aaron
Thank you, a very good tutorial. I need to find the assosiated virtual COM ports to the devices presented by the 32feet.net library. This code helped me to find a way to enumerate the ports using WMI.
But I found that the caption depends on the language and the installed BlueTooth stack. I found a expression that works at least for MS, WidCom, BlueSoleil and StoneStreet (Atheros). It references the class ID and the service name. Here it is:
Select * from Win32_PnPEntity WHERE ClassGuid = “{4d36e978-e325-11ce-bfc1-08002be10318}” AND ( Service = “BTHMODEM” OR Service = “BTCOM”)
All the Best,
Volker Bijewitz
Thank you for that expression!