19

I am trying to dynamically list the USBs connected to the computer that match a certain UsbDeviceClass

The information about the class of the USBs I am trying to list in device manager is the following

Device Manager Information

Ideally it should be able to list Com ports as the devices I am looking to list in particular are Arduinos.

DeviceInformationCollection usbDeviceInfoCollection = await DeviceInformation.FindAllAsync(UsbDevice.GetDeviceClassSelector(new UsbDeviceClass()
{
    ClassCode = 0x02,
    SubclassCode = 0x02,
    ProtocolCode = 0x01
}));
Debug.WriteLineIf(usbDeviceInfoCollection.Count == 1, "1 USB device found");
Debug.WriteLineIf(usbDeviceInfoCollection.Count != 1, usbDeviceInfoCollection.Count + " USB devices found");

for (int i = 0; i < usbDeviceInfoCollection.Count; i++)
{
    Debug.WriteLine("USB Device " + (i + 1));
    Debug.WriteLine("ID: " + usbDeviceInfoCollection[i].Id);
    Debug.WriteLine("Name: " + usbDeviceInfoCollection[i].Name);
    Debug.WriteLine("Properties: " + usbDeviceInfoCollection[i].Properties);
    Debug.WriteLine("");
}

The code above shows how I have been trying to achieve this but so far I have had no luck. This is because when the program runs it returns 0 devices when there is a device attached.

I have also tried using the predefined class UsbDeviceClasses.CdcControl, however, that also did not achieve the results I want.

I'd appreciate any guidance on how to achieve this correctly.


Also as this is a UWP project, I have included the capability below so it should be able to detect the devices I want it to.

<DeviceCapability Name="serialcommunication">
  <Device Id="any">
    <Function Type="name:serialPort"/>
  </Device>
</DeviceCapability>

An example output of the Arduino information from a slight variation of the program using DeviceInformation.FindAllAsync() with no UsbDeviceClass as jsanalytics suggested.

Device 1354
EnclosureLocation: Windows.Devices.Enumeration.EnclosureLocation
ID: \\?\USB#VID_2341&PID_0243#9543231323835131B172#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
IsDefault: False
IsEnabled: True
Kind: DeviceInterface
Name: Genuino Uno (COM6)
Pairing: Windows.Devices.Enumeration.DeviceInformationPairing
Properties: System.__ComObject

Image of registry

enter image description here


Update, there is this page about more specific USB capabilities that includes name:cdcControl, classId:02 * *. However, I can still not get it to enumerate through attached devices like this remote arduino application does; Windows Remote Arduino Experience.


A quick edit for clarification. I am not unable to connect to the device, I am only unable to enumerate devices that are class 02 subclass 02 protocol 01. It is possible to connect to my device via specifying the VID and PID but in this current case, that defies the point.

Dan
  • 7,286
  • 6
  • 49
  • 114
  • So your problem is you get `0` device back? – zc246 Dec 29 '17 at 13:56
  • Does it work without the await? – jdweng Dec 29 '17 at 13:56
  • @jdweng No it doesn't as `FindAllAsync` is a `IAsyncOperation` – Dan Dec 29 '17 at 14:00
  • @Dan Usually, using `string[] PortNames = SerialPort.GetPortNames();` would provide you with the ports with connected devices. from there you can take the names and get the details of the devices connected. – Nkosi Jan 01 '18 at 11:52
  • @Nkosi As far as I'm aware that will not work on UWP. When I tried it I got a Platform not Supported exception – Dan Jan 01 '18 at 12:46
  • 1
    @jsanalytics Yes it does. I edited my question to include an example of the information provided – Dan Jan 01 '18 at 19:30
  • 1
    @jsanalytics The model I am currently using is a [Arduino Uno Rev3](https://store.arduino.cc/arduino-uno-rev3). I appreciate the help – Dan Jan 06 '18 at 16:12
  • 1
    @jsanalytics I updated the photo to show that there is but one entry – Dan Jan 07 '18 at 16:22
  • @jsanalytics Will do! – Dan Jan 12 '18 at 09:13
  • @Dan Did you ever get this to work. Im facing the same issue. – veeman Dec 22 '20 at 07:52
  • @veeman Hi, I didn't exactly get it to work in the sense of the question. But if you are using an Arduino or trying to list serial ports. That can be done quite easily. See [the answer below](https://stackoverflow.com/a/65417757/4601149). Hope it helps. If you have any specific use case let me know and I can try help. – Dan Dec 23 '20 at 00:55

2 Answers2

1

You can enumerate external USB device, first you need add this permission in Package.appxmanifest file.

 <uap:Capability Name="removableStorage" />

Then write this code in your app.

    var removableDevices = KnownFolders.RemovableDevices;
    var externalDrives = await removableDevices.GetFoldersAsync();
    foreach (var item in externalDrives)
      {
          var name = item.DisplayName;
      }

Now for have a refresh list re-run the code.

Parsa Karami
  • 702
  • 1
  • 8
  • 30
  • Thanks for the answer, however, this does not answer the original question correctly. Which is unfortunate as it was a good idea. This works for listing things such as a USB pen drive or an External HDD, however, it does not list things such as Arduinos which is what I want listing – Dan Jan 01 '18 at 17:13
1

SerialDevice.GetDeviceSelector() can be used to answer the specific scenario I had of enumerating serial devices.

DeviceInformationCollection deviceSearchResults = await DeviceInformation.FindAllAsync(SerialDevice.GetDeviceSelector());

But looking at the question of how can the class, subclass, and protocol be used to list the same information, it is unclear.

UsbDevice.GetDeviceClassSelector(new UsbDeviceClass
{
    ClassCode = 0x02,
    SubclassCode = 0x02,
    ProtocolCode = 0x01
});

The code above generates the string

System.Devices.InterfaceClassGuid:="{DEE824EF-729B-4A0E-9C14-B7117D33A817}" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True AND System.DeviceInterface.WinUsb.UsbClass:=2 AND System.DeviceInterface.WinUsb.UsbSubClass:=2 AND System.DeviceInterface.WinUsb.UsbProtocol:=1

There were two reasons this was not working.

The first can be seen when looking at the System.Devices.InterfaceClassGuid. The value of this property for the serial ports listed was not DEE824EF-729B-4A0E-9C14-B7117D33A817 but 86e0d1e0-8089-11d0-9ce4-08003e301f73. SerialDevice.GetDeviceSelector() seems to take this into account as it returns

System.Devices.InterfaceClassGuid:="{86E0D1E0-8089-11D0-9CE4-08003E301F73}" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True

The second is when using DeviceInformation.FindAllAsync to load additional parameters which you would expect to match the values shown in the screenshot in the question, they are actually empty.

The code

private async void List()
{
    DeviceInformationCollection deviceSearchResults = await DeviceInformation.FindAllAsync("", new[]
    {
        "System.Devices.InterfaceClassGuid",
        "System.DeviceInterface.Serial.PortName",
        "System.DeviceInterface.Serial.UsbProductId",
        "System.DeviceInterface.Serial.UsbVendorId",
        "System.Devices.CompatibleIds",
        "System.Devices.ClassGuid",
        "System.DeviceInterface.WinUsb.UsbClass",
        "System.DeviceInterface.WinUsb.UsbSubClass",
        "System.DeviceInterface.WinUsb.UsbProtocol",
    });

    foreach (DeviceInformation device in deviceSearchResults)
    {
        PrintInfo(device);
    }
}

private static void PrintInfo(DeviceInformation device)
{
    Debug.WriteLine("Device:");
    Debug.WriteLine($"\tID: {device.Id}");
    Debug.WriteLine($"\tName: {device.Name}");
    Debug.WriteLine($"\tIs Enabled: {device.IsEnabled}");
    Debug.WriteLine($"\tIs Default: {device.IsDefault}");
    Debug.WriteLine($"\tKind: {device.Kind}");

    if (device.EnclosureLocation != null)
    {
        Debug.WriteLine("\tEnclosure Location:");
        Debug.WriteLine($"\t\tIn Dock: {device.EnclosureLocation.InDock}");
        Debug.WriteLine($"\t\tIn Lid: {device.EnclosureLocation.InLid}");
        Debug.WriteLine($"\t\tPanel: {device.EnclosureLocation.Panel}");
        Debug.WriteLine($"\t\tRotation Angle In Degrees Clockwise: {device.EnclosureLocation.RotationAngleInDegreesClockwise}");
    }

    if (device.Pairing != null)
    {
        Debug.WriteLine("\tPairing Info:");
        Debug.WriteLine($"\t\tCan Pair: {device.Pairing.CanPair}");
        Debug.WriteLine($"\t\tIs Paired: {device.Pairing.IsPaired}");
        Debug.WriteLine($"\t\tProtection Level: {device.Pairing.ProtectionLevel}");
    }

    Debug.WriteLine("\tProperties:");
    foreach ((string key, object value) in device.Properties)
    {
        Debug.WriteLine($"\t\t{key}: {value}");
    }

    Debug.WriteLine("");
}

returns the following output showing that additional parameters such as CompatibleIds is empty.

Device:
    ID: \\?\USB#VID_2341&PID_0043#9563533333135161B150#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
    Name: USB Serial Device (COM3)
    Is Enabled: True
    Is Default: False
    Kind: DeviceInterface
    Enclosure Location:
        In Dock: False
        In Lid: False
        Panel: Right
        Rotation Angle In Degrees Clockwise: 0
    Pairing Info:
        Can Pair: False
        Is Paired: False
        Protection Level: None
    Properties:
        System.ItemNameDisplay: USB Serial Device (COM3)
        System.Devices.DeviceInstanceId: USB\VID_2341&PID_0043\9563533333135161B150
        System.Devices.Icon: C:\Windows\System32\DDORes.dll,-2001
        System.Devices.GlyphIcon: C:\Windows\System32\DDORes.dll,-3001
        System.Devices.InterfaceEnabled: True
        System.Devices.IsDefault: False
        System.Devices.PhysicalDeviceLocation: System.Byte[]
        System.Devices.ContainerId: 57921dd6-46ab-5d6d-a362-0a14d0827375
        System.Devices.InterfaceClassGuid: 86e0d1e0-8089-11d0-9ce4-08003e301f73
        System.DeviceInterface.Serial.PortName: COM3
        System.DeviceInterface.Serial.UsbProductId: 67
        System.DeviceInterface.Serial.UsbVendorId: 9025
        System.Devices.CompatibleIds: 
        System.Devices.ClassGuid: 
        System.DeviceInterface.WinUsb.UsbClass: 
        System.DeviceInterface.WinUsb.UsbSubClass: 
        System.DeviceInterface.WinUsb.UsbProtocol: 

As a side note, if anyone is here looking to achieve something similar in WPF / other desktop applications, which is the approach I took at the time, it can be achieved in a few ways using WMI.

// If in .NET Core / .NET 5 run
// Install-Package System.Management -Version 5.0.0
// See https://www.nuget.org/packages/System.Management/
ManagementObjectSearcher searcher =
    new ManagementObjectSearcher("SELECT * FROM Win32_SerialPort");

foreach (ManagementBaseObject queryObj in searcher.Get())
{
    Debug.WriteLine("COM Port");
    Debug.WriteLine($"DeviceID: {queryObj["DeviceID"]}");
    Debug.WriteLine($"Name: {queryObj["NAME"]}");
    Debug.WriteLine($"Description: {queryObj["Description"]}");
    Debug.WriteLine($"PNPDeviceID: {queryObj["PNPDeviceID"]}");
    Debug.WriteLine("");
}

// If in .NET Core / .NET 5 run
// Install-Package Microsoft.Management.Infrastructure -Version 2.0.0
// See https://www.nuget.org/packages/Microsoft.Management.Infrastructure/
CimSession cimSession = CimSession.Create(null);

IEnumerable<CimInstance> queryInstances =
    cimSession.QueryInstances(@"root\cimv2",
        "WQL",
        @"SELECT * FROM Win32_SerialPort");

foreach (CimInstance cimInstance in queryInstances)
{
    Debug.WriteLine("COM Port");
    Debug.WriteLine($"DeviceID: {cimInstance.CimInstanceProperties["DeviceID"].Value}");
    Debug.WriteLine($"Name: {cimInstance.CimInstanceProperties["NAME"].Value}");
    Debug.WriteLine($"Description: {cimInstance.CimInstanceProperties["Description"].Value}");
    Debug.WriteLine($"PNPDeviceID: {cimInstance.CimInstanceProperties["PNPDeviceID"].Value}");
    Debug.WriteLine("");
}

The issue with the WMI / CIM approach for UWP is that it is not supported, even if the NuGets can be installed. One solution for this could be use desktop bridge to include Win32 code in a UWP app as a background process. See here and here for more information.


A resource that seems to be able to get a lot of information about USBs is USB View. It isn't written in C# but it is open source and is written in C so it might be translatable to C#.

Dan
  • 7,286
  • 6
  • 49
  • 114
  • Hey @Dan, thank you for the bunch of new informations. I tried your listings on my system. However i get only insufficient property outputs. Like `Name/ItemNameDisplay: Dual RS232` or `Name/ItemNameDisplay: TTL232R-3V3`. Also the fields `System.DeviceInterface.Serial.*` are empty. I expected to get some output like in the device manager (`Name/ItemNameDisplay: USB Serial Port (COM11)`, like yours). Also im interested in the manufactuer name and serial number of the devices. But this seems to be not an easy task to get this informations. – veeman Dec 23 '20 at 09:01
  • Currently im using [USB Tree View](https://www.uwe-sieber.de/usbtreeview_e.html), a extened version of the mentioned USB View tool, to lookup the properties. So bascily the properties are there. The questions is i am able to get this in a UWP app. – veeman Dec 23 '20 at 09:12
  • @veeman If you run `DeviceInformation.FindAllAsync("System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True", additional parameters);` and print out all the info, is the com port there? When I run it, it lists the device twice, once with the com port information and once without – Dan Dec 23 '20 at 10:36
  • @veeman But I get your issue, I have no idea why certain field aren't populated even though tools like the one you mentioned clearly highlight the information. Also that tool is pretty cool. Loads of much better information – Dan Dec 23 '20 at 10:38