WDF特点

(1)系统兼容,内部磨合了系统、平台间的差异,对外提供了统一的DDI接口
(2)基于对象的框架。 有一个最基本的对象,其他对象都是在这个对象上进行扩展。对象:驱动对象,设备对象,IO对象,队列对象和目标对象。
(3)框架管着所有对象的声明周期。通过引用计数和父子层级关系来维护这个工作。
(4)对框架对象所设计的一套设施,如:上下文空间、同步锁等。使得框架对象容易操作,又有安全保障。
(5)PME接口(property/method/event)-DDI接口
(6)对WDM的完美封装,实现了趋于完美的PNP处理和电源管理状态机(state machine),
(7)处理IO请求更为简便,使用IO对象实现同步、异步处理。未完成的IO请求的取消操作也方便。
引入IO队列,能够轻松实现多个IO请求的串行、并行和手动处理。并且IO队列还支持PNP和电源管理。
(8)轻松切换到WDM模式。获取获取驱动对象、设备对象和IO对象,然后编写WDM代码

WDF_OBJECT:基本成员

对象类型:类型和长度区分不同类型的对象
对象长度:类型和长度区分不同类型的对象
引用计数:维护对象的生命周期
指向parent的指针:parent指针和子对象列表指针维护对象的继承关系,形成对象树。
子对象列表指针:parent指针和子对象列表指针维护对象的继承关系,形成对象树。

引用计数说明:

内核管理器维护内核对象的方法,为每一个对象维护一个引用计数。
PointerCount:真正的引用对象,使用Obrefer.和Obderefer.系列函数进行操作
HandleCount:句柄引用计数,由于内核对象即可以使用指针对象又可以间接的使用句柄访问,因此pointerCount是handle和pinter的总和,打开handle是pointerCount会加1,handlecount也会加1,但是obrefer函数打开是,point热count会加1,但是handleCount不会加1.

wdf框架对象原则:

(1)只能以句柄的方式引用,不能以指针的方式引用
(2)每个框架对象有唯一的句柄,在创建(WdfObjectCreate)时返回,每次的创建操作对应一个删除操作(wdfobjectDelete),删除将导致引用计数减1,并句柄失效。
(3)通过wdfobjectReferencexxx增加因引用计数,通过wdfobjectDereference减少引用计数。

wdf框架对象上下文空间:
(1)每个框架对象都可以拥有上下文空间
(2)每个框架对象都可以有多个上下文空间,每个空间有唯一的类型信息结构体(WDF_OBJECT_CONTEXT_TYPE_INFO)。

上下文申请空间方法:

创建时方法:框架对象被创建的同时申请了上下文空间,是在WDF_OBJECT_ATTRIBUTES结构体进行的
创建步骤:
(1)定义一个结构体,作为上下文空间的内容
(2)告知结构体的长度,定义一个将来用以从框架对象获取上下文空间的地址(步骤1中的结构体变量的指针),类型如下:typedef struct_context * FuncGetContextSpace(WDFOBJECT obj);
一般使用宏来定义这个函数,WDF_DELCARE_CONTEXT_TYPE_WITH_NAME(struct_context,FuncGetContextSpace);或者WDF_DELCARE_CONTEXT_TYPE(struct_context);
(3)设置对象属性,并创建对象。
创建后方法:框架对象被创建以后,申请了上下文空间
创建步骤:
(1)初始化一个WDF_OBJECT_ATTRIBUTES结构体
(2)调用wdfObjectAllocateContext(),并传入对象句柄(已创建者),为它申请上下文空间。

PME接口:

属性和方法:wdf对象的属性和方法混在一起,都是以WDF DDI接口的形式暴露出来,在WDK文档中也没有严格的区分。
每个属性有Get/Retrieve方法,有可能存在Set/Assign方法Get/Set方法,操作必成功,
Retrieve/Assign方法返回NTSTATUS
事件(事件回调):WDF驱动程序,除了DriverEntry函数以外,其他函数都不外事件函数,或者为事件函数调用的子函数。
基本对象WDFOBJECT的事件回调:

通过结构体WDF_OBJECT_ATTRIBUTES结构体进行事件回调,由于WDFOBJECT对象是所有框架对象的组成部分,

所以WDFOBJECT的事件回调是所有框架对象的事件回调。调用函数WdfCreateObject时创建。

可供设置的回调如下:
PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback //wdfObjectDelete时被调用,会改变引用计数
PFN_WDF_OBJECT_CONTEXT_DESTORY EvtDestoryCallback //引用计数为0时被调用

子对象事件回调:
不同类型的子对象有不同种类的事件回调,由于子对象中包含了一个基对象,所以子对象既可以设置公共的基对象回调,也可以设置子对象回调。
NTSTATUS  WdfDeviceCreate(
IN OUT PWDFDEVICE_INIT*  DeviceInit,//设置子对象属性,设置很多回调,包括pnp和电源回调 通过函数WdfDeviceInitSetPnpPowerEventCallbacks 设置
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  DeviceAttributes,//设置基对象属性
OUT WDFDEVICE*  Device//
);

DDI接口:

分析WdfObjectCreate函数,初始化函数数组WdfFunctions,将所有函数指针指向wdf驱动框架驱动程序wdf010000.sys。
每个wdf驱动程序都有一个函数数组WdfFunctions,并不是所有wdf驱动程序公用的。利用WdfFunctions这一点,很容易实现API HOOK。

父子关系:

父子对象之间的关系是具体的,有别于C++的父类、子类。
父对象拥有对子对象的控制权,父对象被销毁前,会先将自己所有的子对象都销毁。
每个驱动都有一个唯一的驱动对象,驱动对象时所有WDF对象的根对象。
框架对象之间的父子关系,并不能随意搭配。

对象同步:

框架对象可能是一个被争抢的资源,必须设置对象同步。
在框架对象内部包含了这种锁,可以使用wdfObjectAcqurieLock和WdfObjectReleaseLock实现手动同步。
使用更加便利的方式,自动同步机制,包括如下内容:
(1)同步范围
设备同步:设备上的所有队列和文件对象在同一时刻只能有一个对象的一个事件回调被执行。
队列同步:同一时刻,只能由一个事件回调被执行。
不选择范围时,即为不同步(WdfSynchornizationScopeNone),同一时刻可以有任意多个事件回调被执行。
若果设定同步范围,则在队列或下属对象的事件调用被执行前,都必须申请设备对象的同步锁。
此属性通过结构体WDF_OBJECT_ATTRIBUTES的SynchorniationScope进行设置。
(2)运行级别
即事件回调最高可在哪个中断级别(IRQL)上被调用。

对于设备对象的PNP/Power事件回调,驱动总是对他们实施同步调用。

驱动对象和设备对象

驱动对象:

其他一切框架对象的父对象,使用WdfGetDriver函数可以随时获取驱动对象句柄。
只要获取驱动对象,按照继承路线可以搜索到所有的子对象。
驱动对象的作用:
(1)代表了加载到系统空间中的驱动模块
(2)在驱动程序的任何地方,调用WdfGetDriver都可以获取驱动对象句柄,建议把全局变量保存在驱动对象中。
(3)对于PNP类驱动,驱动对象负责注册EvtDriverDeviceAdd,类型与wdm的addDevice函数,用以建立设备栈。
(4)对于非PNP类驱动,一般通过驱动对象注册EvtDriverUnload函数,类型wdm的DriverUnload函数。
(5)可以为驱动初始化一个事件跟踪(WPP机制)

驱动入口DriverEntry:

根据驱动类型,入口函数有多种写法。主要分为:设备驱动、过滤驱动和纯软件驱动。
设备驱动一定要注册EvtDriverDeviceAdd函数。
过滤驱动若要绑定某个物理设备,同样需要注册EvtDriverDeviceAdd。否则不可注册。
纯软件驱动不可注册EvtDriverDeviceAdd。
创建驱动对象API:
NTSTATUS
 WdfDriverCreate(
IN PDRIVER_OBJECT  DriverObject,
IN PCUNICODE_STRING  RegistryPath,
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  DriverAttributes,
IN PWDF_DRIVER_CONFIG  DriverConfig,
OUT OPTIONAL WDFDRIVER*  Driver
);
参数涉及的结构体:
typedef struct _WDF_OBJECT_ATTRIBUTES {
 ULONG  Size;
 PFN_WDF_OBJECT_CONTEXT_CLEANUP  EvtCleanupCallback;
 PFN_WDF_OBJECT_CONTEXT_DESTROY  EvtDestroyCallback;
 WDF_EXECUTION_LEVEL  ExecutionLevel;
 WDF_SYNCHRONIZATION_SCOPE  SynchronizationScope;
 WDFOBJECT  ParentObject;
 size_t  ContextSizeOverride;
 PCWDF_OBJECT_CONTEXT_TYPE_INFO  ContextTypeInfo;
} WDF_OBJECT_ATTRIBUTES, *PWDF_OBJECT_ATTRIBUTES;
typedef struct _WDF_DRIVER_CONFIG {
 ULONG  Size;
 PFN_WDF_DRIVER_DEVICE_ADD  EvtDriverDeviceAdd;
 PFN_WDF_DRIVER_UNLOAD  EvtDriverUnload;
 ULONG  DriverInitFlags;//设置标志
 ULONG  DriverPoolTag;
} WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG;

DriverInitFlags的设置:

typedef enum _WDF_DRIVER_INIT_FLAGS {
WdfDriverInitNonPnpDriver = 0x00000001,//决定_WDF_DRIVER_CONFIG的2个回调是否有效
WdfDriverInitNoDispatchOverride = 0x00000002,//标志驱动程序是一个小端口驱动,一旦设置了小端口驱动标志后,WDF框架将不会用其内部分发的函数来处理收到的IRP,而是任由wdf驱动自己继续调用小端口框架的初始化函数来为其设置分发函数。
WdfVerifyOn = 0x00000004,
WdfVerifierOn = 0x00000008
} WDF_DRIVER_INIT_FLAGS;
PNP类型的DriverEntry:

NTSTAT DriverEntry(PDRIVER DriverObject,PUNICODE_STRING pRegistry)
{
WDF_DRIVER_CONFIG config;
NTSTATUS  status = STATUS_SUCCESS;

WDF_DRIVER_CONFIG_INIT(&config,MyEvtDeviceAdd);
config.EvtDriverUnload = myEvtDriverUnload;

return wdfDeviceCreate(DriverObject,pRegistry,WDF_NO_OBJECT_ATTRIBUTES,&config,WDF_NO_HANDLE);

}

非PNP类型的驱动:
VOID UnlLoad(WDFDRIVER Driver)
{
}

NTSTATUS DriverEntry(PDRIVER pDriverObject,PUNICODE_STRING pRegistry)
{
NTSTATUS status = STATUS_SUCCESS;
WDF_DRIVER_CONFIG config;
WDFDRIVER driver;

WDF_DRIVER_CONFIG_INIT(&config,NULL);
config.DriverInitFlags = WdfDriverInitNonPnpDriver;

//一般会设置EvtDriverUnload事件
//PNP类驱动的后处理,在诸如EvtDeviceD0Exit(断电)、EvtDrviceHardware(移除设备)这类pnp事件中进行。
config.EvtDriverUnload = UnLoad;

return WdfDriverCreate(pDriverObject,pRegistry,NULL,&config,&driver);
}

设备对象:

不同的设备对象类型:
功能设备对象FDO: 功能驱动程序为每一个设备创建一个FDO,在设备栈中位于物理设备对象(FDO)的上层。
物理设备对象PDO:一个总线驱动创建的PDO,在逻辑上代表了物理设备本身,而功能设备FDO代表了系统针对这个PDO所做的处理。
过滤设备对象Filter DO:可以对设备栈中任何一个存在的设备进行过滤
控制设备对象CDO:一般不存在设备栈中,而是一个独立的设备。作为用户程序的接口,通过IO请求进行交互。

设备对象属性:
很对属性,通过WdfDeviceSetXXX、WdfDeviceInitXXX来设置,通过WdfDeviceGetXXX系列函数获取。

创建设备对象:
举例创建CDO设备,在非PNP驱动中在DriverEntry函数内创建,在PNP类驱动中,最好在EvtDriverDeviceAdd事件中添加

WDFDEVICE wdfDevice;
NTSTATUS DriverEntry(PDRIVER pDriverObject,PUNICODE_STRING pRegistry)
{
NTSTATUS status = STATUS_SUCCESS;
WDFDRIVER driver;
PWDFDEVICE_INIT deviceInit;
PDEVICE_OBJECT deviceObject;

//创建框架驱动对象 略
WdfDriverCreate();

//申请一个设备初始化结构体
deviceInit = WdfControlDeviceInitAllocate(
driver,
&SDDL_DEVOBJ_KERNEL_ONLY);

//设置设备特征
 WdfDeviceInitSetCharacteristics(deviceInit,
 FILE_DEVICE_SECURE_OPEN,
 FALSE);

//创建一个框架设备对象

status = WdfDeviceCreate(
deviceInit,
WDF_NO_OBJECT_ATTRIBUTES,
&wdfDevice);

//检查状态
if(NT_STATUS(status))
{
//设置框架设备对象的初始化完成
WdfControlFinishInitializing( wdfDevice );

//获得wdm的设备对象
deviceObject =   WdfDeviceWdmGetDeviceObject(wdfDevice);

}

return status;
}

设备栈:

设备栈并没有因为WDF框架的加入而有变化,在IRP包在设备栈中的流动有所改变。
wdf类型的驱动的IRP在每一个驱动中FDO将IRP封装为WDFREQUEST传递给本驱动中的WDF设备,由WDF设备从WDFREQUEST中解析出IRP下发给底层驱动。
若要控制WDF驱动中IRP的流向,比如不让IRP流入到wdf设备上可使用WdfDeviceInitAssignWdmIrpPreprocessCallback 注册一个名为EvtDeviceWdmIrpPreprocess 的事件函数,在事件中按照WDM的方式处理IRP,包括传递到下层设备或者完成IRP

IO模型:

WDF框架收到IRP后,判断此请求是直接在框架内处理,还是调用WDF注册的事件回调,然后交回驱动处理。
若调用注册的事件回调, 需要将IRP封装成WDFREQUEST。
所有的WDFREQUEST都是从框架中创建并流向WDF驱动的,可以理解为框架和驱动是一对一的关系,框架对一切都有很好的规划,减轻驱动的负担。
IO目标对象:
WDFDEVICE对象仅仅保存了一个指向DEVICE_OBJECT的指针,在WDF模型中,WDFDEVICE与DEVICE_OBJECT是一一对应的,为的是可以通过DEVICE_OBJECT逆推出WDFDEVICE,但是存在不足之处:在另一个驱动(Driver2)中发送命令给当前驱动(Driver1)的设备对象。(??)
框架对象不能再驱动之中传递。
解决此问题,需要使用WDFIOTARGET,首先有一个本地WDFIOTARGET(默认WDFIOTARGET),这个是唯一的,是在创建WDFDEVICE的时候顺便也创建了它,并进行了二者的一一关联。可以有0个或者多个远程WDFIOTARGET,当别的驱动程序或者本驱动程序的其他地方(若果驱动中存在多个设备栈)要发送命令到此设备时,可以通过远程WDFIOTARGET完成。
总结:
(1)框架设备对象(WDFDEVICE)是对底层设备的封装,并唯一对应。
(2)io目标对象是对框架设备对象(WDFDEVICE)的封装,可以有多个io目标对象对应同一个框架设备对象。
(3)框架为WDFDEVICE对象创建唯一的本地(默认)目标对象
(4)WDF驱动可以创建多个远程目标对象。
(5)每一个目标对象都有一个内部队列,发送到目标对象的IO请求都在队列中等候,最后发送给设备对象处理。

IO目标对象的细节:

IO目标是对io请求发送到的目标驱动的描述。比如:向另一个驱动中的设备发送IOCTL命令,这个远程设备对象就是IOCTL命令的IO目标。
用处:
(1)请求以同步方式发送到IO目标对象
(2)请求以异步方式发送到IO目标对象
(3)为发送的IO目标的请求设置超时,过了超时则取消。
(4)IO目标对象可以被启动、停止、关闭、只有启动后的IO目标才可以接收请求,否则将关闭外界请求通道。
(5)跟踪并维护发送的IO目标的IO请求。比如删除目标对象后,所有排队于其中的IO请求对象都将被删除。

调用WdfIoTargetGetDevice函数可以获取被封装的设备对象句柄。WDFDEVICE   WdfIoTargetGetDevice(IN WDFIOTARGET  IoTarget);
IO目标对象分为普通对象和特殊对象两种,普通对象就是WDFIOTARGET,特殊目标对象目前只有USB IO目标对象一种,USB IO目标对象没有一个对应的类型被暴露出来,而是隐藏在诸如WDFUSBDEVICE、WDFUSBPIPE这些对象内部,通过这些对象的句柄来使用特殊IO目标对象。
可以直接从设备对象获取WDFIOTARGET, WDFIOTARGET  WdfDeviceGetIoTarget( IN WDFDEVICE  Device);此函数获得的WDFIOTARGET对象为设备对象的本地(默认)WDFIOTARGET对象,凡是发往此设备上的WDFREQUEST对象,若要在设备栈中继续传递,则应该讲请求发送到此本地目标对象。默认的回调函数如下:

VOID SomeEvtCallback(WDFDEVICE Device,WDFREQUEST Request)
{
//让IO请求在当前设备栈中继续向下传递
WdfRequestSend(Request,WdfDeviceGetIoTarget(Device),NULL);
}

使用一个远程IO目标对象:

(1)创建远程IO目标对象
(2)打开此对象

NTSTATUS
  WdfIoTargetCreate( //创建
    IN WDFDEVICE  Device,
    IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  IoTargetAttributes,
    OUT WDFIOTARGET*  IoTarget
    );

//挂钩
VOID FORCEINLINE
  WDF_IO_TARGET_OPEN_PARAMS_INIT_CREATE_BY_NAME(
    OUT PWDF_IO_TARGET_OPEN_PARAMS  Params,
    IN PUNICODE_STRING  TargetDeviceName,
    IN ACCESS_MASK  DesiredAccess
    );

//打开
NTSTATUS
  WdfIoTargetOpen(
    IN WDFIOTARGET  IoTarget,
    IN PWDF_IO_TARGET_OPEN_PARAMS  OpenParams
    );

WDFIOTARGET有一个状态标志,表示处理启动、停止、终止的状态。只有在启动的状态下发送给它的IO请求才可以被处理。否则根据策略觉得被抛弃或者放入队列等待启动后处理。
NTSTATUS   WdfIoTargetStart(    IN WDFIOTARGET  IoTarget    );//启动
VOID  WdfIoTargetStop(    IN WDFIOTARGET  IoTarget,    IN WDF_IO_TARGET_SENT_IO_ACTION  Action    );  //停止
VOID  WdfIoTargetClose(    IN WDFIOTARGET  IoTarget    );//关闭
WDF_IO_TARGET_STATE  WdfIoTargetGetState(    IN WDFIOTARGET  IoTarget    );//获取当前IOTarget对象的状态。

IOTarget对象的状态
typedef enum _WDF_IO_TARGET_STATE {
  WdfIoTargetStateUndefined = 0, //无效状态-框架内部使用
  WdfIoTargetStarted,//已经启动
  WdfIoTargetStopped,//已经被停止
  WdfIoTargetClosedForQueryRemove,//目标对象所代表的设备对象请求被移除
  WdfIoTargetClosed,//目标对象所代表的设备已经移除,可以通过WdfIoTargetOpen再次打开
  WdfIoTargetDeleted,//目标设备所代表的设备已经被删除(delete),目标设备和设备对象都不再可用。
} WDF_IO_TARGET_STATE, *PWDF_IO_TARGET_STATE;

安全缓冲区:

在wdf框架中,所有的io请求对象都使用了WDFMEMORY对象来表示输入\输出缓冲区。有一下特点:
(1)内部维护三个数据:内存区指针,内存区长度,有效长度(字符串长度)
(2)维护内存区的生命周期:若果内存区是框架申请的,则框架负责内存区的释放,如果是wdf驱动创建的,则驱动自己负责释放。
(3)可以使用内存区的任意部分,通过指定一个偏移(offset)来实现。
创建:
(1)由框架申请空间
(a)由内存池中申请
NTSTATUS
 WdfMemoryCreate(
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  Attributes,
IN POOL_TYPE  PoolType,
IN OPTIONAL ULONG  PoolTag,
IN size_t  BufferSize,
OUT WDFMEMORY*  Memory,
OUT OPTIONAL PVOID*  Buffer
);
内存区将在WdfObjectDelete()的时候一起销毁。

(b)从旁视列表中申请。
NTSTATUS
 WdfMemoryCreateFromLookaside(
IN WDFLOOKASIDE  Lookaside, //WdfLookasideListCreate创建
OUT WDFMEMORY*  Memory
);

(2)预先申请好内存,再交给框架封装。
NTSTATUS
 WdfMemoryCreatePreallocated(
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  Attributes,
IN PVOID  Buffer,
IN size_t  BufferSize,
OUT WDFMEMORY*  Memory
);//创建的内存,但不会维护,调用WdfObjectDelete后不会释放。

NTSTATUS
  WdfMemoryAssignBuffer( //替换对象的内存缓冲区
IN WDFMEMORY  Memory,
IN PVOID  Buffer,
IN size_t  BufferSize
);
内存对象和内存缓冲区是一一对应的,通过以下函数可以获取内存对象的内存区指针。
PVOID
 WdfMemoryGetBuffer(
IN WDFMEMORY  Memory,
OUT OPTIONAL size_t*  BufferSize
);

使用:
NTSTATUS
 WdfMemoryCopyFromBuffer(//写入
IN WDFMEMORY  DestinationMemory,
IN size_t  DestinationOffset,
IN PVOID  Buffer,
IN size_t  NumBytesToCopyFrom
);

NTSTATUS
 WdfMemoryCopyToBuffer(//读出
IN WDFMEMORY  SourceMemory,
IN size_t  SourceOffset,
IN PVOID  Buffer,
IN size_t  NumBytesToCopyTo
);

内存对象(一)

IRP中的用户缓冲区有3中类型:METHOD_BUFFERED、METHOD_DIRECT、METHOD_NEITHER。
对于METHOD_BUFFERED、METHOD_DIRECT两种类型的缓冲类型的IO请求,使用以下方法获取对应的内存对象。

NTSTATUS
WdfRequestRetrieveInputMemory(//获取输入缓冲区所对应的内存对象
    IN WDFREQUEST  Request,
    OUT WDFMEMORY*  Memory
    );

NTSTATUS
  WdfRequestRetrieveOutputMemory(获取输出缓冲区所对应的内存对象
    IN WDFREQUEST  Request,
    OUT WDFMEMORY*  Memory
    );
返回值表示操作是否成功,若不满足以下3个条件会返回STATUS_INVALID_DEVICE_REQUEST
(1)对于Input函数,只有写、设备IO控制两种命令才可以调用。对于Output函数,只有读、设备IO控制码两种命令才可以调用。io控制码是IRP_MJ_DEVICE_CONTROL IRP_MJ_DEVICE_INTERNAL_CONTROL
(2)必须保证IO命令的缓冲方式为METHOD_BUFFERED\METHOD_DIRECT
(3)当IO命令的缓冲方式为METHOD_NEITHER时,必须保证缓冲区指针指向系统空间,而不是用户空间。即IO请求来自内核模块,而不是用户模块。IO的请求码为IRP_MJ_DEVICE_INTERNAL_CONTROL

可以通过WDFMOMORY获取缓冲区指针:
NTSTATUS
 WdfRequestRetrieveInputBuffer(
IN WDFREQUEST  Request,
IN size_t  MinimumRequiredSize,
OUT PVOID*  Buffer,
OUT size_t*  Length
);

NTSTATUS
 WdfRequestRetrieveOutputBuffer(
IN WDFREQUEST  Request,
IN size_t  MinimumRequiredSize,
OUT PVOID*  Buffer,
OUT size_t*  Length
);

可以通过WDFMOMORY获取MDL指针://驱动对象不必为MDL指针调用IoFreeMdl(),应由框架自己维护。
NTSTATUS
 WdfRequestRetrieveInputWdmMdl(
IN WDFREQUEST  Request,
OUT PMDL*  Mdl
);

NTSTATUS
 WdfRequestRetrieveOutputWdmMdl(
IN WDFREQUEST  Request,
OUT PMDL*  Mdl
);

内存对象(二)

IO请求的缓冲方式为METHOD_NEITHER且IO请求来自于用户程序。
(1)获取用户输入输出缓冲区的指针:
NTSTATUS
 WdfRequestRetrieveUnsafeUserInputBuffer(
IN WDFREQUEST  Request,
IN size_t  MinimumRequiredLength,
OUT PVOID*  InputBuffer,
OUT OPTIONAL size_t*  Length
);
NTSTATUS
 WdfRequestRetrieveUnsafeUserOutputBuffer(
IN WDFREQUEST  Request,
IN size_t  MinimumRequiredLength,
OUT PVOID*  OutputBuffer,
OUT OPTIONAL size_t*  Length
);
(2)锁定输入输出内存
NTSTATUS
 WdfRequestProbeAndLockUserBufferForRead(
IN WDFREQUEST  Request,
IN PVOID  Buffer,
IN size_t  Length,
OUT WDFMEMORY*  MemoryObject
);
NTSTATUS
  WdfRequestProbeAndLockUserBufferForWrite(
IN WDFREQUEST  Request,
IN PVOID  Buffer,
IN size_t  Length,
OUT WDFMEMORY*  MemoryObject
);
(3)为此IO请求对象申请上下文空间,并把得到的内存对象保存到IO请求对象的上下文空间中
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(_attributes, _contexttype)
NTSTATUS
 WdfObjectAllocateContext(
IN WDFOBJECT  Handle,
IN PWDF_OBJECT_ATTRIBUTES  ContextAttributes,
OUT OPTIONAL PVOID*  Context
);
Context->inMem = xxxInMem;
Context->outMem = xxxOutMem;
(4)令IO请求入队,如果IO是其他类型可直接入队
NTSTATUS
 WdfDeviceEnqueueRequest(
IN WDFDEVICE  Device,
IN WDFREQUEST  Request
);

框架和IO请求

框架关系

IO请求参数

从IO_STACK_LOCATION中可以找到各种IRP命令子结构体的变量,但在WDFREQUEST中只对部分IRP类型进行了封装,所有只用到了IO_STACK_LOCATION的一个子集。
使用DDI接口可以从WDFREQUEST对象中获得WDFREQUEST对象类型相关的结构体。
VOID
 WdfRequestGetParameters(
IN WDFREQUEST  Request,
OUT PWDF_REQUEST_PARAMETERS  Parameters
  );

队列:

在WDM模型中有时也需要维护一个队列,基本上都是使用LIST_ENTRY创建一个双向链表来使用。
而WDF框架借助队列,实现对IO请求的高效管理。
typedef enum _WDF_IO_QUEUE_DISPATCH_TYPE {
 WdfIoQueueDispatchSequential  = 1, //串行队列实现序列化、一个处理完才能处理下一个
 WdfIoQueueDispatchParallel  = 2,//并行队列,以最快的速度处理,不必等前面的对象被处理完。
 WdfIoQueueDispatchManual  = 3,//手动队列,IO请求入队以后,框架不会理会,wdf驱动在需要的时候自己从队列中取出并处理
 WdfIoQueueDispatchMaximum//无效值
} WDF_IO_QUEUE_DISPATCH_TYPE;
针对队列可以进行PNP/Power配置,发生PNP/Power事件时,可配置以什么方式处理队列中的IO请求。
还可配置一个cancel回调,当上层驱动取消相关请求时被调用。

WDF的一个主要的特点:所有的东西被封装成对象,而所有的操作被定义成事件(event)或回调(Callback)。WDF对每个框架对象都维护一个引用计数,这样就能有效的控制生命周期。

创建IRP请求

目前框架只对5中IRP请求进行WDFREQUEST对象封装,但实际上WDFREQUEST可以封装任何IRP请求。WDF驱动程序除了使用框架通过事件回调传递的WDFREQUEST以外,还可以自己新建任意类型的WDFREQUEST对象。
创建:
方法(1):创建空对象,然后调用格式化函数、将对象格式化为指定类型的命令。
NTSTATUS   WdfRequestCreate( //创建
    IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  RequestAttributes,
    IN OPTIONAL WDFIOTARGET IoTarget,
    OUT WDFREQUEST*  Request
    );

//格式化函数
WdfIoTargetFormatRequestForInternalIoctl
WdfIoTargetFormatRequestForInternalIoctlOthers
WdfIoTargetFormatRequestForIoctl
WdfIoTargetFormatRequestForRead
WdfIoTargetFormatRequestForWrite

方法(2):创建WDFREQUEST对象的方法是直接调用WdfRequestCreateFromIrp,选择从一个已有的IRP结构体进行封装。
NTSTATUS
 WdfRequestCreateFromIrp(
IN OPTIONAL PWDF_OBJECT_ATTRIBUTES  RequestAttributes,
IN PIRP  Irp,
IN BOOLEAN  RequestFreesIrp,
OUT WDFREQUEST*  Request
);

WDF驱动创建WDFREQUEST对象,必须最后调用WdfObjectDelete方法进行删除。
但如果对象是由框架通过参数传递给WDF驱动的,不必调用WdfObjectDelete方法。

发送请求:

BOOLEAN
  WdfRequestSend(
    IN WDFREQUEST  Request,
    IN WDFIOTARGET  Target,
    IN OPTIONAL PWDF_REQUEST_SEND_OPTIONS  RequestOptions
    );
参数3为以下的结构体:
typedef struct _WDF_REQUEST_SEND_OPTIONS {
IN ULONG Size;
IN ULONG Flags;
IN LONGLONG Timeout;
} WDF_REQUEST_SEND_OPTIONS, *PWDF_REQUEST_SEND_OPTIONS;
Flags的重要标志:
WDF_REQUEST_SEND_OPTION_TIMEOUT //超时标志,判断超时Timeout后WdfRequestSend才返回
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS //同步标志,完成后WdfRequestSend才返回

PNP和电源模型

WDF对电源和PNP的设计方式是,进行最基本的处理,框架向程序提供回调接口,如果程序注册了某个回调,则框架在对应的状态变化是调用此回调,否则使用默认的处理方式。

WDF模型驱动-初稿相关推荐

  1. 机器学习(二十三)——Beam Search, NLP机器翻译常用评价度量, 模型驱动 vs 数据驱动

    https://antkillerfarm.github.io/ Beam Search Beam Search(集束搜索)是一种启发式图搜索算法,通常用在图的解空间比较大的情况下,为了减少搜索所占用 ...

  2. 2010年9月blog汇总:敏捷个人和模型驱动开发

    9月份指标产品开发开始同时进行两个客户的开发,所以考虑了客户化如何开发的问题:在企业定额产品上,参与清单综合单价库的产品架构并做了用户调研前期准备工作:再就是整理了一下模型驱动开发理论以及思考了Ope ...

  3. [翻译] DSL和模型驱动开发的最佳实践(3/4)

      哪个是最佳选择       有两种风格的语言设计:一种主张大语言,用一个类支持许多不同的领域概念.另外一种主张小语言,使用一些小但是强大的原始的特征,大一些的特征由库的方式组装构建.      在 ...

  4. MES/MOM的未来:低代码与模型驱动

    来源:佰思杰本文约6000字,建议阅读10分钟本文将深度分析低代码.模型驱动的关系,以及如何支撑MES/MOM的未来. 01 低代码与模型驱动 笔者认为,"低代码"几乎是" ...

  5. 展望:模型驱动的深度学习

    来源:<国家科学评论> 概要:近年来,深度学习在人工智能领域一系列困难问题上取得了突破性成功应用. 模型驱动的深度学习方法 近年来,深度学习在人工智能领域一系列困难问题上取得了突破性成功应 ...

  6. 简议使用业务模型驱动进行软件的设计

    在我工作的这些年里,前几年做的大多项目都是数据库驱动型的,我想很多人也都是这样的.对于数据库驱动型的项目,我们的核心都是围绕数据库在做开发,通常我们都在写CRUD的代码,后来有了代码生成器.ORM,我 ...

  7. MDSF:在线查看【模型驱动软件工厂】文章汇总

    OpenExpressApp是我前年开始写的一个框架,最近没有怎么更新,有些关心的朋友还问我进度以及是否停止了,在这里我想和大家说的是,OEA还一直在做着,我的目标并没有因为我的精力分布较多而改变,O ...

  8. MDA:模型驱动架构 简介

    MDA:模型驱动架构 2011-04-18 23:30 模型驱动架构(MDA)是一种独立于特定平台和软件供应商的软件体系结构设计和开发方法,它适用于设计.部署.集成等软件开发的整个生命周期. MDA ...

  9. WDF驱动中KMDF与UMDF区别

    众所周知, 早期的Windows 95/98的设备驱动是VxD(Virtual Device Driver),其中x表示某一类设备.从Windows 2000开始,开发驱动程序必以WDM(Window ...

最新文章

  1. javascript刷新页面的集中办法
  2. Hadoop学习笔记—18.Sqoop框架学习
  3. python nonetype转换float_如何在Python中将NoneType值从聚合转换为float?
  4. VTK:隐式选择循环用法实战
  5. Eclipse下搭建C语言开发环境
  6. js学习 字符串常用方法
  7. bom实现方块移动_html5实现简单的拼图小游戏
  8. 如何用Linux搭建家庭云服务,使用ownCloud在Linux安装你的个人云服务 私有云的搭建...
  9. Arturia Analog Lab V for Mac - 超强键盘模拟合成器
  10. FastAPI用户安全性解决方案
  11. 【雷达】Tracking radar targets with multiple reflection points
  12. 使用STM32CubeMX创建USB MSC工程
  13. 十一、Spring Boot整合Redis(一)
  14. 2023年最新前端面试题——你也可以成为那个卷王(持续更新中~)
  15. 06-初始OpenGL ES -用GLSL实现画板的功能
  16. 第三章 硬件描述语言verilog(一)
  17. 2012-2020年全国大学生数学建模竞赛的国家一等奖论文
  18. GUI程序中的Matplotlib绘图
  19. 苏杰的产品创新课/图书/企业服务,双11价格确实便宜
  20. python装饰器特性iy雾_扣丁学堂简述Python 装饰器装饰类中的方法

热门文章

  1. C语言编码助手 1.9发布
  2. 利用CSS 3 的动画相关属性制作轮播图特效
  3. 页面Http状态查询工具说明
  4. 揭开浮动布局的秘密(简略版)
  5. leetcode买卖股票最佳时机相关问题分析
  6. Flowable完整安装使用流程
  7. mac 更改所有文件夹显示选项的方法
  8. 本地服务注册不上nacos_Nacos如何实现服务自动注册
  9. 十二、tars 服务,java tars服务client端
  10. 电脑不显示输入密码界面