处理 IRP_MN_SURPRISE_REMOVAL 请求

Windows 2000 及更高版本的 PnP 管理器发送此 IRP 以通知驱动程序设备不再可用于 I/O 操作,并且可能已意外从计算机中删除 (“意外删除”) 。

PnP 管理器出于以下原因发送 IRP_MN_SURPRISE_REMOVAL 请求:

  • 如果总线有热插拔通知,它会通知设备的父总线驱动程序设备已消失。 总线驱动程序调用 IoInvalidateDeviceRelations。 作为响应,PnP 管理器查询总线驱动程序的子级 (BusRelations) IRP_MN_QUERY_DEVICE_RELATIONS。 PnP 管理器确定设备不在新的子级列表中,并为设备启动其意外删除操作。

  • 总线是出于其他原因枚举的,意外删除的设备不包括在子级列表中。 PnP 管理器启动其意外删除操作。

  • 设备的函数驱动程序确定设备不再存在 (,例如,其请求) 重复超时。 总线可能是可枚举的,但它没有热插拔通知。 在这种情况下,函数驱动程序调用 IoInvalidateDeviceState。 作为响应,PnP 管理器将 IRP_MN_QUERY_PNP_DEVICE_STATE 请求发送到设备堆栈。 函数驱动程序在PNP_DEVICE_STATE位掩码中设置 PNP_DEVICE_FAILED 标志,指示设备已发生故障。

  • 驱动程序堆栈成功完成 IRP_MN_STOP_DEVICE 请求,但随后的 IRP_MN_START_DEVICE 请求失败。 在这种情况下,设备可能仍处于连接状态。

所有 PnP 驱动程序都必须处理此 IRP,并且必须将 Irp-IoStatus.Status> 设置为 STATUS_SUCCESS。 调用驱动程序的 AddDevice 例程后,PnP 设备的驱动程序必须准备好随时处理IRP_MN_SURPRISE_REMOVAL。 正确处理 IRP 使驱动程序和 PnP 管理器能够:

  1. 禁用设备,以防它仍处于连接状态。

    如果驱动程序堆栈成功完成 IRP_MN_STOP_DEVICE 请求,但由于某种原因导致后续 IRP_MN_START_DEVICE 请求失败,则必须禁用该设备。

  2. 释放分配给设备的硬件资源,并使其可供其他设备使用。

    设备不再可用后,应立即释放其硬件资源。 然后,PnP 管理器可以将资源重新分配给另一个设备,包括同一设备,用户可能会将其热插回计算机。

  3. 最大程度地降低数据丢失和系统中断的风险。

    支持热插拔的设备及其驱动程序应设计为处理意外删除。 用户希望能够随时删除支持热插拔的设备。

PnP 管理器在系统 线程的上下文中以 IRQL = PASSIVE_LEVEL 发送IRP_MN_SURPRISE_REMOVAL。

在通知用户模式应用程序和其他内核模式组件之前,PnP 管理器将此 IRP 发送给驱动程序。 IRP 完成后,PnP 管理器将 包含 GUID_TARGET_DEVICE_REMOVE_COMPLETE 的 EventCategoryTargetDeviceChange 通知发送到设备上为此类通知注册的内核模式组件。

IRP_MN_SURPRISE_REMOVAL IRP 首先由设备堆栈中的顶部驱动程序处理,然后由下一个较低的驱动程序处理。

为了响应 IRP_MN_SURPRISE_REMOVAL,驱动程序必须按照列出的顺序执行以下操作:

  1. 确定是否已删除设备。

    驱动程序必须始终尝试确定设备是否仍处于连接状态。 如果是,驱动程序必须尝试停止设备并禁用它。

  2. (中断、I/O 端口、内存寄存器和 DMA 通道) 释放设备的硬件资源。

  3. 在父总线驱动程序中,如果驱动程序能够这样做,请关闭总线槽。 调用 PoSetPowerState 以通知电源管理器。 有关其他信息,请参阅 电源管理

  4. 阻止设备上的任何新 I/O 操作。

    驱动程序应处理后续 IRP_MJ_CLEANUPIRP_MJ_CLOSEIRP_MJ_POWERIRP_MJ_PNP 请求,但驱动程序必须阻止任何新的 I/O 操作。 除了关闭、清理和 PnP IRP 外,驱动程序还必须使驱动程序处理的任何后续 IRP 失败(如果设备存在)。

    驱动程序可以在设备扩展中设置一个位,以指示设备已被意外删除。 驱动程序的调度例程应检查此位。

  5. 在设备上失败未完成的 I/O 请求。

  6. 继续向下传递驱动程序不为设备处理的任何 IRP。

  7. 使用 IoSetDeviceInterfaceState 禁用设备接口。

  8. 清理任何特定于设备的分配、内存、事件或其他系统资源。

    驱动程序可以推迟此类清理,直到收到后续 IRP_MN_REMOVE_DEVICE 请求,但如果旧组件具有无法打开的句柄,则永远不会发送删除 IRP。

  9. 将设备对象保留附加到设备堆栈。

    在后续 IRP_MN_REMOVE_DEVICE 请求之前,不要分离和删除设备对象。

  10. 完成 IRP。

    在函数或筛选器驱动程序中:

    • Irp-IoStatus.Status> 设置为 STATUS_SUCCESS。

    • 使用 IoSkipCurrentIrpStackLocation 设置下一个堆栈位置,并使用 IoCallDriver 将 IRP 传递到下一个较低的驱动程序。

    • IoCallDriver 中的状态传播为 DispatchPnP 例程中的返回状态。

    • 不要完成 IRP。

    在正在为子 PDO) 处理此 IRP 的总线驱动程序 (中:

    • Irp-IoStatus.Status> 设置为 STATUS_SUCCESS。

    • 使用 IO_NO_INCREMENT 完成 IRP (IoCompleteRequest) 。

    • DispatchPnP 例程返回。

此 IRP 成功并关闭设备的所有打开句柄后,PnP 管理器将向设备堆栈发送 IRP_MN_REMOVE_DEVICE 请求。 为了响应删除 IRP,驱动程序将其设备对象从堆栈中分离并删除它们。 如果旧组件向设备打开了句柄,并且尽管出现 I/O 故障,但该句柄仍保持打开状态,则 PnP 管理器永远不会发送删除的 IRP。

所有驱动程序都应处理此 IRP,并应注意设备已被物理从计算机中删除。 但是,如果某些驱动程序不处理 IRP,则不会造成不利结果。 例如,不消耗任何系统硬件资源且驻留在基于协议的总线上的设备(如 USB 或 1394)无法占用硬件资源,因为它不消耗任何资源。 删除设备后,驱动程序不会尝试访问设备的风险,因为函数和筛选器驱动程序只能通过父总线驱动程序访问设备。 由于总线支持删除通知,因此当设备消失且总线驱动程序在随后的所有尝试访问设备失败时,将通知父总线驱动程序。

在 Windows 98/Me 上,PnP 管理器不会发送此 IRP。 如果用户在未首先使用适当的用户界面的情况下删除设备,则 PnP 管理器仅向设备的驱动程序发送 IRP_MN_REMOVE_DEVICE 请求。 所有 WDM 驱动程序都必须处理 IRP_MN_SURPRISE_REMOVALIRP_MN_REMOVE_DEVICEIRP_MN_REMOVE_DEVICE的代码应检查驱动程序是否收到了先前的意外删除 IRP,并应处理这两种情况。

使用 GUID_REENUMERATE_SELF_INTERFACE_STANDARD

GUID_REENUMERATE_SELF_INTERFACE_STANDARD 接口使驱动程序能够请求重新使用其设备。

若要使用此接口,请使用 InterfaceType = GUID_REENUMERATE_SELF_INTERFACE_STANDARD 向总线驱动程序发送IRP_MN_QUERY_INTERFACE IRP。 总线驱动程序提供指向REENUMERATE_SELF_INTERFACE_STANDARD结构的指针,该结构包含指向接口的各个例程的指针。 ReenumerateSelf 例程请求总线驱动程序重新调用子设备。

关于PNP_DEVICE_STATE

PNP_DEVICE_STATE类型是描述设备的 PnP 状态的位掩码。 驱动程序返回此类型的值以响应 IRP_MN_QUERY_PNP_DEVICE_STATE 请求。

typedef ULONG PNP_DEVICE_STATE, *PPNP_DEVICE_STATE;

PNP_DEVICE_STATE值中的标志位定义如下。

标记位 说明
PNP_DEVICE_DISABLED

设备实际存在,但在硬件中已禁用。

PNP_DEVICE_DONT_DISPLAY_IN_UI

不要在用户界面中显示设备。 为物理存在但在当前配置中不可用的设备设置,例如笔记本电脑上在取消停靠时不可用的游戏端口。 (另请参阅 DEVICE_CAPABILITIES 结构中的 NoDisplayInUI 标志。)

PNP_DEVICE_FAILED

设备存在,但无法正常工作。

设置此标志和PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED后,必须先停止设备,然后 PnP 管理器分配新的硬件资源, (设备) 不支持不间断的重新平衡。

PNP_DEVICE_NOT_DISABLEABLE

计算机启动时需要设备。 不得禁用此类设备。

驱动程序为正确系统操作所需的设备设置此位。 例如,如果驱动程序收到设备位于 deviceUsageTypepaging) 的分页路径 (IRP_MN_DEVICE_USAGE_NOTIFICATION的通知,驱动程序调用 IoInvalidateDeviceState 并在生成的IRP_MN_QUERY_PNP_DEVICE_STATE请求中设置此标志。

如果为设备设置了此位,则 PnP 管理器会将此设置传播到设备的父设备、其父级的父设备等。

如果为根枚举设备设置了此位,则无法禁用或卸载该设备。

PNP_DEVICE_REMOVED

设备已被物理删除。

PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED

设备的资源要求已更改。

通常,当总线驱动程序确定必须扩展其资源要求才能枚举新的子设备时,会设置此标志。

PNP_DEVICE_DISCONNECTED

设备驱动程序已加载,但此驱动程序已检测到设备不再连接到计算机。 通常,此标志用于与无线设备通信的函数驱动程序。 例如,当设备移出范围时设置标志,在设备移回范围并重新连接后清除标志。

总线驱动程序通常不设置此标志。 如果设备不再连接,则总线驱动程序应停止枚举子设备。 仅当函数驱动程序管理连接时,才使用此标志。

此标志的唯一用途是让客户端知道设备是否已连接。 设置标志不会影响是否加载驱动程序。

PnP 管理器在启动设备后立即通过向设备堆栈发送IRP_MN_QUERY_PNP_DEVICE_STATE请求来查询设备 PNP_DEVICE_STATE 。 为了响应此 IRP,设备的驱动程序在 PNP_DEVICE_STATE 中设置了相应的标志。

如果在初始查询后有任何状态特征发生更改,驱动程序将通过调用 IoInvalidateDeviceState 通知 PnP 管理器。 响应对 IoInvalidateDeviceState 的调用,PnP 管理器再次查询设备的PNP_DEVICE_STATE。

如果设备标记为PNP_DEVICE_NOT_DISABLEABLE,调试器将显示开发节点DNUF_NOT_DISABLEABLE用户标志。 调试器还显示 DisableableDepends 值,该值计算无法禁用设备的原因数。 此值是 X+Y 的总和,如果无法禁用设备,则 X 为 1,Y 是设备无法禁用的子设备计数。