tiechui_lesson03_缓冲读写与自定义控制

学习了与应用层通过缓冲区方式的交互,包括读写,自定义控制等。小坑比较多,大部分是是头文件和设置上的错误,跟着视频敲想快进就跳过了一些细节。包括:

  1. <windef.h> 头文件的引用    //使用DWORD等类型
  2. switch语句中break的设置           //这个总是会容易忽略
  3. 应用层自定义码的设置需要引入头文件winioctl.h 

内核模块代码:

#include <ntifs.h>
#include <windef.h>	//使用DWORD等类型

#define DEVICE_NAME L"\\Device\\MyFirstDevicePlus"		//设备名称
#define SYM_NAME	L"\\??\\MyFirstDevicePlus"			//符号链接

//定义自定义控制码 (做一个减法)
#define IOCTL_MUL	CTL_CODE(FILE_DEVICE_UNKNOWN,0x855,METHOD_BUFFERED, FILE_ANY_ACCESS)

//分发函数
NTSTATUS MyCreate(PDEVICE_OBJECT pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device has be opened!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyClearUp(PDEVICE_OBJECT pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyClearUp!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyClose(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyClose!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyRead(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyRead!");

	//获取当前IRP堆栈信息
	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);

	//对应用户层的读请求
	ULONG readsize = pstack->Parameters.Read.Length;
	DbgPrint("用户请求读大小为%u\n", readsize);
	//对应用户层所分配的缓冲区的内存位置
	PCHAR readbuffer = pirp->AssociatedIrp.SystemBuffer;	

	// readbuffer的赋值实际上是对用户缓冲区的改变
	RtlCopyMemory(readbuffer,
		"This Message Come From Kernel.",
		strlen("This Message Come From Kernel."));

	pirp->IoStatus.Status = status;
	//对于Information的赋值是返回给用户程序实际读取的长度。
	pirp->IoStatus.Information = strlen("This Message Come From Kernel.");

	//输出下字符串的长度
	DbgPrint("Really Read Info Len is %lld\n", 
		strlen("This Message Come From Kernel."));
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}

NTSTATUS MyWrite(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyWrite!");

	//获取当前IRP堆栈信息
	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);

	//对应用户层的写请求
	ULONG writesize = pstack->Parameters.Write.Length;
	DbgPrint("用户请求写大小为%u\n", writesize);
	//对应用户层所分配的缓冲区的内存位置
	PCHAR writebuffer = pirp->AssociatedIrp.SystemBuffer;

	// 写入扩展设备之前先进行清0操作
	RtlZeroMemory(pdevice->DeviceExtension, 200);
	// writebuffer的数据写入到设备扩展里边
	RtlCopyMemory(pdevice->DeviceExtension,writebuffer,writesize);

	DbgPrint("写缓冲区内存地址:%p,设备扩展内容%s\n", writebuffer,
		(PCHAR)pdevice->DeviceExtension);

	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 13;

	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}

NTSTATUS MyControl(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyControl!");

	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);
	ULONG	iocode = pstack->Parameters.DeviceIoControl.IoControlCode;	// 获取控制码

	ULONG inlen = pstack->Parameters.DeviceIoControl.InputBufferLength;
	ULONG outlen = pstack->Parameters.DeviceIoControl.OutputBufferLength;
	ULONG ioinfo = 0;

	DbgPrint("InputBufferLength is %u\n", inlen);
	DbgPrint("OutputBufferLength is %u\n", outlen);

	switch (iocode)
	{
	case IOCTL_MUL:
	{
		//做一个减法

		DWORD indata = *(PDWORD)pirp->AssociatedIrp.SystemBuffer;
		DbgPrint("--Kernel Indata %d \n", indata);
		indata = indata * 5;
		*(PDWORD)pirp->AssociatedIrp.SystemBuffer = indata;z
		ioinfo = 50;

		//别忘记break!
		break;
	}
	default:
		status = STATUS_UNSUCCESSFUL;
		ioinfo = 0;
		break;
	}

	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = ioinfo;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}

//卸载函数
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
	DbgPrint("basedriver 卸载驱动\n");
	if (DriverObject->DeviceObject)
	{
		IoDeleteDevice(DriverObject->DeviceObject);
	}
	UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYM_NAME);
	IoDeleteSymbolicLink(&symLink);
}

//入口函数
NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT DriverObject, 
	IN PUNICODE_STRING RegistryPath
)
{
	UNREFERENCED_PARAMETER(RegistryPath);
	//注册卸载函数
	DriverObject->DriverUnload = DriverUnload;

	NTSTATUS status = STATUS_SUCCESS;
	PDEVICE_OBJECT pdevice;		// 用来接收创建的设备对象
	UNICODE_STRING devicename = { 0 };
	RtlInitUnicodeString(&devicename, DEVICE_NAME);

	//创建设备对象
	status = IoCreateDevice(DriverObject, 
							200,	// 定义设备扩展的大小,用来存放写入的数据
							&devicename, 
							FILE_DEVICE_UNKNOWN, 
							0, 
							TRUE, 
							&pdevice);

	if (!NT_SUCCESS(status))
	{
		KdPrint(("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n",status));
		DbgPrint("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n", status);
	}

	if (pdevice->Flags)
	{
		//不要忘了设备对象的读写方式,否则会蓝屏
		pdevice->Flags |= DO_BUFFERED_IO;	// 缓冲区方式的读写
	}



	//
	//创建成功,创建符号链接
	//
	UNICODE_STRING symname = { 0 };
	RtlInitUnicodeString(&symname, SYM_NAME);
	status = IoCreateSymbolicLink(&symname, &devicename);	// 符号链接名 设备名
	if (!NT_SUCCESS(status))
	{
		KdPrint(("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status));
		DbgPrint("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status);
	}

	//设置分发例程
	DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
	DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MyClearUp;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
	DriverObject->MajorFunction[IRP_MJ_READ] = MyRead;
	DriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyControl;

	return 0;
}

 应用层代码:

#include <stdio.h>
#include <Windows.h>
#include <winioctl.h>// 使用自定义控制码
#include <stdlib.h>

//定义自定义控制码 (做一个乘法)
#define IOCTL_MUL	CTL_CODE(FILE_DEVICE_UNKNOWN,0x855,METHOD_BUFFERED,	FILE_ANY_ACCESS)

int main()
{
	HANDLE hdevice = NULL;
	CHAR readBuffer[50] = { 0 };
	DWORD bread = 0;


	hdevice = CreateFile(L"\\\\.\\MyFirstDevicePlus", 
		GENERIC_WRITE | GENERIC_READ, 0
		, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hdevice != INVALID_HANDLE_VALUE)
	{
		printf("打开设备SUCCESS!\n");
	}
	else {
		printf("打开设备失败!\n");

		system("pause");
	}
	system("pause");


	//读文件
	if (ReadFile(hdevice, (PVOID)readBuffer, 50, &bread, NULL))
	{
		printf("readBuffer --地址 %p --内容 %s\n", readBuffer, readBuffer);
		printf("真正读取长度 bread is %d\n", bread);
	}
	else {
		printf("ReadFile failed !\n");
	}
	system("pause");


	//写文件
	if (WriteFile(hdevice, "This message come from R3.",
		strlen("This message come from R3."), &bread, NULL))
	{

		printf("返回写入长度 bread is %d\n", bread);	// 写固定了 
	}
	else {
		printf("WriteFile failed !\n");
	}
	system("pause");


	//自定义控制
	printf("自定义控制 !\n");
	DWORD a = 8888, b = 0;
	DeviceIoControl(hdevice, IOCTL_MUL, &a, 4, &b, 4, &bread, NULL);

	printf("in %d  out %d   really info %d\n", a, b, bread);



	//关闭句柄
	if (hdevice != nullptr)
	{
		CloseHandle(hdevice);
		printf("关闭句柄!\n");
	}

	system("pause");

	return 0;
}

运行效果:

 将a乘了5并返回给应用层的b变量。

小结:

视频代码写的很粗糙,不过很感谢锤总能带着敲代码找错误,很有帮助,能让我对与内核开发不再那么畏惧。

收获是:

  1. 用户层读数据的时候,在内核层可以将一些数据放到pirp->AssociatedIrp.SystemBuffer缓冲区中,那么用户层程序就可以获取到这个缓冲区中的数据了。
  2. 用户层写数据的时候,在内核层可以将用户层的写入缓冲区的数据先拷贝到设备扩展的区域中。
  3. 自定义控制的写法是设置好控制码,然后在分发例程中对该控制码进行处理,通过之前定义好的缓冲区的方式,与应用层进行交互。
  4. pirp->IoStatus.Information 这个字段是返回给发出请求的函数一些信息,类似下边这样设置后,函数就能获取到这个数值。
    pirp->IoStatus.Information = 13;
  5. 视频中只是简单介绍,想要写好驱动程序,还需要继续练习,加深理解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于标签的协同过滤算法实现与个人兴趣相关的文章推荐

一、前言 在当前信息爆炸的时代&#xff0c;每天都会涌现出大量的文章&#xff0c;人们有时候会感到信息的获取难度比筛选更大。而作为信息的提供者&#xff0c;我们应当为用户提供依据个人兴趣的文章推荐。 本项目中的文章标签相似度推荐功能使用了一种基于标签的协同过滤算…

Java版本的工程项目管理系统源代码之工程项目管理系统面临的挑战

​ ​工程项目管理系统是指从事工程项目管理的企业&#xff08;以下简称工程项目管理企业&#xff09;受业主委托&#xff0c;按照合同约定&#xff0c;代表业主对工程项目的组织实施进行全过程或若干阶段的管理和服务。 ​系统定义 工程项目管理企业不直接与该工程项目的总承包…

易视腾iS-E5-NGH_3798MV100_MT7601_卡刷固件包_当贝纯净桌面

易视腾iS-E5-NGH_3798MV100_MT7601_卡刷固件包_当贝纯净桌面 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&#xff0…

产品经理 - 原型图设计软件

原型图设计软件哪个好用&#xff1f;6款好用软件推荐&#xff01; - 知乎 原型图都可以用什么软件做&#xff1f;11款好用软件分享&#xff01; 摩客, 墨刀 2014 墨刀是A股上市公司万兴科技旗下的在线一体化产品设计协作平台 即时设计是一款支持在线协作的专业级 UI 设计工…

回首来路多感概,最是奋斗动人心。

我们必需要在不同的时代有不同的领悟&#xff0c;才能充满生机地去迎接生命中每个新的开始。 文章目录 前言 初心 成长 收获 憧憬 出发 前言 今天是我成为csdn创作者一周年纪念日&#xff0c;我非常开心能够和大家分享我的写作之旅。在这一年里&#xff0c;我经历了许多挑…

QT QGraphicsView 提升到 QChartView报错 解决方案

QT QGraphicsView 提升到 QChartView报错 解决方案 本文主要描述, 使用QT提供的QChartView来绘制图表,提升QGraphicsView控件继承QChartView后,然后将QGraphicsView提升到我们自己写的类,怎么才能确保提升后编译不报错. [问题描述] 使用QGraphicsView显示图表的时候,我们需要将…

基于 TiDB + Flink 实现的滑动窗口实时累计指标算法

作者&#xff1a;李文杰 前言 在不少的支付分析场景里&#xff0c;大部分累计值指标可以通过 Tn 的方式计算得到 。随着行业大环境由增量市场转为存量市场&#xff0c;产品的运营要求更加精细化、更快速反应&#xff0c;这对各项数据指标的实时性要求已经越来越高。产品如果能…

WRF模式

随着生态文明建设和“碳中和”战略的持续推进&#xff0c;我国及全球气候变化及应对是政府、科学界及商业界关注的焦点。气候是多个领域&#xff08;生态、水资源、风资源及碳中和等问题&#xff09;的主要驱动因素&#xff0c;合理认知气候变化有利于解释生态环境变化机理及过…

使用SaleSmartly自动化流程的 5 个原因

想象一下&#xff0c;如果您可以采用智能数字解决方案来减轻团队和公司的手动和重复业务流程负担。它可以帮助您节省时间、提高公司的底线、消除冗余并增强数据管理。SaleSmartly&#xff08;ss客服&#xff09;就是这样。 通过利用自动化的力量&#xff0c;SaleSmartly&#x…

【Java】继承和多态

文章目录 一、继承1.继承的例子&#xff08;is-a&#xff09;2.组合的例子&#xff08;has-a&#xff09; 二、多态1.重写2.重载 三、继承的语法四、继承的注意事项1.初始化的顺序&#xff1a;2.super关键字 五、继承访问限定符六、多态实现方式七、多态的理解注意事项&#xf…

MGV2000_2+16_当贝纯净桌面卡刷固件包-内有教程

MGV2000_216_当贝纯净桌面卡刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&#xff0c;运行速度提…

Python爬虫入门之爬虫解析提取数据的四种方法

本文主要介绍了Python爬虫入门之爬虫解析提取数据的四种方法&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家Python爬虫的学习有所帮助。 基础爬虫的固定模式 笔者这里所谈的基础爬虫&#xff0c;指的是不需要处理像异步加载、验证码、代理等高阶爬虫技术的爬虫…

数据结构之队列,实现队列的增删改查

目录 一、队列的定义 二、队列的实现 1.使用链表来实现队列 2.实现队列的接口 初始化队列 void QueueInit(Queue *pq) 队尾入队列 void QueuePush(Queue *pq,QDataType data) 队头出队列 void QueuePop(Queue *pq) 获取队列头部元素 QDataType QueueFront(Queue *pq) …

2D火焰特效

Unity面片实现火焰效果 一、效果说明 大家好&#xff0c;我是阿赵。这是一个火焰的效&#xff0c;不过它不是粒子做的&#xff0c;是用一个面片做的&#xff0c;可以理解成是2D的特效。这个例子很简单&#xff0c;但可以拓展一下思路&#xff0c;原来除了用序列帧和粒子做动画…

Cadence Allegro BGA类器件扇孔操作教程

对于BGA扇孔&#xff0c;同样过孔不宜打孔在焊盘上&#xff0c;推荐打孔在两个焊盘的中间位置。很多工程师为了出线方便&#xff0c;随意挪动BGA里面过孔的位置&#xff0c;甚至打在焊盘上面&#xff0c;如图1所示&#xff0c;从而造成BGA区域过孔不规则&#xff0c;易造成后期…

行为型模式-中介者模式

中介者模式 概述 一般来说&#xff0c;同事类之间的关系是比较复杂的&#xff0c;多个同事类之间互相关联时&#xff0c;他们之间的关系会呈现为复杂的网状结构&#xff0c;这是一种过度耦合的架构&#xff0c;即不利于类的复用&#xff0c;也不稳定。例如在下左图中&#xf…

HR如何快速提升工作效率?

从招聘到用人管理各个环节&#xff0c;人力资源部门都是公司最重要的职能部门之一&#xff0c;hr的日常工作涉及众多复杂繁琐的内容&#xff0c;比如人员招聘&#xff0c;考核培训等都离不开大量的数据整理和录入操作&#xff0c;但那些和“人”相关的数据信息&#xff0c;经常…

SOFA Weekly|开源之夏 MOSN 与 Layotto 项目简介、社区会议预告、社区本周贡献

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展 欢迎留言互动&#xff5e; SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&am…

SpringCloud全面学习笔记之初尝美妙篇

目录 前言初识微服务单体架构分布式架构微服务架构初见SpringCloud微服务治理分布式服务架构案例 微服务组件及使用Eureka注册中心提供者和消费者Eureka的结构和作用搭建Eureka服务注册服务服务发现Eureka注册服务总结 Ribbon负载均衡原理负载均衡原理负载均衡策略懒加载 Nacos…

gtest之高级主题

目录 Value-Parameterized测试示例Value-Parameterized实现 类型测试Type-Parameterized Tests注册test程序获取当前测试程序名运行测试程序选项选择性测试重复测试无序执行分发到不同的机器控制输出Controlling How Failures Are Reported结合Sanitizer Value-Parameterized测…