Device enumeration in Windows 8





5.00/5 (3 votes)
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 Device
s.
Each Device
contains a number of DeviceInterface
s. 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.