Find only physical network adapters with WMI Win32_NetworkAdapter class

Posted by Mladen Prajdic on SQL Team See other posts from SQL Team or by Mladen Prajdic
Published on Thu, 04 Nov 2010 13:20:25 GMT Indexed on 2010/12/06 16:58 UTC
Read the original article Hit count: 518

Filed under:

WMI is Windows Management Instrumentation infrastructure for managing data and machines. We can access it by using WQL (WMI querying language or SQL for WMI). One thing to remember from the WQL link is that it doesn't support ORDER BY. This means that when you do SELECT * FROM wmiObject, the returned order of the objects is not guaranteed. It can return adapters in different order based on logged-in user, permissions of that user, etc… This is not documented anywhere that I've looked and is derived just from my observations.

To get network adapters we have to query the Win32_NetworkAdapter class. This returns us all network adapters that windows detect, real and virtual ones, however it only supplies IPv4 data. I've tried various methods of combining properties that are common on all systems since Windows XP.

The first thing to do to remove all virtual adapters (like tunneling, WAN miniports, etc…) created by Microsoft. We do this by adding WHERE Manufacturer!='Microsoft' to our WMI query. This greatly narrows the number of adapters we have to work with. Just on my machine it went from 20 adapters to 5. What was left were one real physical Realtek LAN adapter, 2 virtual adapters installed by VMware and 2 virtual adapters installed by VirtualBox.

If you read the Win32_NetworkAdapter help page you'd notice that there's an AdapterType that enumerates various adapter types like LAN or Wireless and AdapterTypeID that gives you the same information as AdapterType only in integer form. The dirty little secret is that these 2 properties don't work. They are both hardcoded, AdapterTypeID to "0" and AdapterType to "Ethernet 802.3". The only exceptions I've seen so far are adapters that have no values at all for the two properties, "RAS Async Adapter" that has values of AdapterType = "Wide Area Network" and AdapterTypeID = "3" and various tunneling adapters that have values of AdapterType = "Tunnel" and AdapterTypeID = "15". In the help docs there isn't even a value for 15. So this property was of no help.

Next property to give hope is NetConnectionId. This is the name of the network connection as it appears in the Control Panel -> Network Connections. Problem is this value is also localized into various languages and can have different names for different connection. So both of these properties don't help and we haven't even started talking about eliminating virtual adapters. Same as the previous one this property was also of no help.

Next two properties I checked were ConfigManagerErrorCode and NetConnectionStatus in hopes of finding disabled and disconnected adapters. If an adapter is enabled but disconnected the ConfigManagerErrorCode = 0 with different NetConnectionStatus. If the adapter is disabled it reports ConfigManagerErrorCode = 22. This looked like a win by using (ConfigManagerErrorCode=0 or ConfigManagerErrorCode=22) in our condition. This way we get enabled (connected and disconnected adapters).

Problem with all of the above properties is that none of them filter out the virtual adapters installed by virtualization software like VMware and VirtualBox.

The last property to give hope is PNPDeviceID. There's an interesting observation about physical and virtual adapters with this property. Every virtual adapter PNPDeviceID starts with "ROOT\". Even VMware and VirtualBox ones. There were some really, really old physical adapters that had PNPDeviceID starting with "ROOT\" but those were in pre win XP era AFAIK. Since my minimum system to check was Windows XP SP2 I didn't have to worry about those.

The only virtual adapter I've seen to not have PNPDeviceID start with "ROOT\" is the RAS Async Adapter for Wide Area Network. But because it is made by Microsoft we've eliminated it with the first condition for the manufacturer. Using the PNPDeviceID has so far proven to be really effective and I've tested it on over 20 different computers of various configurations from Windows XP laptops with wireless and bluetooth cards to virtualized Windows 2008 R2 servers. So far it always worked as expected. I will appreciate you letting me know if you find a configuration where it doesn't work.

Let's see some C# code how to do this:

ManagementObjectSearcher mos = null;
// WHERE Manufacturer!='Microsoft' removes all of the
// Microsoft provided virtual adapters like tunneling, miniports, and Wide Area Network adapters.
mos = new ManagementObjectSearcher(@"SELECT *
FROM Win32_NetworkAdapter
WHERE Manufacturer != 'Microsoft'"
);

// Trying the ConfigManagerErrorCode and NetConnectionStatus variations
// proved to still not be enough and it returns adapters installed by
// the virtualization software like VMWare and VirtualBox
// ConfigManagerErrorCode = 0 -> Device is working properly. This covers enabled and/or disconnected devices
// ConfigManagerErrorCode = 22 AND NetConnectionStatus = 0 -> Device is disabled and Disconnected.
// Some virtual devices report ConfigManagerErrorCode = 22 (disabled) and some other NetConnectionStatus than 0
mos = new ManagementObjectSearcher(@"SELECT *
FROM Win32_NetworkAdapter
WHERE Manufacturer != 'Microsoft'
AND (ConfigManagerErrorCode = 0
OR (ConfigManagerErrorCode = 22 AND NetConnectionStatus = 0))"
);

// Final solution with filtering on the Manufacturer and PNPDeviceID not starting with "ROOT\"
// Physical devices have PNPDeviceID starting with "PCI\" or something else besides "ROOT\"
mos = new ManagementObjectSearcher(@"SELECT *
FROM Win32_NetworkAdapter
WHERE Manufacturer != 'Microsoft'
AND NOT PNPDeviceID LIKE 'ROOT\\%'"
);
// Get the physical adapters and sort them by their index.
// This is needed because they're not sorted by default
IList<ManagementObject> managementObjectList = mos.Get()
.Cast<ManagementObject>()
.OrderBy(p => Convert.ToUInt32(p.Properties["Index"].Value))
.ToList();

// Let's just show all the properties for all physical adapters.
foreach (ManagementObject mo in managementObjectList)
{
foreach (PropertyData pd in mo.Properties)
Console.WriteLine(pd.Name + ": " + (pd.Value ?? "N/A"));
}

 

That's it. Hope this helps you in some way.

© SQL Team or respective owner