libusb gets the Windows device instance path DevicePath

The interface provided by the libusb.h header file in the current version of libusb (1.0.26) seems to have no way to obtain the device instance path related to the Windows platform. It looks like:

\?\usb#vid_04ca & amp;pid_7070#5 & amp;20d34a76 & amp;0 & amp;6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}

Only interfaces such as libusb_get_port_numbers are provided to obtain the topology.

We can obtain DevicePath through the platform-related interface in the libusb source code, but using a non-public interface means that we must pay attention to source code-related modifications when replacing the version. Currently I have found two ways:

1. Obtain through the path in the winusb_device_priv structure

Obtain the platform-related data structure corresponding to libusb_device through the usbi_get_device_priv function in the libusbi.h header file. The corresponding platform-related data structure for the windows platform is winusb_device_priv, and the path field it contains is to store the DevicePath.

#include "libusb.h"
#include "libusbi.h"

void enum_device()
{
    libusb_init(NULL);
    libusb_device **device_list;
    int count = libusb_get_device_list(NULL, & amp;device_list);
    for(int i = 0; i < count; i + + )
    {
        libusb_device_descriptor desc;
        int ret = libusb_get_device_descriptor(device_list[i], & amp;desc);
        if (ret != LIBUSB_SUCCESS) {
            continue;
        }
        winusb_device_priv *priv = (winusb_device_priv*)usbi_get_device_priv(device_list[i]);
        if (priv) {
            fprintf(stderr, "Device %d path: %s.\\
", i, priv->path);
        }
    }
    libusb_free_device_list(device_list, 1);
    libusb_exit(NULL);
}

It is inferred from the usbi_get_device_priv function that public structures and platform-related structures are close to each other in memory:

#define PTR_ALIGN(v) (((v) + (sizeof(void *) - 1)) & amp; ~(sizeof(void *) - 1))

static inline void *usbi_get_device_priv(struct libusb_device *dev)
{
return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev));
}

The process of obtaining the DevicePath is obtained through the winusb_get_device_list and get_interface_details functions of windows_winusb.c, using the SetupAPI series of interfaces. One thing to note is that the obtained path is converted to uppercase internally, see normalize_path function implementation.

Do not modify the library source code, but directly use the source code files required for the private interface:

The config.h file comes from the msvc folder of the source code.

You may encounter type conversion errors, just do the explicit conversion yourself.

2. Compare session_data in the libusb_device structure with Windows DevInst

session_data on the windows platform is currently the DevInst value of the stored SP_DEVINFO_DATA structure. Compared with the first method, we need to use the SetupAPI interface to enumerate the device information again, and then determine whether session_data and DevInst are equal. If they are equal, we can use the Win32 interface to obtain the DevicePath.

The libusb_device structure is defined in the libusbi.h header file.

#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()
{
    // 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);
    // SetupDiEnumDeviceInfo enumerates device information
    for (int index = 0; SetupDiEnumDeviceInfo(info_set, index, & amp;info_data); index + + )
    {
        fprintf(stderr, "Device %d %d:\\
", index, info_data.DevInst);

        // 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);
}