IRP_MN_QUERY_DEVICE_RELATIONS

PnP 管理器发送此请求以确定设备之间的某些关系。 以下类型的驱动程序处理此请求:

  • 总线驱动程序必须处理 BusRelations 适配器或控制器(总线 FDO)的请求。 筛选器驱动程序可以处理 BusRelations 请求。

  • 总线驱动程序必须处理其子设备(子 PDO)TargetDeviceRelation 请求。

  • 函数和筛选器驱动程序可以处理 RemovalRelationsPowerRelations 请求。

  • 总线驱动程序可以处理 弹出关系 子设备(子 PDO)的请求。

价值

0x07

主要代码

IRP_MJ_PNP

发送时间

PnP 管理器发送此 IRP 以收集有关与指定设备的关系的设备的信息。

PnP 管理器在枚举设备时查询设备的 BusRelations(子设备),并在设备处于活动状态时在其他时间查询设备,例如当驱动程序调用 IoInvalidateDeviceRelations 例程,以指示子设备已到达或离开。

PnP 管理器在删除设备的驱动程序之前查询设备的 RemoveRelations。 PnP 管理器在弹出设备之前查询 RemovalRelations弹出关系

当驱动程序或用户模式应用程序在设备上注册 EventCategoryTargetDeviceChange 时,PnP 管理器将查询设备的 TargetDeviceRelation。 PnP 管理器查询与特定文件对象关联的设备。 IRP_MN_QUERY_DEVICE_RELATIONS 是唯一具有有效文件对象参数的 PnP IRP。 驱动程序可以查询 TargetDeviceRelation的设备堆栈。 驱动程序在发送其 TargetDeviceRelation 查询时不需要提供文件对象。

当设备驱动程序 IoInvalidateDeviceRelations 时,PnP 管理器将查询设备的 PowerRelations,以指示此设备具有隐式电源管理关系的一组设备已更改。 从 Windows 7 开始,支持 PowerRelations 请求。

对于 BusRelationsRemovalRelations弹出关系PowerRelations 请求,PnP 管理器会在 IRQL = PASSIVE_LEVEL 的系统线程上下文中发送 IRP_MN_QUERY_DEVICE_RELATIONS

对于 TargetDeviceRelation 请求,PnP 管理器在任意线程上下文中将此 IRP 发送到 IRQL = PASSIVE_LEVEL。

输入参数

IO_STACK_LOCATION 结构的 Parameters.QueryDeviceRelations.Type 成员指定正在查询的关系类型。 可能的值包括 BusRelations弹出关系RemovalRelationsTargetDeviceRelationPowerRelations

仅当 Parameters.QueryDeviceRelations.TypeTargetDeviceRelation时,当前 IO_STACK_LOCATION 结构的 FileObject 成员才会指向有效的文件对象。

输出参数

在 I/O 状态块中返回。

I/O 状态块

驱动程序将 Irp->IoStatus.Status 设置为STATUS_SUCCESS或故障状态,例如STATUS_INSUFFICIENT_RESOURCES。

成功后,驱动程序会将 Irp->IoStatus.Information 设置为指向所请求关系信息的PDEVICE_RELATIONS指针。 DEVICE_RELATIONS 结构定义如下:

typedef struct _DEVICE_RELATIONS {
  ULONG  Count;
  PDEVICE_OBJECT  Objects[1];  // variable length
} DEVICE_RELATIONS, *PDEVICE_RELATIONS;

操作

如果驱动程序返回响应此 IRP_MN_QUERY_DEVICE_RELATIONS的关系,驱动程序将从分页内存中分配一个 DEVICE_RELATIONS 结构,其中包含计数和设备对象指针的相应数量。 不再需要 PnP 管理器时释放结构。 如果驱动程序替换了另一个驱动程序分配的 DEVICE_RELATIONS 结构,驱动程序必须释放以前的结构。

驱动程序必须引用它在此 IRP 中报告的任何设备的 PDO(ObReferenceObject)。 PnP 管理器在适当时删除引用。

函数或筛选器驱动程序应在设备 AddDevice 例程完成后随时为设备处理此 IRP。 总线驱动程序应准备好在枚举设备后立即处理 BusRelations 的查询

有关处理 即插即用次要 IRP 的常规规则,请参阅 即插即用

以下小节描述了用于处理各种查询的特定作。

BusRelations 请求

当 PnP 管理器查询适配器或控制器的总线关系(子设备)时,总线驱动程序必须返回指向总线上物理存在的任何设备的 PDO 的指针列表。 无论设备是否已启动,总线驱动程序都会报告所有设备。 总线驱动程序可能需要为其总线设备供电,以确定存在哪些子级。

警告 设备对象不能传递给任何将 PDO 作为参数的例程,直到 PnP 管理器为该对象创建设备节点(devnode)。 (如果驱动程序确实传递了设备对象,系统会使用 bug 检查0xCA:PNP_DETECTED_FATAL_ERROR。)PnP 管理器创建开发节点以响应 IRP_MN_QUERY_DEVICE_RELATIONS 请求。 驱动程序可以安全地假定 PDO 的开发节点在收到 IRP_MN_QUERY_RESOURCE_REQUIREMENTS 请求时已创建。

响应此 IRP 的总线驱动程序是总线适配器或控制器的函数驱动程序,而不是适配器或控制器连接到的总线的父总线驱动程序。 非总线设备的函数驱动程序不处理此查询。 此类驱动程序只需将 IRP 传递到下一个较低的驱动程序。 (请参阅下图。筛选器驱动程序通常不处理此查询。

在 Windows Vista 和更高版本的作系统上,我们建议驱动程序始终笔下 IRP_MN_QUERY_DEVICE_RELATIONS IRP 并稍后完成其处理。 此顺序使系统能够以异步方式处理总线关系查询。 (在 Windows Vista 之前的作系统上,驱动程序可以安全地从调度例程返回STATUS_PENDING,但 PnP 管理器不会与任何其他作重叠总线关系查询。

下图显示了驱动程序如何处理总线关系的查询。

关系图,说明处理总线关系的查询的驱动程序。

在图中显示的示例中,PnP 管理器向 USB 中心设备的驱动程序发送 BusRelationsIRP_MN_QUERY_DEVICE_RELATIONS。 PnP 管理器正在请求中心设备的子级列表。

  1. 与所有 PnP IRP 一样,PnP 管理器将 IRP 发送到设备堆栈中的顶部驱动程序。

  2. 可选的筛选器驱动程序可能是堆栈中的顶部驱动程序。 筛选器驱动程序通常不处理此 IRP;它将 IRP 向下传递堆栈。 例如,如果驱动程序在总线上公开不可枚举的设备,筛选器驱动程序可能会处理此 IRP。

  3. USB 中心总线驱动程序处理 IRP。

    USB 中心总线驱动程序:

    • 为任何还没有 PDO 的子设备创建 PDO。

    • 为总线上不再存在的任何设备标记 PDO 非活动状态。 总线驱动程序不会删除此类 PDO。有关何时删除 PDO 的详细信息,请参阅 删除设备

    • 报告总线上存在的任何子设备。

      对于每个子设备,总线驱动程序引用 PDO,并将指向 PDO 的指针置于DEVICE_RELATIONS结构中。

      此示例中有两个 PDO:一个用于游戏杆设备,一个用于键盘设备。

      总线驱动程序应检查另一个驱动程序是否已为此 IRP 创建了DEVICE_RELATIONS结构。 如果是,则总线驱动程序必须添加到现有信息。

      如果总线上没有子设备,驱动程序会将DEVICE_RELATIONS结构中的计数设置为零,并返回成功。

    • 设置 I/O 状态块中的相应值,并将 IRP 传递给下一个较低的驱动程序。 适配器或控制器的总线驱动程序未完成 IRP。

  4. 可选的较低筛选器(如果存在),通常不处理此 IRP。 此类筛选器驱动程序将 IRP 向下传递堆栈。 如果较低筛选器驱动程序处理此 IRP,它可以将 PDO(s)添加到子设备列表中,但不能删除其他驱动程序创建的任何 PDO。

  5. 父总线驱动程序不会处理此 IRP,除非它是设备堆栈中唯一的驱动程序(设备处于原始模式)。 与所有 PnP IRP 一样,父总线驱动程序使用 IoCompleteRequest完成 IRP。

    如果设备堆栈中存在一个或多个总线筛选器驱动程序,则此类驱动程序可能会在 IRP 的路上处理 IRP,并/或在 IRP 的路上备份设备堆栈(如果有 IoCompletion 例程)。 根据 PnP IRP 规则,此类驱动程序可以在沿着堆栈下行的方式将 PDO 添加到 IRP,并/或修改 IRP 上备份堆栈的关系列表(IoCompletion 例程)。

弹出关系请求

驱动程序返回指向在弹出指定设备时可能从系统物理删除的任何设备的 PDO 的指针。 不要报告设备的子级的 PDO;PnP 管理器始终请求在父设备之前删除子设备。

PnP 管理器将 IRP_MN_EJECT IRP 发送到正在弹出的设备。 此类设备的驱动程序还会收到删除 IRP。 设备的弹出关系接收 IRP_MN_REMOVE_DEVICE IRP(而不是 IRP_MN_EJECT IRP)。

只有父总线驱动程序才能响应其某个子设备的 弹出关系 查询。 函数和筛选器驱动程序必须将其传递给设备堆栈中的下一个较低驱动程序。 如果总线驱动程序将此 IRP 作为其适配器或控制器的函数驱动程序接收,则总线驱动程序将执行函数驱动程序的任务,并且必须将 IRP 传递给下一个下一个下级驱动程序。

PowerRelations 请求

从 Windows 7 开始,PowerRelations 查询使驱动程序能够在支持 PnP 枚举的父总线与总线上的枚举子设备之间的传统关系之外指定电源管理关系。 例如,如果总线驱动程序无法枚举总线上的子设备,或者如果设备是多个总线的子级,则 PowerRelations 查询可以描述子设备与总线或总线的电源关系。

当设备的驱动程序调用 IoInvalidateDeviceRelations 例程时,PnP 管理器会发出 PowerRelations 查询,并指定 PowerRelationsType 参数值。

为了响应此查询,目标设备的驱动程序(即作为查询目标的设备)提供了一个 DEVICE_RELATIONS 结构,该结构包含指向电源管理器在启用目标设备之前必须打开的任何其他设备的 PDO 的指针。 相反,只有在目标设备关闭后,才能关闭这些其他设备。 Power Manager 使用查询中的信息来保证这些设备按正确的顺序打开和关闭。

此排序保证仅适用于全局系统睡眠状态转换,包括从 S1、S2、S3(睡眠)、S4(休眠)和 S5(关闭)系统电源状态。 PowerRelations 排序保证不适用于 Dx 设备电源状态转换,而系统保持 S0(运行)系统状态,但 定向运行时电源管理(DFx) 转换的情况除外。

如果目标设备位于特殊文件(如分页文件、休眠文件或故障转储文件)的设备路径上,则目标设备的驱动程序在处理 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP 时必须执行额外的步骤,其中 inPath inPathTRUE。 此驱动程序必须确保为 PowerRelations 查询提供 PDO 的设备也可以支持位于特殊文件的设备路径中。 若要确认此支持,目标设备的驱动程序必须首先将 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP 发送给其中每个设备,并且此 IRP 必须指定与目标设备相同的 UsageNotification.Type。 仅当接收此 IRP 的所有设备都使用成功状态代码完成 IRP 时,目标设备的驱动程序才能成功完成其 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP。 否则,此驱动程序必须通过失败状态代码完成此 IRP。

当此同一驱动程序处理 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP 时,InPathFALSE,驱动程序必须将 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP 发送到同一组依赖设备,就 InPathTRUE的情况。 但是,当 InPathFALSE时,驱动程序不应使用失败状态代码完成此 IRP。

响应 PowerRelations 查询的驱动程序应在为 PowerRelations 查询提供 PDO 的所有设备上注册目标设备更改通知。 若要注册这些通知,驱动程序可以调用 IoRegisterPlugPlayNotification 例程,并指定 EventCategoryTargetDeviceChangeEventCategory 参数值。

RemovalRelations 请求

驱动程序返回指向在删除指定设备的驱动程序时必须删除其驱动程序的任何设备的 PDO 的指针。 不要报告设备的子级的 PDO;PnP 管理器在删除设备之前已请求删除子设备。

删除关系的顺序是未定义的。

设备堆栈中的任何驱动程序都可以处理这种类型的关系查询。 函数或筛选器驱动程序在将 IRP 传递给下一个下一个驱动程序之前处理 IRP。 总线驱动程序处理 IRP,然后完成它。

TargetDeviceRelation 请求

TargetDeviceRelation 查询使 PnP 管理器能够查询控制硬件的 PnP 设备堆栈中 PDO 的非 PnP 设备堆栈。

通常,驱动程序会将 IRP_MN_QUERY_DEVICE_RELATIONS IRP 向下转发其堆栈,直到 IRP 到达特定设备堆栈的底部。 非 PnP 堆栈底部的驱动程序随后将 IRP 转发或重新颁发给相关的 PnP 堆栈。 例如,PnP 管理器可能会将 TargetDeviceRelation 查询发送到文件系统堆栈顶部的设备对象,这是非 PnP 堆栈。 文件系统堆栈中的每个设备对象都会将查询传递给其下方的设备对象,直到查询到达堆栈底部的设备对象。 堆栈中最低设备对象会将 TargetDeviceRelation 查询转发或重新颁发给 PnP 存储卷堆栈顶部的设备对象,然后将查询传递到存储卷堆栈底部的 PDO。

以下列表总结了可以在 PnP 设备堆栈底部安全地获取指向 PDO 的指针的情况:

  • PnP 中的设备对象

    当调用设备的 AddDevice 例程时,PnP 设备堆栈中的设备对象将了解堆栈的 PDO。 如果指针的使用与传入 IRP_MN_REMOVE_DEVICE 消息正确同步,则驱动程序可以安全地缓存指向 PDO 的指针,方法是使用删除锁定例程。

  • 非 PnP 堆栈中的设备对象,不在堆栈底部

    对于不在非 PnP 堆栈底部的设备对象,驱动程序可以发送 TargetDeviceRelation 查询,以获取指向相应 PnP 设备堆栈底部 PDO 的指针。

  • 设备的文件对象

    给定设备的文件对象后,驱动程序可以调用 IoGetRelatedDeviceObject 获取设备对象,然后按照前面的列表项中的说明进行作。

  • 设备对象的句柄

    给定设备对象的句柄,驱动程序可以调用 ObReferenceObjectByHandle 获取设备的文件对象,然后按照前面的列表项中的说明进行作。

父总线驱动程序必须处理其子设备的 TargetDeviceRelation 关系查询。 总线驱动程序使用 ObReferenceObject 引用子设备的 PDO,并返回指向 DEVICE_RELATIONS 结构中的 PDO 的指针。 此关系类型的结构中只有一个 PDO 指针。 当驱动程序或应用程序在设备上取消注册通知时,PnP 管理器会删除对 PDO 的引用。

只有父总线驱动程序响应 TargetDeviceRelation 查询。 函数和筛选器驱动程序必须将其传递给设备堆栈中的下一个较低驱动程序。 如果总线驱动程序将此 IRP 作为其适配器或控制器的函数驱动程序接收,则总线驱动程序将执行函数驱动程序的任务,并且必须将 IRP 传递给下一个下一个下级驱动程序。

如果驱动程序不在基于 PDO 的堆栈中,驱动程序会将新的目标设备关系查询 IRP 发送到与驱动程序执行 I/O 的文件句柄关联的设备对象。

发送此 IRP

驱动程序不得发送 IRP_MN_QUERY_DEVICE_RELATIONS 来请求 BusRelations。 驱动程序不受限制地为 RemovalRelations弹出关系发送此 IRP,但驱动程序不太可能这样做。

驱动程序可以查询 TargetDeviceRelation的设备堆栈。 有关发送 IRP 的信息,请参阅 处理 IRP。 以下步骤特别适用于此 IRP:

  • 设置 IRP 的下一个 I/O 堆栈位置中的值:将 MajorFunction 设置为 IRP_MJ_PNP,将 minorFunction 设置为 IRP_MN_QUERY_DEVICE_RELATIONS 设置 Parameters.QueryDeviceRelations.TypeTargetDeviceRelation,并将 Irp->FileObject 设置为有效的文件对象。

  • IoStatus.Status 初始化为STATUS_NOT_SUPPORTED。

如果驱动程序发送此 IRP 以获取 PDO 以响应驱动程序收到的 TargetDeviceRelationIRP_MN_QUERY_DEVICE_RELATIONS,驱动程序将报告 PDO 并在 IRP 完成时释放返回的关系结构。 如果驱动程序出于其他原因启动了此 IRP,则驱动程序在 IRP 完成时释放关系结构,并在不再需要 PDO 时取消引用 PDO。

要求

标题

Wdm.h (包括 Wdm.h、Ntddk.h 或 Ntifs.h)

另请参阅

AddDevice

IoCompleteRequest

IoGetRelatedDeviceObject

IoInvalidateDeviceRelations

IoRegisterPlugPlayNotification

IRP_MJ_PNP

IRP_MN_DEVICE_USAGE_NOTIFICATION

IRP_MN_EJECT

IRP_MN_QUERY_RESOURCE_REQUIREMENTS

IRP_MN_REMOVE_DEVICE

IO_STACK_LOCATION

ObReferenceObject

ObReferenceObjectByHandle