使用 WDM 驱动程序的函数角色类型来声明函数

注意

从 Windows 10 2004 版本开始,静态驱动程序验证程序 (SDV) 不再需要注释来识别 WDM 驱动程序的调度例程角色类型。 请遵循本页基本和高级初始化部分的指导。

在分析 WDM 驱动程序时,为了向 SDV 提供驱动程序入口点的信息,必须使用函数角色类型声明来声明函数。 函数角色类型在 Wdm.h 中定义。 WDM 驱动程序中 DriverEntry 例程的每个入口点都必须通过指定相应的角色类型来声明。 角色类型是预定义的类型定义,与 WDM 驱动程序中的公认入口点相对应。

例如,要为驱动程序的 Unload 例程创建名为 CsampUnload的函数角色类型声明,请使用预定义的 typedef DRIVER_UNLOAD 角色类型声明。 函数角色类型声明必须出现在函数定义之前。

DRIVER_UNLOAD CsampUnload;

CsampUnload 函数的定义保持不变:

VOID
CsampUnload(
    IN PDRIVER_OBJECT DriverObject
    )
{
    ...
}

SDV 可识别下表所示的入口点类型。

波分复用功能作用类型 波分复用例程

驱动程序初始化

DriverEntry

DRIVER_STARTIO

启动IO

DRIVER_UNLOAD

Unload

驱动程序添加设备

AddDevice

Dispatch_type类型 DRIVER_DISPATCH

驱动程序使用的调度例程。 请参见编写调度例程

io_completion_routine

IoCompletion

IoCompletion 例程是通过调用 IoSetCompletionRoutineIoSetCompletionRoutineEx 并将指向 IoCompletion 例程的函数指针作为第二个参数来设置的。

DRIVER_CANCEL

Cancel

取消例程是通过调用 IoSetCancelRoutine 并将 IRP 取消例程的函数指针作为第二个参数传递给函数来设置的。

IO_DPC_ROUTINE

DpcForIsr

DpcForIsr 例程是通过调用 IoInitializeDpcRequest 并将 DpcForIsr 例程的函数指针作为第二个参数来注册的。 要对 DPC 进行排队,请使用相同的 DPC 对象,在 ISR 例程中调用 IoQueueDpc

kdeferred_routine

CustomDpc

CustomDpc 例程是通过调用 KeInitializeDpc 并将指向 CustomDpc 的函数指针作为第二个参数来设置的。 要为驱动程序排队 CustomDpc,请使用相同的 DPC 对象在 ISR 例程中调用 KeInsertQueueDpc

kservice_routine

中断服务

中断服务例程 (ISR) 为设备中断提供服务,并在必要时安排对接收到的数据进行中断后处理。

功率请求完成

PowerCompletion 回调例程完成对功率 IRP 的处理。 如果驱动程序需要在所有其他驱动程序完成 IRP 后执行其他任务,则会在调用分配 IRP 的 PoRequestPowerIrp 例程时注册 PowerCompletion 回调例程。

工作线程程序

例程

例程ExInitializeWorkItem 函数第二个参数中指定的回调例程。

例程只有在驱动程序调用 ExQueueWorkItem 将工件添加到系统队列时才应这样声明。

声明驱动程序调度例程

从 Windows 10 2004 版开始,调度例程的函数角色类型声明将根据 WDM 驱动程序的 DriverEntry 例程中 DriverObject->MajorFunction 表的初始化情况,自动与 IRP 类别一起完善。

Foo 驱动程序必须使用基本或高级声明方式完成角色声明,才能符合 SDV 标准。

基本和高级初始化

基本样式可以从下面的示例中看出(注意,调度例程名称 FooCreate 和 FooCleanup 只是示例,可以使用任何合适的名称):

DriverObject->MajorFunction[IRP_MJ_CREATE] = FooCreate; //Basic style
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = FooCleanup;

可以采用更先进的方法来缩短所需的清单。 虽然同一调度例程可用于多个 IRP 类别,但一个驱动程序可以用这种方式对两个初始化进行编码:

DriverObject->MajorFunction[IRP_MJ_CREATE] = 
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = FooCreateCleanup; // Advanced style for a multi-role dispatch routine 

为了使驱动程序能够正常运行 SDV, 驱动程序必须只使用基本高级样式,如上图所示。 如果不使用这两种方法,驱动程序上的 SDV 验证将无法按预期运行

函数参数和函数作用类型

按照 C 编程语言的要求,在函数定义中使用的参数类型必须与函数原型的参数类型相匹配,或者在本例中与函数角色类型相匹配。 SDV 依靠函数签名进行分析,并忽略签名不匹配的函数。

例如,应使用 IO_COMPLETION_ROUTINE 函数角色类型声明 IoCompletion 例程:

IO_COMPLETION_ROUTINE myCompletionRoutine;

当您实现 myCompletionRoutine时,参数类型必须与 IO_COMPLETION_ROUTINE 使用的类型一致,即 PDEVICE_OBJECT、PIRP 和 PVOID(语法参见 IoCompletion 例程)。

NTSTATUS
myCompletionRoutine(
 PDEVICE_OBJECT  DeviceObject,
 PIRP  Irp,
 PVOID  Context
 )
{
}

运行驱动程序代码分析以验证函数声明

为帮助确定源代码是否准备就绪,请运行 驱动程序代码分析。 驱动程序的代码分析可检查函数角色类型声明,帮助识别可能遗漏的函数声明,或在函数定义的参数与函数角色类型中的参数不匹配时发出警告。