windows驱动开发-WDF编程

news2024/12/23 13:37:09

文章目录

    • 前言
    • WDF编程前的准备工作
    • WDF编程
      • 创建驱动对象
      • 创建设备对象
      • 设备对象的回调函数
      • 链表操作
      • 驱动的测试代码

前言

注:本文的完整代码见仓库 18-WDF-reflect

代码参考自:junjiexing/libredirect

我不是从事驱动开发的专业人员,打酱油需要用到下驱动,故来了解下WDF编程。

我之前翻看的是《windows驱动开发技术详解》-张帆。书中的代码也敲了一点点,然后跑去看一些小项目的代码,发现还是没搞懂。

其中的一个原因在于,《windows驱动开发技术详解》书中的驱动是NT式驱动,而我看到的都是WDF框架开发的驱动。搞不清楚这些编程框架的本质区别,但是明显的一点是,从驱动开发框架出现的时间上来说,NT早于WDM, WDM早于WDF。ok,我再去补充下WDF编程接口。

windows驱动这块的中文书籍不多,这次翻看的是《Windows设备驱动程序WDF开发》。这本书似乎挺烂,因为我看明白。。网上有个入门视频,非常nice:【WDF驱动编程入门-哔哩哔哩】。如果有的NT驱动编程基础的话,非常容易入门。关于NT编程基础,可以参考我之前的博客:windows驱动开发环境搭建以及helloworld、windows内核编程-文件操作、windows驱动编程-应用与内核通信


WDF编程前的准备工作

需要NT编程基础,可以参考上面的链接。

WDF编程和NT编程差不多。NT编程中有的东西,WDF也有,只是调用的API不同。我们先看下驱动的组成部分:

  • 创建驱动对象。一个驱动对象代表了一个驱动程序,或者说一个内核模块。
  • 指定驱动对象的类型,驱动的卸载操作等。
  • 创建设备对象。一个驱动对象可以有多个设备。设备是接收请求的实体。
  • 为设备对象创建回调函数。打开/关闭设备执行的回调函数。接收DeviceIoControl发送的制代码,并执行相应的操作。(这里是重点)

WDF编程

这是一个demo。代码实现:从用户空间传递字符串给内核中的驱动;驱动将字符串保存在一个双向链表中;用户空间从驱动中提取字符串;

创建驱动对象

	// 创建WDF驱动程序对象
	WDF_DRIVER_CONFIG config;
	WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); // 非pnp驱动,不必提供EvtDriverDeviceAdd
	config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
	config.EvtDriverUnload = driver_unload;
	WDFDRIVER driver;
	NTSTATUS status = WdfDriverCreate(driver_obj, reg_path, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver);
	if (!NT_SUCCESS(status)) {
		KdPrint(("wdf-demo: fail in WdfDriverCreate."));
		return status;
	}

创建设备对象

#include "device.h"

NTSTATUS device_init(WDFDRIVER& driver, WDFDEVICE& device) {
	DECLARE_CONST_UNICODE_STRING(device_name, L"\\Device\\wdf_demo");
	DECLARE_CONST_UNICODE_STRING(dos_device_name, L"\\??\\wdf_demo");

	NTSTATUS status = STATUS_SUCCESS;

	// 创建一个设备对象

	 设置设备类型,设备缓冲模式,设备名
	PWDFDEVICE_INIT device_init = WdfControlDeviceInitAllocate(driver, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);
	if (device_init == NULL) {
		status = STATUS_INSUFFICIENT_RESOURCES;
		KdPrint(("wdf-demo: fail in WdfControlDeviceInitAllocate."));
		return status;
	}
	WdfDeviceInitSetDeviceType(device_init, FILE_DEVICE_UNKNOWN);
	WdfDeviceInitSetIoType(device_init, WdfDeviceIoBuffered); // 缓冲模式,类似DO_BUFFERED_IO
	WdfDeviceInitAssignName(device_init, &device_name);

	 设置设备创建和关闭的回调函数
	WDF_FILEOBJECT_CONFIG file_config;
	WDF_FILEOBJECT_CONFIG_INIT(&file_config, file_create, file_close, file_cleanup);
	WdfDeviceInitSetFileObjectConfig(device_init, &file_config, WDF_NO_OBJECT_ATTRIBUTES);

	status = WdfDeviceCreate(&device_init, WDF_NO_OBJECT_ATTRIBUTES, &device);
	if (!NT_SUCCESS(status)) {
		KdPrint(("wdf-demo: fail in WdfDeviceCreate, ret status is %d", status));
		// WdfControlDeviceInitAllocate得到的WDFDEVICE_INIT 结构,在驱动程序初始化错误的时候必须调用 WdfDeviceInitFree
		// 成功调用 WdfDeviceCreate 后,驱动程序不得调用 WdfDeviceInitFree
		WdfDeviceInitFree(device_init);
		return status;
	}

	 该设备的IO操作
	WDF_IO_QUEUE_CONFIG queue_config;
	WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queue_config, WdfIoQueueDispatchSequential); // 请求串行处理
	queue_config.EvtIoDeviceControl = device_ioctl; // 请求的处理函数
	WDFQUEUE queue;
	status = WdfIoQueueCreate(device, &queue_config, WDF_NO_OBJECT_ATTRIBUTES, &queue);
	if (!NT_SUCCESS(status)) {
		KdPrint(("wdf-demo: fail in WdfIoQueueCreate, ret status is %d", status));
		return status;
	}

	// 创建设备的符号链接
	status = WdfDeviceCreateSymbolicLink(device, &dos_device_name);
	if (!NT_SUCCESS(status)) {
		KdPrint(("wdf-demo: fail in WdfDeviceCreateSymbolicLink, ret status is %d", status));
		return status;
	}

	WdfControlFinishInitializing(device);

	return status;
}

设备对象的回调函数

#include "io_callbacks.h"
#include "../libdemo/demo_common.h"
#include "list.h"

VOID file_create(IN WDFDEVICE device, IN WDFREQUEST request, IN WDFFILEOBJECT object) {
	UNREFERENCED_PARAMETER(device);
	UNREFERENCED_PARAMETER(object);
	KdPrint(("wdf-demo: file_create."));
	WdfRequestComplete(request, STATUS_SUCCESS);
}


VOID file_close(IN WDFFILEOBJECT object)
{
	KdPrint(("wdf-demo: file_close."));
	UNREFERENCED_PARAMETER(object);
}

VOID file_cleanup(IN WDFFILEOBJECT object)
{
	KdPrint(("wdf-demo: file_cleanup."));
	UNREFERENCED_PARAMETER(object);
}

VOID device_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
	IN size_t out_length, IN size_t in_length, IN ULONG code)
{
	UNREFERENCED_PARAMETER(queue);
	UNREFERENCED_PARAMETER(out_length);
	UNREFERENCED_PARAMETER(in_length);

	NTSTATUS status;

	if (code == IOCTL_GET_MSG) {
		KdPrint(("wdf-demo:device_ioctl - IOCTL_GET_MSG"));
		void* outbuf;
		size_t outlen;
		status = WdfRequestRetrieveOutputBuffer(request, 0, &outbuf, &outlen);
		if (!NT_SUCCESS(status)) {
			KdPrint(("wdf-demo:device_ioctl - IOCTL_GET_MSG. get input buffer error: %d", status));
			WdfRequestComplete(request, STATUS_SUCCESS);
			return;
		}

		conn_item* node = remove_head();
		KdPrint(("wdf-demo:device_ioctl - IOCTL_SET_MSG. outbuf is %s", node->connect));
		if (node != NULL) {
			memcpy(outbuf, &node->connect, outlen);
			ExFreePoolWithTag(node, WDF_DEMO_TAG);
			WdfRequestCompleteWithInformation(request, status, strlen((char*)outbuf));
		}
		else {
			WdfRequestComplete(request, STATUS_SUCCESS);
		}
		return;
	}
	else if (code == IOCTL_SET_MSG) {
		KdPrint(("wdf-demo:device_ioctl - IOCTL_SET_MSG"));
		void *inbuf;
		size_t inlen;
		status = WdfRequestRetrieveInputBuffer(request, 0, &inbuf, &inlen);
		if (!NT_SUCCESS(status)) {
			KdPrint(("wdf-demo:device_ioctl - IOCTL_SET_MSG. get input buffer error: %d", status));
			WdfRequestComplete(request, STATUS_SUCCESS);
			return;
		}
		*((char*)inbuf + inlen) = '\0';
		KdPrint(("wdf-demo:device_ioctl - IOCTL_SET_MSG. in buf is %s", (char*)inbuf));
		add_tail(inbuf, inlen);
		WdfRequestComplete(request, STATUS_SUCCESS);
		return;
	}
	else {
		KdPrint(("wdf-demo:device_ioctl - UNKNOWS"));
		WdfRequestComplete(request, STATUS_UNSUCCESSFUL);
	}
}

链表操作

#include "list.h"

LIST_ENTRY connect_list;
WDFWAITLOCK connect_list_lock;

void init_list() {
	WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &connect_list_lock);
	InitializeListHead(&connect_list);
}

BOOLEAN add_tail(PVOID inbuf, size_t inlen) {
	if (inlen >= ENTER_MAX_SIZE) {
		return FALSE;
	}

	conn_item *node = (conn_item*)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(conn_item), WDF_DEMO_TAG);
	if (node == NULL) {
		KdPrint(("fail in ExAllocatePool2"));
		return FALSE;
	}
	memset(node, 0, sizeof(node));
	memcpy(&node->connect, inbuf, ENTER_MAX_SIZE);

	KdPrint(("wdf-demo:add tail . node->connect is %s", node->connect));
	WdfWaitLockAcquire(connect_list_lock, nullptr);
	InsertTailList(&connect_list, &node->list_entry);
	WdfWaitLockRelease(connect_list_lock);

	return TRUE;
}

conn_item* remove_head() {
	WdfWaitLockAcquire(connect_list_lock, nullptr);
	conn_item* node = NULL;
	if (IsListEmpty(&connect_list)) {
		goto end;
	}
	PLIST_ENTRY item = RemoveHeadList(&connect_list);
	node = CONTAINING_RECORD(item, conn_item, list_entry);
end:
	WdfWaitLockRelease(connect_list_lock);
	return node;
}

void clear_list() {
	WdfWaitLockAcquire(connect_list_lock, nullptr);
	while (!IsListEmpty(&connect_list)) {
		PLIST_ENTRY item = RemoveHeadList(&connect_list);
		conn_item* node = CONTAINING_RECORD(item, conn_item, list_entry);
		ExFreePoolWithTag(node, WDF_DEMO_TAG);
	}
	WdfWaitLockRelease(connect_list_lock);
}

驱动的测试代码

#include "../libdemo/demo_common.h"
#include <Windows.h>
#include <iostream>
#include <string>

int main(int argc, char *argv[]) {
    HANDLE device = CreateFile("\\\\.\\wdf_demo", GENERIC_READ | GENERIC_WRITE,
                             0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);                           
  if(device == INVALID_HANDLE_VALUE) {
    DWORD err_code = GetLastError();
    std::cerr << "fail to open device" << std::endl;
    std::cerr << "error code is: " << err_code << std::endl;
    return -1;
  }

  int ret_len = 0;

  std::string msg = "hello world";
  BOOL result = DeviceIoControl(device, IOCTL_SET_MSG, (LPVOID)(msg.c_str()), msg.size(), NULL, 0, (LPDWORD)&ret_len, NULL);
  if(result == 0) {
    DWORD err_code = GetLastError();
    std::cerr << "fail in DeviceIoControl-IOCTL_SET_MSG. error code is: " << err_code << std::endl;
    return 0;
  } 
  std::string msg_2 = "dlrow olleh";
  result = DeviceIoControl(device, IOCTL_SET_MSG, (LPVOID)(msg_2.c_str()), msg_2.size(), NULL, 0, (LPDWORD)&ret_len, NULL);


  char get_msg[1024] = { 0 };
  result = DeviceIoControl(device, IOCTL_GET_MSG, NULL, 0, &get_msg, 1024, (LPDWORD)&ret_len, NULL);
  std::cout << ret_len << std::endl;
  std::cout << get_msg << std::endl;
  char get_msg_2[1024] = { 0 };
  result = DeviceIoControl(device, IOCTL_GET_MSG, NULL, 0, &get_msg_2, 1024, (LPDWORD)&ret_len, NULL);
  std::cout << ret_len << std::endl;
  std::cout << get_msg_2 << std::endl;


  return 0;
}

输出如下所示:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/154978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

区块链Fabric-从入门到实战(二) Fabric环境搭建

Fabric ubuntu16虚拟机环境搭建Ubuntu16镜像ubuntu-16.04.3-server-amd64.iso &#xff08;如有需要&#xff0c;私信我&#xff09;修改软件源cd /etc/apt/sudo cp sources.list sources.list.baksudo gedit sources.listdeb-src http://mirror.neu.edu.cn/ubuntu/ xenial mai…

回望在CSDN的2022和展望全新的2023

前言时光如白驹过隙&#xff0c;这条消息提醒着我加入CSDN有一些日子了。4286天&#xff0c;只是一个普通的数字&#xff0c;却记录着与CSDN的每一天。过去太长&#xff0c;回望过去的2022&#xff0c;有很多的收获和成长。在C站结实了一些朋友&#xff0c;分享了自己在工作中的…

校园跑腿、校园脱单、代理、帮忙拿快递的微信小程序 基于SpringBoot、Mybatis-plus、mysql实现

一、文件夹说明 代码下载 地址:校园跑腿、校园脱单、代理、帮忙拿快递的微信小程序 server 后端项目 project&#xff1a; 项目 CBD&#xff1a; 校园跑腿服务&#xff08;校园CBD中心&#xff09; server-app: 小程序apiserver-pc: 小程序后台管理service-cgs-base-service:…

RK3399平台开发系列讲解(CPU篇)CPUFreq 中央处理器频率调节技术

🚀返回专栏总目录 文章目录 一、CPUFreq组成二、设备树配置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢中央处理器频率调节(Central Processing Unit frequency,CPUFreq)技术可以降低ARM芯片的功耗,例如在系统对任务压力较小时,通过调整处理器工作频率与输入…

Javascript判断点是否在多边型内

射线法Ray-casting Algorithm算法检查点point是否在多边形里面。用法&#xff1a;insidePolygon([[x1,y1],[x2,y2],[x3,y3]...], [x , y])参数说明&#xff1a;polygon多边形坐标集合&#xff0c;格式为[[x1,y1],[x2,y2],[x3,y3]...]。point 测试点坐标, 格式为[x , y]。返回tr…

技术分享| 视频监控融合方案

视频监控系统在各行业应用广泛&#xff0c;从早期的只是简单的实现通过视频记录监控区域的情况&#xff0c;到现在的监控侦测、智能报警、融合通信等功能&#xff0c;视频监控的作用已经不是简单的记录&#xff0c;分布在各地的视频监控摄像头可以通过复杂的软件算法实现更多智…

数据分析的尽头不是可视化,而是行动!行动!行动!

Kyligence Zen 联动飞书&#xff0c;支持一键推送指标&#xff0c;在飞书就能追踪关键指标的最新动态&#xff1b;指标对齐目标&#xff0c;目标拆解为飞书任务&#xff0c;实现从指标洞察到行动的丝滑闭环&#xff01; 指标是衡量目标的量化参数&#xff0c;也是企业将战略目标…

PC端网页特效:轮播图

轮播图 功能需求&#xff1a; 鼠标经过轮播图模块&#xff0c;左右按钮显示&#xff0c;离开隐藏左右按钮。点击右侧按钮一次&#xff0c;图片往左播放一张&#xff0c; 左侧按钮同理。图片播放的同时&#xff0c;下面小圆圈模块跟随一起变化。点击小圆圈&#xff0c;可以播放…

2023年系统集成项目管理工程师报考条件及时间安排

一、报考条件 二、考试时间安排 集成考试一年会考2次&#xff0c;上半年一次、下半年一次 考试内容&#xff1a; 三、考试知识点分布&#xff1a; 给出一点点中项备考攻略 中级我敢说是好考的&#xff0c;题目也不难&#xff0c;主要弄清楚47个过程的输入输出&#xff0c;还有…

Github每日精选(第87期):轻量级图表lightweight-charts

lightweight-charts TradingView 轻量级图表是最小和最快的金融 HTML5 图表之一。 如果您想在网页上将财务数据显示为交互式图表而不影响网页加载速度和性能&#xff0c;轻量级图表库是您的最佳选择。 如果您想用交互式图表替换静态图像图表&#xff0c;它是您的最佳选择。该…

2000亿补贴申请倒计时!维视智造院校实验室建设攻略来了(六)!

#千亿政策贴息助力院校设备升级#近期&#xff0c;关于高校教育信息化的利好政策密集出台。9月7日&#xff0c;国务院常务会议提出对高校、职业院校和实训基地等10大领域设备购置和更新改造新增贷款&#xff0c;实施阶段性鼓励政策&#xff0c;中央财政贴息2.5个百分点&#xff…

电脑重装系统后文件还能恢复吗?恢复文件的详细图文教程

电脑重装系统&#xff0c;简单来说就是重新安装电脑的操作系统。一般选择重新安装电脑的系统&#xff0c;无非是电脑蓝屏、系统运行速度慢、崩溃死机等问题。 很多人会有疑惑&#xff0c;电脑重装系统后文件还能恢复吗&#xff1f;重装系统会造成数据全部被清空的情况&#xf…

js性能优化小技巧(已更新)

1、if多条件判断如果if里面包含多个判断条件&#xff0c;可以把判断条件存到一个数组&#xff0c;然后在去这个数组中检索”输入的值“是否满足条件&#xff1b;function testIf(x) {// 冗余if (x a || x b || x c || x d) {console.log(x)}// 简洁if ([a, b, c, d].includ…

前端vue3+typescript搭建vite项目(初识vite+项目配置完善+屏幕适配)

一、文章引导 #mermaid-svg-zCCPryl8cvuE0QpI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-zCCPryl8cvuE0QpI .error-icon{fill:#552222;}#mermaid-svg-zCCPryl8cvuE0QpI .error-text{fill:#552222;stroke:#55222…

MySQL (五)------多表查询练习

我们在开发中&#xff0c;根据不同的业务需求往往需要通过2张及以上的表中去查询需要的数据。所以我们有必要学习2张及以上的表的查询。其实不管是几张表的查询&#xff0c;都是有规律可循的。 1.1 准备数据 -- 部门表 CREATE TABLE dept (id INT PRIMARY KEY PRIMARY KEY, --…

Databend 开源周报 第 75 期

Databend 是一款强大的云数仓。专为弹性和高效设计。自由且开源。即刻体验云服务&#xff1a;https://app.databend.com 。 What’s New 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 Features & Improvements ✨ Format 实现 JSON 输出格式 …

C++模板进阶(非类型模板参数 + 模板特化)

我们另一篇模板初阶介绍链接&#xff1a;http://t.csdn.cn/Ox8Dm 目录 一、非类型模板参数 1.1 非类型模板参数概念 1.2 模板类型的静态数组 二、模板特化 2.1 函数模板特化 2.2 类模板特化 2.2.1 类模板全特化 2.2.2 类模板半特化&#xff08;偏特化&#xff09; 2.2.…

Facebook运营主页需要注意的几个问题

Facebook运营主页需要注意的几个问题主页的权重和流量都是决定流量的关键因素&#xff0c;也就是我们常说的引流&#xff0c;而流量又是需要转化的&#xff0c;因为只有用户认可你&#xff0c;才会有更多的点击、收藏、分享和主页的链接。在社交媒体时代要想更好地推广品牌产品…

openssl 编译动态库 win11 vs2022

官网 openssl官网 安装perl activestate_perl_官网 需要下载cli_installer 下载后双击下载好的exe 一般就是下面这个执行文件 state-remote-installer.exe 需要按照提示在powershell中执行网页提示的命令。 安装nasm nasm官网 以管理员方式运行安装 并加入环境变量中…

《MySQL高级篇》十、数据库其他调优策略

文章目录1.数据库调优的措施1.1调优的目标1.2 如何定位调优问题1.3 调优的维度和步骤第1步&#xff1a;选择适合的DBMS第2步:优化表设计第3步:优化逻辑查询第4步:优化物理查询第5步:使用Redis或 Memcached 作为缓存第6步&#xff1a;库级优化2. 优化MySQL服务器2.1 优化服务器硬…