3. KMDF驱动程序入口:DriverEntry函数详解、WDF_DRIVER_CONFIG初始化、EvtDriverDeviceAdd回调注册
在KMDF框架中,DriverEntry是所有驱动程序的入口点,类似于应用程序的main函数。当操作系统加载驱动时,首先调用此函数。本章将深入解析DriverEntry的结构、WDF_DRIVER_CONFIG的初始化流程,以及如何正确注册EvtDriverDeviceAdd回调函数。
3.1 DriverEntry函数签名与职责
DriverEntry函数由WDK(Windows Driver Kit)定义,其标准原型如下:
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
);
| 参数 | 类型 | 说明 |
|---|---|---|
| DriverObject | PDRIVER_OBJECT |
指向驱动对象的指针,由I/O管理器创建。驱动需在此对象上注册分发例程。 |
| RegistryPath | PUNICODE_STRING |
指向注册表中驱动服务项的路径,通常用于读取配置信息。 |
DriverEntry的核心职责包括:
- 初始化WDF_DRIVER_CONFIG结构体。
- 调用WdfDriverCreate创建WDF驱动对象。
- 注册EvtDriverDeviceAdd回调(必须)。
- 可选:注册其他事件回调(如
EvtDriverUnload)。
关键提示: 在KMDF中,DriverEntry通常不直接处理硬件或创建设备。这些工作被推迟到EvtDriverDeviceAdd回调中执行,以确保PnP管理器在合适的时机调用。
3.2 WDF_DRIVER_CONFIG初始化
WDF_DRIVER_CONFIG是KMDF驱动配置的核心结构体,它告诉框架驱动的基本属性。初始化此结构体有两种方式:
3.2.1 使用初始化宏
推荐使用WDF_DRIVER_CONFIG_INIT宏进行初始化,它会自动设置结构体大小和默认值:
WDF_DRIVER_CONFIG config;
WDF_DRIVER_CONFIG_INIT(
&config,
MyEvtDeviceAdd // 指定EvtDriverDeviceAdd回调
);
3.2.2 手动初始化
如果需要自定义更多字段,可以手动赋值:
WDF_DRIVER_CONFIG config;
RtlZeroMemory(&config, sizeof(WDF_DRIVER_CONFIG));
config.Size = sizeof(WDF_DRIVER_CONFIG);
config.EvtDriverDeviceAdd = MyEvtDeviceAdd;
config.DriverInitFlags = WDF_DRIVER_INIT_FLAGS::WdfDriverInitNonPnpDriver; // 非PnP驱动标志
config.EvtDriverUnload = MyEvtDriverUnload;
| 字段 | 默认值 | 说明 |
|---|---|---|
Size |
sizeof(WDF_DRIVER_CONFIG) |
结构体大小,用于版本兼容。 |
EvtDriverDeviceAdd |
NULL |
必须设置,否则驱动加载失败。 |
EvtDriverUnload |
NULL |
可选,驱动卸载时的清理回调。 |
DriverInitFlags |
WdfDriverInitPnpDriver |
默认为PnP驱动,可改为非PnP。 |
DriverPoolTag |
'DfWx' |
用于内存池标记,便于调试。 |
3.3 EvtDriverDeviceAdd回调注册
EvtDriverDeviceAdd是KMDF驱动中最重要的回调之一。当PnP管理器检测到驱动支持的设备时,会调用此回调。驱动在此回调中创建设备对象、初始化硬件、设置I/O队列等。
3.3.1 回调函数原型
NTSTATUS
MyEvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
);
| 参数 | 说明 |
|---|---|
| Driver | 由WdfDriverCreate返回的WDF驱动对象句柄。 |
| DeviceInit | 指向WDFDEVICE_INIT结构的指针,用于配置设备属性(如设备名称、同步模式、文件对象等)。 |
3.3.2 注册流程
在DriverEntry中,通过WdfDriverCreate函数将配置与驱动对象关联:
NTSTATUS status;
WDFDRIVER driver;
WDF_DRIVER_CONFIG config;
WDF_DRIVER_CONFIG_INIT(&config, MyEvtDeviceAdd);
status = WdfDriverCreate(
DriverObject, // 传入的驱动对象
RegistryPath, // 注册表路径
WDF_NO_OBJECT_ATTRIBUTES, // 可选属性
&config, // 驱动配置
&driver // 返回的WDF驱动句柄
);
if (!NT_SUCCESS(status)) {
// 错误处理
return status;
}
常见错误: 如果
WdfDriverCreate失败,驱动将无法加载。常见原因包括:EvtDriverDeviceAdd为NULL、配置结构体大小不匹配、或驱动对象已被其他框架占用。
3.4 完整DriverEntry示例
以下是一个典型的KMDF驱动入口实现:
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
WDFDRIVER driver;
NTSTATUS status;
// 1. 初始化配置结构体,注册EvtDriverDeviceAdd
WDF_DRIVER_CONFIG_INIT(&config, MyEvtDeviceAdd);
// 2. 可选:设置驱动卸载回调
config.EvtDriverUnload = MyEvtDriverUnload;
// 3. 创建WDF驱动对象
status = WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
&driver
);
if (!NT_SUCCESS(status)) {
KdPrint(("WdfDriverCreate failed with status 0x%x\n", status));
return status;
}
KdPrint(("KMDF Driver loaded successfully.\n"));
return STATUS_SUCCESS;
}
3.5 DriverEntry与WDF驱动对象关系图
下图展示了DriverEntry、WDF_DRIVER_CONFIG与WDF驱动对象之间的创建关系:
3.6 常见问题与最佳实践
最佳实践清单:
- 始终使用WDF_DRIVER_CONFIG_INIT宏初始化配置,避免手动设置
Size字段遗漏。 - 在EvtDriverDeviceAdd中完成所有设备初始化工作,不要在DriverEntry中操作硬件。
- 如果驱动支持多个设备,确保EvtDriverDeviceAdd是重入的(reentrant),因为PnP可能并发调用。
- 使用KdPrint或TraceEvents在DriverEntry中添加日志,便于调试加载失败问题。
避坑指南:
- 不要将
EvtDriverDeviceAdd设置为NULL,否则WdfDriverCreate返回STATUS_INVALID_PARAMETER。 - 不要在DriverEntry中调用
WdfDeviceCreate,设备创建必须在EvtDriverDeviceAdd中完成。 - 如果驱动是非PnP的,需设置
DriverInitFlags为WdfDriverInitNonPnpDriver,并自行管理设备生命周期。