11、Windows驱动开发技术详解笔记(7) 基本语法回顾

简介: 9、驱动程序与应用程序通信  1)使用WriteFile通信  可以在应用层调用ReadFile 和WriteFile 分别从驱动中读取和写入数据,他们通过两个不同的IRP来传递信息。 http://msdn.

9、驱动程序与应用程序通信 

1)使用WriteFile通信 

可以在应用层调用ReadFile WriteFile 分别从驱动中读取和写入数据,他们通过两个不同的IRP来传递信息。

http://msdn.microsoft.com/en-us/library/ff549427%28VS.85%29.aspx

一个例子如下:

首先是我们的应用层程序代码:

 

代码
 
  
1 #include " windows.h "
2
3 #include " stdio.h "
4
5 int main()
6
7 {
8
9 char szInBuffer[ 20 ] = { 0 };
10
11 DWORD nLen = 0 ;
12
13 // 打开设备句柄
14
15 HANDLE hDevice = ::CreateFile( " \\\\.\\Test " , // 符号链接
16
17 GENERIC_READ | GENERIC_WRITE,
18
19 0 ,
20
21 NULL,
22
23 OPEN_EXISTING,
24
25 FILE_ATTRIBUTE_NORMAL,
26
27 NULL);
28
29 if (hDevice == INVALID_HANDLE_VALUE)
30
31 { return - 1 ;
32
33 }
34
35 // 向驱动设备写入连续10 个字节的A
36
37 memset(szInBuffer, ' A ' , 10 );
38
39 BOOL ret = WriteFile(hDevice,
40
41 szInBuffer,
42
43 10 ,
44
45 & nLen,
46
47 NULL);
48
49 // 关闭设备句柄
50
51 CloseHandle(hDevice);
52
53 return 0 ;
54
55 }
56
57 下面开始写驱动层的代码,首先添加一个IRP_MJ_WRITE 的派遣例程,如下所示:
58
59 NTSTATUS
60
61 TestDispatchWrite(
62
63 IN PDEVICE_OBJECT DeviceObject,
64
65 IN PIRP Irp
66
67 )
68
69 {
70
71 NTSTATUS Status = STATUS_SUCCESS;
72
73 PIO_STACK_LOCATION irpStack;
74
75 // 得到当前栈
76
77 irpStack = IoGetCurrentIrpStackLocation(Irp);
78
79 // 输出缓冲区字节数和内容
80
81 DbgPrint( " [Test] %d " , irpStack -> Parameters.Write.Length);
82
83 DbgPrint( " [Test] %s " , Irp -> AssociatedIrp.SystemBuffer);
84
85 // 完成IRP
86
87 Irp -> IoStatus.Information = irpStack -> Parameters.Write.Length;
88
89 Irp -> IoStatus.Status = Status;
90
91 IoCompleteRequest(Irp, IO_NO_INCREMENT);
92
93 return Status;
94
95 }

 

另外,还要有:在DriverEntry 中添加一行“deviceObject->Flags |= DO_BUFFERED_IO;”

这样,加裁驱动后,运行应用层代码。

2)使用DeviceIoControl 通信

使用前面的方法,我们得调用ReadFileWriteFile来读写数据,实际上更好更通用的做法是使用DeviceIoControl函数。这个函数还可以用来做一些除读写之外的操作。

DeviceIoControl函数会使操作系统产生一个IRP_MJ_DEVICE_CONTROL类型的IRP,然后这个IRP 会被分发到相应的派遣例程中。

我们先来看一下DeviceIoControl 函数的原型声明:

BOOL DeviceIoControl(

HANDLE hDevice, // handle to device

DWORD dwIoControlCode, // operation

LPVOID lpInBuffer, // input data buffer

DWORD nInBufferSize, // size of input data buffer

LPVOID lpOutBuffer, // output data buffer

DWORD nOutBufferSize, // size of output data buffer

LPDWORD lpBytesReturned, // byte count

LPOVERLAPPED lpOverlapped // overlapped information

);

需要重点掌握的是第二个参数dwIoControlCode,它是I/O 控制码,即IOCTL值,是一个32位的无符号整型数值。

实际上DeviceIoControlReadFileWriteFile相差不大,不过它可以同时提供输入/输出缓冲区,而且还可以通过控制码传递一些特殊信息。IOCTL值的定义必须遵循DDK的规定,我们可以使用宏CTL_CODE来声明,如下:

#define MY_DVC_IN_CODE \

(ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, \

0xa01, \

METHOD_BUFFERED, \

FILE_READ_DATA|FILE_WRITE_DATA)

其中0xa01这个数字是用户可以自定义的,其他的参数请照抄。

3)一个示例如下: 

驱动程序: 

#define MY_DVC_IN_CODE \ 

(ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, \

0xa09, \

METHOD_BUFFERED, \

FILE_READ_DATA|FILE_WRITE_DATA)

代码
 
   
1 /* ***********************************************************************
2
3 * 函数名称:MyDeviceIoControl
4
5 * 功能描述:创建设备文件
6
7 * 参数列表:
8
9 pDevObj:功能设备对象
10
11 pIrp:从IO请求包
12
13 * 返回 值:返回状态
14
15 ************************************************************************ */
16
17 NTSTATUS MyDeviceIoControl(
18
19 PDEVICE_OBJECT dev,
20
21 PIRP irp)
22
23 {
24
25 // 得到irpsp的目的是为了得到功能号、输入输出缓冲
26
27 // 长度等信息。
28
29 PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
30
31 // 首先要得到功能号
32
33 ULONG code = irpsp -> Parameters.DeviceIoControl.IoControlCode;
34
35 // 得到输入输出缓冲长度
36
37 ULONG in_len = irpsp -> Parameters.DeviceIoControl.InputBufferLength;
38
39 ULONG out_len = irpsp -> Parameters.DeviceIoControl.OutputBufferLength;
40
41 // 请注意输入输出缓冲是公用内存空间的
42
43 PVOID buffer = irp -> AssociatedIrp.SystemBuffer;
44
45 // 如果是符合定义的控制码,处理完后返回成功
46
47 if (code == MY_DVC_IN_CODE)
48
49 {
50
51 // … 在这里进行需要的处理动作
52
53 DbgPrint( " [Test] %s " , L " In if code == MY_DVC_IN_CODE\n " );
54
55 // KdPrint(("In if code == MY_DVC_IN_CODE\n"));
56
57 // 因为不返回信息给应用,所以直接返回成功即可。
58
59 // 没有用到输出缓冲
60
61 irp -> IoStatus.Information = 0 ;
62
63 irp -> IoStatus.Status = STATUS_SUCCESS;
64
65 }
66
67 else
68
69 {
70
71 // 其他的请求不接受。直接返回错误。请注意这里返
72
73 // 回错误和前面返回成功的区别。
74
75 DbgPrint( " [Test] %s " , L " In if esle code != MY_DVC_IN_CODE\n " );
76
77 irp -> IoStatus.Information = 0 ;
78
79 irp -> IoStatus.Status = STATUS_INVALID_PARAMETER;
80
81 }
82
83 IoCompleteRequest (irp,IO_NO_INCREMENT);
84
85 return irp -> IoStatus.Status;
86
87 }
88
89 pDriverObject -> MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDeviceIoControl;
90
91 应用程序:
92
93 #include " windows.h "
94
95 #include " stdio.h "
96
97 #include " Driver.h "
98
99 int main()
100
101 {
102
103 HANDLE device = CreateFile(L " \\\\.\\MyCDOSL1 " ,
104
105 GENERIC_READ | GENERIC_WRITE, 0 , 0 ,
106
107 OPEN_EXISTING,
108
109 FILE_ATTRIBUTE_SYSTEM, 0 );
110
111 DWORD length = 0 ; // 返回的长度
112
113 if (device == INVALID_HANDLE_VALUE)
114
115 {
116
117 // … 打开失败,说明驱动没加载
118
119 printf( " Error in device == INVALID_HANDLE_VALUE\n " );
120
121 }
122
123 char in_buffer[ 10 ] = { " Helllo " };
124
125 int in_buffer_len = 10 ;
126
127 BOOL ret = DeviceIoControl(device,
128
129 MY_DVC_IN_CODE, // 功能号
130
131 in_buffer, // 输入缓冲,要传递的信息,预先填好
132
133 in_buffer_len, // 输入缓冲长度
134
135 NULL, // 没有输出缓冲
136
137 0 , // 输出缓冲的长度为0
138
139 & length, // 返回的长度
140
141 NULL);
142
143 if ( ! ret)
144
145 {
146
147 // … DeviceIoControl失败
148
149 printf( " Error in !ret\n " );
150
151 }
152
153 // 关闭
154
155 CloseHandle(device);
156
157 return 0 ;
158
159 }

 


4)驱动层信息传出

驱动主动通知应用和应用通知驱动的通道是同一个,只是方向反过来。应用程序需要开启一个线程调用DeviceIoControl,(调用ReadFile亦可)。而驱动在没有消息的时候,则阻塞这个IRP的处理,等待有信息的时候返回。

应用程序调用DeviceIoControl,当没有消息的时候,这个调用不返回,应用程序自动等待(相当于等待事件),有消息的时候函数返回,并从缓冲区中读到消息。

实际上,驱动内部要实现这个功能,还是要用事件的,只是不用在应用和驱动之间传递事件了。

驱动内部需要制作一个链表,当有消息要通知应用的时候,则把消息放入链表中并设置事件,在DeviceIoControl的处理中等待事件。驱动如果有消息要通知应用,必须把消息放入队列尾并设置事件g_my_notify_eventMyGetPendingHead获得第一条消息。

代码
 
   
1 NTSTATUS MyDeviceIoCtrlOut(PIRP irp,ULONG out_len)
2
3 {
4
5 MY_NODE * node;
6
7 ULONG pack_len;
8
9 // 获得输出缓冲区。
10
11 PVOID buffer = irp -> AssociatedIrp.SystemBuffer;
12
13 // 从队列中取得第一个。如果为空,则等待直到不为空。
14
15 while ((node = MyGetPendingHead()) == NULL)
16
17 {
18
19 KeWaitForSingleObject(
20
21 & g_my_notify_event, // 一个用来通知有请求的事件
22
23 Executive,KernelMode,FALSE, 0 );
24
25 }
26
27 // 有请求了。此时请求是node。获得PACK要多长。
28
29 pack_len = MyGetPackLen(node);
30
31 if (out_len < pack_len)
32
33 {
34
35 irp -> IoStatus.Information = pack_len; // 这里写需要的长度
36
37 irp -> IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
38
39 IoCompleteRequest (irp,IO_NO_INCREMENT);
40
41 return irp -> IoStatus.Status;
42
43 }
44
45 // 长度足够,填写输出缓冲区。
46
47 MyWritePackContent(node,buffer);
48
49 // 头节点被发送出去了,可以删除了
50
51 MyPendingHeadRemove ();
52
53 // 返回成功
54
55 irp -> IoStatus.Information = pack_len; // 这里写填写的长度
56
57 irp -> IoStatus.Status = STATUS_SUCCESS;
58
59 IoCompleteRequest (irp,IO_NO_INCREMENT);
60
61 return irp -> IoStatus.Status;
62
63 }
64
65 这个函数的处理要追加到MyDeviceIoControl中。如下:
66
67 NTSTATUS MyDeviceIoControl(
68
69 PDEVICE_OBJECT dev,
70
71 PIRP irp)
72
73 {
74
75
76
77 if (code == MY_DVC_OUT_CODE)
78
79 return MyDeviceIoCtrlOut(dev,irp);
80
81
82
83 }

 


在这种情况下,应用可以循环调用DeviceIoControl,来取得驱动驱动通知它的信息。

参考

1Windows 驱动开发技术详解

2http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx

3Windows驱动学习笔记,灰狐

目录
相关文章
|
7天前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
18 0
|
7天前
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
15 0
|
7天前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
34 0
|
7天前
|
开发者 iOS开发 C#
Uno Platform 入门超详细指南:从零开始教你打造兼容 Web、Windows、iOS 和 Android 的跨平台应用,轻松掌握 XAML 与 C# 开发技巧,快速上手示例代码助你迈出第一步
【8月更文挑战第31天】Uno Platform 是一个基于 Microsoft .NET 的开源框架,支持使用 C# 和 XAML 构建跨平台应用,适用于 Web(WebAssembly)、Windows、Linux、macOS、iOS 和 Android。它允许开发者共享几乎全部的业务逻辑和 UI 代码,同时保持原生性能。选择 Uno Platform 可以统一开发体验,减少代码重复,降低开发成本。安装时需先配置好 Visual Studio 或 Visual Studio for Mac,并通过 NuGet 或官网下载工具包。
13 0
|
7天前
|
iOS开发 Android开发 MacOS
从零到全能开发者:解锁Uno Platform,一键跨越多平台应用开发的神奇之旅,让你的代码飞遍Windows、iOS、Android、macOS及Web,技术小白也能秒变跨平台大神!
【8月更文挑战第31天】从零开始,踏上使用Uno Platform开发跨平台应用的旅程。只需编写一次代码,即可轻松部署到Windows、iOS、macOS、Android及Web(通过WASM)等多个平台。Uno Platform为.NET生态带来前所未有的灵活性和效率,简化跨平台开发。首先确保安装了Visual Studio或VS Code及.NET SDK,然后选择合适的项目模板创建新项目。项目结构类似传统.NET MAUI或WPF项目,包含核心NuGet包。通过简单的按钮示例,你可以快速上手并构建应用。Uno Platform让你的技术探索之旅充满无限可能。
12 0
|
7天前
|
Kubernetes Cloud Native 开发者
探索云原生技术:Kubernetes入门与实践探索Windows操作系统的隐藏功能
【8月更文挑战第31天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性、效率和可靠性的关键。本文将带你了解云原生的核心组件之一——Kubernetes(K8s),通过浅显易懂的语言和实际代码示例,引导你步入这一强大工具的世界。无论你是初学者还是有经验的开发者,本篇都将为你打开一扇通向高效资源管理与自动化部署的大门。
|
2月前
|
Linux Apache C++
FFmpeg开发笔记(三十五)Windows环境给FFmpeg集成libsrt
该文介绍了如何在Windows环境下为FFmpeg集成SRT协议支持库libsrt。首先,需要安装Perl和Nasm,然后编译OpenSSL。接着,下载libsrt源码并使用CMake配置,生成VS工程并编译生成srt.dll和srt.lib。最后,将编译出的库文件和头文件按照特定目录结构放置,并更新环境变量,重新配置启用libsrt的FFmpeg并进行编译安装。该过程有助于优化直播推流的性能,减少卡顿问题。
70 2
FFmpeg开发笔记(三十五)Windows环境给FFmpeg集成libsrt
|
24天前
|
Windows
Windows 映射网络驱动器及删除-此网格连接不存在
Windows 映射网络驱动器及删除-此网格连接不存在
21 0
|
26天前
|
存储 编译器 Linux
Windows 32 汇编笔记(二):使用 MASM
Windows 32 汇编笔记(二):使用 MASM
|
2月前
|
存储 安全 数据安全/隐私保护
Windows 32 汇编笔记(一):基础知识
Windows 32 汇编笔记(一):基础知识
下一篇
DDNS