Shows how to find a USB device in the Windows Device Management by a given Device Instance ID, determine the used USB port number, get its parent device (its USB hub), open the hub and perform the IOCTL_USB_HUB_CYCLE_PORT call.
↵
Introduction
Sometimes, USB devices stop working as expected and re-plugging them helps. Performing the re-plug by code might be handy.
If you want to try if restarting a USB port is helpful, you can try with my UsbTreeView or RestartUsbPort.
Background
Microsoft says IOCTL_USB_HUB_CYCLE_PORT power-cycles the USB port and thus initiates a re-enumeration of the attached device.
The function is available under XP but usually only for hubs which run with the Microsoft standard driver. Third party drivers usually return ERROR_UNKNOWN_FUNCTION
.
Under Vista and Windows 7, it is no more supported by the Windows standard drivers, it always fails with ERROR_NOT_SUPPORTED
.
Since Windows 8, it works again but in contrast to XP, admin privileges are required. Without admin privileges, it fails as under Vista and Win7 with ERROR_NOT_SUPPORTED
or since Win10 version 1903 with ERROR_GEN_FAILURE
, both of which are quite misleading here since it's a privilege issue.
Using the Code
I have put everything in one single function which expects the USB device's Device Instance ID as only parameter.
bool CycleUsbDevice(char* pszUsbDeviceId)
{
DEVINST DevInst = 0;
if ( CR_SUCCESS != CM_Locate_DevNode(&DevInst, pszUsbDeviceId, 0) ) {
return false; }
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);
DEVINST DevInstHub = 0;
if ( CR_SUCCESS != CM_Get_Parent(&DevInstHub, DevInst, 0) ) {
return false; }
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; }
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) );
}
Points of Interest
Microsoft's documentation for IOCTL_USB_HUB_CYCLE_PORT is wrong: It says "Output buffer: None
" which is wrong. The StatusReturned struct
member leads to the conclusion that the USB_CYCLE_PORT_PARAMS struct
is intended as in and out buffer. It works that way and dwBytes
contains 8 afterwards, which is the expected sizeof(USB_CYCLE_PORT_PARAMS)
.
What Makes It Fail
In contrast to an attempt to restart a device by means of the Device Manager IOCTL_USB_HUB_CYCLE_PORT
does not ask for permission, so no driver can veto it and so it always succeeds.
But the port must have a USB device attached and a driver installed. Otherwise, it fails with error 433 (ERROR_NO_SUCH_DEVICE
) or since Windows 10 21H2 with error 50 (ERROR_NOT_SUPPORTED
) which again is misleading.
Windows 11 comes with a proper error 1167 (ERROR_DEVICE_NOT_CONNECTED
).
History
- 18th June, 2023 - First release
- 23rd March, 2024 - Replaced the
SetupDi
enumeration of all hubs by a simple CM_Get_Device_Interface_List
call, which also removes the dependency on setupapi.dll