在 UMDF 中完成 I/O 请求
警告
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 入门。
每个 I/O 请求最终必须由 UMDF 驱动程序完成。 若要完成请求,驱动程序必须调用 IWDFIoRequest::Complete 或 IWDFIoRequest::CompleteWithInformation 方法。 当驱动程序完成请求时,它指示以下方案之一:
请求的 I/O 操作已成功完成。
请求的 I/O 操作已启动,但在完成之前失败。
请求的 I/O 操作在收到时不受支持或无效,因此无法与设备通信。
请求的 I/O 操作 已取消。
驱动程序调用 IWDFIoRequest::CompleteWithInformation 方法以传递有关请求操作的其他信息。 例如,对于读取操作,驱动程序应提供读取的字节数。
若要完成 I/O 请求,驱动程序必须在调用 IWDFIoRequest::Complete 或 IWDFIoRequest::CompleteWithInformation 时将相应的完成状态传递给 CompletionStatus 参数。 驱动程序使用 HRESULT 代码来传达已完成请求的状态。
UMDF 驱动程序主机进程先将 HRESULT 代码转换为 NTSTATUS 代码,然后再将完成的请求传递给反射器 (Wudfrd.sys) 。 反射器将 NTSTATUS 代码传递给操作系统。 操作系统先将 NTSTATUS 代码转换为 Microsoft Win32 错误代码,然后再将结果呈现给调用应用程序。
若要确保驱动程序的错误代码可以正确转换,应使用以下任一方法创建错误代码:
使用 Winerror.h 中的错误代码并应用 HRESULT_FROM_WIN32 宏。
使用 Ntstatus.h 中的错误代码并应用 HRESULT_FROM_NT 宏。
有关这些宏的详细信息,请参阅Microsoft Windows SDK文档。
以下示例代码演示如何使用合适的错误代码完成请求:
VOID
STDMETHODCALLTYPE
CMyQueue::OnWrite(
__in IWDFIoQueue *pWdfQueue,
__in IWDFIoRequest *pWdfRequest,
__in SIZE_T BytesToWrite
)
{
--------------------
if( BytesToWrite > MAX_WRITE_LENGTH ) {
pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_MORE_DATA), 0);
return;
}
---------------------
}
当驱动程序成功完成请求时,它将返回S_OK,即 HRESULT 值。 由于 S_OK 等效于 Winerror.h 中的NO_ERROR和 Ntstatus.h 中的STATUS_SUCCESS,因此不需要转换宏。
如果为反射器启用了 驱动程序验证程序 ,它将标识无效状态代码并导致系统 bug 检查。
注意 Windows XP 驱动程序验证程序错误地导致 Win32 错误代码的系统错误检查,其值超过十进制 1024 (1024L) 。 如果你的驱动程序在 Windows XP 上运行,如果为反射器启用驱动程序验证程序,请注意此问题。
如果驱动程序以前向较低级别的驱动程序发送了请求,则当较低级别的驱动程序完成请求时,驱动程序需要通知。 为了注册通知,驱动程序调用 IWDFIoRequest::SetCompletionCallback 方法,以在较低级别驱动程序完成请求时为框架调用的方法注册接口。 驱动程序实现 IRequestCallbackRequestCompletion::OnCompletion 回调函数,以执行完成请求所需的操作。
驱动程序无法完成它通过调用 IWDFDevice::CreateRequest 创建的 I/O 请求。 相反,驱动程序必须调用 IWDFObject::D eleteWdfObject 来删除请求对象,通常是在 I/O 目标完成请求之后。
例如,驱动程序可能会收到读取或写入请求,该请求的数据量大于驱动程序的 I/O 目标一次可以处理的数据量。 驱动程序必须将数据划分为多个较小的请求,并将这些较小的请求发送到一个或多个 I/O 目标。 处理这种情况的方法包括:
调用 IWDFDevice::CreateRequest 以创建表示较小请求的单个附加请求对象。
驱动程序可以同步将此请求发送到 I/O 目标。 较小请求的 IRequestCallbackRequestCompletion::OnCompletion 回调函数可以调用 IWDFIoRequest2::Reuse ,以便驱动程序可以重用请求并再次将其发送到 I/O 目标。 I/O 目标完成最后一个较小的请求后, OnCompletion 回调函数可以调用 IWDFObject::D eleteWdfObject 来删除驱动程序创建的请求对象,驱动程序可以调用 IWDFIoRequest::Complete 来完成原始请求。
调用 IWDFDevice::CreateRequest 以创建多个表示较小请求的其他请求对象。
驱动程序的 I/O 目标可以异步处理这些多个较小的请求。 驱动程序可以为每个较小的请求注册 OnCompletion 回调函数。 每次调用 OnCompletion 回调函数时,它都可以调用 IWDFObject::D eleteWdfObject 来删除驱动程序创建的请求对象。 I/O 目标完成所有较小的请求后,驱动程序可以调用 IWDFIoRequest::Complete 来完成原始请求。
获取完成信息
若要获取有关其他驱动程序已完成的 I/O 请求的信息,基于 UMDF 的驱动程序可以:
使用 IWDFRequestCompletionParams 接口获取 I/O 请求的完成状态和其他信息。
使用 IWDFIoRequestCompletionParams 接口获取 I/O 请求的内存缓冲区。
使用 IWDFUsbRequestCompletionParams 接口获取内存缓冲区以及与发送到 USB 目标管道对象的请求相关的其他信息。
此外,基于 UMDF 的驱动程序可以使用 IWDFIoRequest2::GetStatus 方法在请求完成之前或之后获取 I/O 请求的当前状态。