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

注意

从 Windows 10 版本 2004 开始,静态驱动程序验证程序 (SDV) 不再需要批注来标识 WDM 驱动程序的调度例程的角色类型。 请按照本页 基本和高级初始化 部分中的指导进行操作。

若要在分析 WDM 驱动程序时通知 SDV 驱动程序的入口点,必须使用函数角色类型声明来声明函数。 函数角色类型在 Wdm.h 中定义。 必须通过指定相应的角色类型来声明 WDM 驱动程序中 DriverEntry 例程中的每个入口点。 角色类型是预定义的 typedefs,对应于 WDM 驱动程序中识别的入口点。

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

DRIVER_UNLOAD CsampUnload;

CsampUnload 函数的定义保持不变:

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

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

WDM 函数角色类型 WDM 例程

DRIVER_INITIALIZE

DriverEntry

DRIVER_STARTIO

StartIO

DRIVER_UNLOAD

卸载

DRIVER_ADD_DEVICE

AddDevice

Dispatch_type (类型) DRIVER_DISPATCH

调度例程 (驱动程序使用的) 。 请参阅 编写调度例程

IO_COMPLETION_ROUTINE

IoCompletion

通过调用 IoSetCompletionRoutine 或 IoSetCompletionRoutineEx 并将函数指针作为第二个参数传递给 IoCompletion 例程来设置 IoCompletion 例程。

DRIVER_CANCEL

取消

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

IO_DPC_ROUTINE

DpcForIsr

通过调用 IoInitializeDpcRequest 并将函数指针作为第二个参数传递给 DpcForIsr 例程来注册 DpcForIsr 例程。 若要将 DPC 排队,请使用同一 DPC 对象从 ISR 例程调用 IoQueueDpc

KDEFERRED_ROUTINE

CustomDpc

通过调用 KeInitializeDpc 并将函数指针作为第二个参数传递给 CustomDpc 来设置 CustomDpc 例程。 若要对驱动程序的 CustomDpc 进行排队,请使用同一 DPC 对象从 ISR 例程调用 KeInsertQueueDpc

KSERVICE_ROUTINE

InterruptService

中断服务例程 (ISR) 设备中断,并在必要时计划接收数据的中断后处理。

REQUEST_POWER_COMPLETE

PowerCompletion 回调例程完成电源 IRP 的处理。 如果驱动程序需要在所有其他驱动程序完成 IRP 后执行其他任务,驱动程序在调用分配 IRP 的 PoRequestPowerIrp 例程期间注册 PowerCompletion 回调例程。

WORKER_THREAD_ROUTINE

例程所返回的值

例程 是在 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
 )
{
}

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

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