Windows enumerates device information

Enumeration of device information is mainly implemented through the interface in the SetupAPI module, in conjunction with the corresponding device class GUID.

Here we take enumerating USB device information as an example, including device instance addresses, etc.:

#include <stdio.h>
#include <Windows.h>
#include <SetupAPI.h>
#pragma comment(lib, "SetupAPI.lib")
#include <devguid.h>
// The specific device GUID requires initguid, such as usbiodef
#include <initguid.h>
// USB device
// GUID_DEVINTERFACE_USB_DEVICE
#include <usbiodef.h>

void enum_device_info()
{
    // HDEVINFO identifies the device information set
    HDEVINFO info_set;
    // SetupDiGetClassDevs returns a handle to the device information set containing the device information element requested by the local computer
    //Interface document: https://learn.microsoft.com/zh-cn/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsa
    // To return a device that supports a device interface of any class, set the DIGCF_DEVICEINTERFACE and DIGCF_ALLCLASSES flags, then set ClassGuid to NULL
    //info_set = SetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    // To return only devices that support a device interface of the specified class, set the DIGCF_DEVICEINTERFACE flag and provide the class GUID of the device interface class using the ClassGuid parameter
    //actually use the GUID of the corresponding device
    GUID device_guid{GUID_DEVINTERFACE_USB_DEVICE};
    info_set = SetupDiGetClassDevsA( & amp;device_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    // If the operation fails, return INVALID_HANDLE_VALUE
    if (info_set == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "SetupDiGetClassDevs: [err code] %d.\\
", GetLastError());
        return;
    }

    // SP_DEVINFO_DATA identifies the device in the device information set
    SP_DEVINFO_DATA info_data = { 0 };
    info_data.cbSize = sizeof(info_data);
    constexpr DWORD buf_size = 2048;
    BYTE info_buf[buf_size + 1] = { 0 };
    // SetupDiEnumDeviceInfo enumerates device information
    for (int index = 0; SetupDiEnumDeviceInfo(info_set, index, & amp;info_data); index + + )
    {
        fprintf(stderr, "Device %d:\\
", index);

        // SetupDiGetDeviceRegistryProperty retrieves the specified plug and play device properties
        // Only part of the SPDRP_* parameters are listed here.
        // If an ERROR_INSUFFICIENT_BUFFER error is returned, it means that the buffer is not enough to store the data. Reset the buffer according to the size returned by the last parameter.
        // Device Description
        // The SPDRP_DEVICEDESC function retrieves the REG_SZ string containing the device description
        if (!SetupDiGetDeviceRegistryPropertyA(info_set, & amp;info_data, SPDRP_DEVICEDESC, NULL, info_buf, buf_size, NULL)) {
            fprintf(stderr, "Device Desc: [err code] %d %d.\\
", GetLastError(), ERROR_INSUFFICIENT_BUFFER);
        } else {
            fprintf(stderr, "Device Desc: %s.\\
", info_buf);
        }

        //Equipment equipment class
        // The SPDRP_CLASS function retrieves a REG_SZ string containing the device settings class for the device
        if (!SetupDiGetDeviceRegistryPropertyA(info_set, & amp;info_data, SPDRP_CLASS, NULL, info_buf, buf_size, NULL)) {
            fprintf(stderr, "Device Class: [err code] %d.\\
", GetLastError());
        } else {
            fprintf(stderr, "Device Class: %s.\\
", info_buf);
        }

        //Device setting class GUID
        // The SPDRP_CLASSGUID function retrieves a REG_SZ string that contains the GUID that represents the device's device settings class
        if (!SetupDiGetDeviceRegistryPropertyA(info_set, & amp;info_data, SPDRP_CLASSGUID, NULL, info_buf, buf_size, NULL)) {
            fprintf(stderr, "Device Class GUID: [err code] %d.\\
", GetLastError());
        } else {
            fprintf(stderr, "Device Class GUID: %s.\\
", info_buf);
        }

        // device friendly name
        // The SPDRP_FRIENDLYNAME function retrieves a REG_SZ string containing the friendly name of the device
        if (!SetupDiGetDeviceRegistryPropertyA(info_set, & amp;info_data, SPDRP_FRIENDLYNAME, NULL, info_buf, buf_size, NULL)) {
            fprintf(stderr, "Device Friendly Name: [err code] %d.\\
", GetLastError());
        } else {
            fprintf(stderr, "Device Friendly Name: %s.\\
", info_buf);
        }

        // Return the hardware ID list
        // The SPDRP_HARDWAREID function retrieves a REG_MULTI_SZ string containing a list of device hardware IDs
        if (!SetupDiGetDeviceRegistryPropertyA(info_set, & amp;info_data, SPDRP_HARDWAREID, NULL, info_buf, buf_size, NULL)) {
            fprintf(stderr, "Device Hardware ID: [err code] %d.\\
", GetLastError());
        } else {
            fprintf(stderr, "Device Hardware ID: %s.\\
", info_buf);
        }

        // Get device instance ID
        // The SetupDiGetDeviceInstanceIdA function retrieves the device instance ID associated with the device information element
        if (!SetupDiGetDeviceInstanceIdA(info_set, & amp;info_data, (PSTR)info_buf, buf_size, NULL)) {
            fprintf(stderr, "Device Instance ID: [err code] %d.\\
", GetLastError());
        } else {
            fprintf(stderr, "Device Instance ID: %s.\\
", info_buf);
        }

        // Get the device instance path
        // SP_DEVICE_INTERFACE_DATA device interface in the device information set
        SP_DEVICE_INTERFACE_DATA interface_data = { 0 };
        interface_data.cbSize = sizeof(interface_data);
        // SetupDiEnumDeviceInterfaces enumerates the device interfaces contained in the device information set
        BOOL ret = SetupDiEnumDeviceInterfaces(info_set, NULL, (LPGUID) & amp;device_guid, index, & amp;interface_data);
        if (!ret) continue;
        ULONG required_len = 0;
        // SetupDiGetDeviceInterfaceDetail returns detailed information about the device interface
        //The first call is to get the length, here it returns false
        SetupDiGetDeviceInterfaceDetailA(info_set, & amp;interface_data, NULL, 0, & amp;required_len, NULL);
        if (required_len <= 0) continue;
        ULONG predicted_len = required_len;
        // SP_INTERFACE_DEVICE_DETAIL_DATA contains the path to the device interface
        SP_INTERFACE_DEVICE_DETAIL_DATA_A detail_data = { 0 };
        detail_data.cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A);
        // Retrieve plug and play device information
        if (SetupDiGetDeviceInterfaceDetailA(info_set,
                                              &interface_data,
                                              &detail_data,
                                             predicted_len,
                                              &required_len,
                                              & amp;info_data)) {
            fprintf(stderr, "Device Instance Path: %s.\\
", detail_data.DevicePath);
        }
    }

    // SetupDiDestroyDeviceInfoList deletes the device information set and releases all associated memory
    SetupDiDestroyDeviceInfoList(info_set);
}

Output result:

Device 0:
Device Desc: USB Composite Device.
Device Class: USB.
Device Class GUID: {36fc9e60-c465-11cf-8056-444553540000}.
Device Friendly Name: [err code] 13. // 13 corresponds to ERROR_INVALID_DATA
Device Hardware ID: USB\VID_04CA &PID_7070 &REV_0023.
Device Instance ID: USB\VID_04CA & amp;PID_7070\5 & amp;20D34A76 & amp;0 & amp;6.
Device Instance Path: \?\usb#vid_04ca & amp;pid_7070#5 & amp;20d34a76 & amp;0 & amp;6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}.
Device 1:
Device Desc: Generic Bluetooth Adapter.
Device Class: Bluetooth.
Device Class GUID: {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}.
Device Friendly Name: [err code] 13.
Device Hardware ID: USB\VID_0CF3 &PID_E500 &REV_0001.
Device Instance ID: USB\VID_0CF3 & amp;PID_E500\5 & amp;20D34A76 & amp;0 & amp;14.
Device Instance Path: \?\usb#vid_0cf3 & amp;pid_e500#5 & amp;20d34a76 & amp;0 & amp;14#{a5dcbf10-6530-11d2-901f-00c04fb951ed}.