如何在Windows上检测USB速度

How to detect USB speed on Windows

本文关键字:检测 USB 速度 Windows      更新时间:2023-10-16

我使用setup API函数查找USB设备,然后使用createfile与之通信。即使用SetupDiGetClassDevs、SetupDiEnumDeviceInterfaces、SetupDiGetDeviceInterfaceDetail等。

我想能够确定设备是否以USB2速度或USB3速度连接,即SuperSpeed或非

如何通过Windows API实现这一点?

这就是我最终要做的。这很复杂。无法相信没有更简单的方法:

#include "stdafx.h"
#include <Windows.h>
#include <Setupapi.h>
#include <winusb.h>
#undef LowSpeed
#include <Usbioctl.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
class Usb_Device
{
private:
    std::wstring _driverKey;
    char _speed;
public:
    Usb_Device(int adapterNumber, std::wstring devicePath, char speed);
    virtual char GetSpeed(std::wstring driverKey);
};
class Usb_Hub : public Usb_Device
{
private:
    bool _isRootHub;
    std::wstring _deviceDescription;
    std::wstring _devicePath;
    std::vector<std::unique_ptr<Usb_Device>> _devices;
public:
    Usb_Hub(std::wstring devicePath, char speed);
    virtual char GetSpeed(std::wstring driverKey) override;
};
class Usb_Controller
{
private:
    GUID _interfaceClassGuid;
    std::wstring _devicePath;
    std::wstring _deviceDescription;
    std::wstring _driverKey;
    std::vector<std::unique_ptr<Usb_Device>> _devices;
public:
    Usb_Controller();
    char GetSpeed(std::wstring driverKey);
};
static std::unique_ptr<Usb_Device> BuildDevice(int portCount, std::wstring devicePath)
{
    std::unique_ptr<Usb_Device> ret;
    HANDLE handle = INVALID_HANDLE_VALUE;
    DWORD bytes = -1;
    DWORD bytesReturned = -1;
    BOOL isConnected = FALSE;
    char speed;
    // Open a handle to the Hub device
    handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (handle != INVALID_HANDLE_VALUE)
    {
        bytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX);
        PUSB_NODE_CONNECTION_INFORMATION_EX nodeConnection = (PUSB_NODE_CONNECTION_INFORMATION_EX)(new char[bytes]);
        nodeConnection->ConnectionIndex = portCount;
        if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, nodeConnection, bytes, nodeConnection, bytes, &bytesReturned, 0))
        {
            isConnected = nodeConnection->ConnectionStatus == USB_CONNECTION_STATUS::DeviceConnected;
            speed = nodeConnection->Speed;
        }
        if (isConnected)
        {
            if (nodeConnection->DeviceDescriptor.bDeviceClass == 0x09 /*HubDevice*/)
            {
                bytes = sizeof(USB_NODE_CONNECTION_NAME);
                PUSB_NODE_CONNECTION_NAME nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
                nodeConnectionName->ConnectionIndex = portCount;
                if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
                {
                    bytes = nodeConnectionName->ActualLength;
                    delete[] nodeConnectionName;
                    nodeConnectionName = (PUSB_NODE_CONNECTION_NAME)(new char[bytes]);
                    nodeConnectionName->ConnectionIndex = portCount;
                    if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeConnectionName, bytes, nodeConnectionName, bytes, &bytesReturned, 0))
                    {
                        std::wstring name = std::wstring(L"\\?\") + std::wstring(nodeConnectionName->NodeName);
                        ret = std::unique_ptr<Usb_Device>(new Usb_Hub(name, speed));
                    }
                }
                delete[] nodeConnectionName;
            }
            else
            {
                ret = std::unique_ptr<Usb_Device>(new Usb_Device(portCount, devicePath, speed));
            }
        }
        else
        {
            // Chuck this device
        }
        delete[] nodeConnection;
        CloseHandle(handle);
    }
    return ret;
}
Usb_Controller::Usb_Controller()
{
    BOOL success = TRUE;
    for (int index = 0; success; index++)
    {
        GUID guid;
        HRESULT hr = CLSIDFromString(L"{3abf6f2d-71c4-462a-8a92-1e6861e6af27}", (LPCLSID)&guid);
        unsigned char* ptr = new unsigned char[2048]; // Should really do two calls, but that's more effort
        HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
        // Create a device interface data structure
        SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
        deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        // Start the enumeration.
        success = SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, index, &deviceInterfaceData);
        if (success)
        {
            _interfaceClassGuid = deviceInterfaceData.InterfaceClassGuid;
            // Build a DevInfo data structure.
            SP_DEVINFO_DATA deviceInfoData = { 0 };
            deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
            // Now we can get some more detailed informations.
            DWORD nRequiredSize = 0;
            SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, 0, 0, &nRequiredSize, 0);
            if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
            {
                PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[nRequiredSize]);
                memset(deviceInterfaceDetailData, 0, nRequiredSize);
                deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                if (SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterfaceData, deviceInterfaceDetailData, nRequiredSize, &nRequiredSize, &deviceInfoData))
                {
                    _devicePath = deviceInterfaceDetailData->DevicePath;
                    // Get the device description and driver key name.
                    DWORD requiredSize = 0;
                    DWORD regType = REG_SZ;
                    if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DEVICEDESC, &regType, ptr, 2048, &requiredSize))
                    {
                        _deviceDescription = reinterpret_cast<wchar_t*>(ptr);
                    }
                    if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInfoData, SPDRP_DRIVER, &regType, ptr, 2048, &requiredSize))
                    {
                        _driverKey = reinterpret_cast<wchar_t*>(ptr);
                    }
                }
                delete[] deviceInterfaceDetailData;
            }
            SetupDiDestroyDeviceInfoList(deviceInfoHandle);
            std::unique_ptr<Usb_Device> hub(new Usb_Hub(_devicePath, -1));
            _devices.push_back(std::move(hub));
        }
        else
        {
            success = false;
        }
        delete[] ptr;
    }
}
char Usb_Controller::GetSpeed(std::wstring driverKey)
{
    char speed = -1;
    for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
    {
        if (*it != nullptr)
        {
            speed = (*it)->GetSpeed(driverKey);
        }
    }
    return speed;
}
Usb_Hub::Usb_Hub(std::wstring devicePath, char speed) :
    Usb_Device(-1, devicePath, speed)
{
    HANDLE handle1 = INVALID_HANDLE_VALUE;
    HANDLE handle2 = INVALID_HANDLE_VALUE;
    _deviceDescription = L"Standard-USB-Hub";
    _devicePath = devicePath;
    DWORD bytesReturned = -1;
    DWORD bytes = -1;
    BOOL success = TRUE;
    // Open a handle to the host controller.
    handle1 = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (handle1 != INVALID_HANDLE_VALUE)
    {
        USB_ROOT_HUB_NAME rootHubName;
        memset(&rootHubName, 0, sizeof(USB_ROOT_HUB_NAME));
        // Get the root hub name.
        if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, &rootHubName, sizeof(USB_ROOT_HUB_NAME), &bytesReturned, 0))
        {
            if (rootHubName.ActualLength > 0)
            {
                PUSB_ROOT_HUB_NAME actualRootHubName = (PUSB_ROOT_HUB_NAME)(new char[rootHubName.ActualLength]);
                if (DeviceIoControl(handle1, IOCTL_USB_GET_ROOT_HUB_NAME, nullptr, 0, actualRootHubName, rootHubName.ActualLength, &bytesReturned, 0))
                {
                    _isRootHub = true;
                    _deviceDescription = L"RootHub";
                    _devicePath = std::wstring(L"\\?\") + std::wstring(actualRootHubName->RootHubName);
                }
                delete[] actualRootHubName;
            }
        }
        // Now let's open the hub (based upon the hub name we got above).
        int PortCount = 0;
        handle2 = CreateFile(_devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
        if (handle2 != INVALID_HANDLE_VALUE)
        {
            bytes = sizeof(USB_NODE_INFORMATION);
            PUSB_NODE_INFORMATION nodeInfo = (PUSB_NODE_INFORMATION)(new char[bytes]);
            memset(nodeInfo, 0, sizeof(USB_NODE_INFORMATION));
            nodeInfo->NodeType = USB_HUB_NODE::UsbHub;
            // Get the hub information.
            if (DeviceIoControl(handle2, IOCTL_USB_GET_NODE_INFORMATION, nodeInfo, bytes, nodeInfo, bytes, &bytesReturned, 0))
            {
                DWORD d = GetLastError();
                PortCount = nodeInfo->u.HubInformation.HubDescriptor.bNumberOfPorts;
            }
            delete[] nodeInfo;
            CloseHandle(handle2);
        }
        CloseHandle(handle1);
        for (int index = 1; index <= PortCount; index++)
        {
            std::unique_ptr<Usb_Device> device = BuildDevice(index, _devicePath);
            _devices.push_back(std::move(device));
        }
    }
    else
    {
        success = FALSE;
    }
}
char Usb_Hub::GetSpeed(std::wstring driverKey)
{
    char speed = Usb_Device::GetSpeed(driverKey);
    if (speed == -1)
    {
        for (auto it = _devices.begin(); it != _devices.end() && speed == -1; ++it)
        {
            if (*it != nullptr)
            {
                speed = (*it)->GetSpeed(driverKey);
            }
        }
    }
    return speed;
}
Usb_Device::Usb_Device(int adapterNumber, std::wstring devicePath, char speed)
{
    _speed = speed;
    HANDLE handle = CreateFile(devicePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if (handle != INVALID_HANDLE_VALUE)
    {
        // Get the Driver Key Name (usefull in locating a device)
        DWORD bytesReturned = -1;
        DWORD bytes = sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME);
        PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
        driverKey->ConnectionIndex = adapterNumber;
        // Use an IOCTL call to request the Driver Key Name
        if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
        {
            bytes = driverKey->ActualLength;
            delete[] driverKey;
            driverKey = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)(new char[bytes]);
            driverKey->ConnectionIndex = adapterNumber;
            if (DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, driverKey, bytes, driverKey, bytes, &bytesReturned, 0))
            {
                _driverKey = driverKey->DriverKeyName;
            }
        }
        delete[] driverKey;
        CloseHandle(handle);
    }
}
char Usb_Device::GetSpeed(std::wstring driverKey)
{
    return _speed;
}
int main()
{
    Usb_Controller controller;
    GUID guid;
    HRESULT hr = CLSIDFromString(L"{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}", (LPCLSID)&guid);
    HDEVINFO deviceInfoHandle = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (deviceInfoHandle != INVALID_HANDLE_VALUE)
    {
        int deviceIndex = 0;
        while (true)
        {
            SP_DEVICE_INTERFACE_DATA deviceInterface = { 0 };
            deviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
            if (SetupDiEnumDeviceInterfaces(deviceInfoHandle, 0, &guid, deviceIndex, &deviceInterface))
            {
                DWORD cbRequired = 0;
                SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, 0, 0, &cbRequired, 0);
                if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
                {
                    PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(new char[cbRequired]);
                    memset(deviceInterfaceDetail, 0, cbRequired);
                    deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                    if (!SetupDiGetDeviceInterfaceDetail(deviceInfoHandle, &deviceInterface, deviceInterfaceDetail, cbRequired, &cbRequired, 0))
                    {
                        deviceIndex++;
                        continue;
                    }
                    // Initialize the structure before using it.
                    memset(deviceInterfaceDetail, 0, cbRequired);
                    deviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
                    // Call the API a second time to retrieve the actual
                    // device path string.
                    BOOL status = SetupDiGetDeviceInterfaceDetail(
                        deviceInfoHandle,  // Handle to device information set
                        &deviceInterface,     // Pointer to current node in devinfo set
                        deviceInterfaceDetail,   // Pointer to buffer to receive device path
                        cbRequired,   // Length of user-allocated buffer
                        &cbRequired,  // Pointer to arg to receive required buffer length
                        NULL);        // Not interested in additional data
                    BOOL success = TRUE;
                    for (int i = 0; success; i++)
                    {
                        SP_DEVINFO_DATA deviceInterfaceData = { 0 };
                        deviceInterfaceData.cbSize = sizeof(SP_DEVINFO_DATA);
                        // Start the enumeration.
                        success = SetupDiEnumDeviceInfo(deviceInfoHandle, i, &deviceInterfaceData);
                        DWORD RequiredSize = 0;
                        DWORD regType = REG_SZ;
                        unsigned char* ptr = new unsigned char[2048];
                        if (SetupDiGetDeviceRegistryProperty(deviceInfoHandle, &deviceInterfaceData, SPDRP_DRIVER, &regType, ptr, 2048, &RequiredSize))
                        {
                            char speed = controller.GetSpeed(reinterpret_cast<wchar_t*>(ptr));
                            std::wcout << std::wstring(reinterpret_cast<wchar_t*>(ptr)) << std::endl;
                            std::wcout << L"Speed: " << (int)speed << std::endl;
                        }
                        delete[] ptr;
                    }
                    auto hDeviceHandle = CreateFile(
                        deviceInterfaceDetail->DevicePath,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_FLAG_OVERLAPPED,
                        NULL);
                    CloseHandle(hDeviceHandle);
                    delete[] deviceInterfaceDetail;
                }
            }
            else
            {
                break;
            }
            ++deviceIndex;
        }
        SetupDiDestroyDeviceInfoList(deviceInfoHandle);
    }
    return 0;
}

我想你必须在链接中尝试WinUSB——这里有一个检测USB速度的示例代码。如果你想要WinUSB的描述,你可以在这里找到。