简体   繁体   中英

How to enumerate the USB devices attached to a hub in Windows

I want to get a list of all the USB devices attached to a Windows machine. The following program does this perfectly except that it only finds the USB devices attached directly to the PC including hubs but not the devices attached to the hubs:

void MainWindow::getConnectedUSB()
{
    GUID GUID_DEVINTERFACE_USB_HUB={ 0xf18a0e88, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8} };
    GUID GUID_DEVINTERFACE_USB_DEVICE ={ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
    GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER={ 0x3abf6f2d, 0x71c4, 0x462a, {0x8a, 0x92, 0x1e, 0x68, 0x61, 0xe6, 0xaf, 0x27}};
    getConnectedUSB(&GUID_DEVINTERFACE_USB_DEVICE);
    getConnectedUSB(&GUID_DEVINTERFACE_USB_HUB);
    getConnectedUSB(&GUID_DEVINTERFACE_USB_HOST_CONTROLLER);
}

void MainWindow::getConnectedUSB(GUID *ClassGuid)
{
    HDEVINFO                         hDevInfo;
    SP_DEVICE_INTERFACE_DATA         DevIntfData;
    PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
    SP_DEVINFO_DATA                  DevData;

    DWORD dwMemberIdx = 0;
    DWORD dwSize;

    hDevInfo = SetupDiGetClassDevs(ClassGuid, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);

    if (hDevInfo != INVALID_HANDLE_VALUE)
    {
        DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

        SetupDiEnumDeviceInterfaces(hDevInfo, NULL, ClassGuid, dwMemberIdx, &DevIntfData);

        while(GetLastError() != ERROR_NO_MORE_ITEMS)
        {
            DevData.cbSize = sizeof(DevData);
            SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
            DevIntfDetailData = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize));
            DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

            if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, DevIntfDetailData, dwSize, &dwSize, &DevData))
            {
                QString devicePath = QString::fromWCharArray(DevIntfDetailData->DevicePath);
                qDebug() << devicePath;
                if (devicePath.startsWith("\\\\?\\usb#vid"))
                        m_connectedUSB.push_back(devicePath);
            }

            HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
            SetupDiEnumDeviceInterfaces(hDevInfo, NULL, ClassGuid, ++dwMemberIdx, &DevIntfData);
        }

        SetupDiDestroyDeviceInfoList(hDevInfo);
    }
}

It uses a little bit of QString magic to collect the DevicePath data into m_connectedUSB which is a QStringList but otherwise it is all regular Windows system calls. What more do I need to do get it to iterate around the devices attached to hubs (and hubs attached to hubs and so forth - I'd like it to get everything)?

This isn't a great answer since all it does is call PowerShell to get the work done. However it is much better than nothing, and because it returns the data as a JSON string it should be fairly robust.

int MainWindow::getConnectedPNP()
{
    QProcess process;
    QString program = "PowerShell";
    QStringList arguments;
    // PowerShell -Command "& {Get-PnpDevice | Select-Object Status,Class,FriendlyName,InstanceId,Manufacturer,Present | ConvertTo-Json}"
    arguments << "-Command" << "& {Get-PnpDevice | Select-Object Status,Class,FriendlyName,InstanceId,Manufacturer,Present | ConvertTo-Json}";
    process.start(program, arguments);
    if (!process.waitForStarted()) return __LINE__;
    if (!process.waitForFinished()) return __LINE__;
    QByteArray result = process.readAll();
    QJsonDocument jsonDoc = QJsonDocument::fromJson(result);
    if (jsonDoc.isNull()) return __LINE__;
    if (!jsonDoc.isArray()) return __LINE__;
    QJsonArray jsonArray = jsonDoc.array();
    for (auto &&it : jsonArray)
    {
        if (!it.isObject()) continue;
        QJsonObject obj = it.toObject();
        if (obj["Status"].toString() != "OK") continue;
        if (obj["Present"].toBool() != true) continue;
        m_connectedPNPClass.push_back(obj["Class"].toString());
        m_connectedPNPFriendlyName.push_back(obj["FriendlyName"].toString());
        m_connectedPNPInstanceId.push_back(obj["InstanceId"].toString());
        m_connectedPNPManufacturer.push_back(obj["Manufacturer"].toString());
    }
    if (m_connectedPNPClass.size() == 0) return __LINE__;
    return 0;
}

It returns a big list of all the PNP devices registered on the system and in this code I am just adding the returned strings to QStringLists. If you don't do the Select-Object you get an even bigger JSON string with even more information. To get the USB devices you just have to look at the InstanceId string because it starts with USB. It is not absolutely ideal because it seems to return multiple lines for some devices, and of course it depends on PowerShell being on the path. On the plus side, the FriendlyName value is a handy thing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM