WDF驱动开发(2)- CONTEXT和IO QUEUE

来源:互联网 发布:ubuntu 一键lamp 编辑:程序博客网 时间:2024/06/06 01:50

WDF里面,大多数对象都支持自定义的数据,比如给设备对象创建一个context。

对象上下文

先自定义一个结构,比如

typedef struct{WDFQUEUE _DefaultQueue;}DEVICE_CONTEXT;

里面放了一个对象WDFQUEUE. 然后给设备对象创建一个上下文内存块。在使用之前先要声明一下这个结构,相当于告诉框架,我们需要使用一个context。

WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT);

如果不需要context,那么可以这么初始化一个设备属性,WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs);

如果需要context,那么要换个函数,WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&object_attribs, DEVICE_CONTEXT); 这样就会分配一个DEVICE_CONTEXT的内存块,并且将内存块的指针保存到WDF_OBJECT_ATTRIBUTES里面。看一下WDF_OBJECT_ATTRIBUTES的定义:

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;

最后一个成员ContextTypeInfo就指向分配出来的context内存块。

可以根据对象来得到这个context:

dev_ctx = WdfObjectGetTypedContext(control_device, DEVICE_CONTEXT);RtlZeroMemory(dev_ctx, sizeof(DEVICE_CONTEXT));

上面2行代码的意思就是获取设备对象control_device的上下文内存块,并且清零。

 

IO QUEUE,存放WDFREQUEST的队列

WDF提供了一个对象WDFREQUEST, WDFREQUEST其实是对WDM里面IRP的一个封装。WDF还提供了另外一个对象也就是WDFQUEUE,这个队列会存放IRP请求。

我们可以给设备对象control_device创建一个IOQUEUE,比如:(WDFQUEUE存放在了设备对象的上下文里面)

默认队列不能使用WdfIoQueueDispatchManual,不然创建queue会失败。这个我试过。

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&qcfg, WdfIoQueueDispatchParallel);qcfg.PowerManaged = WdfFalse;  //非pnp驱动,也就无需电源管理了.qcfg.EvtIoDefault = EvtIoPDPControlDevice; //创建一个queuestatus = WdfIoQueueCreate(control_device,&qcfg,WDF_NO_OBJECT_ATTRIBUTES,&dev_ctx->_DefaultQueue);  if( !NT_SUCCESS(status) )  { KdPrint(("Create IoQueue failed, %x\n", status));goto DriverEntry_Complete;}

注意, 需要在WdfControlFinishInitializing(control_device);之前创建IOQUEUE. 我试过在WdfControlFinishInitializing(control_device);后面创建,会失败。

在这个例子里面,我们提供了一个EvtIoDefault的回调函数,简单实现一下,就是打印一下WDFREQUEST的type并且完成这个请求。

static VOID EvtIoPDPControlDevice( WDFQUEUE Queue , WDFREQUEST Request ){//WDF_REQUEST_PARAMETERS Params;  WDF_REQUEST_PARAMETERS_INIT(&Params);  WdfRequestGetParameters(Request,&Params);KdPrint(("EvtIoPDPControlDevice, type: %x\n", Params.Type));WdfRequestComplete(Request, STATUS_SUCCESS);}

 

完整代码:

#include <fltKernel.h>#include <wdf.h>#include <wdfdriver.h>#include <wdfrequest.h>#define MYWDF_KDEVICE L"\\Device\\MyWDF_Device"//设备名称,其他内核模式下的驱动可以使用#define MYWDF_LINKNAME L"\\DosDevices\\MyWDF_LINK"//符号连接,这样用户模式下的程序可以使用这个驱动设备。typedef struct{WDFQUEUE _DefaultQueue;}DEVICE_CONTEXT;WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT);//声明回调EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;EVT_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate;EVT_WDF_FILE_CLOSE EvtFileClose;EVT_WDF_IO_QUEUE_IO_DEFAULT EvtIoPDPControlDevice;NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject , IN PUNICODE_STRING RegistryPath ){  NTSTATUS status;WDF_OBJECT_ATTRIBUTES object_attribs;//驱动对象相关WDF_DRIVER_CONFIG cfg;//驱动的配置WDFDRIVER drv = NULL;//wdf framework 驱动对象//设备对象相关PWDFDEVICE_INIT device_init = NULL;UNICODE_STRING ustring;WDF_FILEOBJECT_CONFIG f_cfg;WDFDEVICE control_device;PDEVICE_OBJECT dev = NULL;DEVICE_CONTEXT* dev_ctx = NULL;//IO QUEUE相关WDF_IO_QUEUE_CONFIG qcfg;KdPrint(("DriverEntry [start]\n"));//初始化WDF_DRIVER_CONFIGWDF_DRIVER_CONFIG_INIT(   &cfg,   NULL //不提供AddDevice函数);cfg.DriverInitFlags = WdfDriverInitNonPnpDriver;  //指定非pnp驱动cfg.DriverPoolTag   = (ULONG)'PEPU';  cfg.EvtDriverUnload = EvtDriverUnload;  //指定卸载函数//创建一个framework驱动对象,在WDF程序里面,WdfDriverCreate是必须要调用的。//framework驱动对象是其他所有wdf对象的父对象,换句话说framework驱动对象是wdf对象树的顶点,它没有父对象了。status = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&cfg,&drv);if(!NT_SUCCESS(status)){goto DriverEntry_Complete;}KdPrint(("Create wdf driver object successfully\n"));//创建一个设备//先要分配一块内存WDFDEVICE_INIT,这块内存在创建设备的时候会用到。device_init = WdfControlDeviceInitAllocate(drv,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);  if( device_init == NULL )  {    status = STATUS_INSUFFICIENT_RESOURCES;    goto DriverEntry_Complete;  }//创建设备的名字,内核模式下,名字类似: L"\\Device\\MyWDF_Device"RtlInitUnicodeString(&ustring, MYWDF_KDEVICE);  //将设备名字存入device_init中status = WdfDeviceInitAssignName(device_init,&ustring);if(!NT_SUCCESS(status)){goto DriverEntry_Complete;}KdPrint(("Device name Unicode string: %wZ (this name can only be used by other kernel mode code, like other drivers)\n", &ustring));//配置FILEOBJECT配置文件,设置FILECREATE,FILECLOSE回调。WDF_FILEOBJECT_CONFIG_INIT(&f_cfg,EvtDeviceFileCreate,EvtFileClose,NULL);//将FILEOBJECT的设置存入device_init中WdfDeviceInitSetFileObjectConfig(device_init,&f_cfg,WDF_NO_OBJECT_ATTRIBUTES);//初始化设备属性//WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs);//在设备属性里面增加一个DEVICE_CONTEXT,跟WDF_OBJECT_ATTRIBUTES_INIT相比,WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE//会分配一块内存并且存入WDF_OBJECT_ATTRIBUTES里面 (object_attribs.ContextTypeInfo)。DEVICE_CONEXT是个自定义结构。WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&object_attribs, DEVICE_CONTEXT);//根据前面创建的device_init来创建一个设备. (control device)status = WdfDeviceCreate(&device_init,&object_attribs,&control_device);if(!NT_SUCCESS(status)){KdPrint(("create device failed\n"));goto DriverEntry_Complete;}//得到设备的上下文dev_ctx = WdfObjectGetTypedContext(control_device, DEVICE_CONTEXT);RtlZeroMemory(dev_ctx, sizeof(DEVICE_CONTEXT));//创建IO queue//初始化IO QUEUE CONFIG, WdfIoQueueDispatchParallel意思是//The framework presents requests to the driver's request handlers as soon as the requests are available. WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&qcfg, WdfIoQueueDispatchParallel);qcfg.PowerManaged = WdfFalse;  //非pnp驱动,无需电源管理。qcfg.EvtIoDefault = EvtIoPDPControlDevice; //给设备control_device创建IO QUEUEstatus = WdfIoQueueCreate(control_device,&qcfg,WDF_NO_OBJECT_ATTRIBUTES,&dev_ctx->_DefaultQueue);  if( !NT_SUCCESS(status) )  { KdPrint(("Create IoQueue failed, %x\n", status));goto DriverEntry_Complete;}//创建符号连接,这样用户模式下的程序可以使用这个驱动。这个是必须的,不然用户模式下的程序不能访问这个设备。RtlInitUnicodeString(&ustring,MYWDF_LINKNAME);status = WdfDeviceCreateSymbolicLink(control_device,&ustring);if( !NT_SUCCESS(status) )  {KdPrint(("Failed to create Link\n")); goto DriverEntry_Complete;  }KdPrint(("Create symbolic link successfully, %wZ (user mode code should use this name, like in CreateFile())\n", &ustring));WdfControlFinishInitializing(control_device);//创建设备完成。/*******************************************到这里,我们就成功创建了一个control device。control device 是不支持png和power的,而且我们也不需要手工是删除。因为framework会帮我们删除,看MSDNIf your driver creates control device objects but does not create framework device objects that support PnP and power management, the driver does not have to delete the control device objects. In this case, the framework deletes the control device objects after the driver's EvtDriverUnload callback function returns. 更多细节看MSDN,如http://msdn.microsoft.com/en-us/library/windows/hardware/ff545424(v=vs.85).aspx*******************************************/KdPrint(("Create device object successfully\n"));KdPrint(("DriverEntry succeeds [end]\n"));DriverEntry_Complete:return status;}static VOID EvtDriverUnload( WDFDRIVER Driver ){  KdPrint(("unload driver\n"));KdPrint(("Doesn't need to clean up the devices, since we only have control device here\n"));}/* EvtDriverUnload */VOID EvtDeviceFileCreate( __in WDFDEVICE Device, __in WDFREQUEST Request, __in WDFFILEOBJECT FileObject ){KdPrint(("EvtDeviceFileCreate"));WdfRequestComplete(Request, STATUS_SUCCESS);}VOID EvtFileClose( __in  WDFFILEOBJECT FileObject ){KdPrint(("EvtFileClose"));}static VOID EvtIoPDPControlDevice( WDFQUEUE Queue , WDFREQUEST Request ){//从WDFREQUEST里面获取参数WDF_REQUEST_PARAMETERS Params;  WDF_REQUEST_PARAMETERS_INIT(&Params);  WdfRequestGetParameters(Request,&Params);KdPrint(("EvtIoPDPControlDevice, type: %x\n", Params.Type));WdfRequestComplete(Request, STATUS_SUCCESS);}


简单调用一下:

// MyTest.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <Windows.h>#define MYDEVICE L"\\\\.\\MyWDF_LINK"int _tmain(int argc, _TCHAR* argv[]){HANDLE hDevice = CreateFile(MYDEVICE,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (hDevice == INVALID_HANDLE_VALUE){wprintf(L"Failed to open device %s, err: %x\n", MYDEVICE, GetLastError());}else{wprintf(L"Open device %s successfully\n", MYDEVICE);byte buf[10] = {0};DWORD dwRet = 0;BOOL b = ReadFile(hDevice, buf, 10, &dwRet, NULL);wprintf(L"call ReadFile, ret: %d\n", b);b = WriteFile(hDevice, buf, 10, &dwRet, NULL);wprintf(L"call WriteFile, ret: %d\n", b);CloseHandle(hDevice);wprintf(L"Closed handle\n");}return 0;}


可以看到:


驱动打印出了2个IO, 一个类型是3,一个是4. 查看一下msdn,3确实是READ, 4是WRITE,正确。也就是说用户模式代码引起了驱动调用2次回调EvtIoPDPControlDevice。这正是我们想要的,没有问题。

typedef enum _WDF_REQUEST_TYPE {   WdfRequestTypeCreate                  = 0x0,  WdfRequestTypeCreateNamedPipe         = 0x1,  WdfRequestTypeClose                   = 0x2,  WdfRequestTypeRead                    = 0x3,  WdfRequestTypeWrite                   = 0x4,  WdfRequestTypeQueryInformation        = 0x5,  WdfRequestTypeSetInformation          = 0x6,  WdfRequestTypeQueryEA                 = 0x7,  WdfRequestTypeSetEA                   = 0x8,  WdfRequestTypeFlushBuffers            = 0x9,  WdfRequestTypeQueryVolumeInformation  = 0xa,  WdfRequestTypeSetVolumeInformation    = 0xb,  WdfRequestTypeDirectoryControl        = 0xc,  WdfRequestTypeFileSystemControl       = 0xd,  WdfRequestTypeDeviceControl           = 0xe,  WdfRequestTypeDeviceControlInternal   = 0xf,  WdfRequestTypeShutdown                = 0x10,  WdfRequestTypeLockControl             = 0x11,  WdfRequestTypeCleanup                 = 0x12,  WdfRequestTypeCreateMailSlot          = 0x13,  WdfRequestTypeQuerySecurity           = 0x14,  WdfRequestTypeSetSecurity             = 0x15,  WdfRequestTypePower                   = 0x16,  WdfRequestTypeSystemControl           = 0x17,  WdfRequestTypeDeviceChange            = 0x18,  WdfRequestTypeQueryQuota              = 0x19,  WdfRequestTypeSetQuota                = 0x1A,  WdfRequestTypePnp                     = 0x1B,  WdfRequestTypeOther                   = 0x1C,  WdfRequestTypeUsb                     = 0x40,  WdfRequestTypeNoFormat                = 0xFF,  WdfRequestTypeMax                     = 0x100} WDF_REQUEST_TYPE;



 


 

 

原创粉丝点击