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
相关产品推荐
相关产品推荐

