就凑合写写,凑合看看吧
写过滤驱动的时候,一般有一个这样的需要,
在过滤某个请求request1的时候,进入到过滤的dispatch1函数,此时,我们需要发送IRP查询某些信息,但这些信息需要在request1完成后才能查询得到。
于是常见的会有2种写法:
第一种:
CompleteRoutine(context)
{
KeSetEvent(&context->NoticEvent, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
///
dispatch1(irp)
{
IO_STATUS_BLOCK ioStatusBlock;
IoSetCompletionRoutine( Irp, CompleteRoutine, (PVOID)context, TRUE, TRUE, TRUE );
iocallDriver(NextDevice, irp);
KeWaitForSingleObject(&context->NoticEvent)
queryIrp = TdiBuildInternalDeviceControlIrp( TDI_QUERY_INFORMATION,
NextDeviceObject,
irps->FileObject,
NULL,
&ioStatusBlock
);
iocallDriver(queryIrp)
}
=================================
CompleteRoutine2(context)
{
.....
iocallDriver(queryIrp)
.....
}
第二种写法:
dispatch1(irp)
{
IO_STATUS_BLOCK ioStatusBlock;
queryIrp = TdiBuildInternalDeviceControlIrp( TDI_QUERY_INFORMATION,
NextDeviceObject,
irps->FileObject,
NULL,
&ioStatusBlock
);
IoSetCompletionRoutine( Irp, CompleteRoutine2, (PVOID)context, TRUE, TRUE, TRUE );
iocallDriver(NextDevice, irp);
}
2种方法的代码大概如上,它们的区别主要是发起queryIrp的不同位置,第一种是直接在dispatch1中发起,第二种则是在完成例程中发起(不少网上流传的代码是这样)。
在极端的情况下会发生严重的后果,这个严重的情况就是当上面的第二种情况涉及到3条线程的时候则会出现。哪3条线程呢?
第1条:dispatch1()这个原始的请求线程
第2条:CompleteRoutine2(),当request1被异步完成时,那CompleteRoutine2就是在第2条线程中被调用的
第3条:在CompleteRoutine2()中发起queryIRP,当queryIrp这个IRP被异步完成时,则会IoCompleteRequest(queryIRP)的调用者是第3条线程
在上面情况发生时,第3条线程是Io manager在处理,它会使用special apc来完成,这个special apc的执行环境会回到第2条线程中执行,这时会把结果拷到当初建立的queryIrp 的ioStatusBlock 结构中
但如果这时候线程1,已经返回或者退出了的话,那这个ioStatusBlock 就很危险了,因为当初的ioStatusBlock 是在第1条线程栈中的,special apc 拷结果的时候,也许ioStatusBlock 的地址是新起线程的栈(第1条线程退出),
或者还是线程1的其它函数的栈。发生这种情况就会发现蓝屏的原因不知道为什么栈上的数据被改了,原因很难查明,特别还不是必现的!
完