USB设备重启指南

Windows操作系统中,有时候USB设备可能会遇到问题,需要重启。本文将介绍如何通过编程方式重启USB设备,以及在不同版本的Windows系统中需要注意的事项。

微软文档中提到,使用IOCTL_USB_HUB_CYCLE_PORT命令可以重启USB端口,从而重新枚举连接的设备。这个功能在WindowsXP中可用,但通常只对使用微软标准驱动的集线器有效。第三方驱动通常会返回ERROR_UNKNOWN_FUNCTION错误。在Vista和Windows 7中,微软的标准驱动不再支持这个功能,总是返回ERROR_NOT_SUPPORTED错误。从Windows 8开始,这个功能又可以使用了,但与XP不同,需要管理员权限。没有管理员权限的话,会返回ERROR_NOT_SUPPORTED错误,或者从Windows 10版本1903开始返回ERROR_GEN_FAILURE错误,这两者都很容易让人误解,因为实际上这是一个权限问题。

使用代码重启USB设备

以下是一个C++函数,它接受USB设备的设备实例ID作为唯一的参数,并尝试重启该设备。

bool CycleUsbDevice(char* pszUsbDeviceId) { // 第一步:在设备管理器中找到USB设备 DEVINST DevInst = 0; if (CR_SUCCESS != CM_Locate_DevNode(&DevInst, pszUsbDeviceId, 0)) { return false; } // 第二步:确定USB端口号 char szLocation[64] = ""; DWORD dwType = 0; ULONG uLen = sizeof(szLocation); if (CR_SUCCESS != CM_Get_DevNode_Registry_Property(DevInst, CM_DRP_LOCATION_INFORMATION, &dwType, szLocation, &uLen, 0)) { return false; } if (0 != strncmp(szLocation, "Port_#", 6)) { return false; } int PortNumber = atoi(szLocation + 6); // 第三步:USB集线器是父设备 DEVINST DevInstHub = 0; if (CR_SUCCESS != CM_Get_Parent(&DevInstHub, DevInst, 0)) { return false; } // 第四步:请求USB集线器的“设备接口”即“DevicePath” char szHubDevPath[MAX_PATH] = ""; char szHubDeviceID[MAX_DEVICE_ID_LEN]; if (CR_SUCCESS != CM_Get_Device_ID(DevInstHub, szHubDeviceID, MAX_DEVICE_ID_LEN, 0)) { return false; } if (CR_SUCCESS != CM_Get_Device_Interface_List((GUID*)&GUID_DEVINTERFACE_USB_HUB, szHubDeviceID, szHubDevPath, sizeof(szHubDevPath), CM_GET_DEVICE_INTERFACE_LIST_PRESENT)) { return false; } if (!szHubDevPath[0]) { return false; } // 第五步:打开集线器 HANDLE hHub = CreateFile(szHubDevPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hHub) { return false; } // 第六步:调用IOCTL_USB_HUB_CYCLE_PORT typedef struct _USB_CYCLE_PORT_PARAMS { ULONG ConnectionIndex; ULONG StatusReturned; } USB_CYCLE_PORT_PARAMS, *PUSB_CYCLE_PORT_PARAMS; USB_CYCLE_PORT_PARAMS CyclePortParams = { PortNumber, 0 }; DWORD dwBytes; int res = DeviceIoControl(hHub, IOCTL_USB_HUB_CYCLE_PORT, &CyclePortParams, sizeof(CyclePortParams), &CyclePortParams, sizeof(CyclePortParams), &dwBytes, NULL); CloseHandle(hHub); return (0 != res && 0 == CyclePortParams.StatusReturned); }

注意事项

微软的IOCTL_USB_HUB_CYCLE_PORT文档有误:它说“输出缓冲区:无”,这是错误的。USB_CYCLE_PORT_PARAMS结构体的StatusReturned成员表明,这个结构体是作为输入输出缓冲区使用的。它就是这样工作的,之后dwBytes包含8,这是预期的sizeof(USB_CYCLE_PORT_PARAMS)大小。

可能导致失败的原因

与通过设备管理器重启设备不同,IOCTL_USB_HUB_CYCLE_PORT不会请求权限,因此没有驱动程序可以否决它,所以它总是成功的。但是,端口必须有USB设备连接并且安装了驱动程序。否则,它会失败,并返回错误433 (ERROR_NO_SUCH_DEVICE) 或者从Windows10 21H2开始返回错误50 (ERROR_NOT_SUPPORTED),这同样很容易让人误解。Windows 11则给出了正确的错误1167 (ERROR_DEVICE_NOT_CONNECTED)。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485