支持 UMDF 1.x 驱动程序中的内核模式客户端

警告

UMDF 2 是 UMDF 的最新版本,取代了 UMDF 1。 所有新的 UMDF 驱动程序都应使用 UMDF 2 编写。 不会向 UMDF 1 添加任何新功能,并且较新版本的 Windows 10 上对 UMDF 1 的支持有限。 通用 Windows 驱动程序必须使用 UMDF 2。

存档的 UMDF 1 示例可在 Windows 11 版本 22H2 - 2022 年 5 月驱动程序示例更新中找到。

有关详细信息,请参阅使用 UMDF 入门

UMDF 1.9 及更高版本允许 UMDF 驱动程序支持 内核模式客户端。 内核模式客户端可以是以下任一客户端:

  • 存在于设备驱动程序堆栈中 UMDF 驱动程序上方的内核模式驱动程序。

  • 支持一个设备的一个设备堆栈的内核模式驱动程序会打开另一个设备的句柄,后者的驱动程序堆栈包含 UMDF 驱动程序。

换句话说,支持内核模式客户端的 UMDF 驱动程序可以从内核模式驱动程序接收 I/O 请求。 内核模式驱动程序可以转发从用户模式应用程序收到的 I/O 请求,也可以创建新的 I/O 请求并将其发送到用户模式驱动程序。

若要确定 UMDF 驱动程序是否必须支持内核模式客户端,必须了解要向其添加驱动程序的驱动程序堆栈,以及驱动程序将驻留在该堆栈中的哪个位置。 还必须确定来自另一个堆栈的驱动程序是否可以将 I/O 请求发送到驱动程序的设备。

在以下条件中,驱动程序必须支持内核模式客户端:

  • 内核模式驱动程序可以直接位于驱动程序堆栈中的 UMDF 驱动程序的上方。 例如,内核模式筛选器驱动程序可能直接驻留在基于 UMDF 的函数驱动程序之上。

  • 另一个堆栈中的内核模式驱动程序可以将 I/O 请求发送到驱动程序的设备。 例如,驱动程序可能会创建一个符号链接,另一个堆栈中的内核模式驱动程序可以使用该链接打开驱动程序设备的句柄。 然后,内核模式驱动程序可以将 I/O 请求发送到设备。

如何在 UMDF 驱动程序中支持内核模式客户端

仅当 UMDF 驱动程序启用了对内核模式客户端的支持时,UMDF 驱动程序才能从内核模式驱动程序接收 I/O 请求。 此外,如果设备安装尝试在设备的驱动程序堆栈中将内核模式驱动程序加载到 UMDF 驱动程序之上,则框架仅允许在 UMDF 驱动程序已启用对内核模式客户端的支持时加载驱动程序。

若要启用 UMDF 驱动程序对内核模式客户端的支持,UMDF 驱动程序的 INF 文件必须在其 INF DDInstall 中包含 UmdfKernelModeClientPolicy 指令。WDF 部分。 如果 UMDF 驱动程序的 INF 文件不包含此指令,则 UMDF 不允许运行 UMDF 驱动程序上方安装的内核模式驱动程序。

框架提供了两种对支持内核模式客户端的驱动程序有用的方法。 驱动程序可以调用 IWDFIoRequest2::GetRequestorMode 方法来确定 I/O 请求来自内核模式还是用户模式。 如果 I/O 请求来自用户模式,驱动程序可以调用 IWDFIoRequest2::IsFromUserModeDriver 来确定请求来自应用程序还是来自其他用户模式驱动程序。

内核模式驱动程序的限制

仅当内核模式驱动程序满足以下要求时,UMDF 驱动程序才能处理来自内核模式驱动程序的 I/O 请求:

  • 当内核模式驱动程序发送 I/O 请求时,必须在 IRQL = PASSIVE_LEVEL 运行。

  • 除非驱动程序已将 UmdfFileObjectPolicy INF 指令设置为 AllowNullAndUnknownFileObjects,否则内核模式驱动程序发送到用户模式驱动程序的每个 I/O 请求都必须具有关联的文件对象。 框架之前必须已收到 I/O 管理器创建了文件对象的通知。 (此类通知会导致框架调用用户模式驱动程序的 IQueueCallbackCreate::OnCreateFile 回调函数,但该回调函数是可选的.)

  • I/O 请求不能包含 IRP_MJ_INTERNAL_DEVICE_CONTROL 函数代码。

  • I/O 请求的缓冲区不得包含指向其他信息的指针,因为用户模式驱动程序无法取消引用指针。

  • 如果 I/O 请求包含指定“两者”缓冲区访问方法的 I/O 控制代码 ,则内核模式驱动程序必须在创建 I/O 请求的应用程序的进程上下文中发送 I/O 请求。 有关如何在基于 UMDF 的驱动程序中支持“既不”方法的详细信息,请参阅 在 UMDF 驱动程序中使用非缓冲 I/O 和直接 I/O

  • UMDF 驱动程序可能会在用户模式下修改 I/O 请求的输出数据。 因此,内核模式驱动程序必须验证它从用户模式驱动程序接收的任何输出数据。

  • 内核模式客户端通常应验证 UMDF 驱动程序传递给 IWDFIoRequest::CompleteWithInformation的信息值。 如果客户端是 KMDF 驱动程序,则可以调用 WdfRequestGetCompletionParams 以在IO_STATUS_BLOCK结构中获取此信息。

    通常,框架不会验证 UMDF 驱动程序传递给 IWDFIoRequest::CompleteWithInformation 的信息值。 (此参数通常指定传输的字节数。) 框架仅验证输出缓冲区和 缓冲 I/O 数据访问方法的信息值。 (例如,如果访问方法是缓冲 I/O.)

处理 UMDF 1.x 驱动程序中的返回状态值

将返回状态值从用户模式传递到内核模式需要特别注意,如下所示:

  • UMDF 版本 1 驱动程序通常接收 HRESULT 类型的返回值,而基于 KMDF 和 WDM 的内核模式驱动程序通常接收 NTSTATUS 类型的值。 如果为 UMDF 1。x 驱动程序完成 I/O 请求,如果驱动程序具有内核模式客户端,则驱动程序对 IWDFIoRequest::CompleteIWDFIoRequest::CompleteWithInformation 的调用应指定驱动程序从 NTSTATUS 值生成的 HRESULT 值。 通常为 UMDF 1。x 驱动程序应使用 Winerror.h) 中定义的HRESULT_FROM_NT宏 (将状态返回到内核模式客户端。 以下示例演示如何在完成请求时使用此宏。

    hr = HRESULT_FROM_NT(STATUS_BUFFER_OVERFLOW)
    request->Complete(HRESULT_FROM_NT(STATUS_BUFFER_OVERFLOW);
    return hr;
    

    若要将特定的 HRESULT 值返回到内核模式客户端,以下回调必须使用 HRESULT_FROM_NT 宏:

    若要使用 ntstatus.h 中定义的 NTSTATUS 值,请使用 UMDF 1。x driver 必须先包含这两行,然后才能包含任何其他标头。

    #define UMDF_USING_NTSTATUS
    #include <ntstatus.h>
    

    请勿使用 HRESULT_FROM_NT 宏将STATUS_SUCCESS从 NTSTATUS 值转换为 HRESULT 值。 只需返回S_OK,如以下示例所示。

    request->Complete(S_OK);
    
  • 框架代表 UMDF 驱动程序完成某些 I/O 请求。 有时框架不会将 HRESULT 类型的返回值转换为等效的 NTSTATUS 值,因此框架可能会将 HRESULT 类型的完成状态传递给内核模式客户端。

    由于这种情况,内核模式客户端在测试 I/O 请求的完成状态时不应使用 NT_ERROR 宏,因为 NT_ERROR 宏不会为 HRESULT 错误值返回 TRUE 。 在测试 I/O 请求的完成状态时,内核模式驱动程序应使用 NT_SUCCESS 宏。

早期 UMDF 版本中的内核模式客户端支持

对于低于版本 1.9 的 UMDF 版本,驱动程序的 INF 文件可以包含 INF AddReg 指令,以在设备硬件密钥WUDF 子项下创建REG_DWORD大小的 UpperDriverOk 注册表值。

如果 UpperDriverOk 注册表值设置为非零数,则框架允许内核模式驱动程序加载到用户模式驱动程序之上。 内核模式驱动程序可以将 I/O 请求从用户模式应用程序转发到 UMDF 驱动程序,但内核模式驱动程序无法将内核模式下创建的 I/O 请求发送到 UMDF 驱动程序。

对于 UMDF 版本 1.9 及更高版本, UpperDriverOk 注册表值已过时,仅支持现有驱动程序。 新驱动程序应使用 UmdfKernelModeClientPolicy 指令。