Click here to Skip to main content
Click here to Skip to main content

Device enumeration in Windows 8

, 20 Sep 2012 Public Domain
Rate this:
Please Sign up or sign in to vote.
Exploring the Windows.Devices.Enumeration.DeviceInformation and Windows.Devices.Enumeration.Pnp APIs.

Introduction

Windows 8 introduces a new set of APIs for enumerating devices. They take a little getting used to. This article describes how they fit together, and what kind of output they give.

Why would you use these APIs? In my case I want to discover all UPnP devices attached to the computer so I can play audio to them. And I want to write this in an app for the Windows App Store, so I have to use the appropriate Windows 8 APIs.

The code in this article is all in VB, but it will work fine in C# as well.

The code in this article uses WinRT APIs and only run on Windows 8. You can use them in App Store apps, or in desktop/console apps by following these instructions.

What information you can query

There are five main queries you'll be using in the Windows.Devices.Enumeration namespace:

Await DeviceInformation.FindAllAsync()
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceContainer, ...)
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.Device, ...)
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterface, ...) 
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterfaceClass, ...)  
Await Pnp.PnpObject.CreateFromIdAsync(..., id, ...)

They are not interchangeable. Each one retrieves a different sort of information about a different sort of entity. Let's look at each in turn, with sample output. (Note: the accompanying zip file also includes a complete dump of all devices on my system, in case you want to cross-reference them.)

Each DeviceContainer contains a number of Devices. Each Device contains a number of DeviceInterfaces. The DeviceContainer is the level at which you know if something is connected or switched on. The DeviceInterface is the level at which you interact with the device.

DeviceInformation.FindAllAsync - this retrieves cursory information about each device, e.g.:

[DeviceInformation.FindAllAsync]  - SA-NS300
System.ItemNameDisplay=SA-NS300
System.Devices.Icon=C:\Windows\System32\DDORes.dll,-2363
{51236583-0C4A-4FE8-B81F-166AEC13F510},123=C:\Windows\SYSTEM32\DDORes.dll,-3097
System.Devices.IsDefault=False
System.Devices.DeviceInstanceId=SWD\DAFUPnPProvider\uuid:5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.InterfaceEnabled=True   

Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceContainer, ...) - this retrieves information about PnP devices such as things you've plugged into USB or Wi-Fi UPnP devices. Technically, each thing you plug in is a "DeviceContainer" which contains one or more "Device" (usually just one). It's the DeviceContainer which knows whether it's connected or not (via its System.Devices.Connected property). Here's what I get from my Sony SA-NS300 Wi-Fi speakers:

[PnpObject.FindAllAsync(DeviceContainer)]  - SA-NS300
System.ItemNameDisplay=SA-NS300
System.Devices.IsDefault=False
System.Devices.Icon=C:\Windows\System32\DDORes.dll,-2363
System.Devices.FriendlyName=SA-NS300
System.Devices.Connected=True 
System.Devices.Manufacturer=Sony Corporation
System.Devices.ModelName=Network Speaker
System.Devices.PresentationUrl=http://169.254.235.214/
System.Devices.DiscoveryMethod={UPnP}
System.Devices.Paired=True
PKEY_DeviceDisplay_IsShowInDisconnectedState=False
System.Devices.LocalMachine=False
PKEY_DeviceDisplay_IsMetadataSearchInProgress=False
PKEY_DeviceDisplay_IsNotInterestingForDisplay=False
PKEY_DeviceDisplay_IsDeviceUniquelyIdentifiable=True
PKEY_DeviceDisplay_AssociationArray={HardwareId\UMB#Sony_Corporation#SA-NS300#
   urn:schemas-upnp-org:device:MediaRenderer:1,CompatibleId\UMB#
   urn:schemas-upnp-org:device:MediaRenderer:1,CompatibleId\SWD#
   GenericRaw,CompatibleId\SWD#Generic,InterfaceClass\{2A323D9D-EDF1-
   430B-AB95-5860894493D4},InterfaceClass\{D0875FB4-2196-4C7A-A63D-E416ADDD60A1},
   InterfaceClass\{8660E926-FF3D-580C-959E-8B8AF44D7CDE},InterfaceClass\{4C38E836-
   6A2F-5949-9406-1788EA20D1D5},InterfaceClass\{07B357BA-B46A-5072-A024-68A038054D72},
   InterfaceClass\{AE9EB9C4-8819-51D8-879D-9A42FFB89D4E},InterfaceClass\
   {4B8C50DD-9515-5A28-BCFD-A9ED2D92F273},AllItems}
System.Devices.NotWorkingProperly=False
System.Devices.IsShared=False
System.Devices.IsNetworkConnected=True
PKEY_DeviceDisplay_RequiresPairingElevation=False
PKEY_DeviceDisplay_Category={Multimedia.DMR}
PKEY_DeviceDisplay_PrimaryCategory=Multimedia.DMR
PKEY_DeviceDisplay_AlwaysShowDeviceAsConnected=False
System.Devices.HardwareIds={UMB\Sony_Corporation/SA-NS300/urn:schemas-upnp-org:device:MediaRenderer:1}

PnP.PnpObject.FindAllAsync(Pnp.PnpObjectType.Device) - this retrieves much more detailed information about constituent devices that make up a DeviceContainer. My Sony SA-NS300 Wi-Fi speakers are a DeviceContainer that contains just a single Device. (You can relate the Device back to its DeviceContainer via its "System.Devices.ContainerId" property.)

[PnpObject.FindAllAsync(Device)]  - SA-NS300
System.ItemNameDisplay=SA-NS300
System.Devices.DeviceInstanceId=SWD\DAFUPnPProvider\uuid:5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.ContainerId=5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.IpAddress={192.168.0.122}
System.Devices.FriendlyName=SA-NS300
PKEY_Device_DevNodeStatus=25190410
PKEY_Device_ProblemCode=0
System.Devices.Parent=SWD\MSDAS\{ce958e9a-424f-4c88-86f4-11314821e75a}
PKEY_PNPX_CompatibleTypes={Sony Corporation/SA-NS300,urn:schemas-upnp-org:device:MediaRenderer:1}
PKEY_Device_BusReportedDeviceDesc=Network Speaker
System.Devices.Present=True
System.Devices.DeviceHasProblem=False
PKEY_PNPX_GlobalIdentity=uuid:5f9ec1b3-ed59-1900-4530-0007f521ebd6
PKEY_PNPX_Types={urn:schemas-upnp-org:device:MediaRenderer:1}
PKEY_PNPX_XAddrs={192.168.0.122:3911}
PKEY_PNPX_ID=uuid:5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.Manufacturer=Sony Corporation
PKEY_PNPX_ManufacturerUrl=http://www.sony.net/
System.Devices.ModelName=SA-NS300
System.Devices.PresentationUrl=http://192.168.0.122/
PKEY_PNPX_DeviceCategory={MediaDevices}
PKEY_PNPX_PhysicalAddress={0,7,245,33,235,214}
PKEY_PNPX_NetworkInterfaceLuid=19984723346456576
PKEY_PNPX_NetworkInterfaceGuid=8761549f-f8b6-439a-aed1-6d7920306768
PKEY_SSDP_DevLifeTime=1800
PKEY_SSDP_NetworkInterface={0,0,0,0,159,84,97,135,182,248,154,67,174,209,109,
  121,32,48,103,104,0,7,245,33,235,214,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,2,0,0,0,192,168,0,122,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0}
System.Devices.DiscoveryMethod={UPnP}
System.Devices.IsNetworkConnected=True
PKEY_DeviceDisplay_Category={Multimedia.DMR}
PKEY_Device_FriendlyNameAttributes=2
PKEY_Device_ManufacturerAttributes=1
System.Devices.InLocalMachineContainer=False
System.Devices.HardwareIds={UMB\Sony_Corporation/SA-NS300/urn:schemas-upnp-org:device:MediaRenderer:1}
System.Devices.CompatibleIds={UMB\urn:schemas-upnp-org:device:MediaRenderer:1,SWD\GenericRaw,SWD\Generic}
PKEY_Device_Class=SoftwareDevice
PKEY_Device_ClassGuid=62f9c741-b25a-46ce-b54c-9bccce08b6f2
PKEY_Device_Driver={62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0010
PKEY_Device_ConfigFlags=0
PKEY_Device_Manufacturer=Sony Corporation
PKEY_Device_FriendlyName=SA-NS300
PKEY_Device_LocationInfo=http://192.168.0.122:8080/description.xml
PKEY_Device_PDOName=\Device\0000005d
System.Devices.DeviceCapabilities=244
PKEY_Device_BusTypeGuid=06d10322-7de0-4cef-8e25-197d0e7442e2
PKEY_Device_LegacyBusType=15
PKEY_Device_BusNumber=0 
PKEY_Device_EnumeratorName=SWD 
PKEY_Device_PowerData={56,0,0,0,1,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,1,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0}
PKEY_Device_RemovalPolicy=2
PKEY_Device_RemovalPolicyDefault=2
PKEY_Device_InstallState=0
PKEY_Device_BaseContainerId=5f9ec1b3-ed59-1900-4530-0007f521ebd6
PKEY_Device_DriverDate=6/20/2006 5:00:00 PM -07:00
PKEY_Device_DriverVersion=6.2.9200.16384
PKEY_Device_DriverDesc=Generic software device
PKEY_Device_DriverInfPath=c_swdevice.inf
PKEY_Device_DriverInfSection=SoftwareDevice
PKEY_Device_MatchingDeviceId=SWD\GenericRaw
PKEY_Device_DriverProvider=Microsoft
PKEY_Device_DriverRank=16723969
PKEY_Device_NoConnectSound=True 
System.Devices.SafeRemovalRequired=False
PKEY_DrvPkg_VendorWebSite=http://www.sony.net/

Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterface, ...) - each device exposes one or more "interface". It's through these interfaces that you actually get to control it. For instance, my Sony SA-NS300 speakers have two interfaces: ConnectionManager (to get information about the protocols it supports) and AVTransport (to tell it to actually play audio).

[[PnpObject.FindAllAsync(DeviceInterface)]  - SA-NS300 
System.ItemNameDisplay=SA-NS300
System.Devices.IsDefault=False
System.Devices.Icon=C:\Windows\System32\DDORes.dll,-2363
System.Devices.ServiceId=urn:upnp-org:serviceId:AVTransport
System.Devices.InterfaceClassGuid=4c38e836-6a2f-5949-9406-1788ea20d1d5
System.Devices.DeviceInstanceId=SWD\DAFUPnPProvider\uuid:5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.ContainerId=5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.InterfaceEnabled=True
System.Devices.ServiceAddress={http://192.168.0.122:8080/AVTransport/ctrl}
PKEY_PNPX_ServiceTypes={urn:schemas-upnp-org:service:AVTransport:1}
PKEY_PNPX_ServiceControlUrl=http://192.168.0.122:8080/AVTransport/ctrl
PKEY_PNPX_ServiceDescUrl=http://192.168.0.122:8080/AVTransport/desc.xml
PKEY_PNPX_ServiceEventSubUrl=http://192.168.0.122:8080/AVTransport/evt

[PnpObject.FindAllAsync(DeviceInterface)]  - SA-NS300
System.ItemNameDisplay=SA-NS300
System.Devices.IsDefault=False
System.Devices.Icon=C:\Windows\System32\DDORes.dll,-2363
System.Devices.ServiceId=urn:upnp-org:serviceId:ConnectionManager
System.Devices.InterfaceClassGuid=ae9eb9c4-8819-51d8-879d-9a42ffb89d4e
System.Devices.DeviceInstanceId=SWD\DAFUPnPProvider\uuid:5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.ContainerId=5f9ec1b3-ed59-1900-4530-0007f521ebd6
System.Devices.InterfaceEnabled=True
System.Devices.ServiceAddress={http://192.168.0.122:8080/ConnectionManager/ctrl}
PKEY_PNPX_ServiceTypes={urn:schemas-upnp-org:service:ConnectionManager:1}
PKEY_PNPX_ServiceControlUrl=http://192.168.0.122:8080/ConnectionManager/ctrl
PKEY_PNPX_ServiceDescUrl=http://192.168.0.122:8080/ConnectionManager/desc.xml
PKEY_PNPX_ServiceEventSubUrl=http://192.168.0.122:8080/ConnectionManager/evt

Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterfaceClass, ...) - you get almost no information from this query. All it tells you are the basic categories of hardware, e.g.:

[PnpObject.FindAllAsync(DeviceInterfaceClass)]  - Gyrometer 
System.ItemNameDisplay=Gyrometer
 
[PnpObject.FindAllAsync(DeviceInterfaceClass)]  - Orientation sensor  
System.ItemNameDisplay=Orientation sensor

Pnp.PnpObject.CreateFromIdAsync(..., id, ...) - If you already have a DeviceContainerId, then you can use this to get its information directly (rather than doing a query). Likewise if you have a DeviceInstanceId then you can get a device.

Property-lists and AQS filters

Here's a complete example of using one of the query APIs:

Dim PKEY_PNPX_ServiceControlUrl = "{656A3BB3-ECC0-43FD-8477-4AE0404A96CD},16388"
Dim props = {"System.Devices.DeviceInstanceId", "System.Devices.ServiceId", PKEY_PNPX_ServiceControlUrl}
Dim aqsFilter =  "System.Devices.ServiceId:=""urn:upnp-org:serviceId:" & _
    "ConnectionManager"" AND System.Devices.DeviceInstanceId:=""" & id & """"
Dim ifaces = Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterface, props, aqsFilter)

We've already seen the first argument, Pnp.PnpObjectType.DeviceInterface, which determines what kind of object we were querying (and therefore what kind of properties it might return).

The second argument props is an IEnumerable(Of String). Here you have to explicitly list out which properties you want to retrieve. It will only give back information about the properties you asked for. In this case I have asked for three properties. Many properties have easy-to-remember string names, like the first two. But other properties don't, and you have to call them by their "canonical name" (consisting of an all-caps GUID and an integer). The canonical names are all defined in header files in the Windows SDK. I have also written them all in VB, in the accompanying source code.

The third optional argument aqsFilter is an easy way to limit which results to get. In this case my filter looks like this:

System.Devices.ServiceId:="..." AND  System.Devices.DeviceInstanceId:="..."

Note that you can use boolean conjunctions like AND, OR, and NOT; you can use parentheses; use := for comparison; put string arguments in quotation marks; and write boolean arguments without quotation marks, e.g., System.Devices.InterfaceEnabled:=true.

Points of interest

The device-enumeration APIs will return an enormous number of results. There will be lots of results from months-old devices that e.g., a friend might have connected to your home Wi-Fi. You have to look in a DeviceContainer for its System.Devices.Connected property to see whether it's still connected.

Windows 8 has the notion of "pairing". In some cases you have to go to the Windows 8 control panel and Add Device. This will "pair" the device to your machine. The APIs in this article will only enumerate devices which have been paired to your machine.

What next? What do you do with all the information you've enumerated? ... I don't know yet! I assume it depends critically on what kind of hardware it is!

Disclaimer: Although I work at Microsoft, this article is done purely on my own as a hobby project. It's outside my area of professional expertise. I'm just learning. I have no connection to the device-enumeration team.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

Share

About the Author

ljw1004
Technical Lead
United States United States
Lucian studied theoretical computer science in Cambridge and Bologna, and then moved into the computer industry. Since 2004 he's been paid to do what he loves -- designing and implementing programming languages! The articles he writes on CodeProject are entirely his own personal hobby work, and do not represent the position or guidance of the company he works for. (He's on the VB/C# language team at Microsoft).

Comments and Discussions

 
QuestionEnumerating Devices Used by Each Process? PinmemberDarwin Airola29-May-13 8:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 20 Sep 2012
Article Copyright 2012 by ljw1004
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid