WDF驱动


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

好书推荐

Windows驱动开发技术详解

竹林蹊径:深入浅出Windows驱动开发

寒江独钓:Windows内核安全开发...

Xp, ndk

Win10: wdk10


image.png


好书推荐

Windows设备驱动程序WDF开发

作者:武安河


image.png










HelloWorld驱动:NT+WDM+WDF


驱动开发:NT-->WDM-->WDF

WDF:UMDF, KMDF

User mode Driver Framework

Kernel mode Driver Framework



一、概念介绍

NT驱动程序模型:NT式驱动程序模型是一种比较老式的驱动程序模型,但适用于现有的Windows系统。NT式驱动模型没有固定的形式,最简单的NT式驱动程序模型这一特点,程序开发者可以编写一个完全不支持硬件工作的驱动程序,却可以将代码运行在内核模式中。


WDM驱动程序模型:WDM式驱动程序在NT式驱动程序的基础上,还必须:

    1、包括wdm.h头文件,而不是ntddk.h(wdm.h是ntddk.h的一部分)

    2、被设计为一种WDM驱动程序类型,如总线驱动、功能驱动,过滤驱动等;

    3、创建设备对象属于WDM设备对象类型(物理设备对象、功能设备对象、过滤设备对象);

    4、支持即插即用(inf实现的功能);

    5、支持电源管理;

    6、支持Windows管理规范(WMI)


二、加载驱动

NT设备驱动程序的动态加载主要是由服务控制管理程序组件来完成的。

Windows服务可以在系统启动时加载,用户也可以按需在服务控制平台开启或者关闭服务。程序员可以通过Windows提供的相关服务函数进行加载或者卸载该服务等。服务程序更是可以在用户还没有登录系统时,就载入系统并且被执行。

  加载NT驱动一般分为4个步骤:

    1、调用OpenSCManager打开SCM管理器;

    2、调用OpenService打开此项服务

    3、调用ControlService传递SERVICE_CONTROL_STOP来停止服务;

    4、调用DeleteService卸载此项服务;

    5、关闭句柄;


和NT式驱动不同,WDM式驱动程序不是被当作服务来加载的,因此不能简单地依靠修改注册表来加载驱动。

WDM式驱动比NT式驱动增加了对即插即用的支持,这需要安装的时候提供一个INF文件进行配合。这里增加了一个设置AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP(即插即用)管理器调用。并设置对IRP_MJ_PNP的IRP的回调函数。这都是NT和WDM驱动最大的不同点。而且在WDM驱动中,大部分卸载工作都不是由DriverUnload来处理,而是放在对IRP_MN_REMOVE_DEVICE的IRP的处理函数中处理。


三、头文件

  NT式的驱动程序要导入的头文件时NTDDK.H,而WDM式的驱动要导入的头文件为WDM.H.


四、服务形式

通俗点NT式驱动,以服务的形式启动在系统里,WDM驱动加载需要inf文件,在C:\Windows下有一个名为INF的隐藏文件夹,我们可以找到很多*.INF、*.pnf格式的文件,前者即所谓的设备信息文件,后者是预编译信息文件。用记事本可以打开INF文件,其中记录了必要的硬件安装信息,包括设备类型、设备生产厂商名称、适用产品等信息,Windows可以据此自动安装驱动程序。


五、WDF驱动模型

如所周知,自Windows 2000开始,开发驱动程序必以WDM为基础的,但其开发难度之大,根本不能奢望像用户模式应用程序开发那样容易。为改善这种局面,微软推出了新的驱动程序开发环境。要预先指出的是,这不是另起炉灶改弦更张,而是以WDM为基础进行了建模和封装,显著特点是降低了开发难度。因为:

1、   将原来普通程序设计中基于对象的技术应用到了驱动开发中。WDM中虽也有对象模型,但与真正的基于对象技术根本就不是一回事。为了实现基于对象的技术,微软精心设计了对象模型并进行了封装。属性、方法、事件等等“一个都不能少”。

2、   无论内核模式的驱动程序KMDF或者用户模式的驱动程序UMDF,都采用同一套对象模型构建(wdf),采用同一个基础承载。这个基础就是WDF。WDF虽然已经是经过封装和定义的对象模型,但对内核模式和用户模式对象来说,WDF又是两者的父对象。换言之两者都是继承了WDF才得到的,或者都是从WDF派生而来的。相对于内核模式,派生出的对象称为“KMD框架”即KMDF;相对于用户模式,派生出的模型称为“UMD框架”即UMDF。无论何种模式的框架,其内部封装的方法、执行的行为其实还是用WDM完成的。

3、   更重要的,也是微软反复炫耀的是封装了驱动程序中的某些共同行为:例如即插即用和电源管理就属于这种共同行为。因为大多数驱动程序中都需要处理即插即用和电源管理问题,据说这大概要上千行的代码,况且,没有相当水平还不一定能处理好。为了一劳永逸,WDF干脆将即插即用和电源管理封装了进了对象之内,一举成了对象的缺省(默认)行为。

4、   改变了操作系统内核与驱动程序之间的关系,WDM驱动程序中,一方面要处理硬件,另一方面要处理驱动程序与操作系统内核的交互。现在WDF则将驱动程序与操作系统内核之间进行了分离,驱动程序与操作系统交互工作交给框架内封装的方法(函数)完成(比如:mfc),这样驱动开发者只需专注处理硬件的行为即可。这不仅避免了顾此失彼两面不周的弊端,也由于双方的分离,对操作系统内的某些改动,硬件制造商配套驱动程序的开发都有莫大的好处。

5、   两种模式的驱动程序(KMDF、UMDF)都使用同一环境进行构建,这一环境称为WDK。

即KMDF,UMDF的开发环境为WDK。

Windows Driver Kit (WDK): 把测试套件(test suites)集成进来,DDK 就成了WDK。WDK是针对微软操作系统系列的驱动器集成开发系统。它组合了Windows DDK和Hardware Compatibility Test (HCT) kits(硬件兼容性测试工具),同时提供了微软内部用来测试Windows操作系统稳定性和可靠性的测试套件。


六、WDF与WDM关联和区别

  WDM是Win32设备驱动程序体系结构。Windows设备驱动程序,过去是WDM(Windows Driver Model)框架,编程复杂,初学者难以掌握其编程要领。为了解决这一问题,微软对WDM驱动程序的架构做了改进,形成了全新的WDF(Windows Driver Foundation)框架结构。它提供了面向对象和事件驱动的驱动程序开发框架,大大降低了开发难度。从现在开始,掌握Windows设备驱动程序的开发人员,由过去的“专业”人士,将变为“普通”大众。WDF驱动程序包括两个类型,一个是内核级的,称为KMDF(Kernel-Mode Driver Framework),为SYS文件;另一个是用户级的,称为UMDF(User-Mode Driver Framework),为DLL文件。


WDM驱动模型和WDF驱动模型的最大的区别是:

    1) wdf驱动框架对WDM进行了一次封装,WDF框架就好像C++中的基类一样,且这个基类中的model,IO model ,pnp和电源管理模型;且提供了一些与操作系统相关的处理函数,这些函数好像C++中的虚函数一样,WDF驱动中能够对这些函数进行override;特别是Pnp管理和电源管理!基本上都由WDF框架做了,而WDF的功能驱动几乎不要对它进行特殊的处理;

    2) WDF驱动模型 与WDM驱动模型的另外一个主要区别是:WDF驱动采用队列进行IO处理,而WDM中将所有的IO操作都用默认的队列进行处理,如果要进行IRp同步,必须使用StartIO;

    3) WDF是面向对象的,而WDM是面向过程的,WDF提供对象的封装,如将IRP封装成WDFREQUEST,对象提供方法和Event。

*********************************************************************


    总结:NT类似于C语言,没有固定格式;WDM类似于C++语言,基于C语言实现某些驱动类型的实现;WDF类似于MFC框架(C#),基于C++做了进一步的封装。



七、WDM与WDF

Windows平台下的设备驱动程序从Windows 2000开始都是以WDM ( Windows Driver Model) 框架为平台进行开发。以此模型开发,开发者需要一方面实现驱动程序与硬件的交互,另一方面要对操作系统内核进行操作,难度大。驱动程序容易出现问题,这也是Windows2000以来操作系统容易蓝屏的原因。

为了改善这种局面,降低驱动程序开发者的开发难度,提高系统稳定性,微软推出了新的驱动程序开发模型WDF。

驱动程序的内存模式:buffered,direct,neither

WDF对WDM进行了封装,将驱动程序中与操作系统交互的细节由框架实现。这样驱动程序就从内核中分离出来,开发者只需要专注处理与硬件的交互,简化了驱动程序的设计,提高了整个系统的可靠性和稳定性。WDM与WDF样例驱动程序对比如下表所示。通过该表可以看到基于WDF框架的驱动程序,代码量显著减少,开发起来更加容易。


WDF是UMDF(User Mode Driver Framework,用户模式驱动程序框架)和KMDF(Kernel Mode Driver Framework,内核模式驱动程序框架)的总和。由于本课题基于PCIe硬件设备进行驱动开发,涉及到内存读写等内核操作,所以使用KMDF框架来编写驱动程序。

1.2 Windows驱动程序

我们知道Windows是一个分层的操作系统,它的运行依赖于上层组件向下层组件的调用。一个简化的I/O流模型如下图:


1.3 WDF框架

通过参考《竹林蹊径--深入浅出Windows驱动开发》

和《Developing_drivers_with_the_Microsoft_Windows_Driver_Foundation》,

WDF抽象的框架如下图所示:

PME: Property Method Event

Class Person{

Private: //property

Int age;

Char name[64];


Public://method

Int getAge();

Void setAge(int agemm);


Public://event

Function* funAge;

};


///系列一:派遣函数

Driverobject-->majorFunction[IRP_MJ_READ] = XXXread;

Driverobject-->majorFunction[IRP_MJ_WRITE] = XXXread;

Driverobject-->majorFunction[IRP_MJ_CONTROL_DEVICE] = XXXread;


///系列三:事件回调函数


思考问题:

请自己思考如何从IRP到WdfRequest,如何封装?

如何从派遣函数到事件回调函数?如何一步一步封装?


// Message--->IRP---->WdfRequest---》IRP---》硬件驱动


 

WDF已经把驱动程序开发做了很好的封装,开发者只需要定义框架对象和编写事件回调函数。WDF中也采用对象,但是它和C++这种编程语言中的对象不相同,毕竟WDF是C写的。

如何解释WDF的对象我也做不到,我的一直把WDF中的对象看做一种数据结构,比如WDFDRIVER对象,其实就是一个与驱动程序相关的结构体。

另外,WDF中也有例程这样的称呼,是对routine的翻译,可以理解为函数体。所以简单来说,对PCIe驱动程序来说,开发者需要定义一些结构体,编写几个函数就ok了,具体如何被调用,那就是WDF框架内部的事情了,我们只需要知道,当我们注册好编写好的回调例程之后,当事件发生(设备插入、设备打开、I/O操作等)时,WDF会自动帮我们调用相关的例程。


HelloWorld---NT
#include <ntifs.h>
VOID UnloadDriver(PDRIVER_OBJECT pDriverObject) {
KdPrint(("Unload Driver success."));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {
KdPrint(("DriverPath:%wZ", pRegistryPath));
KdPrint(("Hello,myDriver"));
// 主动创建设备
IoCreateDevice(.....)
IoCreateSysbolicLink(......)
pDriverObject->DriverUnload = UnloadDriver;
return STATUS_SUCCESS;
}


HelloWorld---WDM
参考: 【Windows驱动开发系列二:WDM设备驱动开发】
/************************************************************************
* 文件名称:HelloWDM.cpp   
*************************************************************************/
#include "HelloWDM.h"
/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
      pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/
#pragma INITCODE 
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Enter DriverEntry\n"));
///1.创建设备:被动创建设备,被即插即用组件来调用
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
///2.分发例程
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 
pDriverObject->MajorFunction[IRP_MJ_CREATE] = 
pDriverObject->MajorFunction[IRP_MJ_READ] = 
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
///3.卸载例程
pDriverObject->DriverUnload = HelloWDMUnload;
KdPrint(("Leave DriverEntry\n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函数名称:HelloWDMAddDevice
* 功能描述:添加新设备
* 参数列表:
      DriverObject:从I/O管理器中传进来的驱动对象
      PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象
* 返回 值:返回添加新设备状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{ 
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");
///1.创建设备
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
if( !NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
///2.添加到设备栈
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");
///3.创建符号链接
pdx->ustrDeviceName = devName;
pdx->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);
if( !NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
status = IoCreateSymbolicLink(&symLinkName,&devName);
if( !NT_SUCCESS(status))
{
return status;
}
}
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
KdPrint(("Leave HelloWDMAddDevice\n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函数名称:DefaultPnpHandler
* 功能描述:对PNP IRP进行缺省处理
* 参数列表:
      pdx:设备对象的扩展
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/ 
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter DefaultPnpHandler\n"));
IoSkipCurrentIrpStackLocation(Irp);
KdPrint(("Leave DefaultPnpHandler\n"));
return IoCallDriver(pdx->NextStackDevice, Irp);
}
/************************************************************************
* 函数名称:HandleRemoveDevice
* 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理
* 参数列表:
      fdo:功能设备对象
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HandleRemoveDevice\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
NTSTATUS status = DefaultPnpHandler(pdx, Irp);
IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
    //调用IoDetachDevice()把fdo从设备栈中脱开:
    if (pdx->NextStackDevice)
        IoDetachDevice(pdx->NextStackDevice);
    //删除fdo:
    IoDeleteDevice(pdx->fdo);
KdPrint(("Leave HandleRemoveDevice\n"));
return status;
}
/************************************************************************
* 函数名称:HelloWDMPnp
* 功能描述:对即插即用IRP进行处理
* 参数列表:
      fdo:功能设备对象
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
                        IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMPnp\n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) = 
{
DefaultPnpHandler,// IRP_MN_START_DEVICE
DefaultPnpHandler,// IRP_MN_QUERY_REMOVE_DEVICE
HandleRemoveDevice,// IRP_MN_REMOVE_DEVICE
DefaultPnpHandler,// IRP_MN_CANCEL_REMOVE_DEVICE
DefaultPnpHandler,// IRP_MN_STOP_DEVICE
DefaultPnpHandler,// IRP_MN_QUERY_STOP_DEVICE
DefaultPnpHandler,// IRP_MN_CANCEL_STOP_DEVICE
DefaultPnpHandler,// IRP_MN_QUERY_DEVICE_RELATIONS
DefaultPnpHandler,// IRP_MN_QUERY_INTERFACE
DefaultPnpHandler,// IRP_MN_QUERY_CAPABILITIES
DefaultPnpHandler,// IRP_MN_QUERY_RESOURCES
DefaultPnpHandler,// IRP_MN_QUERY_RESOURCE_REQUIREMENTS
DefaultPnpHandler,// IRP_MN_QUERY_DEVICE_TEXT
DefaultPnpHandler,// IRP_MN_FILTER_RESOURCE_REQUIREMENTS
DefaultPnpHandler,// 
DefaultPnpHandler,// IRP_MN_READ_CONFIG
DefaultPnpHandler,// IRP_MN_WRITE_CONFIG
DefaultPnpHandler,// IRP_MN_EJECT
DefaultPnpHandler,// IRP_MN_SET_LOCK
DefaultPnpHandler,// IRP_MN_QUERY_ID
DefaultPnpHandler,// IRP_MN_QUERY_PNP_DEVICE_STATE
DefaultPnpHandler,// IRP_MN_QUERY_BUS_INFORMATION
DefaultPnpHandler,// IRP_MN_DEVICE_USAGE_NOTIFICATION
DefaultPnpHandler,// IRP_MN_SURPRISE_REMOVAL
};
ULONG fcn = stack->MinorFunction;
if (fcn >= arraysize(fcntab))
{// unknown function
status = DefaultPnpHandler(pdx, Irp); // some function we don't know about
return status;
}// unknown function
#if DBG
static char* fcnname[] = 
{
"IRP_MN_START_DEVICE",
"IRP_MN_QUERY_REMOVE_DEVICE",
"IRP_MN_REMOVE_DEVICE",
"IRP_MN_CANCEL_REMOVE_DEVICE",
"IRP_MN_STOP_DEVICE",
"IRP_MN_QUERY_STOP_DEVICE",
"IRP_MN_CANCEL_STOP_DEVICE",
"IRP_MN_QUERY_DEVICE_RELATIONS",
"IRP_MN_QUERY_INTERFACE",
"IRP_MN_QUERY_CAPABILITIES",
"IRP_MN_QUERY_RESOURCES",
"IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
"IRP_MN_QUERY_DEVICE_TEXT",
"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
"",
"IRP_MN_READ_CONFIG",
"IRP_MN_WRITE_CONFIG",
"IRP_MN_EJECT",
"IRP_MN_SET_LOCK",
"IRP_MN_QUERY_ID",
"IRP_MN_QUERY_PNP_DEVICE_STATE",
"IRP_MN_QUERY_BUS_INFORMATION",
"IRP_MN_DEVICE_USAGE_NOTIFICATION",
"IRP_MN_SURPRISE_REMOVAL",
};
KdPrint(("PNP Request (%s)\n", fcnname[fcn]));
#endif // DBG
status = (*fcntab[fcn])(pdx, Irp);
KdPrint(("Leave HelloWDMPnp\n"));
return status;
}
/************************************************************************
* 函数名称:HelloWDMDispatchRoutine
* 功能描述:对缺省IRP进行处理
* 参数列表:
      fdo:功能设备对象
      Irp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
 IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMDispatchRoutine\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;// no bytes xfered
IoCompleteRequest( Irp, IO_NO_INCREMENT );
KdPrint(("Leave HelloWDMDispatchRoutine\n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函数名称:HelloWDMUnload
* 功能描述:负责驱动程序的卸载操作
* 参数列表:
      DriverObject:驱动对象
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMUnload\n"));
KdPrint(("Leave HelloWDMUnload\n"));
}


HelloWorld---KMDF


核心模式驱动程序架构是微软公司推出的Windows驱动程序基础(Windows Driver Foundation)之一,建构Windows XP与Windows Server 2003的核心模式(Kernel-Mode)驱动程序所需的基本功能,包括对即插即用(PNP)、电源管理(Power Manager)、I/O队列、直接存储器访问(DMA)、Windows Management Instrumentation(WMI)和同步处理等的完整支持。

KMDF的设计并不能用来取代WDM,它提供“Skeletal WDM”建置来替代WDM;当前KMDF并不支持总线筛选驱动程序(Bus Filter Driver)



Kernel-Mode Driver Framework当前支持下列类型的核心模式(kernel mode)驱动程序之创建:

即插即用(PNP)设备所使用的Function Driver。

即插即用(PNP)设备所使用的Filter Driver。

即插即用(PNP)设备堆栈(Stack)所使用的Bus Driver。

Windows NT 4.0类型设备所使用的Control设备驱动程序。

KMDF是可重新进入程序库(Reentrant Library)



与WDM的关系

自Windows 2000开始,开发驱动程序必以WDM为基础的,但开发难度太大,无法像用户模式应用程序开发那样容易。KMDF支持驱动程序在Windows Driver Model环境中撰写驱动程序,简化其中的过程,但是KMDF的设计并不能用来取代WDM,它提供“Skeletal WDM”建置来替代WDM。早期的WDM可支持Windows 98、Windows Me、Windows 2000和Windows XP;至于WDF计划支持Windows XP,以及更新的版本。

KMDF系以对象为基底创建于WDM架构之上。不同的功能有不同的对象,KMDF在实现上包含了:

Plug and Play and power management

I/O queues

Direct memory access(DMA)

Windows Management Instrumentation(WMI)

Synchronization


驱动程序进入点

在Windows操作系统中驱动程序的起始点都是在DriverEntry函数,DriveryEntry是驱动程序的进入点(entry point)。在DriverEntry函数的实现里,你需要具现化(instantiate)你的WDFDRIVER对象,并且告知WDF framework要去哪里调用你的系统。


 NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING  RegistryPath
    )
 {
  WDF_DRIVER_CONFIG config;
  NTSTATUS status = ''S_OK'';
 
  KdPrint((__DRIVER_NAME "DriverEntry Begin\n"));
 
  WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);
  status = WdfDriverCreate(
                      DriverObject,
                      RegistryPath,
                      ''WDF_NO_OBJECT_ATTRIBUTES'',
                      &config, // Pointer to config structure
                      ''WDF_NO_HANDLE''); // or NULL, Pointer to get WDFDRIVER handle
  if(T_SUCCESS(status))
  {
    KdPrint((__DRIVER_NAME "WdfDriverCreate failed with status 0x%08x\n", status));
  }
 
  KdPrint((__DRIVER_NAME "DriverEntry End\n"));
 
  return status;
 }
Add Device
EvtDeviceAdd函数,在系统发现新硬件插入时被调用。这个函数将挑起WDF驱动程序架构的大部分工作,EvtDeviceAdd事件被唤起之余一定会带出一个WDFDRIVER对象,并且指向一个WDFDEVICE_INIT结构。在设备产生(device crated)之前,必先进行初始化的动作。如果EvtDeviceAdd运行成功,那么EvtDevicePrepareHardware是架构下一个被运行的函数,用以保证驱动程序能够访问硬件。
WDFSTATUS DioEvtDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit)
 {
  WDFSTATUS status = STATUS_SUCCESS;
  WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
  WDF_OBJECT_ATTRIBUTES objAttributes;
  WDFDEVICE device;
  PDIO_DEVICE_CONTEXT devContext;
  WDF_IO_QUEUE_CONFIG ioCallbacks;
  WDF_INTERRUPT_CONFIG interruptConfig;
  WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
 
  WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
  pnpPowerCallbacks.EvtDevicePrepareHardware = DioEvtPrepareHardware;
  pnpPowerCallbacks.EvtDeviceReleaseHardware = DioEvtReleaseHardware;
  pnpPowerCallbacks.EvtDeviceD0Entry= DioEvtDeviceD0Entry;
  pnpPowerCallbacks.EvtDeviceD0Exit = DioEvtDeviceD0Exit;
 
  WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, pnpPowerCallbacks);
 
  WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes);
 
  WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&objAttributes, DIO_DEVICE_CONTEXT);
 
  status = WdfDeviceInitUpdateName(DeviceInit, L"\\device\\WDFDIO");
 
  status = WdfDeviceCreate(&DeviceInit,    // Device Init structure
                           &objAttributes, // Attributes for WDF Device
                           &device);       // return new WDF Device pointer,
 
  devContext = DioGetContextFromDevice(device); // Get device extension
 
  devContext->WdfDevice = device;
 
  // Create a symbolic link for the control object
  status = WdfDeviceCreateSymbolicLink(device, L"\\DosDevices\\WDFDIO");
 
  WDF_IO_QUEUE_CONFIG_INIT(&ioCallbacks,
                             WdfIoQueueDispatchSerial,
                             WDF_NO_EVENT_CALLBACK,     // StartIo
                             WDF_NO_EVENT_CALLBACK);    // CancelRoutine
 
  ioCallbacks.EvtIoDeviceControl = DioEvtDeviceControlIoctl;
  status = WdfDeviceCreateDefaultQueue(device,
                                        &ioCallbacks,
                                        ''WDF_NO_OBJECT_ATTRIBUTES'',
                                        NULL); // pointer to default queue
 
  WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,       // Configure the Interrupt object
                              FALSE,                // auto-queue DPC?
                              DioIsr,               // ISR
                              DioDpc);              // Defered Procedule Call
 
  interruptConfig.EvtInterruptEnable = DioEvtInterruptEnable;
  interruptConfig.EvtInterruptDisable = DioEvtInterruptDisable;
 
  status = WdfInterruptCreate(device,
                              &interruptConfig,
                              &objAttributes,
                              &devContext->WdfInterrupt);
 
  WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings,  // Initialize idle policy
                                              IdleCannotWakeFromS0);
 
  status = WdfDeviceUpdateS0IdleSettings(device, &idleSettings);
 
  return status;
 }








HelloWorld---UMDF



User-Mode Driver Framework(用户模式驱动程序架构,简称UMDF),是美国微软公司所提出视窗驱动程序基础(Windows Driver Foundation)的一部分,运行于用户模式(user mode),仅能访问用户地址空间,是核心模式驱动程序框架(Kernel-Mode Driver Framework,KMDF)的子集合(subset),因此UMDF所提供的函数支持少于KMDF,两者使用相同的状态机器、使用相同的 I/O 模型


UMDF提供即插即用(PNP)、电源管理(Power Manager)、异步输出输入等功能,可设置 I/O 队列,但他的限制在于不处理中断(Interrupt)、不运行 DMA(直接存储器访问,Direct Memory Access),且不能使用核心模式资源如:未标签页集区(NonPaged Pool),也不完全支持同步化领域(Synchronization scope),在技术上UMDF使用COM(Component Object Model)的动态链接程序库(DLL)为基底技术


COM架构

UMDF驱动程序是一个基于COM架构的动态链接档(DLL),但UMDF并不使用COM的动态时期运行函数(runtimelibrary),单仅是借用了COM的样式。

UMDF 调用 DllGetClassObject API获取一个指针(pointer),这个指针指向一个IClassFactory的接口,并且激活 CreateInstance 这个属于IClassFactory 接口的函数来产生一个驱动程序的存储器实体(instance)。DLL 照例提供一些函数可以让COM 使用IWDFDriver-based的对象:

DllCanUnloadNow

DllGetClassObject

DllRegisterServer

DllUnregisterServer


DllMain

UMDF 驱动程序是一个动态链接库(Dynamic Link Library),运行的时候如同一个进程内(in-process)的 COM server,其代码中包括了DllMain,这是著名的DLL档的进入点(entry point)。 


BOOL WINAPI DllMain(

       HINSTANCE ModuleHandle,

       DWORD Reason,

       PVOID /* Reserved */)

{  if (DLL_PROCESS_ATTACH == Reason)

    {    WPP_INIT_TRACING(MYDRIVER_TRACING_ID);

        g_ModuleHandle = ModuleHandle; 

    }  

else if (DLL_PROCESS_DETACH == Reason) {    

    WPP_CLEANUP();

  }  

    return TRUE;

};


UMDF接口

IWDFObject: 定义基本的 WDF 对象类型(WDF object)

IWDFDriver: 代表驱动程序对象(driver object)

IWDFDevice: 代表设备对象(device object)

IWDFIoQueue: 代表 I/O 要求的队列(IO Request Queue)

IWDFIoRequest: I/O 要求描述(IO Request Description)

IWDFIoTarget: 代表 I/O 要求的目标驱动程序(IO Target)

IWDFMemory: 提供访问存储器区域(Memory)















Windows驱动开发之日志打印 - TraceEvents


Windows驱动开发之日志打印

《Windows驱动开发技术详解》一书中,介绍了一种“Windows驱动程序日志打印和查看的方法”,具体就是:在需要打印日志的地方,调用“KdPrint”函数,该函数类似标准C的printf(print file)函数。然后用“DebugView.exe”软件查看日志。



TraceView (TraceView.exe) 配置并控制跟踪会话并显示格式化从实时跟踪会话的跟踪消息和跟踪日志。


TraceView 的命令行接口是已弃用和已过时。 应优先使用专用的 SDK 和 WDK 中包含的命令行工具:Tracepdb, Tracelog,和Tracefmt。

TraceView跟踪控制器和一个跟踪使用者, TraceView 可用于启用、 配置、 启动、 更新和停止跟踪会话;若要显示实时或记录的跟踪消息;若要将合并来自不同的提供程序在单个显示屏; 跟踪消息若要筛选显示的跟踪消息;并将跟踪消息转换为文本格式。

工具中位于 TraceView\<平台>子目录的 Windows Driver Kit (WDK) 中,其中<平台>表示正在跟踪会话,例如,x86、 x64、 或 arm64 的平台。

本部分介绍 TraceView 随附在 Windows 10 Fall Creators Update (1709) WDK 和更高版本的版本。



一、引入

事实上,微软也提供了一个日志打印和日志查看机制,它可以查看指定的驱动文件的日志,并根据“Level”和“Flag”进行日志过滤,也可以保存成文件。

这套机制就是“WPP”和“TraceView.exe”。


随便打开一份微软提供的驱动示例代码,如:PLX9x5x,都会看到它里面有WPP。


在WDF框架中,有一个“trace.h”头文件,在驱动的入口函数中,会用到


    // Initialize WDF WPP tracing.

    //

    WPP_INIT_TRACING( DriverObject, RegistryPath );

 

    //

    // TraceEvents function is mapped to DoTraceMessage provided by

    // WPP by using a directive in the sources file.

    //

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,

                "Pci9656 Sample - Driver Framework Edition.");</span>


此外在驱动中大量用到“TraceEvents”,取代之前的“KdPrint”,用于打印日志信息。


TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");


TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");



 二、使用


WPP的使用比较简单,WDF的框架代码已将将它初始化好了,我们只需在用到的地方,调用“TraceEvents”  即可。其中,“TraceEvents”的前两个参数分别指定“Level”和“Flag”。


然后,在安装了VS2015的PC上,“开始”->“Windows Kits”->“Windows Software Development Kits”,打开一个文件夹,再在其中的“Tools->X64”目录下找到“TraceView.exe”。


安装它的说明书操作,先“创建一个新的会话”。




然后“Add Provider”,并选择你驱动文件的“.pdb”文件即可。


创建好后,使用应用程序调驱动进行测试,有时会发现traceview中没有反应。这是因为,默认的“Level”设置的是“Error”级别,你需要重新设置,比如设为“Infomation”级别。这个设置的“Level”表示的是最低过滤级。





https://blog.csdn.net/u013218636/article/details/100020874


https://docs.microsoft.com/zh-cn/windows-hardware/drivers/devtest/traceview-overview


https://blog.csdn.net/xiangbaohui/article/details/106424665?utm_medium=distribute.pc_relevant.none-task-blog-title-7&spm=1001.2101.3001.4242


https://blog.csdn.net/Sagittarius_Warrior/article/details/51205607?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control




好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***