# Windows驱动程序开发入门:从原理到实践

世界杯球童 2025-10-20 18:05:33 894

Windows驱动程序开发入门:从原理到实践

前言

Windows驱动程序是操作系统与硬件设备之间的桥梁,它们在系统的最底层运行,拥有访问硬件资源的特权。本文将从基础概念开始,逐步深入Windows驱动开发的核心知识,并通过代码示例帮助读者理解驱动程序的工作原理。

一、什么是Windows驱动程序?

Windows驱动程序(Device Driver)是一种特殊的软件组件,运行在内核模式下,负责管理特定的硬件设备。它们充当应用程序和硬件之间的翻译器,将操作系统的标准化调用转换为特定硬件能够理解的指令。

驱动程序的特点

内核模式执行:拥有系统的最高权限

硬件抽象:为上层应用提供统一的接口

中断处理:响应硬件产生的中断信号

内存管理:直接操作物理内存和I/O端口

二、Windows驱动架构

Windows驱动程序遵循Windows驱动模型(WDM)或更现代的Windows驱动框架(WDF)。

2.1 WDM架构层次

复制代码

应用程序层

Win32 API

I/O管理器

功能驱动程序 (Function Driver)

总线驱动程序 (Bus Driver)

硬件设备

2.2 驱动程序类型

按功能分类:

功能驱动程序(FDO):实现设备的主要功能

物理设备对象驱动程序(PDO):表示总线上的设备

筛选驱动程序(Filter Driver):在设备栈中添加功能

按框架分类:

WDM驱动:传统的Windows驱动模型

KMDF驱动:内核模式驱动框架

UMDF驱动:用户模式驱动框架

三、开发环境搭建

3.1 必需工具

Visual Studio 2019/2022

Windows SDK

Windows Driver Kit (WDK)

虚拟机(用于测试)

3.2 环境配置

xml

复制代码

x64

Debug

Windows10

true

KMDF

Universal

四、第一个驱动程序:Hello World

让我们从一个最简单的KMDF驱动程序开始。

4.1 驱动程序入口点

c

复制代码

#include

#include

// 驱动程序卸载回调函数

VOID HelloDriverUnload(IN PDRIVER_OBJECT DriverObject)

{

UNREFERENCED_PARAMETER(DriverObject);

KdPrint(("HelloDriver: Driver Unload called\n"));

}

// 驱动程序入口点

NTSTATUS DriverEntry(

_In_ PDRIVER_OBJECT DriverObject,

_In_ PUNICODE_STRING RegistryPath

)

{

NTSTATUS status;

WDF_DRIVER_CONFIG config;

// 打印调试信息

KdPrint(("HelloDriver: DriverEntry called\n"));

// 初始化WDF驱动配置

WDF_DRIVER_CONFIG_INIT(&config, NULL);

// 设置卸载函数

DriverObject->DriverUnload = HelloDriverUnload;

// 创建WDF驱动对象

status = WdfDriverCreate(

DriverObject,

RegistryPath,

WDF_NO_OBJECT_ATTRIBUTES,

&config,

WDF_NO_HANDLE

);

if (!NT_SUCCESS(status)) {

KdPrint(("HelloDriver: WdfDriverCreate failed: 0x%x\n", status));

return status;

}

KdPrint(("HelloDriver: Driver loaded successfully\n"));

return STATUS_SUCCESS;

}

4.2 设备对象创建

c

复制代码

// 设备添加回调函数

NTSTATUS HelloEvtDeviceAdd(

_In_ WDFDRIVER Driver,

_Inout_ PWDFDEVICE_INIT DeviceInit

)

{

NTSTATUS status;

WDFDEVICE device;

WDF_OBJECT_ATTRIBUTES deviceAttributes;

UNREFERENCED_PARAMETER(Driver);

KdPrint(("HelloDriver: HelloEvtDeviceAdd called\n"));

// 初始化设备属性

WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes);

// 创建设备对象

status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);

if (!NT_SUCCESS(status)) {

KdPrint(("HelloDriver: WdfDeviceCreate failed: 0x%x\n", status));

return status;

}

// 创建设备接口

status = WdfDeviceCreateDeviceInterface(

device,

&GUID_DEVINTERFACE_HELLO, // 自定义GUID

NULL

);

if (!NT_SUCCESS(status)) {

KdPrint(("HelloDriver: WdfDeviceCreateDeviceInterface failed: 0x%x\n", status));

return status;

}

return status;

}

五、I/O请求处理

驱动程序的核心功能是处理来自应用程序的I/O请求。

5.1 I/O队列配置

c

复制代码

NTSTATUS HelloCreateIoQueue(WDFDEVICE Device)

{

NTSTATUS status;

WDF_IO_QUEUE_CONFIG queueConfig;

WDFQUEUE queue;

// 配置默认I/O队列

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(

&queueConfig,

WdfIoQueueDispatchSequential

);

// 设置I/O回调函数

queueConfig.EvtIoRead = HelloEvtIoRead;

queueConfig.EvtIoWrite = HelloEvtIoWrite;

queueConfig.EvtIoDeviceControl = HelloEvtIoDeviceControl;

// 创建I/O队列

status = WdfIoQueueCreate(

Device,

&queueConfig,

WDF_NO_OBJECT_ATTRIBUTES,

&queue

);

if (!NT_SUCCESS(status)) {

KdPrint(("HelloDriver: WdfIoQueueCreate failed: 0x%x\n", status));

}

return status;

}

5.2 读取操作处理

c

复制代码

VOID HelloEvtIoRead(

_In_ WDFQUEUE Queue,

_In_ WDFREQUEST Request,

_In_ size_t Length

)

{

NTSTATUS status;

PVOID buffer;

size_t bufferLength;

UNREFERENCED_PARAMETER(Queue);

KdPrint(("HelloDriver: Read request, Length = %zu\n", Length));

// 获取请求缓冲区

status = WdfRequestRetrieveOutputBuffer(

Request,

Length,

&buffer,

&bufferLength

);

if (!NT_SUCCESS(status)) {

KdPrint(("HelloDriver: WdfRequestRetrieveOutputBuffer failed: 0x%x\n", status));

WdfRequestComplete(Request, status);

return;

}

// 向缓冲区写入数据

const char* message = "Hello from Kernel Driver!";

size_t messageLength = strlen(message);

if (bufferLength >= messageLength) {

RtlCopyMemory(buffer, message, messageLength);

WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, messageLength);

} else {

WdfRequestComplete(Request, STATUS_BUFFER_TOO_SMALL);

}

}

5.3 写入操作处理

c

复制代码

VOID HelloEvtIoWrite(

_In_ WDFQUEUE Queue,

_In_ WDFREQUEST Request,

_In_ size_t Length

)

{

NTSTATUS status;

PVOID buffer;

size_t bufferLength;

UNREFERENCED_PARAMETER(Queue);

KdPrint(("HelloDriver: Write request, Length = %zu\n", Length));

// 获取输入缓冲区

status = WdfRequestRetrieveInputBuffer(

Request,

Length,

&buffer,

&bufferLength

);

if (!NT_SUCCESS(status)) {

KdPrint(("HelloDriver: WdfRequestRetrieveInputBuffer failed: 0x%x\n", status));

WdfRequestComplete(Request, status);

return;

}

// 处理写入的数据

KdPrint(("HelloDriver: Received %zu bytes of data\n", bufferLength));

// 完成请求

WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, bufferLength);

}

六、设备控制代码处理

DeviceIoControl是驱动程序与应用程序通信的重要方式。

c

复制代码

#define IOCTL_HELLO_GET_VERSION \

CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_READ_DATA)

#define IOCTL_HELLO_SET_DATA \

CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA)

VOID HelloEvtIoDeviceControl(

_In_ WDFQUEUE Queue,

_In_ WDFREQUEST Request,

_In_ size_t OutputBufferLength,

_In_ size_t InputBufferLength,

_In_ ULONG IoControlCode

)

{

NTSTATUS status = STATUS_SUCCESS;

size_t bytesTransferred = 0;

UNREFERENCED_PARAMETER(Queue);

UNREFERENCED_PARAMETER(OutputBufferLength);

UNREFERENCED_PARAMETER(InputBufferLength);

switch (IoControlCode) {

case IOCTL_HELLO_GET_VERSION:

{

PVOID outputBuffer;

size_t outputBufferSize;

ULONG version = 0x00010001; // 版本1.1

status = WdfRequestRetrieveOutputBuffer(

Request,

sizeof(ULONG),

&outputBuffer,

&outputBufferSize

);

if (NT_SUCCESS(status)) {

RtlCopyMemory(outputBuffer, &version, sizeof(ULONG));

bytesTransferred = sizeof(ULONG);

KdPrint(("HelloDriver: Get version: 0x%x\n", version));

}

}

break;

case IOCTL_HELLO_SET_DATA:

{

PVOID inputBuffer;

size_t inputBufferSize;

status = WdfRequestRetrieveInputBuffer(

Request,

1,

&inputBuffer,

&inputBufferSize

);

if (NT_SUCCESS(status)) {

KdPrint(("HelloDriver: Set data, size = %zu\n", inputBufferSize));

// 处理设置的数据

bytesTransferred = inputBufferSize;

}

}

break;

default:

status = STATUS_INVALID_DEVICE_REQUEST;

KdPrint(("HelloDriver: Unknown IOCTL: 0x%x\n", IoControlCode));

break;

}

WdfRequestCompleteWithInformation(Request, status, bytesTransferred);

}

七、用户态测试程序

为了测试我们的驱动程序,需要编写一个用户态应用程序。

cpp

复制代码

#include

#include

#include

#define IOCTL_HELLO_GET_VERSION \

CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_READ_DATA)

int main()

{

HANDLE hDevice;

DWORD bytesReturned;

char buffer[256];

ULONG version;

// 打开设备

hDevice = CreateFile(

L"\\\\.\\HelloDevice",

GENERIC_READ | GENERIC_WRITE,

0,

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

NULL

);

if (hDevice == INVALID_HANDLE_VALUE) {

std::cout << "Failed to open device: " << GetLastError() << std::endl;

return -1;

}

std::cout << "Device opened successfully!" << std::endl;

// 读取数据

if (ReadFile(hDevice, buffer, sizeof(buffer), &bytesReturned, NULL)) {

buffer[bytesReturned] = '\0';

std::cout << "Read from driver: " << buffer << std::endl;

}

// 写入数据

const char* writeData = "Hello from user application!";

if (WriteFile(hDevice, writeData, strlen(writeData), &bytesReturned, NULL)) {

std::cout << "Successfully wrote " << bytesReturned << " bytes" << std::endl;

}

// 发送控制代码

if (DeviceIoControl(

hDevice,

IOCTL_HELLO_GET_VERSION,

NULL, 0,

&version, sizeof(version),

&bytesReturned,

NULL)) {

std::cout << "Driver version: " << std::hex << version << std::endl;

}

CloseHandle(hDevice);

return 0;

}

八、调试技巧

8.1 内核调试

使用WinDbg进行内核调试是驱动开发的重要技能:

复制代码

// 设置调试输出级别

!ed nt!Kd_DEFAULT_Mask 0xFFFFFFFF

// 查看驱动加载状态

lm m hello*

// 设置断点

bp HelloEvtIoRead

// 查看调用栈

kb

8.2 调试宏定义

c

复制代码

#ifdef DBG

#define DebugPrint(x) DbgPrint x

#else

#define DebugPrint(x)

#endif

// 使用示例

DebugPrint(("HelloDriver: Processing request, ID = %d\n", requestId));

九、最佳实践

9.1 错误处理

c

复制代码

NTSTATUS ProcessRequest(WDFREQUEST Request)

{

NTSTATUS status = STATUS_SUCCESS;

__try {

// 处理请求的代码

status = DoActualWork();

} __except (EXCEPTION_EXECUTE_HANDLER) {

status = GetExceptionCode();

KdPrint(("HelloDriver: Exception caught: 0x%x\n", status));

}

return status;

}

9.2 资源管理

c

复制代码

NTSTATUS AllocateResources()

{

PVOID buffer = NULL;

buffer = ExAllocatePoolWithTag(

NonPagedPool,

BUFFER_SIZE,

'lleH' // 'Hell' tag

);

if (buffer == NULL) {

return STATUS_INSUFFICIENT_RESOURCES;

}

// 使用buffer...

// 清理资源

if (buffer) {

ExFreePoolWithTag(buffer, 'lleH');

}

return STATUS_SUCCESS;

}

十、总结

Windows驱动程序开发是一个复杂但充满挑战的领域。本文介绍了:

基础概念:驱动程序的作用和特点

架构理解:WDM和WDF框架

开发实践:从Hello World到完整的I/O处理

调试技巧:内核调试和错误处理

最佳实践:资源管理和异常处理

进阶学习方向

高级I/O模型:异步I/O和DMA

电源管理:设备电源状态管理

即插即用:PnP和电源管理事件

过滤驱动:上层和下层过滤驱动

UMDF开发:用户模式驱动框架

驱动开发需要深厚的系统知识和仔细的编程态度。建议在虚拟机环境中进行开发和测试,避免因驱动错误导致系统崩溃。

参考资源

Microsoft Windows Driver Kit Documentation

Windows Internals (Mark Russinovich)

OSR Online (驱动开发社区)

Windows Hardware Dev Center

希望这篇文章能为Windows驱动开发的初学者提供有价值的入门指导。驱动开发是一个需要不断实践和学习的过程,祝愿大家在这个充满挑战的领域中取得成功!

站点统计