A Comprehensive Guide to Enumerating USB Devices in Windows Using C#

Nov 24, 2025 · Programming · 15 views · 7.8

Keywords: C# | USB Device Enumeration | WMI | Win32_USBHub | Device Tree

Abstract: This article provides an in-depth exploration of methods for enumerating connected USB devices in Windows environments using the C# programming language. By analyzing various WMI (Windows Management Instrumentation) classes, including Win32_USBHub, Win32_PnPEntity, and Win32_USBControllerDevice, it compares their strengths and weaknesses and offers complete code examples. Key topics include utilizing the System.Management namespace for device queries, constructing device information classes, and handling device tree structures. Additionally, the article briefly contrasts related commands in Linux systems, such as lsusb, to provide a cross-platform perspective. Covering implementations from basic queries to advanced device relationship mapping, it is suitable for intermediate to advanced developers.

Introduction

Enumerating connected USB devices is a common requirement in software development, particularly in system management, device monitoring, or driver development scenarios. Based on high-scoring Q&A from Stack Overflow, this article delves into how to achieve this in Windows systems using C#. We start with basic methods and progressively expand to more comprehensive solutions, ensuring code accuracy and efficiency.

Querying USB Devices with WMI

Windows Management Instrumentation (WMI) offers a standardized way to access system management information. In C#, through the System.Management namespace, we can easily query USB devices. The initial approach uses the Win32_USBHub class, but it is important to note its limitation: it only returns USB hub devices, not all USB devices. Below is a basic example demonstrating how to query and display USB device information.

using System;
using System.Collections.Generic;
using System.Management;

class Program
{
    static void Main(string[] args)
    {
        var usbDevices = GetUSBDevices();
        foreach (var usbDevice in usbDevices)
        {
            Console.WriteLine($"Device ID: {usbDevice.DeviceID}, PNP Device ID: {usbDevice.PnpDeviceID}, Description: {usbDevice.Description}");
        }
        Console.Read();
    }

    static List<USBDeviceInfo> GetUSBDevices()
    {
        List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
        using var searcher = new ManagementObjectSearcher(@"Select * From Win32_USBHub");
        using ManagementObjectCollection collection = searcher.Get();
        foreach (var device in collection)
        {
            devices.Add(new USBDeviceInfo(
                (string)device.GetPropertyValue("DeviceID"),
                (string)device.GetPropertyValue("PNPDeviceID"),
                (string)device.GetPropertyValue("Description")
            ));
        }
        return devices;
    }
}

class USBDeviceInfo
{
    public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
    {
        this.DeviceID = deviceID;
        this.PnpDeviceID = pnpDeviceID;
        this.Description = description;
    }
    public string DeviceID { get; private set; }
    public string PnpDeviceID { get; private set; }
    public string Description { get; private set; }
}

This code uses ManagementObjectSearcher to query the Win32_USBHub class, retrieving device ID, PNP device ID, and description. However, as noted in supplementary answers, Win32_USBHub only covers hub devices and may miss non-hub devices, such as components of composite devices.

Extended Methods: Using Win32_PnPEntity and Win32_USBControllerDevice

For a more comprehensive enumeration of USB devices, it is advisable to use the Win32_PnPEntity class with a WHERE clause to filter device IDs starting with "USB%". However, this approach may include non-USB devices like Bluetooth or HID devices. A better solution is to query Win32_USBControllerDevice, which provides PNPDeviceID pairs of Antecedent (controller) and Dependent (device), offering a complete list of all USB devices.

Below is an improved code example showing how to query Win32_USBControllerDevice and build a device list.

using System;
using System.Collections.Generic;
using System.Management;

class EnhancedUSBEnumerator
{
    static void Main(string[] args)
    {
        var usbDevices = GetCompleteUSBDevices();
        foreach (var device in usbDevices)
        {
            Console.WriteLine($"PNP Device ID: {device.PnpDeviceID}, Description: {device.Description}");
        }
        Console.Read();
    }

    static List<USBDeviceInfo> GetCompleteUSBDevices()
    {
        List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
        using var searcher = new ManagementObjectSearcher(@"Select * From Win32_USBControllerDevice");
        using ManagementObjectCollection collection = searcher.Get();
        foreach (var item in collection)
        {
            string dependentID = ((string)item["Dependent"]).Split('=')[1].Trim('"');
            // Optional: Query Win32_PnPEntity for detailed information
            var details = GetDeviceDetails(dependentID);
            devices.Add(new USBDeviceInfo(dependentID, dependentID, details?.Description ?? "Unknown"));
        }
        return devices;
    }

    static USBDeviceInfo GetDeviceDetails(string pnpDeviceID)
    {
        using var searcher = new ManagementObjectSearcher($@"Select * From Win32_PnPEntity Where PNPDeviceID = '{pnpDeviceID}'");
        using ManagementObjectCollection collection = searcher.Get();
        foreach (var device in collection)
        {
            return new USBDeviceInfo(
                (string)device.GetPropertyValue("DeviceID"),
                (string)device.GetPropertyValue("PNPDeviceID"),
                (string)device.GetPropertyValue("Description")
            );
        }
        return null;
    }
}

This method retrieves PNPDeviceIDs of all USB devices via Win32_USBControllerDevice and optionally queries Win32_PnPEntity for detailed descriptions, ensuring comprehensive enumeration including HID devices and other non-hub devices.

Device Tree Structure and Advanced Mapping

In complex applications, understanding the hierarchical structure of USB devices is crucial. When querying Win32_USBControllerDevice, the return order may reflect the device tree structure, such as root hubs followed by their child devices. By analyzing PNPDeviceID and registry parameters like ParentIdPrefix, a device tree can be constructed. Below is a conceptual example illustrating how to map parent-child relationships.

// Pseudocode: Building a USB device tree
public class USBDeviceTree
{
    public void BuildTree()
    {
        var controllers = GetUSBControllers(); // Query Win32_USBController
        var controllerDevices = GetUSBControllerDevices(); // Query Win32_USBControllerDevice
        // Map devices to controllers based on Antecedent and Dependent
        // Infer hierarchy using order or ParentIdPrefix
    }
}

This approach allows recursive iteration of the device tree, from controllers to root hubs, then to child devices and composite device components. For precise parent-child relationships, using the SetupDi API is recommended, but WMI queries provide a convenient alternative.

Cross-Platform Perspective: USB Device Enumeration in Linux Systems

As a supplement, the reference article discusses methods for enumerating USB devices in Linux systems. For example, the lsusb command lists all USB devices, while ls /dev/ttyUSB* only shows serial devices. This highlights differences across operating systems: in Linux, USB devices may map to files like /dev/ttyUSB0, whereas HID devices such as mice and keyboards do not appear in tty lists but are displayed via lsusb.

Below is a simple Bash script example for listing USB devices in Linux.

#!/bin/bash
# Use lsusb command to enumerate USB devices
lsusb
# Example output: Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

This emphasizes that in cross-platform development, enumeration strategies must be adapted to the target system. In Windows, WMI provides a unified interface, while in Linux, command-line tools and file system interfaces are more common.

Performance Optimization and Error Handling

In practical applications, performance and error handling must be considered when enumerating USB devices. For instance, frequent WMI queries can impact system performance; it is advisable to cache results or use asynchronous operations. Additionally, when handling device plug-and-play events, an event listening mechanism should be implemented. Below is a simple event handling example.

using System.Management;

class USBDeviceMonitor
{
    public void StartMonitoring()
    {
        var query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent");
        using var watcher = new ManagementEventWatcher(query);
        watcher.EventArrived += (sender, e) =>
        {
            Console.WriteLine("USB device change detected");
            // Re-enumerate devices
        };
        watcher.Start();
    }
}

This code uses ManagementEventWatcher to listen for device change events, ensuring the application can respond in real-time to USB device connections or disconnections.

Conclusion

This article has detailed various methods for enumerating USB devices in Windows systems using C#, from basic Win32_USBHub queries to more comprehensive combinations of Win32_USBControllerDevice and Win32_PnPEntity. Through code examples and theoretical analysis, we emphasized the importance of comprehensive enumeration and device tree mapping. Cross-platform comparisons provided a broader perspective. Developers should choose appropriate methods based on specific needs and pay attention to performance optimization and error handling to build robust applications.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.