目录
介绍
有时,USB设备会按预期停止工作,重新插入它们会有所帮助。通过代码执行重新插入可能很方便。
如果您想尝试重新启动USB端口是否有帮助,您可以尝试使用我的UsbTreeView或RestartUsbPort。
背景
Microsoft表示IOCTL_USB_HUB_CYCLE_PORT重新启动USB端口,从而启动连接设备的重新枚举。
该函数在XP下可用,但通常仅适用于使用Microsoft标准驱动程序运行的集线器。第三方驱动程序通常会返回ERROR_UNKNOWN_FUNCTION。
在Vista和Windows 7下,Windows标准驱动程序不再支持它,它总是以ERROR_NOT_SUPPORTED失败。
从Windows 8开始,它再次工作,但与XP相比,需要管理员权限。如果没有管理员权限,它会失败,如在Vista和Win7下出现ERROR_NOT_SUPPORTED或自Win10版本1903起出现ERROR_GEN_FAILURE,这两者都非常具有误导性,因为它是一个权限问题。
使用代码
我已将所有内容放在一个函数中,该函数需要USB设备的设备实例ID作为唯一参数。
//--------------------------------------------------------------------------------
// pszUsbDeviceId must be the Device Instance ID of the USB device to restart ----
// returns true on success, false on any failure ---------------------------------
//--------------------------------------------------------------------------------
bool CycleUsbDevice(char* pszUsbDeviceId)
{
// Step 1: Find the USB device in the device manager
DEVINST DevInst = 0;
if ( CR_SUCCESS != CM_Locate_DevNode(&DevInst, pszUsbDeviceId, 0) ) {
return false; //----
}
// Step 2: Determine the USB port number.
// Since Vista it can be read reliably from the device location info:
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; //----
}
// 0123456789
// like "Port_#0004.Hub_#0014"
if ( 0 != strncmp(szLocation, "Port_#", 6) ) {
return false; //----
}
int PortNumber = atoi(szLocation + 6); // leading zeros are ok with atoi
// Step 3: The USB hub is the parent device
DEVINST DevInstHub = 0;
if ( CR_SUCCESS != CM_Get_Parent(&DevInstHub, DevInst, 0) ) {
return false; //----
}
// Step 4: Request the USB hub's "device interface" aka the "DevicePath"
char szHubDevPath[MAX_PATH] = "";
// Get the hub's device instance ID
char szHubDeviceID[MAX_DEVICE_ID_LEN];
if ( CR_SUCCESS != CM_Get_Device_ID(
DevInstHub,
szHubDeviceID, MAX_DEVICE_ID_LEN, 0) ) {
return false; //----
}
// Get the hub's device path (no need for CM_Get_Device_Interface_List_Size
// since we request a single one rather a list of unknown length)
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; //----
}
// Step 5: Open the hub
HANDLE hHub = CreateFile(
szHubDevPath,
GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if ( INVALID_HANDLE_VALUE == hHub ) {
return false; //----
}
// Step 6: Call 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 }; // in and out
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) );
}
//----------------------------------------------------------------------------------------
兴趣点
Microsoft的IOCTL_USB_HUB_CYCLE_PORT文档是错误的:它说“Output buffer: None”是错误的。该StatusReturned struct成员得出的结论是,USB_CYCLE_PORT_PARAMS struct用作in和out缓冲区。它以这种方式工作,dwBytes之后包含8个,这是预期的sizeof(USB_CYCLE_PORT_PARAMS)。
是什么让它失败
与尝试重新启动设备相反,设备管理器IOCTL_USB_HUB_CYCLE_PORT不请求许可,因此没有驱动程序可以否决它,因此它总是成功。
但端口必须连接了USB设备并安装了驱动程序。否则,它会因错误433(ERROR_NO_SUCH_DEVICE) 或自Windows 10 21H2以来出现错误50(ERROR_NOT_SUPPORTED),这再次具有误导性。
Windows 11带有正确的错误1167(ERROR_DEVICE_NOT_CONNECTED)。
https://www.codeproject.com/Articles/5363023/How-to-Restart-a-USB-Port