2. UMDF驱动对象模型:核心接口详解
UMDF(User-Mode Driver Framework)驱动对象模型是驱动开发者必须掌握的基础。该模型通过一组COM风格的接口,将WDM(Windows Driver Model)中复杂的IRP处理、设备栈遍历、I/O请求路由等操作封装为易于使用的对象。本章将深入剖析四个核心接口:IWDFDriver、IWDFDevice、IWDFIoQueue 和 IWDFIoRequest,并揭示它们之间的协作关系。
2.1 对象模型全景图
下图展示了UMDF驱动中四个核心接口的层次关系与数据流路径。驱动入口通过IWDFDriver创建设备,设备通过IWDFDevice创建队列,队列接收并分发IWDFIoRequest,请求最终被驱动处理并完成。
2.2 IWDFDriver:驱动工厂与入口
IWDFDriver 是UMDF驱动对象的根。它在驱动加载时由框架创建,代表整个驱动实例。驱动入口函数 DllGetClassObject 或 IDriverEntry::OnDeviceAdd 中,驱动通过此接口完成初始化。
| 方法 | 功能描述 | 典型调用时机 |
|---|---|---|
| CreateDevice | 创建并初始化一个新的设备对象(IWDFDevice) | OnDeviceAdd 回调中 |
| CreateWdfObject | 创建框架管理的自定义上下文对象 | 设备初始化或请求处理时 |
| RetrieveVersionString | 获取当前UMDF框架版本信息 | 调试或版本兼容性检查 |
| IsVersionAvailable | 检查指定UMDF版本是否可用 | 驱动加载时的版本验证 |
CreateDevice 返回失败,通常意味着设备栈创建失败。常见原因包括:设备接口GUID注册冲突、父设备对象未就绪、或资源不足。此时应检查 WUDFHost.exe 进程的日志(%SystemRoot%\System32\LogFiles\Wudf\)。
2.3 IWDFDevice:设备实例与栈管理
IWDFDevice 代表一个具体的设备实例。它是驱动与设备栈交互的核心枢纽,负责创建I/O队列、管理电源策略、处理PnP事件以及暴露设备属性。
每个设备对象都关联一个设备栈(Device Stack),UMDF设备栈位于内核模式设备栈之上,通过反射器(Reflector)进行通信。
IWDFDevice 关键方法
- CreateIoQueue:创建指定类型的I/O队列(顺序、并行、手动)。
- GetDefaultIoQueue:获取驱动默认的I/O队列,通常用于简单场景。
- RetrieveDevicePropertyStore:访问设备的属性存储区,用于读取硬件配置。
- CommitPnpState:提交PnP状态变更,通知框架设备已就绪。
OnDeviceAdd 回调中,必须调用 CommitPnpState 才能完成设备初始化。如果遗漏此调用,设备将始终处于“未就绪”状态,导致后续I/O请求无法下发。调试时可通过 !wudfext.umdevstacks 命令检查设备状态。
2.4 IWDFIoQueue:I/O请求的调度中枢
IWDFIoQueue 是UMDF驱动中管理I/O请求的核心对象。它接收来自框架的IRP,并根据配置的调度类型将请求分发给驱动回调。UMDF支持三种队列类型:
| 队列类型 | 调度行为 | 适用场景 | 并发度 |
|---|---|---|---|
| WdfIoQueueDispatchSequential | 一次只处理一个请求,完成后才分发下一个 | 需要严格串行化的操作(如寄存器访问) | 1 |
| WdfIoQueueDispatchParallel | 所有请求同时分发,驱动需自行同步 | 高性能、无依赖的I/O操作 | N(无限制) |
| WdfIoQueueDispatchManual | 框架不自动分发,驱动主动调用 RetrieveNextRequest 获取 |
自定义调度策略、请求合并、优先级控制 | 由驱动控制 |
队列回调接口
驱动通过实现 IQueueCallbackRead、IQueueCallbackWrite、IQueueCallbackDeviceIoControl 等接口来接收队列分发的请求。例如:
// 实现读请求回调
class MyIoQueue : public IQueueCallbackRead {
public:
void OnRead(
IWDFIoQueue* pQueue,
IWDFIoRequest* pRequest,
SIZE_T BytesToRead
) override {
// 处理读请求
// ...
pRequest->CompleteWithInformation(S_OK, BytesRead);
}
};
2.5 IWDFIoRequest:请求的完整生命周期
IWDFIoRequest 代表一个从应用程序或内核下发的I/O请求。它封装了IRP的所有关键信息,包括操作码、输入输出缓冲区、完成状态等。理解其生命周期是调试IRP处理故障的核心。
请求生命周期状态机
IWDFIoRequest 核心方法
| 方法 | 用途 | 注意事项 |
|---|---|---|
| Complete | 完成请求并返回状态 | 只能调用一次,调用后请求对象失效 |
| CompleteWithInformation | 完成请求并附带传输字节数 | 用于读写操作,传递实际传输大小 |
| Forward | 将请求转发到另一个队列或驱动 | 必须设置完成回调以接收最终状态 |
| GetDeviceIoControlParameters | 获取IOCTL码、输入输出缓冲区大小 | 仅对DeviceIoControl请求有效 |
| GetInputMemory / GetOutputMemory | 获取请求的输入/输出缓冲区 | 返回 IWDFMemory 对象,需检查有效性 |
| Cancel | 取消请求 | 需配合队列的取消回调使用 |
!wudfext.umirp 命令可以查看当前未完成的UMDF请求。如果发现请求长时间处于“处理中”状态,通常意味着驱动忘记调用 Complete 或 Forward。此时应检查回调函数中是否存在提前返回的分支路径。
2.6 四接口协作流程示例
以下代码片段展示了从驱动入口到请求完成的完整协作流程:
// 1. OnDeviceAdd 中创建设备和队列
HRESULT MyDriver::OnDeviceAdd(
IWDFDriver* pDriver,
IWDFDeviceInitialize* pDeviceInit
) {
IWDFDevice* pDevice = nullptr;
HRESULT hr = pDriver->CreateDevice(pDeviceInit, nullptr, &pDevice);
if (FAILED(hr)) return hr;
// 2. 创建并行队列
IWDFIoQueue* pQueue = nullptr;
hr = pDevice->CreateIoQueue(
nullptr, // 默认回调接口
WdfIoQueueDispatchParallel, // 并行调度
true, // 默认队列
false, // 不自动转发
&pQueue
);
if (FAILED(hr)) return hr;
// 3. 设置读请求回调
pQueue->SetReadCallback(new MyReadCallback());
pDevice->CommitPnpState();
return S_OK;
}
// 4. 读请求回调中处理请求
void MyReadCallback::OnRead(
IWDFIoQueue* pQueue,
IWDFIoRequest* pRequest,
SIZE_T BytesToRead
) {
IWDFMemory* pOutputMemory = nullptr;
pRequest->GetOutputMemory(&pOutputMemory);
if (pOutputMemory) {
// 模拟读取数据
pOutputMemory->CopyFromBuffer(0, "Hello", 5);
pRequest->CompleteWithInformation(S_OK, 5);
} else {
pRequest->Complete(E_FAIL);
}
}
GetInputMemory 或 GetOutputMemory 获取的 IWDFMemory 对象,在不再使用时必须调用 Release 释放。否则会导致UMDF宿主进程(WUDFHost.exe)内存持续增长,最终触发资源耗尽故障。
2.7 常见故障模式与排查对照
| 故障现象 | 可能原因 | 涉及接口 | 排查命令/方法 |
|---|---|---|---|
| 设备无法启动 | CreateDevice 失败或未调用 CommitPnpState | IWDFDriver, IWDFDevice | !wudfext.umdevstacks |
| I/O请求无响应 | 队列未设置回调或回调未注册 | IWDFIoQueue | !wudfext.umirp |
| 系统蓝屏(0x9F) | 请求未完成,导致IRP超时 | IWDFIoRequest | !analyze -v, !wudfext.umirp |
| 内存泄漏 | IWDFMemory 未 Release | IWDFIoRequest | !wudfext.wudfheapdump |
| 请求重复完成 | Complete 被调用多次 | IWDFIoRequest | 启用UMDF验证器(Verifier) |