Windows驱动编程基础教程 联系客服

发布时间 : 星期四 文章Windows驱动编程基础教程更新完毕开始阅读01cc64564b73f242326c5f2a

楚狂人Windows驱动编程基础教程

irp->IoStatus.Information = 0;

irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest (irp,IO_NO_INCREMENT); return irp->IoStatus.Status; }

当然,在前面设置分发函数的时候,应该加上:

DriverObject->MajorFunctions[IRP_MJ_CREATE] = MyCreateClose; DriverObject->MajorFunctions[IRP_MJ_CLOSE] = MyCreateClose;

在应用层,打开和关闭这个设备的代码如下:

HANDLE device=CreateFile(\ GENERIC_READ|GENERIC_WRITE,0,0, OPEN_EXISTING,

FILE_ATTRIBUTE_SYSTEM,0);

if (device == INVALID_HANDLE_VALUE) {

// ?. 打开失败,说明驱动没加载,报错即可 }

// 关闭

CloseHandle(device);

8.3 应用层信息传入

应用层传入信息的时候,可以使用WriteFile,也可以使用DeviceIoControl。DeviceIoControl是双向的,在读取设备的信息也可以使用。因此本书以DeviceIoControl为例子进行说明。DeviceIoControl称为设备控制接口。其特点是可以发送一个带有特定控制码的IRP。同时提供输入和输出缓冲区。应用程序可以定义一个控制码,然后把相应的参数填写在输入缓冲区中。同时可以从输出缓冲区得到返回的更多信息。 当驱动得到一个DeviceIoControl产生的IRP的时候,需要了解的有当前的控制码、输入缓冲区的位置和长度,以及输出缓冲区的位置和长度。其中控制码必须预先用一个宏定义。定义的示例如下:

#define MY_DVC_IN_CODE \\

(ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, \\ 0xa01, \\

METHOD_BUFFERED, \\

FILE_READ_DATA|FILE_WRITE_DATA)

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

楚狂人Windows驱动编程基础教程

下面是获得这三个要素的例子:

NTSTATUS MyDeviceIoControl( PDEVICE_OBJECT dev, PIRP irp) {

// 得到irpsp的目的是为了得到功能号、输入输出缓冲 // 长度等信息。

PIO_STACK_LOCATION irpsp =

IoGetCurrentIrpStackLocation(irp); // 首先要得到功能号

ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode; // 得到输入输出缓冲长度 ULONG in_len =

irpsp->Parameters.DeviceIoControl.InputBufferLength; ULONG out_len =

irpsp->Parameters.DeviceIoControl.OutputBufferLength; // 请注意输入输出缓冲是公用内存空间的

PVOID buffer = irp->AssociatedIrp.SystemBuffer;

// 如果是符合定义的控制码,处理完后返回成功 if(code == MY_DVC_IN_CODE) {

? 在这里进行需要的处理动作

// 因为不返回信息给应用,所以直接返回成功即可。 // 没有用到输出缓冲

irp->IoStatus.Information = 0;

irp->IoStatus.Status = STATUS_SUCCESS; } else {

// 其他的请求不接受。直接返回错误。请注意这里返 // 回错误和前面返回成功的区别。 irp->IoStatus.Information = 0;

irp->IoStatus.Status = STATUS_INVALID_PARAMETER; }

IoCompleteRequest (irp,IO_NO_INCREMENT); return irp->IoStatus.Status; }

在前面设置分发函数的时候,要加上:

DriverObject->MajorFunctions[IRP_MJ_DEVICE_CONTROL] = MyCreateClose;

楚狂人Windows驱动编程基础教程

应用程序方面,进行DeviceIoControl的代码如下: HANDLE device=CreateFile(\ GENERIC_READ|GENERIC_WRITE,0,0, OPEN_EXISTING,

FILE_ATTRIBUTE_SYSTEM,0); BOOL ret;

DWORD length = 0; // 返回的长度

if (device == INVALID_HANDLE_VALUE) {

// ? 打开失败,说明驱动没加载,报错即可 }

BOOL ret = DeviceIoControl(device, MY_DVC_IN_CODE, // 功能号

in_buffer, // 输入缓冲,要传递的信息,预先填好 in_buffer_len, // 输入缓冲长度 NULL, // 没有输出缓冲

0, // 输出缓冲的长度为0 &length, // 返回的长度 NULL);

if(!ret) {

// ? DeviceIoControl失败。报错。 }

// 关闭

CloseHandle(device);

8.4 驱动层信息传出

驱动主动通知应用和应用通知驱动的通道是同一个。只是方向反过来。应用程序需要开启一个线程调用DeviceIoControl,(调用ReadFile亦可)。而驱动在没有消息的时候,则阻塞这个IRP的处理。等待有信息的时候返回。 有的读者可能听说过在应用层生成一个事件,然后把事件传递给驱动。驱动有消息要通知应用的时候,则设置这个事件。但是实际上这种方法和上述方法本质相同:应用都必须开启一个线程去等待(等待事件)。而且这样使应用和驱动之间交互变得复杂(需要传递事件句柄)。这毫无必要。

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

楚狂人Windows驱动编程基础教程

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

驱动内部需要制作一个链表。当有消息要通知应用的时候,则把消息放入链表中(请参考前面的“使用LIST_ENTRY”),并设置事件(请参考前面的“使用事件”)。在DeviceIoControl的处理中等待事件。下面是一个例子:这个例子展示的是驱动中处理DeviceIoControl的控制码为MY_DVC_OUT_CODE的部分。实际上驱动如果有消息要通知应用,必须把消息放入队列尾并设置事件g_my_notify_event。MyGetPendingHead获得第一条消息。请读者用以前的知识自己完成其他的部分。

NTSTATUS MyDeviceIoCtrlOut(PIRP irp,ULONG out_len) {

MY_NODE *node; ULONG pack_len; // 获得输出缓冲区。

PVOID buffer = irp->AssociatedIrp.SystemBuffer;

// 从队列中取得第一个。如果为空,则等待直到不为空。 while((node = MyGetPendingHead()) == NULL) {

KeWaitForSingleObject(

&g_my_notify_event,// 一个用来通知有请求的事件 Executive,KernelMode,FALSE,0); }

// 有请求了。此时请求是node。获得PACK要多长。 pack_len = MyGetPackLen(node); if(out_len < pack_len) {

irp->IoStatus.Information = pack_len; // 这里写需要的长度 irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; IoCompleteRequest (irp,IO_NO_INCREMENT); return irp->IoStatus.Status; }

// 长度足够,填写输出缓冲区。 MyWritePackContent(node,buffer); // 头节点被发送出去了,可以删除了 MyPendingHeadRemove (); // 返回成功

irp->IoStatus.Information = pack_len; // 这里写填写的长度 irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest (irp,IO_NO_INCREMENT); return irp->IoStatus.Status; }