驱动层信息传出
2021-07-01
180
0
驱动主动通知应用和应用通知驱动的通道是同一个。只是方向反过来。应用程序需要开启一个线程调用DeviceIoControl,(调用ReadFile亦可)。而驱动在没有消息的时候,则阻塞这个IRP的处理。等待有信息的时候返回。
有的读者可能听说过在应用层生成一个事件,然后把事件传递给驱动。驱动有消息要通知应用的时候,则设置这个事件。但是实际上这种方法和上述方法本质相同:应用都必须开启一个线程去等待(等待事件)。而且这样使应用和驱动之间交互变得复杂(需要传递事件句柄)。这毫无必要。
让应用程序简单的调用DeviceIoControl就可以了。当没有消息的时候,这个调用不返回。应用程序自动等待(相当于等待事件)。有消息的时候这个函数返回。并从缓冲区中读到消息。
实际上,驱动内部要实现这个功能,还是要用事件的。只是不用在应用和驱动之间传递事件了。
驱动内部需要制作一个链表。当有消息要通知应用的时候,则把消息放入链表中(请参考前面的“使用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;
}
这个函数的处理要追加到MyDeviceIoControl中。如下:
NTSTATUS MyDeviceIoControl(
PDEVICE_OBJECT dev,
PIRP irp)
{
…
if(code == MY_DVC_OUT_CODE)
return MyDeviceIoCtrlOut(dev,irp);
…
}
在这种情况下,应用可以循环调用DeviceIoControl,来取得驱动驱动通知它的信息。