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

libusb如何获取USB设备的COM端口号(如COMxx)

解决libusb无法获取COM端口号的问题

libusb是专注于USB底层通信的跨平台库,本身不提供直接获取COM端口号的API——因为COM端口是操作系统为USB串口类设备(如CDC-ACM)分配的逻辑节点,属于系统设备管理范畴,需要结合对应平台的系统API或文件系统信息来获取。以下分平台给出实现方案:

Windows平台

Windows的COM端口信息存储在注册表中,可通过设备VID/PID匹配查询对应注册表项获取端口号:

实现代码片段

#ifdef _WIN32
#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <devguid.h>

DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9ce4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);

void get_com_port_windows(libusb_device *dev) {
    struct libusb_device_descriptor desc;
    libusb_get_device_descriptor(dev, &desc);
    
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE) return;

    SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0};
    deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    
    for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_COMPORT, i, &deviceInterfaceData); i++) {
        DWORD requiredSize = 0;
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);
        
        PSP_DEVICE_INTERFACE_DETAIL_DATA pDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
        if (!pDetailData) continue;
        pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
        
        SP_DEVINFO_DATA devInfoData = {0};
        devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        
        if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, pDetailData, requiredSize, NULL, &devInfoData)) {
            char hardwareId[256];
            if (SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_HARDWAREID, NULL, (PBYTE)hardwareId, sizeof(hardwareId), NULL)) {
                char targetId[32];
                snprintf(targetId, sizeof(targetId), "VID_%04X&PID_%04X", desc.idVendor, desc.idProduct);
                if (strstr(hardwareId, targetId) != NULL) {
                    HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
                    if (hKey != INVALID_HANDLE_VALUE) {
                        char portName[32];
                        DWORD size = sizeof(portName);
                        RegQueryValueEx(hKey, "PortName", NULL, NULL, (LPBYTE)portName, &size);
                        printf("COM端口: %s\n", portName);
                        RegCloseKey(hKey);
                    }
                }
            }
        }
        free(pDetailData);
    }
    SetupDiDestroyDeviceInfoList(hDevInfo);
}
#endif

调用方式

在你代码的VID/PID判断块中添加:

if (desc.idVendor == 0xAAAA && desc.idProduct == 0xBBBB)
{
    #ifdef _WIN32
    get_com_port_windows(dev);
    #endif
}

Linux平台

Linux下USB串口设备映射为/dev/ttyACM*或/dev/ttyUSB*节点,可通过/sys文件系统匹配VID/PID查找对应节点:

实现代码片段

#ifdef __linux__
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

void get_com_port_linux(libusb_device *dev) {
    struct libusb_device_descriptor desc;
    libusb_get_device_descriptor(dev, &desc);
    char target_vid[16], target_pid[16];
    snprintf(target_vid, sizeof(target_vid), "%04x", desc.idVendor);
    snprintf(target_pid, sizeof(target_pid), "%04x", desc.idProduct);

    DIR *dir = opendir("/sys/class/tty");
    if (!dir) return;

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type != DT_LNK) continue;

        char path[256];
        snprintf(path, sizeof(path), "/sys/class/tty/%s/device", entry->d_name);
        char real_path[256];
        if (readlink(path, real_path, sizeof(real_path)-1) == -1) continue;
        real_path[sizeof(real_path)-1] = '\0';

        char vid_path[256], pid_path[256];
        snprintf(vid_path, sizeof(vid_path), "%s/idVendor", real_path);
        snprintf(pid_path, sizeof(pid_path), "%s/idProduct", real_path);

        char vid[16], pid[16];
        FILE *f = fopen(vid_path, "r");
        if (!f) continue;
        fgets(vid, sizeof(vid), f);
        fclose(f);
        vid[strcspn(vid, "\n")] = '\0';

        f = fopen(pid_path, "r");
        if (!f) continue;
        fgets(pid, sizeof(pid), f);
        fclose(f);
        pid[strcspn(pid, "\n")] = '\0';

        if (strcmp(vid, target_vid) == 0 && strcmp(pid, target_pid) == 0) {
            printf("COM端口: /dev/%s\n", entry->d_name);
        }
    }
    closedir(dir);
}
#endif

调用方式

同样在VID/PID判断块中添加:

if (desc.idVendor == 0xAAAA && desc.idProduct == 0xBBBB)
{
    #ifdef __linux__
    get_com_port_linux(dev);
    #endif
}

macOS平台

macOS下USB串口设备映射为/dev/tty.usbmodem*或/dev/tty.usbserial*,需通过IOKit框架查询设备属性获取端口路径:

实现代码片段

#ifdef __APPLE__
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/usb/IOUSBLib.h>

void get_com_port_macos(libusb_device *dev) {
    struct libusb_device_descriptor desc;
    libusb_get_device_descriptor(dev, &desc);

    CFMutableDictionaryRef matching = IOServiceMatching(kIOSerialBSDClientType);
    if (!matching) return;

    io_iterator_t iterator;
    kern_return_t kr = IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iterator);
    if (kr != KERN_SUCCESS) return;

    io_service_t service;
    while ((service = IOIteratorNext(iterator)) != 0) {
        io_service_t usbDevice = IOServiceGetMatchingParent(service, IOServiceMatching(kIOUSBDeviceClassName));
        if (usbDevice) {
            SInt32 vid, pid;
            kr = IORegistryEntryCreateCFProperty(usbDevice, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0, (CFTypeRef*)&vid);
            if (kr != KERN_SUCCESS) {
                IOObjectRelease(usbDevice);
                continue;
            }
            kr = IORegistryEntryCreateCFProperty(usbDevice, CFSTR(kUSBProductID), kCFAllocatorDefault, 0, (CFTypeRef*)&pid);
            if (kr != KERN_SUCCESS) {
                IOObjectRelease(usbDevice);
                continue;
            }

            if ((uint16_t)vid == desc.idVendor && (uint16_t)pid == desc.idProduct) {
                CFStringRef devicePath = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0);
                if (devicePath) {
                    char path[256];
                    CFStringGetCString(devicePath, path, sizeof(path), kCFStringEncodingUTF8);
                    printf("COM端口: %s\n", path);
                    CFRelease(devicePath);
                }
            }
            IOObjectRelease(usbDevice);
        }
        IOObjectRelease(service);
    }
    IOObjectRelease(iterator);
}
#endif

调用方式

在VID/PID判断块中添加:

if (desc.idVendor == 0xAAAA && desc.idProduct == 0xBBBB)
{
    #ifdef __APPLE__
    get_com_port_macos(dev);
    #endif
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 01:15:37