You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Windows API检测USB移动设备并获取友好设备信息?

检测Windows上的USB存储设备(含移动智能设备)

需求

检测所有连接到Windows的USB存储设备,包括智能手机这类移动设备。


现有尝试分析

1. 基于磁盘设备接口(GUID_DEVINTERFACE_DISK)

此方法可检测硬盘和U盘,但无法识别USB连接的移动设备。

代码示例:

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, NULL, NULL,
                                             DIGCF_PRESENT | DIGCF_DEVICEINTERFACE | DIGCF_PROFILE);                                         
SP_DEVINFO_DATA deviceInfoData;
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
                                             
for (DWORD dwDeviceIndex = 0; SetupDiEnumDeviceInfo(deviceInfoSet, dwDeviceIndex, &deviceInfoData); dwDeviceIndex++)
{
    DEVINST hDevInst = deviceInfoData.DevInst;
    DEVINST hParentDevInst;
    CONFIGRET cRet = CM_Get_Parent(&hParentDevInst, hDevInst, 0);
    TCHAR wsParentDeviceID[MAX_DEVICE_ID_LEN];
    cRet = CM_Get_Device_ID(hParentDevInst, wsParentDeviceID, sizeof(wsParentDeviceID) / sizeof(TCHAR), 0);
    TCHAR wsDeviceID[MAX_DEVICE_ID_LEN];
    cRet = CM_Get_Device_ID(hDevInst, wsDeviceID, sizeof(wsDeviceID) / sizeof(TCHAR), 0);
    
    // Get device friendly name
    DEVPROPTYPE propertyType;
    WCHAR deviceName[MAX_PATH];
    DWORD bufferSize = sizeof(deviceName);
    if (SetupDiGetDeviceProperty(deviceInfoSet, &deviceInfoData, &DEVPKEY_Device_FriendlyName, &propertyType,
        reinterpret_cast<PBYTE>(deviceName), bufferSize, NULL, 0))
    {
        std::wcout << L"Device friendly name = " << deviceName << std::endl;
    }
    else
    {
        std::wcout << L"No device friendly name detected\n";
    }

    // Get manufacturer
    WCHAR manufacturer[MAX_PATH];
    bufferSize = sizeof(manufacturer);
    if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_MFG, NULL,
        reinterpret_cast<PBYTE>(manufacturer), bufferSize, NULL))
    {
        std::wcout << L"Device manufacturer = " << manufacturer << std::endl;
    }
    else
    {
        std::wcout << L"No device manufacturer detected\n";
    }
}
if (deviceInfoSet)
{
    SetupDiDestroyDeviceInfoList(deviceInfoSet);
}

输出示例:

Device friendly name = VendorCo ProductCode USB Device
Device manufacturer = (Standard disk drives)
DeviceId = USBSTOR\DISK&VEN_VENDORCO&PROD_PRODUCTCODE&REV_2.00\3755241199071418340&0
Parent DeviceId = USB\VID_FFFF&PID_5678\3755241199071418340

Device friendly name = VBOX HARDDISK
Device manufacturer = (Standard disk drives)
DeviceId = SCSI\DISK&VEN_VBOX&PROD_HARDDISK\4&2621AEAE&0&000000
Parent DeviceId = PCI\VEN_8086&DEV_2829&SUBSYS_00000000&REV_02\3&267A616A&0&68

2. 基于通用USB设备接口(GUID_DEVINTERFACE_USB_DEVICE)

此方法可检测移动设备,但获取的信息不够友好,无法获取Windows显示的设备型号名称。

代码片段:

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE,
                               NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);

输出示例:

No device friendly name detected
Device manufacturer = Huawei
DeviceId = USB\VID_2717&PID_FF40\D323C6439871
Parent DeviceId = USB\ROOT_HUB30\4&24054718&0&0

No device friendly name detected
Device manufacturer = (Standard system devices)
DeviceId = USB\VID_80EE&PID_0021\5&12C8F4C0&0&1
Parent DeviceId = USB\ROOT_HUB30\4&24054718&0&0

No device friendly name detected
Device manufacturer = Compatible USB storage device
DeviceId = USB\VID_FFFF&PID_5678\3755241199071418340
Parent DeviceId = USB\ROOT_HUB30\4&24054718&0&0

3. 基于Windows便携设备类(GUID_DEVCLASS_WPD)

此方法可检测外接硬盘、U盘及安卓手机,能获取厂商信息和多数设备的友好名称,但部分手机无友好名称,且无法获取所有设备的型号信息。

代码片段:

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&GUID_DEVCLASS_WPD, NULL, NULL, DIGCF_PRESENT);

解决方案:结合WPD API与设备属性查询

核心思路

使用Windows Portable Devices (WPD) API直接枚举便携设备,该API专为移动设备设计,可直接获取设备的友好名称、厂商、型号等信息;同时可结合SetupDi系列API补充存储设备的信息,实现完整的USB存储设备(含移动设备)检测。

代码示例:使用WPD API获取设备详细信息

#include <PortableDeviceApi.h>
#include <PortableDevice.h>
#include <iostream>
#include <vector>

#pragma comment(lib, "PortableDeviceGUIDs.lib")
#pragma comment(lib, "PortableDevice.lib")

void EnumerateWPDDevices()
{
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        std::wcerr << L"CoInitialize failed: 0x" << std::hex << hr << std::endl;
        return;
    }

    IPortableDeviceManager* pDeviceManager = nullptr;
    hr = CoCreateInstance(CLSID_PortableDeviceManager, NULL, CLSCTX_INPROC_SERVER, 
                          IID_IPortableDeviceManager, (VOID**)&pDeviceManager);
    if (FAILED(hr))
    {
        std::wcerr << L"CoCreateInstance(PortableDeviceManager) failed: 0x" << std::hex << hr << std::endl;
        CoUninitialize();
        return;
    }

    // 获取设备数量
    DWORD deviceCount = 0;
    hr = pDeviceManager->GetDevices(NULL, &deviceCount);
    if (FAILED(hr) || deviceCount == 0)
    {
        std::wcout << L"No WPD devices detected." << std::endl;
        pDeviceManager->Release();
        CoUninitialize();
        return;
    }

    // 分配内存存储设备ID
    std::vector<PWSTR> deviceIDs(deviceCount);
    hr = pDeviceManager->GetDevices(deviceIDs.data(), &deviceCount);
    if (FAILED(hr))
    {
        std::wcerr << L"GetDevices failed: 0x" << std::hex << hr << std::endl;
        pDeviceManager->Release();
        CoUninitialize();
        return;
    }

    // 遍历每个设备,获取详细信息
    for (DWORD i = 0; i < deviceCount; i++)
    {
        PWSTR deviceID = deviceIDs[i];
        std::wcout << L"=== Device " << (i+1) << L" ===" << std::endl;
        std::wcout << L"Device ID: " << deviceID << std::endl;

        // 获取友好名称
        WCHAR friendlyName[MAX_PATH] = {0};
        DWORD nameSize = ARRAYSIZE(friendlyName);
        hr = pDeviceManager->GetDeviceFriendlyName(deviceID, friendlyName, &nameSize);
        std::wcout << L"Friendly Name: " << (SUCCEEDED(hr) && friendlyName[0] ? friendlyName : L"Unknown") << std::endl;

        // 获取厂商名称
        WCHAR manufacturer[MAX_PATH] = {0};
        DWORD manuSize = ARRAYSIZE(manufacturer);
        hr = pDeviceManager->GetDeviceManufacturer(deviceID, manufacturer, &manuSize);
        std::wcout << L"Manufacturer: " << (SUCCEEDED(hr) && manufacturer[0] ? manufacturer : L"Unknown") << std::endl;

        // 获取设备型号
        WCHAR model[MAX_PATH] = {0};
        DWORD modelSize = ARRAYSIZE(model);
        hr = pDeviceManager->GetDeviceModel(deviceID, model, &modelSize);
        std::wcout << L"Model: " << (SUCCEEDED(hr) && model[0] ? model : L"Unknown") << std::endl;

        std::wcout << L"-------------------------" << std::endl;
        CoTaskMemFree(deviceID);
    }

    pDeviceManager->Release();
    CoUninitialize();
}

int main()
{
    EnumerateWPDDevices();
    return 0;
}

补充优化

  1. 区分移动设备与普通存储设备:可通过设备ID中的VID/PID(USB厂商/产品ID),或检查设备是否同时支持WPD和大容量存储接口,来筛选移动设备。
  2. 补充缺失信息:若WPD API无法获取某些设备的信息,可使用SetupDiGetDeviceRegistryProperty查询设备注册表属性,如SPDRP_DEVICEDESC、SPDRP_FRIENDLYNAME等,补充缺失的名称信息。
  3. 权限注意:确保程序以足够权限运行,避免因权限不足无法读取设备信息。

内容的提问来源于stack exchange,提问作者RobertGG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:54:55