在系统运行时,用户可以通过以下两种方式之一删除设备:有序 删除,这意味着用户通知系统设备即将删除(例如,使用“拔出”或“弹出硬件”程序):或 意外删除,这意味着用户拔下设备而不通知系统。 如果总线支持意外删除(例如 USB),设备的驱动程序必须能够处理设备的突然消失。
有序删除
用户可通过系统的“拔出或弹出硬件”程序、通过设备管理器禁用设备,或按下 可弹出设备的弹出 按钮,来请求移除设备。 框架允许删除或禁用设备,除非驱动程序具有以下功能:
调用 WdfDeviceSetSpecialFileSupport,并且设备上打开了一个特殊文件。
提供了 EvtDeviceQueryRemove 回调函数,该回调函数已拒绝删除。
对于支持设备的每个函数和筛选器驱动程序,框架一次按顺序执行以下作,从驱动程序堆栈中最高的驱动程序开始:
如果驱动程序使用的是自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoSuspend 回调函数。
框架会停止驱动程序的所有电源管理的 I/O 队列。
如果硬件和驱动程序支持 DMA,则框架将调用驱动程序的 EvtDmaEnablerSelfManagedIoStop、 EvtDmaEnablerFlush 和 EvtDmaEnablerDisable 回调函数(如果存在),用于创建的每个 DMA 通道。
框架调用驱动程序的 EvtDeviceD0ExitPreInterruptsDisabled 回调函数(如果存在),然后针对每个中断调用驱动程序的 EvtInterruptDisable 回调函数(如果存在),以便驱动程序可以禁用设备中断。
框架调用驱动程序的 EvtDeviceD0Exit 回调函数(如果存在)。
框架调用驱动程序的 EvtDeviceReleaseHardware 回调函数(如果存在),并向其传递 PnP 管理器分配给设备的硬件资源列表。
如果驱动程序使用的是自管理 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoFlush 回调函数。
如果驱动程序使用的是自管理 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoCleanup 回调函数。
总线驱动程序是堆栈中最后调用的驱动程序。 当框架调用总线驱动程序的 EvtDeviceD0Exit 回调函数时,回调函数会将设备(总线的子设备)的电源状态设置为 D3。 总线驱动程序可以通过调用 WdfDeviceInitSetReleaseHardwareOrderOnFailure 来控制框架何时调用其 EvtDeviceReleaseHardware 回调函数。
意外删除
用户意外地拔出设备。 设备总线的驱动程序发现设备缺失,并调用 WdfChildListUpdateChildDescriptionAsMissing。
对于支持设备的每个函数和筛选器驱动程序,框架一次按顺序执行以下作,从驱动程序堆栈中最高的驱动程序开始:
- 框架调用驱动程序的 EvtDeviceSurpriseRemoval 回调函数(如果存在)。
- 如果设备在拔出时处于工作状态(D0),则:
- 框架会停止驱动程序的所有电源管理的 I/O 队列。
- 如果驱动程序使用的是自托管 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoSuspend 回调函数。
- 如果硬件和驱动程序支持 DMA,则框架将调用驱动程序的 EvtDmaEnablerSelfManagedIoStop、 EvtDmaEnablerFlush 和 EvtDmaEnablerDisable 回调函数(如果存在),用于创建的每个 DMA 通道。
- 框架调用驱动程序的 EvtDeviceD0ExitPreInterruptsDisabled 和 EvtInterruptDisable 回调函数(如果存在),以便驱动程序可以禁用设备中断。
- 框架调用驱动程序的 EvtDeviceD0Exit 回调函数(如果存在)。
- 框架调用驱动程序的 EvtDeviceReleaseHardware 回调函数(如果存在),并传递 PnP 管理器分配给设备的硬件资源列表。
- 如果驱动程序使用的是自管理 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoFlush 回调函数。
- 如果驱动程序使用的是自管理 I/O,框架将调用驱动程序的 EvtDeviceSelfManagedIoCleanup 回调函数。
请注意,可以随时意外删除设备。 因此,框架可能会在与前面步骤中显示的时间不同的时间调用驱动程序的 EvtDeviceSurpriseRemoval 回调函数。 例如,如果用户在进入低功率状态时意外拔出设备,则框架可能会在调用 EvtDeviceReleaseHardware 回调函数后调用 EvtDeviceSurpriseRemoval 回调函数。 不得以假定它和其他回调函数在特定序列中调用的方式编写 EvtDeviceSurpriseRemoval 回调函数的代码。
此外,框架不会将设备的 EvtDeviceSurpriseRemoval 回调函数与该设备前面步骤中列出的任何回调函数同步。 因此, EvtDeviceSurpriseRemoval 回调函数可能会运行,而之前列出的另一个回调函数也在运行。