最近因为工作关系,接触了一下WDF驱动开发。
WDF驱动其实是微软公司提供的一套驱动开发的框架。有了这个框架之后,开发驱动会简单一些。WDF本身是从WDM基础上封装而成的。WDF里面封装了很多对象,如WDFDRIVER等。如果要学习使用WDF来开发驱动,个人感觉还是需要WDM的一些基础,不然很多东西挺难理解的。
写了一个简单的WDF驱动(非pnp),基本步骤如下:
创建framework 驱动对象
几乎任何一个WDF驱动一开始就要创建一个framework的驱动对象,这个对象是所有其他对象的parent对象。
//初始化WDF_DRIVER_CONFIG WDF_DRIVER_CONFIG_INIT( &cfg, NULL //不提供adddevice函数 ); cfg.DriverInitFlags = WdfDriverInitNonPnpDriver; //非pnp驱动 cfg.DriverPoolTag = (ULONG)'PEPU'; cfg.EvtDriverUnload = EvtDriverUnload; //卸载函数 // //创建一个framework的驱动对象。 status = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&cfg,&drv); if(!NT_SUCCESS(status)) { goto DriverEntry_Complete; } KdPrint(("Create wdf driver object successfully\n"));
这样,一个framework的驱动对象就创建好了。
创建一个设备(control device)
1. 要创建一个control device,首先得先分配一块内存
//分配一块内存 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; }
WdfControlDeviceInitAllocate函数分配了一块内存给结构WDFDEVICE_INIT,这个结构在创建control device的时候会被用到。更多细节看MSDN:http://msdn.microsoft.com/en-us/library/windows/hardware/ff545841(v=vs.85).aspx
2. 然后给这个设备绑定一个设备名字,注意这个设备名字只能被内核模式下的代码所看到,比如其他的内核驱动,用户模式代码是看不到的。
就好象是WDM里面的IoCreateDevice函数的第三个参数。
//设备名字,如: 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));
3. 给设备绑定2个回调。
如
//设置2个回调,FileCreate和FileClose WDF_FILEOBJECT_CONFIG_INIT(&f_cfg,EvtDeviceFileCreate,EvtFileClose,NULL); //存入DEVICE_INIT结构中 WdfDeviceInitSetFileObjectConfig(device_init,&f_cfg,WDF_NO_OBJECT_ATTRIBUTES);
这样当用户模式的代码调用CreateFile和CloseHandle的时候,这2个回调会被调用。
4. 初始化设备属性并且创建设备
这里创建一个control device
WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs); //创建一个设备. (control device) status = WdfDeviceCreate(&device_init,&object_attribs,&control_device); if(!NT_SUCCESS(status)) { KdPrint(("create device failed\n")); goto DriverEntry_Complete; }
5. 创建一个符号连接
有了这个符号连接,用户模式代码才能找到这个设备。就好像WDM里面IoCreateSymbolicLink()做的事情。
//创建一个符号连接 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));
6. 完成设备的创建
WdfControlFinishInitializing(control_device);
到这里,一个设备就创建成功了。这是一个control device,有关control device可以查看http://msdn.microsoft.com/en-us/library/windows/hardware/ff545424(v=vs.85).aspx
framework驱动对象的创建和设备对象的创建都发生在DriverEntry()里面。
回调函数的实现
接下来就是回调函数的实现,这里就写了几行简单的代码。
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")); }
只有在FileCreate里面写了一行代码直接将irp请求完成。
这样一个最最简单的WDF驱动就写完了。
完整代码
#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"//符号连接,这样用户模式下的程序可以使用这个驱动设备。 //声明回调 EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; EVT_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate; EVT_WDF_FILE_CLOSE EvtFileClose; 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; KdPrint(("DriverEntry [start]\n")); //初始化WDF_DRIVER_CONFIG WDF_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_init来创建一个设备. (control device) status = WdfDeviceCreate(&device_init,&object_attribs,&control_device); if(!NT_SUCCESS(status)) { KdPrint(("create device failed\n")); 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会帮我们删除,看MSDN If 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")); }
用DDK编译一下就可以了,顺便贴一下sources里面的内容
TARGETNAME=mywdf TARGETTYPE=DRIVER DRIVERTYPE=UNKNOWN KMDF_VERSION_MAJOR=1 KMDF_VERSION_MINOR=9 UMDF_MINOR_VERSION=9 WINDDK=c:\winddk\7600.16385.0 TARGETLIBS= $(DDK_LIB_PATH)\Rtlver.lib \ $(DDK_LIB_PATH)\Wdmsec.lib #C_DEFINES=$(C_DEFINES) INCLUDES=$(WINDDK)\inc\wdf\kmdf\$(KMDF_VERSION_MAJOR)$(KMDF_VERSION_MINOR);..\ SOURCES = \ mywdf.c
这样就得到了一个mywdf.sys,然后安装。一直以来,驱动的安装总是让人那么纠结。一堆的配置inf,好在已经有很多现成的了。网上搜索就可以搜到一堆,这里就不在介绍了。
安装完驱动后,这个驱动有什么用呢?其实啥用都没有,但是可以写几行代码证实一下。如:
#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); CloseHandle(hDevice); wprintf(L"Closed handle\n"); } return 0; }
这是个用户模式代码,运行一下,就会发现
用debugview看一下驱动的输出,看到了驱动里面的log,这就说明用户模式的代码CreateFile和CloseHandle成功的引起驱动调用2个回调。