I have my KMDF Virtual USB Bus driver installed in the system, and when I uninstall it from Device Manager by clicking “Uninstall Device” from the right‑click menu while a virtual USB device is still plugged into my virtual USB bus, I see the “Uninstall Device” window with the progress bar “Uninstalling devices…”. This window never closes unless the virtual USB device is unplugged.
I could send an IO control code that disconnects all virtual USB devices connected to the virtual USB bus, which would solve the problem, but I have not found a single device callback that is invoked when the device is being uninstalled. Here is the list of callbacks I tried:
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&wdfPnpPowerCallbacks);
wdfPnpPowerCallbacks.EvtDevicePrepareHardware = ControllerWdfEvtDevicePrepareHardware;
wdfPnpPowerCallbacks.EvtDeviceReleaseHardware = ControllerWdfEvtDeviceReleaseHardware;
wdfPnpPowerCallbacks.EvtDeviceD0Entry = ControllerWdfEvtDeviceD0Entry;
wdfPnpPowerCallbacks.EvtDeviceD0Exit = ControllerWdfEvtDeviceD0Exit;
wdfPnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = ControllerWdfEvtDeviceD0EntryPostInterruptsEnabled;
wdfPnpPowerCallbacks.EvtDeviceD0ExitPreInterruptsDisabled = ControllerWdfEvtDeviceD0ExitPreInterruptsDisabled;
wdfPnpPowerCallbacks.EvtDeviceQueryRemove = ControllerWdfEvtDeviceQueryRemove;
wdfPnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup = ControllerWdfEvtWdfDeviceSelfManagedIoCleanup;
wdfPnpPowerCallbacks.EvtDeviceQueryStop = ControllerWdfEvtDeviceQueryStop;
wdfPnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend = ControllerWdfEvtDeviceSelfManagedIoSuspend;
wdfPnpPowerCallbacks.EvtDeviceSurpriseRemoval = ControllerWdfEvtDeviceSurpriseRemoval;
These callbacks are invoked in the following order if I send manually an IO control code that disconnects all virtual USB devices during uninstall, and then the virtual USB bus is uninstalled:
ControllerWdfEvtDeviceQueryRemove
ControllerWdfEvtDeviceSelfManagedIoSuspend Device=0000467B973A4368
ControllerWdfEvtDeviceD0ExitPreInterruptsDisabled
ControllerWdfEvtDeviceD0Exit
ControllerWdfEvtDeviceReleaseHardware
ControllerWdfEvtWdfDeviceSelfManagedIoCleanup Device=0000467B973A4368
ControllerWdfEvtCleanupCallback
My question is: How can I receive information from the operating system that my virtual USB bus driver is being uninstalled, so that I can properly clean up the connected virtual USB devices? Alternatively, how should this problem be correctly resolved?
Not an expert in USB; if the PDO→ReferenceCount is not 0, then IRP_MN_REMOVE_DEVICE is not called. Verify that the file object is closed before issuing uninstall.
No, that's not the case.
2: kd> !devnode ffffd40d23829610 1
DevNode 0xffffd40d23829610 for PDO 0xffffd40d308e2750
InstancePath is "ROOT\USB\0000"
ServiceName is "USBVD"
State = DeviceNodeStarted (0x30a)
Previous State = DeviceNodeEnumerateCompletion (0x30f)
DevNode 0xffffd40d2c4d3a30 for PDO 0xffffd40d2f1fb710
InstancePath is "USB\ROOT_HUB30\1&2b53a856&75&0"
ServiceName is "USBHUB3"
State = DeviceNodeStarted (0x30a)
Previous State = DeviceNodeEnumerateCompletion (0x30f)
DevNode 0xffffd40d2b906420 for PDO 0xffffd40d2f1e32e0
InstancePath is "USB\VID_046D&PID_C05A\2&5a6ee88&0&1"
ServiceName is "HidUsb"
State = DeviceNodeQueryRemoved (0x312)
Previous State = DeviceNodeStarted (0x30a)
DevNode 0xffffd40d2b90c460 for PDO 0xffffd40d3310a060
InstancePath is "HID\VID_046D&PID_C05A\3&209a1d04&0&0000"
State = DeviceNodeDeleted (0x316)
Previous State = DeviceNodeRemoved (0x314)
In order for the system to call EvtDeviceQueryRemove on "ROOT\USB\0000", all child devices must first transition to removable states. The UDE framework I use to create virtual USB devices does not expose an EvtDeviceQueryRemove callback for the virtual child devices it creates. As a result, when the virtual USB bus is being uninstalled, the process becomes stuck on one of the child virtual USB devices because its URB_INTERRUPT_IN queue is not empty.
The queue remains non‑empty because virtual USB HID devices do not respond to USB_INTERRUPT_IN requests unless their state changes (for example, when the virtual mouse is moved or clicked). When the device USB\VID_046D&PID_C05A\2&5a6ee88&0&1 enters the DeviceNodeQueryRemoved state and I start moving the virtual mouse, the device uninstalls correctly — the HID driver stops sending new URB_INTERRUPT_IN requests in that state.
This appears to be a design flaw in the UDE framework: when the parent device is being removed, all child device queues must be drained, yet the framework provides no notification that would allow the driver to flush or cancel pending URBs.
A practical workaround seems to be monitoring all virtual USB devices for the DeviceNodeQueryRemoved state and issuing a PLUG_OUT IOCTL when the number of open handles drops to zero. This allows the device to be uninstalled even if it is still “in use” by a client application, and prevents the system from hanging during shutdown simply because the user did not interact with the virtual USB mouse.