wdf编程

WDF是基于WDM的Windows驱动编程框架,WDF极大地方便了Windows驱动的开发。

1、WDF核心

WDF(Windows Driver Frameworks),WDF支持两种类型的驱动,一种是KMDF(Kernel-Mode Driver Framework)内核模式驱动框架,一种是UMDF(User-Mode Driver Framework)用户模式驱动框架。

UMDF和KMDF的区别从他们的名字就可以看出来,一个运行在在内核模式下,一个运行在用户模式下。用户模式下的驱动是因为有些驱动

不需要使用到内核的结构体等信息,通常用来做中间层为Win32应用程序和KMDF或其他操作系统组件之间的接口,因此才产生了所谓用户模式下的驱动这一类型。它们之间最大的区别就是运行层级的区别,其他的差别也因此而生。

WDF包含一个DriverEntry例程和一系列事件回调函数,这些事件回调函数在基于框架的驱动使用的对象驱动模型中定义了。回调函数调用WDF框架导出的对象模型。

当你创建一个WDF驱动,通常需要做下面几件事:

  • 使用一个框架驱动对象来代表驱动

驱动的DriverEntry例程调用WdfDriverCreate创建一个框架驱动对象(即Driver.c中WdfDriverCreate中的DriverObject)用来代表这个驱动。同时WdfDriverCreate函数为驱动注册EvtDriverDeviceAdd回调函数,每当PnP(Plug and Play)管理器报告发现了一个驱动支持的设备时,框架就会调用该回调函数。

  • 使用一个框架设备对象来支持驱动中的PnP和电源管理

所有驱动都需要调用WdfDeviceCreate来为每一个驱动支持的设备创建一个框架设备对象(即Device.c中WdfDeviceCreate中的device)。这个设备可以是一块插入计算机的硬件也可以是纯软件设备。框架设备对象支持PnP和电源管理操作,同时驱动可以注册一个当设备加入或离开工作状态的通知事件回调函数。有关PnP和电源管理

  • 使用一个框架队列对象框架请求对象来支持驱动中的I/O操作

所有的驱动在接收来自应用或者其他驱动的read、write或者设备I/O control请求时,必须调用WdfIoQueueCreate,来创建一个框架队列对象(即Queue.c中WdfIoQueueCreate中的queue)用来代表I/O队列。通常来说,驱动为每个I/O队列注册一个或多个请求处理器(如:EvtIoRead、EvtIoWrite等)。当I/O管理器向驱动发送一个I/O请求,框架会为这个请求创建一个框架请求对象,并将该对象放入到一个I/O队列中,随后调用一个请求处理器去通知驱动这个请求可用。驱动接收到I/O请求之后可以requeue、complete、cancel或者forward这个请求。有关框架队列对象框架请求对象

  • 使用一个框架中断对象来处理设备中断

驱动通过调用WdfInterruptCreate为每个中断创建一个框架中断对象并注册到回调函数的方式来处理设备中断。这些回调函数通过开启或关闭中端来充当ISR和DPC。有关硬件中断

  • KMDF驱动可以使用框架的DMA启用对象DMA事务对象来处理设备的DMA(Direct Memory access)操作

如果你的KMDF驱动的设备支持DMA操作,那么驱动需要调用WdfDmaEnablerCreate函数创建一个DMA启用对象同时调用WdfDmaTransactionCreate函数创建一个或多个DMA事务对象DMA事务对象定义了EvtProgramDma回调函数使硬件设备完成DMA操作。有关DMA操作

  • 使用框架的I/O目标对象向其他驱动发送I/O请求

当前驱动可以通过框架的I/O目标对象将I/O请求发送给下一层的驱动。有关I/OTargets

  • KMDF驱动可以使用框架的WMI提供程序对象WMI实例对象来支持WMI(Windows Management Instrumentation)功能

大部分支持WMI的KMDF驱动需要调用WdfWmiInstanceCreate函数来注册收发WMI数据的回调函数。有关WMI

  • 使用框架提供的同步能力

所有的的驱动都需要意识到多处理器的同步问题,并且使用框架提供的synchronization techniques函数。

  • 使用框架提供的其他对象和功能

框架提供了其他可用的对象和功能,查看WDF Support Objects获取更多信息。

2、WDF体系结构

WDF为驱动程序提供了基于对象的接口。框架定义

3、DriverEntry例程

DriverEntry是驱动加载之后调用的第一个例程,它负责初始化整个驱动。类似于main函数,可看作整个驱动的入口。

定义:

1
2
3
4
NTSTATUS DriverEntry(
  _In_ PDRIVER_OBJECT DriverObject,
  _In_ PUNICODE_STRING RegistryPath
  );

参数:

DriverObject[in]

指向驱动对象结构体的指针,用来代表一个驱动的WDM驱动对象。

RegistryPath[in]

指向一个Unicode编码字符串结构体的指针,它指定了注册表中驱动程序的Parameters项路径。

返回值:

成功返回STATUS_SUCCESS,否则返回一个在ntstatus.h中定义的错误状态码。

备注:

和所有的WDM驱动程序一样,基于框架的驱动程序必须要有DriverEntry例程,该例程在加载驱动时调用,基于框架的驱动程序的DriverEntry例程必须:

  • 激活WPP软件跟踪

DriverEntry需要包含一个WPP_INIT_TRACING宏来开启软件跟踪,UMDF和KMDF所使用的WPP_INIT_TRACING不同,但是它们都是用来注册供应商的GUID并且初始化软件跟踪索要使用到的结构体的。开启WPP之后,用户可以使用Tracelog工具开启并控制跟踪。

  • 调用WdfDriverCreate

调用WdfDriverCreate函数使驱动程序可以使用WDF提供的接口(所有驱动程序都使用DriverEntry例程,但是调用WdfDriverCreate之后才能使用WDF框架),在调用该方法之前,驱动程序无法调用其他的框架例程。

  • 分配所有可能用到的非设备特定的系统资源和全局变量

通常而言,驱动将系统资源和单个设备相关联,因此基于框架的驱动需要在EvtDriverDeviceAdd回调中分配大多数资源,该函数在系统检测到单个设备时被调用。

由于UMDF驱动程序的多个实例可能由单独的Wudfhost实例托管,所以全局变量可能无法在UMDF驱动程序的所有实例中使用。

  • 从注册表获取驱动特定的参数

某些驱动需要从注册表中获取参数,这些驱动调用WdfDriverOpenParametersRegistryKey来打开包含这些参数的注册表项。

  • 提供一个返回值

注意:

对于一个UMDF驱动程序来说,由于它运行在用户模式下,这个框架可能分别加载多个UMDF驱动实例到不同的主机进程实例中,由此将导致如下结果:

  • 如果在不同主机进程中加载UMDF驱动程序的实例,框架可能多次调用DriverEntry例程。相对的,对于KMDF驱动,框架只调用一次它的DriverEntry例程;
  • 如果UMDF驱动在DriverEntry例程创建了一个全局变量,则该变量可能不可用于驱动程序的所有实例。但是KMDF驱动程序这么做是可以的。