WDF框架学习 WDF是什么? Windows Driver Framework (WDF) 是微软提供的一套用于开发Windows设备驱动程序的框架。WDF分为两个子框架:
KMDF (Kernel-Mode Driver Framework):用于开发内核模式的驱动程序。 UMDF (User-Mode Driver Framework):用于开发用户模式的驱动程序。 WDF框架简化了驱动程序开发的复杂性,提供了丰富的API和工具,使得开发者能够更专注于业务逻辑的实现,而不是底层驱动的细节。
总之,将WDF理解为对WDM做了封装,类似于SDK和MFC之间的关系
第一节:第一个WFP驱动程序: 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 #include <ntddk.h> #include <wdf.h> #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) void MyDriverUnload ( _In_ WDFDRIVER Driver ) { Driver; DebugPrint ("MyDriverUnload: Driver is being unloaded.\n" ); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; WDF_DRIVER_CONFIG config; WDFDRIVER driver; KdBreakPoint (); WDF_DRIVER_CONFIG_INIT (&config, WDF_NO_EVENT_CALLBACK); config.DriverInitFlags |= WdfDriverInitNonPnpDriver; config.EvtDriverUnload = MyDriverUnload; status = WdfDriverCreate ( DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver ); if (!NT_SUCCESS (status)) { DebugPrint ("WFP驱动初始化失败!\n" ); return status; } DebugPrint ("WFP驱动初始化成功!\n" ); return STATUS_SUCCESS; }
相关函数介绍 WDF_DRIVER_CONFIG_INIT
WDF_DRIVER_CONFIG_INIT
是 Windows Driver Framework (WDF) 中的一个宏,用于初始化 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;
EvtDriverDeviceAdd
作用:指定设备添加回调函数。当设备被添加到驱动程序时,WDF 框架会调用这个函数。
EvtDriverUnload
:指定驱动程序卸载回调函数。当驱动程序被卸载时,WDF 框架会调用这个函数。如果不需要卸载回调函数,可以设置为 NULL。并WDF 框架会自动释放 WDF 对象(如设备对象、队列对象等,因此不需要手动释放这些对象,只需要释放掉自己用到的其他对象
DriverInitFlags
:WdfDriverInitNonPnpDriver
:表示这是一个非 PnP 驱动程序。WdfDriverInitNoDispatchOverride
:表示驱动程序不会覆盖默认的调度例程。WdfDriverInitNoFormatChaining
:表示驱动程序不会使用格式链。
DriverPoolTag
:指定驱动程序使用的内存池标签。内存池标签是一个 4 字符的标识符,用于调试和跟踪内存分配。如果不需要自定义内存池标签,可以设置为 0。
WDF_DRIVER_CONFIG
是 Windows Driver Framework (WDF) 中的一个重要结构体,用于配置 WDF 驱动程序的行为和属性。它在 DriverEntry
函数中用于初始化驱动程序,并传递给 WdfDriverCreate
函数。
1 2 3 4 5 6 VOID FORCEINLINE WDF_DRIVER_CONFIG_INIT ( _Out_ PWDF_DRIVER_CONFIG Config, _In_opt_ PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd )
其中:**EvtDriverDeviceAdd
** 如果不需要设备添加回调函数,可以传递 WDF_NO_EVENT_CALLBACK
。
调用时机:当该驱动有相关设备被创建,则会调用这个EvtDriverDeviceAdd
WdfDriverCreate
WdfDriverCreate
并不是创建一个新的驱动对象,而是创建一个 WDF 驱动程序对象,并将其与现有的 DriverObject
关联起来,以便使用 WDF 框架的功能。
1 2 3 4 5 6 7 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 ) ;
WDF_DRIVER_CONFIG
是 Windows Driver Framework (WDF) 中的一个重要结构体,用于配置 WDF 驱动程序的行为和属性。它在 DriverEntry
函数中用于初始化驱动程序,并传递给 WdfDriverCreate
函数。
介绍下_WDF_OBJECT_ATTRIBUTES
结构
1 2 3 4 5 6 7 8 9 10 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;
其中, 如果你定义了 EvtCleanupCallback
和 EvtDestroyCallback
,那么你需要在这些回调函数中手动释放与 WDF 对象相关的资源。 否则WDF框架会自动清除掉这些对象的资源
**EvtCleanupCallback
**:
当 WDF 设备对象 的引用计数降为 0 时,WDF 框架会调用 EvtCleanupCallback
。
此时设备对象仍然有效,开发人员可以访问对象的上下文内存和其他属性。
**EvtDestroyCallback
**:
在 EvtCleanupCallback
执行完毕后,WDF 框架会调用 EvtDestroyCallback
。
此时设备对象已经无效,开发人员不能访问对象的上下文内存或其他属性。
WdfObjectDereference
用来减少WDF设备的引用计数
第二节:设备对象,文件操作,符号链接 WdfControlDeviceInitAllocate
是 Windows Driver Framework (WDF) 中的一个函数,用于为控制设备分配一个 WDFDEVICE_INIT 结构体。
1 2 3 4 PWDFDEVICE_INIT WdfControlDeviceInitAllocate ( [in] WDFDRIVER Driver, [in] const UNICODE_STRING *SDDLString ) ;
SDDLString
:举个例子SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX
定义了以下权限:
系统(SY) :完全访问。
管理员组(BA) :完全访问。
所有用户(WD) :完全访问。
受限代码(RC) :完全访问。
WdfDeviceInitAssignName
是 Windows 驱动程序框架(WDF)中的一个函数,用于为设备对象分配一个名称。
1 2 3 4 NTSTATUS WdfDeviceInitAssignName ( [in] PWDFDEVICE_INIT DeviceInit, [in, optional] PCUNICODE_STRING DeviceName ) ;
设备对象中的驱动程序框架文件对象(WDFFILEOBJECT) 可以理解为内核驱动程序与应用层通信的桥梁 。它在 WDF(Windows 驱动程序框架)中扮演了非常重要的角色,主要用于管理与设备对象相关的用户模式句柄和上下文数据。
WDF_FILEOBJECT_CONFIG_INIT 函数初始化驱动程序WDF_FILEOBJECT_CONFIG
结构。
1 2 3 4 5 6 void WDF_FILEOBJECT_CONFIG_INIT ( [out] PWDF_FILEOBJECT_CONFIG FileEventCallbacks, [in, optional] PFN_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate, [in, optional] PFN_WDF_FILE_CLOSE EvtFileClose, [in, optional] PFN_WDF_FILE_CLEANUP EvtFileCleanup ) ;
当用户模式应用程序调用 CreateFile
或内核模式组件调用 ZwCreateFile
时, WDF 会触发 EvtDeviceFileCreate
用户模式应用程序调用 CloseHandle
或内核模式组件调用 ZwClose
时,WDF 会触发EvtWdfFileCleanup
。
当文件对象完全关闭时,驱动程序的 EvtWdfFileClose
被调用。
WdfDeviceInitSetFileObjectConfig
该方法注册事件回调函数并设置驱动程序框架文件对象的配置信息。
1 2 3 4 5 void WdfDeviceInitSetFileObjectConfig ( [in] PWDFDEVICE_INIT DeviceInit, [in] PWDF_FILEOBJECT_CONFIG FileObjectConfig, [in, optional] PWDF_OBJECT_ATTRIBUTES FileObjectAttributes ) ;
完整代码:
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 #include <ntddk.h> #include <wdf.h> #include <sddl.h> #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) EVT_WDF_DEVICE_FILE_CREATE EvtWdfDeviceFileCreate; EVT_WDF_FILE_CLOSE EvtWdfFileClose; EVT_WDF_FILE_CLEANUP EvtWdfFileCleanup; void EvtWdfFileCleanup ( WDFFILEOBJECT FileObject ) { FileObject; } void EvtWdfFileClose ( WDFFILEOBJECT FileObject ) { FileObject; } void EvtWdfDeviceFileCreate ( WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject ) { Device; Request; FileObject; } void MyDriverUnload ( _In_ WDFDRIVER Driver ) { Driver; DebugPrint ("MyDriverUnload: Driver is being unloaded.\n" ); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; WDF_DRIVER_CONFIG config; WDFDRIVER driver; WDFDEVICE Device; PWDFDEVICE_INIT DeviceInit; UNICODE_STRING DeviceName = RTL_CONSTANT_STRING (L"\\Device\\HelloWDF" ); UNICODE_STRING SymbolicLinkName = RTL_CONSTANT_STRING (L"\\??\\HelloWDF" ); WDF_FILEOBJECT_CONFIG FileConfig; KdBreakPoint (); WDF_DRIVER_CONFIG_INIT (&config, WDF_NO_EVENT_CALLBACK); config.DriverInitFlags |= WdfDriverInitNonPnpDriver; config.EvtDriverUnload = MyDriverUnload; status = WdfDriverCreate ( DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver ); DeviceInit = WdfControlDeviceInitAllocate (driver,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX); if (DeviceInit == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto End; } status=WdfDeviceInitAssignName (DeviceInit, &DeviceName); if (!NT_SUCCESS (status)) { DebugPrint ("设备分配失败!\n" ); goto End; } WDF_FILEOBJECT_CONFIG_INIT (&FileConfig, EvtWdfDeviceFileCreate, EvtWdfFileClose, EvtWdfFileCleanup); WdfDeviceInitSetFileObjectConfig (DeviceInit, &FileConfig, WDF_NO_OBJECT_ATTRIBUTES); status = WdfDeviceCreate (&DeviceInit,WDF_NO_OBJECT_ATTRIBUTES,&Device); if (!NT_SUCCESS (status)) { DebugPrint ("设备创建失败\n" ); goto End; } status = WdfDeviceCreateSymbolicLink (Device, &SymbolicLinkName); if (!NT_SUCCESS (status)) { DebugPrint ("符号链接创建失败\n" ); goto End; } WdfControlFinishInitializing (Device); DebugPrint ("WFP驱动初始化成功!\n" ); return STATUS_SUCCESS; End: DebugPrint ("WFP驱动初始化失败!\n" ); return STATUS_UNSUCCESSFUL; }
总结
**DriverEntry
**:驱动程序的入口函数,负责初始化驱动程序。
**WdfDriverCreate
**:创建 WDF 驱动程序对象。
**WdfControlDeviceInitAllocate
**:分配设备初始化结构。
**WdfDeviceInitAssignName
**:设置设备名称。
**WDF_FILEOBJECT_CONFIG_INIT
**:初始化文件对象配置。
**WdfDeviceInitSetFileObjectConfig
**:设置文件对象配置。
**WdfDeviceCreate
**:创建设备对象。
**WdfDeviceCreateSymbolicLink
**:创建符号链接。
**WdfControlFinishInitializing
**:完成设备初始化。
第三节:编写应用程序访问WDF驱动 在第二节,我们写了对于文件的三个回调,分别是以下三个
当然要说明: 文件对象的回调函数(如 EvtWdfDeviceFileCreate
、EvtWdfFileClose
和 EvtWdfFileCleanup
)主要用于管理文件对象的生命周期,而不是直接处理 I/O 请求(如 ReadFile
和 WriteFile
)。WDF 使用队列 (Queue)机制来处理 I/O 请求,而不是像 WDM 那样直接通过回调函数处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 void EvtWdfFileCleanup ( WDFFILEOBJECT FileObject ) void EvtWdfFileClose ( WDFFILEOBJECT FileObject ) void EvtWdfDeviceFileCreate ( WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject )
其中,Create回调里面有一个Request,这个我们必须处理它
处理的方式还是比较多的,例如
1.直接完成请求: (当然你也可以验证资质)
1 2 WdfRequestComplete (Request, STATUS_SUCCESS);
2.初始化文件对象上下文
1 2 3 4 5 6 7 8 9 10 11 12 PFILE_CONTEXT fileContext = FileObjectGetContext (FileObject); fileContext->Buffer = ExAllocatePoolWithTag (NonPagedPool, 1024 , 'BufT' ); if (fileContext->Buffer == NULL ) { WdfRequestComplete (Request,STATUS_INSUFFICIENT_RESOURCES); return ; } WdfRequestComplete (Request, STATUS_SUCCESS);
3.异步完成请求 : 如果设备对象的打开操作需要较长时间(例如等待某个资源),可以将请求标记为挂起,并在操作完成后异步完成请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void EvtWdfDeviceFileCreate ( WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject ) { UNREFERENCED_PARAMETER (Device); UNREFERENCED_PARAMETER (FileObject); WdfRequestMarkPending (Request); MyAsyncOpenDevice (Request); } void MyAsyncOpenDevice (WDFREQUEST Request) { KeDelayExecutionThread (KernelMode, FALSE, 1000 ); WdfRequestComplete (Request, STATUS_SUCCESS); }
用winobj
可以看到全局的符号链接:
直接在应用层去访问这个设备
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 #include <windows.h> #include <stdio.h> #define DEVICE_NAME L"\\\\.\\HelloWDF" int main () { system ("pause" ); HANDLE hDevice = INVALID_HANDLE_VALUE; BOOL result = FALSE; DWORD bytesReturned = 0 ; char outputBuffer[100 ] = { 0 }; hDevice = CreateFile ( DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hDevice == INVALID_HANDLE_VALUE) { printf ("Failed to open device. Error: %d\n" , GetLastError ()); return 1 ; } return 0 ; }
可以看到成功断下来了
第四节:实现WDF的IO队列 本节将实现对文件设备的控制操作
首先我们先建立一个IO队列的配置
1 WDF_IO_QUEUE_CONFIG ioConfig
然后用WDF_IO_QUEUE_CONFIG_INIT
来初始化这个ioConfig
1 2 3 4 void WDF_IO_QUEUE_CONFIG_INIT ( [out] PWDF_IO_QUEUE_CONFIG Config, [in] WDF_IO_QUEUE_DISPATCH_TYPE DispatchType ) ;
其中 DispatchType枚举值是这样的
值
描述
WdfIoQueueDispatchInvalid
无效的分发类型(仅用于内部验证)。
WdfIoQueueDispatchSequential
顺序分发:I/O 请求按顺序处理,一次只处理一个请求。
WdfIoQueueDispatchParallel
并行分发:I/O 请求可以同时处理,适用于高性能场景。
WdfIoQueueDispatchManual
手动分发:驱动程序需要手动从队列中取出请求并处理。
WdfIoQueueDispatchMax
枚举的最大值(仅用于内部验证)。
在_WDF_IO_QUEUE_CONFIG
结构中,我们可以发现很多WDM熟悉的东西,例如READ,WRITE,IO_DEVICE_CONTROL,这就是对应的回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 typedef struct _WDF_IO_QUEUE_CONFIG { ULONG Size; WDF_IO_QUEUE_DISPATCH_TYPE DispatchType; WDF_TRI_STATE PowerManaged; BOOLEAN AllowZeroLengthRequests; BOOLEAN DefaultQueue; PFN_WDF_IO_QUEUE_IO_DEFAULT EvtIoDefault; PFN_WDF_IO_QUEUE_IO_READ EvtIoRead; PFN_WDF_IO_QUEUE_IO_WRITE EvtIoWrite; PFN_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; PFN_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoInternalDeviceControl; PFN_WDF_IO_QUEUE_IO_STOP EvtIoStop; PFN_WDF_IO_QUEUE_IO_RESUME EvtIoResume; PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue; union { struct { ULONG NumberOfPresentedRequests; } Parallel; } Settings; WDFDRIVER Driver; } WDF_IO_QUEUE_CONFIG, *PWDF_IO_QUEUE_CONFIG;
这里做一个表格帮助理解
WDF 回调函数
对应三环 API
描述
EvtIoDefault
无直接对应
默认处理逻辑,覆盖未分类的请求。
EvtIoRead
ReadFile,
ReadFileEx
处理读请求。
EvtIoWrite
WriteFile
, WriteFileEx
处理写请求。
EvtIoDeviceControl
DeviceIoControl
处理用户模式的 IOCTL 请求。
EvtIoInternalDeviceControl
无直接对应
处理内核模式的 IOCTL 请求。
EvtIoStop
无直接对应
恢复队列请求
EvtIoResume
无直接对应
恢复队列请求
EvtIoCanceledOnQueue
CancelIo
, CancelIoEx
用户取消队列中未完成的请求时调用。
总结一下顺序
设备初始化 :
调用 WdfDeviceInitAllocate
或 WdfControlDeviceInitAllocate
分配并初始化 WDFDEVICE_INIT
结构。
设置设备属性(如设备名称、安全描述符等)。
文件对象配置初始化 :
使用 WDF_FILEOBJECT_CONFIG_INIT
初始化 WDF_FILEOBJECT_CONFIG
结构。
设置文件对象的回调函数(如 EvtWdfDeviceFileCreate
、EvtWdfFileClose
等)。
设置文件对象配置 :
调用 WdfDeviceInitSetFileObjectConfig
将文件对象配置与设备初始化结构关联。
创建设备对象 :
调用 WdfDeviceCreate
创建设备对象。
创建 I/O 队列 :
在设备对象创建成功后,调用 WdfIoQueueCreate
创建 I/O 队列。
完成设备创建 WdfControlFinishInitializing(Device);
本节完整驱动程序:
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 #include <ntddk.h> #include <wdf.h> #include <sddl.h> #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) EVT_WDF_DEVICE_FILE_CREATE EvtWdfDeviceFileCreate; EVT_WDF_FILE_CLOSE EvtWdfFileClose; EVT_WDF_FILE_CLEANUP EvtWdfFileCleanup; VOID m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL ( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) { Queue; Request; OutputBufferLength; InputBufferLength; IoControlCode; DebugPrint ("IO_DEVICE_CONTROL\n" ); WdfRequestComplete (Request, STATUS_SUCCESS); } void EvtWdfFileCleanup ( WDFFILEOBJECT FileObject ) { FileObject; DebugPrint ("clean_up\n" ); } void EvtWdfFileClose ( WDFFILEOBJECT FileObject ) { FileObject; DebugPrint ("close\n" ); } void EvtWdfDeviceFileCreate ( WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject ) { Device; Request; FileObject; DebugPrint ("file_create\n" ); WdfRequestComplete (Request, STATUS_SUCCESS); } void MyDriverUnload ( _In_ WDFDRIVER Driver ) { Driver; DebugPrint ("MyDriverUnload: Driver is being unloaded.\n" ); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; WDF_DRIVER_CONFIG config; WDFDRIVER driver; WDFDEVICE Device; PWDFDEVICE_INIT DeviceInit; UNICODE_STRING DeviceName = RTL_CONSTANT_STRING (L"\\Device\\HelloWDF" ); UNICODE_STRING SymbolicLinkName = RTL_CONSTANT_STRING (L"\\??\\HelloWDF" ); WDF_FILEOBJECT_CONFIG FileConfig; WDF_IO_QUEUE_CONFIG ioConfig; WDFQUEUE Queue; KdBreakPoint (); WDF_DRIVER_CONFIG_INIT (&config, WDF_NO_EVENT_CALLBACK); config.DriverInitFlags |= WdfDriverInitNonPnpDriver; config.EvtDriverUnload = MyDriverUnload; status = WdfDriverCreate ( DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver ); DeviceInit = WdfControlDeviceInitAllocate (driver,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX); if (DeviceInit == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; goto End; } status=WdfDeviceInitAssignName (DeviceInit, &DeviceName); if (!NT_SUCCESS (status)) { DebugPrint ("设备分配失败!\n" ); goto End; } WDF_FILEOBJECT_CONFIG_INIT (&FileConfig, EvtWdfDeviceFileCreate, EvtWdfFileClose, EvtWdfFileCleanup); WdfDeviceInitSetFileObjectConfig (DeviceInit, &FileConfig, WDF_NO_OBJECT_ATTRIBUTES); status = WdfDeviceCreate (&DeviceInit,WDF_NO_OBJECT_ATTRIBUTES,&Device); if (!NT_SUCCESS (status)) { DebugPrint ("设备创建失败\n" ); goto End; } WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (&ioConfig, WdfIoQueueDispatchSequential); ioConfig.EvtIoDeviceControl = m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL; status = WdfIoQueueCreate (Device, &ioConfig, WDF_NO_OBJECT_ATTRIBUTES, &Queue); if (!NT_SUCCESS (status)) { DebugPrint ("设备队列创建失败\n" ); goto End; } status = WdfDeviceCreateSymbolicLink (Device, &SymbolicLinkName); if (!NT_SUCCESS (status)) { DebugPrint ("符号链接创建失败\n" ); goto End; } WdfControlFinishInitializing (Device); DebugPrint ("WFP驱动初始化成功!\n" ); return STATUS_SUCCESS; End: DebugPrint ("WFP驱动初始化失败!\n" ); return STATUS_UNSUCCESSFUL; }
应用层:
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 #include <windows.h> #include <stdio.h> #define DEVICE_NAME L"\\\\.\\HelloWDF" int main () { system ("pause" ); HANDLE hDevice = INVALID_HANDLE_VALUE; BOOL result = FALSE; DWORD bytesReturned = 0 ; char inputBuffer[100 ] = "Test input data" ; char outputBuffer[100 ] = { 0 }; hDevice = CreateFile ( DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hDevice == INVALID_HANDLE_VALUE) { printf ("Failed to open device. Error: %d\n" , GetLastError ()); return 1 ; } result = DeviceIoControl ( hDevice, NULL , inputBuffer, sizeof (inputBuffer), outputBuffer, sizeof (outputBuffer), &bytesReturned, NULL ); return 0 ; }
实现效果:
第五节:从0实现支持pnp的WDF驱动 什么是Pnp,为什么需要Pnp 我们来讨论下pnp
首先什么是pnp? PnP(Plug and Play,即插即用)是一种功能,让操作系统能够自动检测和配置新硬件设备,而无需用户干预。PnP 驱动程序会响应硬件的添加、移除、资源分配和电源管理事件。
我们来看看WDM是如何实现Pnp的
在 Windows 驱动模型 (WDM) 中,支持 PnP 是非常重要的特性。PnP 驱动主要通过响应系统发送的 IRP_MN_XXX 类型的 I/O 请求包 (IRP) 来实现对设备的动态管理,例如设备启动 (IRP_MN_START_DEVICE
)、设备移除 (IRP_MN_REMOVE_DEVICE
) 和设备停止 (IRP_MN_STOP_DEVICE
)
通过这些NT驱动没有的IRP,实现了Pnp
那么在WDF中,对Pnp进行了进一步的封装,WDF 使用回调函数(如 EvtDeviceAdd
和 EvtDevicePrepareHardware
)来处理 PnP 和电源管理事件,而不是直接处理 IRP。
为什么我们需要Pnp?为什么不把创建对象,也就是WdfDeviceCreate
直接写在DriverEntry
?
这里要分控制设备和功能设备
控制设备 对于控制设备(如非即插即用设备或虚拟设备),可以在 DriverEntry
或类似的初始化函数中调用 WdfDeviceCreate
创建设备对象,因为这些设备与硬件无关,不依赖 PnP 机制。
功能设备 对于功能设备,系统会通过 PnP 机制向驱动程序通知设备的到达事件,驱动程序在 EvtDriverDeviceAdd
中为设备创建相应的 WDFDEVICE
,并初始化硬件资源。
我们现在写的都是创建的虚拟设备,实际上并没有硬件插入的
我们可以这样想象一个场景:
假设你正在为一款即插即用的PCI网卡编写一个驱动程序。这张网卡可能有多个型号,且可能有多个实例插入到同一台电脑上(例如插了两块相同型号的网卡)。 如果说是将WdfDeviceCreate
写在DriverEntry,但是驱动已经调用过一遍DriverEntry,不能再调用了,这下就没办法生成一个新的设备对象,但是如果写在EvtDriverDeviceAdd
,就可以在系统感知到设备插入自动调用生成一个设备对象,这就是为什么我们需要pnp
实现的示例代码
Device.h,Device.c:这里是实现当有设备插入,自动创建设备对象并设置回调函数的相关实现
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 #ifndef DEVICE_H #define DEVICE_H #include <ntddk.h> #include <wdf.h> NTSTATUS m_EVT_WDF_DRIVER_DEVICE_ADD ( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit ) ;#endif #include <ntddk.h> #include <wdf.h> #include "Device.h" #include "Queue.h" #include <initguid.h> #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) DEFINE_GUID (DEVICEINTERFACE, 0x8C9528E5 , 0xE99D , 0xDBE9 , 0xA4 , 0x10 , 0xF1 , 0x7C , 0xDB , 0x96 , 0x66 , 0x0B );void EvtWdfFileCleanup ( WDFFILEOBJECT FileObject ) { FileObject; DebugPrint ("clean_up\n" ); } void EvtWdfFileClose ( WDFFILEOBJECT FileObject ) { FileObject; DebugPrint ("close\n" ); }; void EvtWdfDeviceFileCreate ( WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject ) { Device; Request; FileObject; DebugPrint ("file_create\n" ); WdfRequestComplete (Request, STATUS_SUCCESS); } NTSTATUS m_EVT_WDF_DRIVER_DEVICE_ADD ( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit ) { Driver; DeviceInit; NTSTATUS status=STATUS_SUCCESS; WDF_FILEOBJECT_CONFIG FileConfig; WDFDEVICE Device; WDF_IO_QUEUE_CONFIG IoQueueConfig; UNICODE_STRING DeviceName = RTL_CONSTANT_STRING (L"\\Device\\HelloWDF" ); status = WdfDeviceInitAssignName (DeviceInit,&DeviceName); if (!NT_SUCCESS (status)) { DebugPrint ("设备分配失败!\n" ); goto End; } WDF_FILEOBJECT_CONFIG_INIT (&FileConfig, EvtWdfDeviceFileCreate, EvtWdfFileClose, EvtWdfFileCleanup); WdfDeviceInitSetFileObjectConfig (DeviceInit, &FileConfig, WDF_NO_OBJECT_ATTRIBUTES); status = WdfDeviceCreate (&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &Device); if (!NT_SUCCESS (status)) { DebugPrint ("设备创建失败\n" ); goto End; } WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (&IoQueueConfig, WdfIoQueueDispatchSequential); IoQueueConfig.EvtIoDeviceControl = m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL; WDFQUEUE IoQueue = NULL ; status = WdfIoQueueCreate (Device, &IoQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &IoQueue); if (!NT_SUCCESS (status)) { DebugPrint ("Failed to create I/O queue: 0x%X\n" , status); goto End; } WdfDeviceCreateDeviceInterface (Device, &DEVICEINTERFACE, NULL ); WdfControlFinishInitializing (Device); DebugPrint ("Wdf_Driver_Add\n" ); return status; End: status = STATUS_UNSUCCESSFUL; return status; }
可以发现这里并没有使用建立符号链接,而是选择了为设备注册一个设备接口,连设备名都不需要了
WdfDeviceCreateDeviceInterface
用途 :为设备注册一个设备接口,供用户模式应用程序通过设备接口 GUID 进行访问。
特性
WdfDeviceCreateDeviceInterface
WdfDeviceCreateSymbolicLink
用途
动态设备发现和访问
提供固定路径直接访问设备
PnP 支持
完全支持
需要开发者手动管理插拔、状态变化
设备实例管理
可用于多个设备实例,每个实例动态注册接口 GUID
一个符号链接通常绑定一个设备实例
用户模式访问
用户程序通过接口 GUID 枚举和打开设备
用户程序通过固定符号链接路径打开设备
API 兼容性
使用 SetupDi
系列 API
使用传统 Win32 API (CreateFile
)
适用场景
现代驱动、支持动态枚举
传统驱动、调试用
Queue.h和Queue.c 这里是实现具体回调函数
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 #ifndef QUEUE_H #define QUEUE_H #include <ntddk.h> #include <wdf.h> VOID m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL ( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) ;#endif #include <ntddk.h> #include <wdf.h> #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) VOID m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL ( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) { Queue; Request; OutputBufferLength; InputBufferLength; IoControlCode; DebugPrint ("IO_DEVICE_CONTROL\n" ); WdfRequestComplete (Request, STATUS_SUCCESS); }
DriverEntry:
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 #include <ntddk.h> #include <wdf.h> #include "Device.h" #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status; WDF_DRIVER_CONFIG config; WDFDRIVER driver; KdBreakPoint (); WDF_DRIVER_CONFIG_INIT (&config, m_EVT_WDF_DRIVER_DEVICE_ADD); config.EvtDriverUnload = MyDriverUnload; status = WdfDriverCreate ( DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver ); if (!NT_SUCCESS (status)) { DebugPrint ("注册驱动的框架对象失败\n" ); return status; } return status; }
注意,相比于前几节,如果在WDF_DRIVER_CONFIG_INIT
设置了EVT_DRIVER_DEVICE_ADD
,那么这里一定不能添加config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
另外
对于即插即用驱动程序(PnP驱动程序),通常不需要指定 EvtDriverUnload
回调函数,因为即插即用驱动程序的卸载是由设备管理器(Device Manager)和操作系统自动处理的。
即插即用驱动程序的卸载通常是通过删除设备节点或禁用设备来触发的,而不是通过显式的卸载函数。
加载和卸载驱动:
平时我们都是用KmdManager
直接加载驱动,但是这样会有一个不好的点,就是仅仅添加驱动,但是不会创建设备,所以我们不好直接用它来测试pnp驱动
所以我们用hdwwiz
使用 hdwwiz
添加 INF 文件时,Windows 会执行以下操作:
解析 INF 文件,获取设备的硬件 ID 和驱动程序信息。
创建设备节点(Device Node)。
将设备节点与已加载的驱动程序关联。
调用驱动程序的 AddDevice
例程来创建设备对象。
关键点 :
如果驱动程序已经加载(例如通过 sc create
或 net start
),Windows 不会重新加载驱动程序,因此不会再次调用 DriverEntry
。
如果驱动程序未加载,Windows 会先加载驱动程序(调用 DriverEntry
),然后再调用 AddDevice
。
在设备管理器这里可以选择禁用设备和卸载设备
第六节:使用Pnp的应用程序 示例代码如下:
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 #include <windows.h> #include <setupapi.h> #include <tchar.h> #include <stdio.h> #include <initguid.h> DEFINE_GUID (DEVICEINTERFACE, 0x8C9528E5 , 0xE99D , 0xDBE9 , 0xA4 , 0x10 , 0xF1 , 0x7C , 0xDB , 0x96 , 0x66 , 0x0B );void GetDeviceInterfacePath () { HDEVINFO deviceInfoSet; SP_DEVICE_INTERFACE_DATA deviceInterfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL ; DWORD requiredSize = 0 ; BOOL success; system ("pause" ); deviceInfoSet = SetupDiGetClassDevs ( &DEVICEINTERFACE, NULL , NULL , DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (deviceInfoSet == INVALID_HANDLE_VALUE) { printf ("SetupDiGetClassDevs failed\n" ); return ; } deviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA); success = SetupDiEnumDeviceInterfaces ( deviceInfoSet, NULL , &DEVICEINTERFACE, 0 , &deviceInterfaceData); if (!success) { printf ("SetupDiEnumDeviceInterfaces failed\n" ); SetupDiDestroyDeviceInfoList (deviceInfoSet); return ; } SetupDiGetDeviceInterfaceDetail ( deviceInfoSet, &deviceInterfaceData, NULL , 0 , &requiredSize, NULL ); deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc (requiredSize); deviceInterfaceDetailData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); success = SetupDiGetDeviceInterfaceDetail ( deviceInfoSet, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL , NULL ); if (!success) { printf ("SetupDiGetDeviceInterfaceDetail failed\n" ); free (deviceInterfaceDetailData); SetupDiDestroyDeviceInfoList (deviceInfoSet); return ; } printf ("Device Path: %s\n" , deviceInterfaceDetailData->DevicePath); HANDLE deviceHandle = CreateFile ( deviceInterfaceDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, 0 , NULL ); if (deviceHandle == INVALID_HANDLE_VALUE) { printf ("Failed to open device\n" ); } else { printf ("Device opened successfully\n" ); CloseHandle (deviceHandle); } free (deviceInterfaceDetailData); SetupDiDestroyDeviceInfoList (deviceInfoSet); } int main () { GetDeviceInterfacePath (); return 0 ; }
成功触发pnp的回调
第七节:WDF中文数字转换驱动 关于IO控制码的编写:
1 2 #define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16 ) | ((Access) << 14 ) | ((Function) << 2 ) | (Method))
**DeviceType
**: 指定设备类型 ,可以是预定义的设备类型(例如 FILE_DEVICE_DISK
、FILE_DEVICE_KEYBOARD
等)。如果设备类型未知,可以使用 FILE_DEVICE_UNKNOWN
。
Function
:指定功能代码。通常从 0x800 开始,以避免与系统保留的功能代码冲突。
Method
:作用:指定数据传输方法。 取值: METHOD_BUFFERED:使用缓冲 I/O(数据通过系统缓冲区传递)。 METHOD_IN_DIRECT:使用直接 I/O(输入数据通过直接内存访问传递)。 METHOD_OUT_DIRECT:使用直接 I/O(输出数据通过直接内存访问传递)。 METHOD_NEITHER:不使用系统缓冲区,直接传递用户模式指针。
**Access
**:作用:指定访问权限。 取值: FILE_ANY_ACCESS:允许任何访问。 FILE_READ_ACCESS:允许读访问。 FILE_WRITE_ACCESS:允许写访问。 FILE_READ_ACCESS | FILE_WRITE_ACCESS:允许读写访问。
例如这样定义一个IOCTL_CODE
1 #define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN,0X800,METHOD_BUFFERED,FILE_ANY_ACCESS)
IO_DEVICE_CONTROL
处理回调函数
1 2 3 4 5 6 7 8 9 10 11 12 VOID m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL ( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) ;
检索 I/O 请求的输出缓冲区。
1 2 3 4 5 6 NTSTATUS WdfRequestRetrieveOutputBuffer ( [in] WDFREQUEST Request, [in] size_t MinimumRequiredSize, [out] PVOID *Buffer, [out, optional] size_t *Length ) ;
其中MinimumRequiredSize
参数用于指定驱动程序需要的最小输出缓冲区大小 ,如果用户层提供的缓冲区大小小于 MinimumRequiredSize
,WdfRequestRetrieveOutputBuffer
会返回 STATUS_BUFFER_TOO_SMALL
,驱动程序可以拒绝处理请求。
WdfRequestRetrieveInputBuffer 也是如此
应用层代码
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 #include <windows.h> #include <stdio.h> #include <setupapi.h> #include <initguid.h> DEFINE_GUID (DEVICEINTERFACE, 0x8C9528E5 , 0xE99D , 0xDBE9 , 0xA4 , 0x10 , 0xF1 , 0x7C , 0xDB , 0x96 , 0x66 , 0x0B );#define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) BOOL GetDevicePath (LPGUID InterfaceGuid, char * DevicePath, size_t DevicePathSize) { HDEVINFO deviceInfoSet = SetupDiGetClassDevsA (InterfaceGuid, NULL , NULL , DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (deviceInfoSet == INVALID_HANDLE_VALUE) { printf ("SetupDiGetClassDevsA failed: %lu\n" , GetLastError ()); return FALSE; } SP_DEVICE_INTERFACE_DATA deviceInterfaceData; deviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA); if (!SetupDiEnumDeviceInterfaces (deviceInfoSet, NULL , InterfaceGuid, 0 , &deviceInterfaceData)) { printf ("SetupDiEnumDeviceInterfaces failed: %lu\n" , GetLastError ()); SetupDiDestroyDeviceInfoList (deviceInfoSet); return FALSE; } DWORD requiredSize = 0 ; SetupDiGetDeviceInterfaceDetailA (deviceInfoSet, &deviceInterfaceData, NULL , 0 , &requiredSize, NULL ); if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) { printf ("SetupDiGetDeviceInterfaceDetailA size query failed: %lu\n" , GetLastError ()); SetupDiDestroyDeviceInfoList (deviceInfoSet); return FALSE; } PSP_DEVICE_INTERFACE_DETAIL_DATA_A deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A)malloc (requiredSize); if (!deviceInterfaceDetailData) { printf ("Memory allocation failed.\n" ); SetupDiDestroyDeviceInfoList (deviceInfoSet); return FALSE; } deviceInterfaceDetailData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA_A); if (!SetupDiGetDeviceInterfaceDetailA (deviceInfoSet, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL , NULL )) { printf ("SetupDiGetDeviceInterfaceDetailA failed: %lu\n" , GetLastError ()); free (deviceInterfaceDetailData); SetupDiDestroyDeviceInfoList (deviceInfoSet); return FALSE; } strncpy (DevicePath, deviceInterfaceDetailData->DevicePath, DevicePathSize - 1 ); DevicePath[DevicePathSize - 1 ] = '\0' ; free (deviceInterfaceDetailData); SetupDiDestroyDeviceInfoList (deviceInfoSet); return TRUE; } int main () { system ("pause" ); char devicePath[MAX_PATH] = { 0 }; if (!GetDevicePath ((LPGUID)&DEVICEINTERFACE, devicePath, sizeof (devicePath))) { printf ("Failed to get device path.\n" ); return 1 ; } printf ("Device Path: %s\n" , devicePath); HANDLE hDevice = CreateFileA ( devicePath, GENERIC_READ | GENERIC_WRITE, 0 , NULL , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hDevice == INVALID_HANDLE_VALUE) { printf ("Failed to open device: %lu\n" , GetLastError ()); return 1 ; } while (1 ) { char inputBuffer[0x20 ]; char outputBuffer[256 ] = { 0 }; DWORD bytesReturned; scanf ("%s" , inputBuffer); BOOL result = DeviceIoControl ( hDevice, IOCTL_TEST, inputBuffer, sizeof (inputBuffer), outputBuffer, sizeof (outputBuffer), &bytesReturned, NULL ); if (result) { printf ("Driver response: %s\n" , outputBuffer); } else { printf ("DeviceIoControl failed: %lu\n" , GetLastError ()); } } CloseHandle (hDevice); return 0 ; }
驱动代码
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <ntddk.h> #include <wdf.h> #define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__) #define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN,0X800,METHOD_BUFFERED,FILE_ANY_ACCESS) VOID m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL ( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) { UNREFERENCED_PARAMETER (Queue); NTSTATUS status = STATUS_SUCCESS; PVOID inputBuffer = NULL ; PVOID outputBuffer = NULL ; const char * digitToChinese[] = { "零" , "一" , "二" , "三" , "四" , "五" , "六" , "七" , "八" , "九" }; char temp[0x100 ]; switch (IoControlCode) { case IOCTL_TEST: { status = WdfRequestRetrieveInputBuffer (Request, InputBufferLength, &inputBuffer, NULL ); if (!NT_SUCCESS (status)) { KdPrint (("Failed to retrieve input buffer: 0x%X\n" , status)); break ; } status = WdfRequestRetrieveOutputBuffer (Request, OutputBufferLength, &outputBuffer, NULL ); if (!NT_SUCCESS (status)) { KdPrint (("Failed to retrieve output buffer: 0x%X\n" , status)); break ; } const char * input = (const char *)inputBuffer; char * output = (char *)outputBuffer; size_t outputIndex = 0 ; for (size_t i = 0 ; input[i] != '\0' ; i++) { char digit = input[i]; if (digit >= '0' && digit <= '9' ) { const char * chineseDigit = digitToChinese[digit - '0' ]; size_t len = strlen (chineseDigit); if (outputIndex + len >= OutputBufferLength&& outputIndex + len >= 0x100 ) { status = STATUS_BUFFER_TOO_SMALL; KdPrint (("Output buffer too small. Required: %zu, Provided: %zu\n" , outputIndex + len, OutputBufferLength)); break ; } strcpy (&temp[outputIndex], chineseDigit); outputIndex += len; } else { status = STATUS_INVALID_PARAMETER; KdPrint (("Invalid character in input: %c\n" , digit)); break ; } } if (NT_SUCCESS (status)) { if (outputIndex < OutputBufferLength) { temp[outputIndex] = '\0' ; strcpy (output, temp); WdfRequestSetInformation (Request, outputIndex + 1 ); } else { status = STATUS_BUFFER_TOO_SMALL; } } break ; } default : status = STATUS_INVALID_DEVICE_REQUEST; KdPrint (("Unsupported IOCTL: 0x%X\n" , IoControlCode)); break ; } WdfRequestComplete (Request, status); }
第八节:WDF驱动框架对象介绍
WDF 对象
对应的 WDM 对象
WDFCHILDLIST
DEVICE_RELATIONS for Plug and Play enumerations
WDFREQUEST
IRP
WDFCMRESLIST
CM_RESOURCE_LIST
WDFSPINLOCK
KSPIN_LOCK
WDFCOLLECTION
None (most closely related to LIST_ENTRY and its related functions)
WDFSTRING
UNICODE_STRING
WDFCOMMONBUFFER
PVOID returned by AllocateCommonBuffer
WDFTIMER
KTIMER
WDFDEVICE
DEVICE_OBJECT
WDFUSBDEVICE
Attached DEVICE_OBJECT for a USB-enumerated stack
WDFDMAENABLER
DMA_ADAPTER
WDFUSBINTERFACE
USB_INTERFACE_DESCRIPTOR
WDFDMATRANSACTION
None
WDFUSBPIPE
USBD_PIPE_HANDLE
WDFDPC
KDPC
WDFWAITLOCK
KEVENT, KeEnterCriticalRegion, KeLeaveCriticalRegion
WDFDRIVER
DRIVER_OBJECT
WDFWMIPROVIDER
None: accessed through parameters to driver-defined DpWmiXxx callback functions
WDFFILEOBJECT
FILE_OBJECT
WDFWMIPROVIDER
None: accessed through parameters to driver-defined DpWmiXxx callback functions
WDFINTERRUPT
PKINTERRUPT
WDFWORKITEM
IO_WORKITEM
WDFIORESLIST
IO_RESOURCE_LIST
WDFIORESREQLIST
IO_RESOURCE_REQUIREMENTS_LIST
WDFIOTARGET
DEVICE_OBJECT
WDFKEY
HANDLE returned by ZwCreateKey or ZwOpenKey
WDFLOOKASIDE
PAGED_LOOKASIDE_LIST or NPAGED_LOOKASIDE_LIST
WDFMEMORY
None: accessed through a PVOID returned by memory allocation functions
WDFOBJECT
None
WDFQUEUE
IO_CSQ
预定义对象(Predefined) :由 WDF 框架自动创建和管理的对象。
驱动程序创建的对象(Driver created) :由驱动程序显式创建和管理的对象。
遇到的坑