Windows驱动开发入门-WDF初探
基本结构
WDF基本对象包括驱动对象WDFDRIVER和设备对象WDFDEVICE。
每个设备驱动都必须有与之对应的唯一的驱动对象,该对象由内核执行体I/O管理器在驱动首次加载时创建,结构定义为:
1 2 3 4 5 6 7
| 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;
|
驱动至少创建一个设备对象,每个设备对象包含指向下一个对象的指针,形成一个设备链。设备对象是能使软件操作硬件的数据结构,包括位于设备堆栈最底层的物理设备对象PDO,和位于PDO之上的功能设备对象FDO。每类总线都有与之对应的驱动,总线驱动检测到新设备时,PnP管理器创建一个PDO,驱动负责创建FDO并挂载到PDO上。过滤设备对象FiDO位于堆栈中FDO的上层或下层。WDF中设备对象由WdfDeviceCreate
创建:
1 2 3 4 5 6 7 8 9
| #include <fltKernel.h> #include <wdf.h> #include <wdfdriver.h> #include <wdfrequest.h> _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FORCEINLINE WdfDeviceCreate( _Inout_ PWDFDEVICE_INIT* DeviceInit, _In_opt_ PWDF_OBJECT_ATTRIBUTES DeviceAttributes, _Out_ WDFDEVICE* Device );
|
一个基本的DriverEntry例程为:
1 2 3 4 5 6 7
| NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath){ WDF_DRIVER_CONFIG config; NTSTATUS status; WDF_DRIVER_CONFIG_INIT(&config,EvtDeviceAdd); status=WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&config,WDF_NO_HANDLE); return status; };
|
驱动初始化完成后,PnP管理器用DeviceAdd例程来完成驱动锁控制设备的初始化工作,包括创建设备对象、创建I/O队列、设置GUID接口、设置事件回调例程等,原型如下:
1 2 3 4 5
| typedef EVT_WDF_DRIVER_DEVICE_ADD *PFN_WDF_DRIVER_DEVICE_ADD; typedef _Function_class_(EVT_WDF_DRIVER_DEVICE_ADD) _IRQL_requires_same_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS EVT_WDF_DRIVER_DEVICE_ADD( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit );
|
一个典型的DriverEntry例程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #ifdef ALLOC_PRAGMA #pragma alloc_text (INIT, DriverEntry) #endif NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath){ WDF_DRIVER_CONFIG config; NTSTATUS status=STATUS_SUCCESS; WDF_OBJECT_ATTRIBUTES attributes; WPP_INIT_TRACING(DriverObject, RegistryPath); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.EvtCleanupCallback = MyEvtDriverContextCleanup; WDF_DRIVER_CONFIG_INIT(&config,MyEvtDeviceAdd); config.EvtDriverUnload=MyEvtDriverUnload; status = WdfDriverCreate(DriverObject,RegistryPath,&attributes,&config,WDF_NO_HANDLE); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status); WPP_CLEANUP(DriverObject); return status; } TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); return status; }
|
其中WDF_DRIVER_CONFIG结构如下:
1 2 3 4 5 6 7
| 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;
|
对于DriverPoolTag成员,标记中每个字符ASCII值必须介于0和127之间。若该值位0,则框架使用驱动内核模式服务名称的前4个字符创建默认池标记。若服务名以“WDF”开头,不区分大小写,则用后4个字符。若可用字符少于4个,则用“FxDr”。
其中WDF_DRIVER_CONFIG_INIT
用于初始化驱动的WDF_DRIVER_CONFIG结构:
1 2 3 4
| VOID FORCEINLINE WDF_DRIVER_CONFIG_INIT( _Out_ PWDF_DRIVER_CONFIG Config, _In_opt_ PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd );
|
当PnP管理器报告设备存在时,驱动的EvtDriverDeviceAdd事件回调函数执行设备初始化操作,原型如下。操作成功返回STATUS_SUCCESS,否则返回Ntstatus.h中定义的错误状态值之一。总线驱动检测到硬件标识符与驱动支持的硬件标识符匹配后,框架将调用驱动的EvtDriverDeviceAdd回调函数。通过提供INF文件来指定驱动支持的硬件ID,操作系统在首次将其中一个设备连接到计算机时使用该文件来安装驱动。
1 2 3 4 5
| typedef EVT_WDF_DRIVER_DEVICE_ADD *PFN_WDF_DRIVER_DEVICE_ADD; typedef _Function_class_(EVT_WDF_DRIVER_DEVICE_ADD) _IRQL_requires_same_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS EVT_WDF_DRIVER_DEVICE_ADD( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit );
|
用WdfDriverCreate
创建一个框架驱动对象,该对象是所有其他对象的父对象。
1 2 3 4 5 6 7
| _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FORCEINLINE WdfDriverCreate( _In_ PDRIVER_OBJECT DriverObject, _In_ PCUNICODE_STRING RegistryPath, _In_opt_ PWDF_OBJECT_ATTRIBUTES DriverAttributes, _In_ PWDF_DRIVER_CONFIG DriverConfig, _Out_opt_ WDFDRIVER* Driver );
|
WDK模板中EvtDriverDeviceAdd回调写法为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, driverstudy3EvtDeviceAdd) #endif NTSTATUS MyEvtDeviceAdd(_In_ WDFDRIVER Driver,_Inout_ PWDFDEVICE_INIT DeviceInit){ NTSTATUS status; UNREFERENCED_PARAMETER(Driver); PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); status = MyCreateDevice(DeviceInit); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); return status; }; #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, MyCreateDevice) #endif NTSTATUS MyCreateDevice(_Inout_ PWDFDEVICE_INIT DeviceInit){ WDF_OBJECT_ATTRIBUTES deviceAttributes; PDEVICE_CONTEXT deviceContext; WDFDEVICE device; NTSTATUS status; PAGED_CODE(); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT); status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device); if (NT_SUCCESS(status)) { deviceContext = DeviceGetContext(device); deviceContext->PrivateDeviceData = 0; status = WdfDeviceCreateDeviceInterface(device,&GUID_DEVINTERFACE_My,NULL); if (NT_SUCCESS(status)) status = MyQueueInitialize(device); } return status; }
|
其中WdfDeviceCreateDeviceInterface
为指定设备创建设备接口:
1 2 3 4 5 6
| #include <wdfdevice.h> _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FORCEINLINE WdfDeviceCreateDeviceInterface( _In_ WDFDEVICE Device, _In_ CONST GUID* InterfaceClassGUID, _In_opt_ PCUNICODE_STRING ReferenceString );
|
创建控制设备
先用WdfControlDeviceInitAllocate
分配一块内存给WDFDEVICE_INIT结构,该结构创建控制设备时用到。
1 2 3 4
| _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) PWDFDEVICE_INIT FORCEINLINE WdfControlDeviceInitAllocate( _In_ WDFDRIVER Driver, _In_ CONST UNICODE_STRING* SDDLString );
|
对于SDDLString参数,可用Wdmsec.h文件中定义的一些常量。一个分配内存的例子为:
1 2 3 4 5
| 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; }
|
接下来为设备绑定一个设备名字,该名字只能被内核模式下代码看到,用户模式下看不到。
1 2 3 4 5
| #define MYWDF_KDEVICE L"\\Device\\MyWDF_Device" RtlInitUnicodeString(&ustring,MYWDF_KDEVICE); status=WdfDeviceInitAssignName(device_init,&ustring); if(!NT_SUCCESS(status)) goto DriverEntry_Complete;
|
其中WdfDeviceInitAssignName
用法如下:
1 2 3 4
| _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FORCEINLINE WdfDeviceInitAssignName( _In_ PWDFDEVICE_INIT DeviceInit, _In_opt_ PCUNICODE_STRING DeviceName );
|
接下来给设备绑定两个回调函数,分别为EvtDeviceFileCreate和EvtFileClose。
1 2
| WDF_FILEOBJECT_CONFIG_INIT(&f_cfg,EvtDeviceFileCreate,EctFileClose,NULL); WdfDeviceInitSetFileObjectConfig(device_init,&f_cfg,WDF_NO_OBJECT_ATTRIBUTE);
|
其中WDF_FILEOBJECT_CONFIG_INIT
用于初始化驱动的WDF_FILEOBJECT_CONFIG结构:
1 2 3 4 5 6
| VOID FORCEINLINE WDF_FILEOBJECT_CONFIG_INIT( _Out_ PWDF_FILEOBJECT_CONFIG FileEventCallbacks, _In_opt_ PFN_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate, _In_opt_ PFN_WDF_FILE_CLOSE EvtFileClose, _In_opt_ PFN_WDF_FILE_CLEANUP EvtFileCleanup );
|
其中WdfDeviceInitSetFileObjectConfig
用于注册事件回调函数,并设置驱动框架文件对象的配置信息:
1 2 3 4 5
| _IRQL_requires_max_(DISPATCH_LEVEL) VOID FORCEINLINE WdfDeviceInitSetFileObjectConfig( _In_ PWDFDEVICE_INIT DeviceInit, _In_ PWDF_FILEOBJECT_CONFIG FileObjectConfig, _In_opt_ PWDF_OBJECT_ATTRIBUTES FileObjectAttributes );
|
接着初始化设备属性并创建设备:
1 2 3 4
| WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs); status=WdfDeviceCreate(&device_init,&object_attribs,&control_device); if(!NT_SUCCESS(status)) goto DriverEntry_Complete;
|
其中WDF_OBJECT_ATTRIBUTES_INIT
用于初始化驱动的WDF_OBJECT_ATTRIBUTES结构:
1 2 3
| VOID FORCEINLINE WDF_OBJECT_ATTRIBUTES_INIT( _Out_ PWDF_OBJECT_ATTRIBUTES Attributes );
|
其中WdfDeviceCreate
创建框架设备对象:
1 2 3 4 5
| _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FORCEINLINE WdfDeviceCreate( _Inout_ PWDFDEVICE_INIT* DeviceInit, _In_opt_ PWDF_OBJECT_ATTRIBUTES DeviceAttributes, _Out_ WDFDEVICE* Device );
|
该函数失败时返回值常见的有:
返回值 |
含义 |
STATUS_INVALID_PARAMETER |
提供无效设备句柄或DeviceInit句柄 |
STATUS_INVALID_DEVICE_STATE |
驱动已为设备创建了设备对象 |
STATUS_INVALID_SECURITY_DESCR |
没为设备对象提供名称 |
STATUS_INSUFFICIENT_RESOURCES |
无法分配设备对象 |
STATUS_OBJECT_NAME_COLLISION |
设备名称已存在 |
然后创建一个符号链接,此时用户模式代码才能找到该设备:
1 2 3 4
| RtlInitUnicodeString(&ustring,MYWDF_LINKNAME); status=WdfDeviceCreateSymbolicLink(control_device,&ustring); if(!NT_SUCCESS(status)) goto DriverEntry_Complete;
|
其中WdfDeviceCreateSymbolicLink
创建指向指定设备的符号链接:
1 2 3 4
| _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS FORCEINLINE WdfDeviceCreateSymbolicLink( _In_ WDFDEVICE Device, _In_ PCUNICODE_STRING SymbolicLinkName );
|
最后完成设备的创建:
1
| WdfControlFinishInitializing(control_device);
|
实战
常规对项目进行设置,并在链接器的附加依赖项中添加“$(DDK_LIB_PATH)\wdmsec.lib”,inf文件内容如下,如需修改就把所有mydrv字样改为工程名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| [Version] Signature = "$WINDOWS NT$" Class = System ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318} Provider = %ManufacturerName% CatalogFile = mydrv.cat DriverVer = PnpLockdown = 1 [DestinationDirs] DefaultDestDir = 13 [SourceDisksNames] 1 = %DiskName%,,,"" [SourceDisksFiles] mydrv.sys = 1,, [Manufacturer] %ManufacturerName% = Standard,NT$ARCH$.10.0...16299 [Standard.NT$ARCH$.10.0...16299] %mydrv.DeviceDesc% = mydrv_Device, Root\mydrv [mydrv_Device.NT] CopyFiles = File_Copy [File_Copy] mydrv.sys [mydrv_Device.NT.Services] AddService = mydrv,%SPSVCINST_ASSOCSERVICE%, mydrv_Service_Inst [mydrv_Service_Inst] DisplayName = %mydrv.SVCDESC% ServiceType = 1 StartType = 3 ErrorControl = 1 ServiceBinary = %13%\mydrv.sys [mydrv_Device.NT.Wdf] KmdfService = mydrv, mydrv_wdfsect [mydrv_wdfsect] KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE = 0x00000002 ManufacturerName = "<Your manufacturer name>" DiskName = "mydrv Installation Disk" mydrv.DeviceDesc = "mydrv Device" mydrv.SVCDESC = "mydrv Service"
|
主代码mydrv.cpp为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #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; EXTERN_C NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS status; WDF_OBJECT_ATTRIBUTES object_attribs; WDF_DRIVER_CONFIG cfg; WDFDRIVER drv = NULL; PWDFDEVICE_INIT device_init = NULL; UNICODE_STRING ustring; WDF_FILEOBJECT_CONFIG f_cfg; WDFDEVICE control_device; PDEVICE_OBJECT dev = NULL; DbgPrint("hello,DriverEntry has started------v2-------\n"); KdPrint(("DriverEntry [start]\n")); WDF_DRIVER_CONFIG_INIT(&cfg, NULL); cfg.DriverInitFlags = WdfDriverInitNonPnpDriver; cfg.DriverPoolTag = (ULONG)'PEPU'; cfg.EvtDriverUnload = EvtDriverUnload; status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &cfg, &drv); if (!NT_SUCCESS(status)) goto DriverEntry_Complete; KdPrint(("Create wdf driver object successfully\n")); 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; } RtlInitUnicodeString(&ustring, MYWDF_KDEVICE); 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)); WDF_FILEOBJECT_CONFIG_INIT(&f_cfg, EvtDeviceFileCreate, EvtFileClose, NULL); WdfDeviceInitSetFileObjectConfig(device_init, &f_cfg, WDF_NO_OBJECT_ATTRIBUTES); WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs); 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); 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")); } VOID EvtDeviceFileCreate(__in WDFDEVICE Device, __in WDFREQUEST Request, __in WDFFILEOBJECT FileObject) { KdPrint(("EvtDeviceFileCreate")); WdfRequestComplete(Request, STATUS_SUCCESS); } VOID EvtFileClose(__in WDFFILEOBJECT FileObject) { KdPrint(("EvtFileClose")); }
|
编译可能会显示Inf2Cat失败,可设置“Use Local Time”为“是”。