设为首页 收藏本站
查看: 1039|回复: 0

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

[复制链接]

尚未签到

发表于 2015-5-14 07:22:59 | 显示全部楼层 |阅读模式
9、驱动程序与应用程序通信
1)使用WriteFile通信
可以在应用层调用ReadFile 和WriteFile 分别从驱动中读取和写入数据,他们通过两个不同的IRP来传递信息。
http://msdn.microsoft.com/en-us/library/ff549427%28VS.85%29.aspx
一个例子如下:
首先是我们的应用层程序代码:
  


DSC0000.gif DSC0001.gif 代码



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 通信
使用前面的方法,我们得调用ReadFile和WriteFile来读写数据,实际上更好更通用的做法是使用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位的无符号整型数值。
实际上DeviceIoControl与ReadFile和WriteFile相差不大,不过它可以同时提供输入/输出缓冲区,而且还可以通过控制码传递一些特殊信息。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_event,MyGetPendingHead获得第一条消息。




代码



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,来取得驱动驱动通知它的信息。

参考
【1】Windows 驱动开发技术详解
【2】http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx
【3】Windows驱动学习笔记,灰狐

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-66691-1-1.html 上篇帖子: Windows phone 7开发(二.页面间跳转与传值) 下篇帖子: [博客园西安俱乐部活动通知]微软 Windows 7 社区发布(2009-10-25)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表