处理取消IRP

IRP通过KDevice的分发例程进入驱动程序。驱动程序可以立即处理它并返回状态信息,或者是将它排队并返回STATUS_PENDING

当一个IRP被排队,驱动程序应该使这个IRP可以取消,因为如果这个IRP的发起者提出一个取消请求,I/O管理器应该可以通知驱动程序响应这个请求。如果一个IRP拥有一个取消例程,则它是可以取消的。取消例程可以通过KDevice::QueueIrp, KIrp::TestAndSetCancelRoutine, KIrp::SetCancelRoutine进行设置。当驱动程序用KDevice::QueueIrp来排队IRP时,必须决定这个IRP是否可以取消。而用KDriverManagedQueue::QueueIrp排队的IRP总是可以取消的。

如果当一个请求已经被处理到驱动程序不能再取消它时,必须将这个请求的取消例程设置为NULL(用KIrp::TestAndSetCancelRoutineKIrp::SetCancelRoutine)。当一个IRP可以取消时驱动程序不能完成它。如果一个IRP的取消标志被设置时就没有必要清除取消例程。

 

下面是完成取消例程的步骤:

       KDevice子类的声明中,使用DEVMEMBER_CANCELIRP宏把取消例程声明为设备类(即KDevice的子类,译注)的成员函数。一些驱动开发者可能会发现用不同的取消成员函数处理不同种类的IRP或过程的不同阶段是很有用的。

 

class MyDevice : public KDevice
{
   public:
     MyDevice(void);
     . . .
     DEVMEMBER_CANCELIRP(MyDevice, Cancel)
 };

 

当你用KDevice::QueueIrp排队一个IRP时,将第二个参数设置为LinkTo(Cancel),这样可以连接IRP和设备类的静态函数(即取消例程,译注),这个函数是设备类的声明里用DEVMEMBER_CANCELIRP宏声明的非静态成员。

 

QueueIrp( I, LinkTo(Cancel) );

 

书写取消成员函数。当系统调用它时,它保存系统范围内的取消自旋锁,必须在完成这个请求之前释放。取消自旋锁在取消例程返回后不能继续保存。

 

VOID MyDevice::Cancel(KIrp I)
{
   if ( PIRP(I) == CurrentIrp() )
   {
     CancelSpinLock::Release(I->CancelIrql);
     I.Information() = 0;
     I.Status() = STATUS_CANCELLED;
     NextIrp(I);
   }
   else
   {
     KDeviceQueue dq(DeviceQueue());
     BOOLEAN bRemoved = dq.RemoveSpecificEntry(I);
     CancelSpinLock::Release(I->CancelIrql);
     if ( bRemoved )
     {
       I.Information() = 0;
       I.Complete(STATUS_CANCELLED);
     }
   }
}

在你的StarIo函数内,你必须检查取消标志并且清除取消例程。使用KIrp::TestAndSetCancelRoutine函数来完成:

 

VOID MyDevice::StartIo(KIrp I)
{
   if ( ! TestAndSetCancelRoutine(LinkTo(Cancel), NULL, CurrentIrp() )
     return;
   . . .
   // normal IRP processing
   . . .
}

 注意,强烈建议你给QueueIrp传递非空取消例程,但这不是必须的。如果你排队一个空取消历程的IRP,你必须始终在保存全局取消自旋锁时,在StartIo函数的开始部分测试取消标志。你可以调用TestAndSetCancelRoutine函数并把前两个参数设置为NULL完成这个功能。如果函数返回FALSE,你必须用STATUS_CANCELLED状态结束IRP,因为排队时没有有效的取消例程。

阅读更多

取消键盘驱动中的IRP

12-25

键盘过滤驱动中,挂接到键盘设备上之后,第一个按键,过滤驱动中是接收不到的.rn现在要解决这个问题:rn1.向键盘模拟发送一个按键,将那个IRP消耗掉rn2.结束掉这个IRPrnrn第一种想法:我在网上找到一些资料,但是都是汇编的,因为程序有32和64两种平台的,64位又不支持__asm,我对汇编实在不熟悉,也就没有改rn现在想从第二个办法入手,现在做的是,找到kbdclass的驱动对像,然后,遍历,驱动下面的设备,在设备中的DeviceExtension->ReadQueue中rn获取IRP,然后,cancel掉.rn用ObReferenceObjectByName这个函数找到kdbclass的驱动对像指针rncancel的代码如下:(我是从DDK里面找到源码)rn[code=c]rnVOIDrnKeyboardClassCleanupQueue (rn IN PDEVICE_OBJECT DeviceObject,rn IN PKDB_DEVICE_EXTENSION DeviceExtension,rn IN PFILE_OBJECT FileObjectrn )rn/*++rnRoutine Description:rnrn This does the work of MouseClassCleanup so that we can also do that workrn during remove device for when the grand master isn't enabled.rnrnrn--*/rnrnrn KdPrint(("In KeyboardClassCleanupQueue\n"));rn PIRP irp;rn LIST_ENTRY listHead, *entry;rn KIRQL irql;rn PIO_STACK_LOCATION stack;rn PDRIVER_CANCEL oldCancelRoutine;rnrn UNREFERENCED_PARAMETER(DeviceObject);rnrn InitializeListHead(&listHead);rnrn KeAcquireSpinLock(&DeviceExtension->SpinLock, &irql);rnrn /*if (IsListEmpty(DeviceExtension->ReadQueue.Flink))rn rn KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);rn return ;rn */rnrn for (entry = DeviceExtension->ReadQueue.Flink;rn entry != &DeviceExtension->ReadQueue;) rn PLIST_ENTRY nextEntry = entry->Flink;rn irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);rn stack = IoGetCurrentIrpStackLocation (irp);rnrn //rn // If no FileObject is given, lets cleanup all of them,rn // otherwise lets try to find only the matching onesrn //rn if (NULL == FileObject || stack->FileObject == FileObject) rn RemoveEntryList (entry);rnrn oldCancelRoutine = IoSetCancelRoutine (irp, NULL);rnrn //rn // IoCancelIrp() could have just been called on this IRP.rn // What we're interested in is not whether IoCancelIrp() was calledrn // (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (orrn // is about to call) our cancel routine. To check that, check the resultrn // of the test-and-set macro IoSetCancelRoutine.rn //rn if (oldCancelRoutine) rn //rn // Cancel routine not called for this IRP. Queue this IRP.rn //rn irp->IoStatus.Status = STATUS_CANCELLED;rn irp->IoStatus.Information = 0;rnrn InsertTailList (&listHead, &irp->Tail.Overlay.ListEntry);rn rn else rn //rn // This IRP was just cancelled and the cancel routine was (or willrn // be) called. The cancel routine will complete this IRP as soon asrn // we drop the spinlock. So don't do anything with the IRP.rn //rn // Also, the cancel routine will try to dequeue the IRP, so make thern // IRP's listEntry point to itself.rn //rn ASSERT (irp->Cancel);rn InitializeListHead (&irp->Tail.Overlay.ListEntry);rn rn rn entry = nextEntry;rn // break;rn rnrn KeReleaseSpinLock(&DeviceExtension->SpinLock, irql);rnrn //rn // Complete these irps outside of the spin lockrn //rn while (! IsListEmpty (&listHead)) rn entry = RemoveHeadList (&listHead);rn irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry);rnrn IoCompleteRequest (irp, IO_NO_INCREMENT);rn IoReleaseRemoveLock (&DeviceExtension->RemoveLock, irp);rn rnrnrn[/code]rn代码在进入for之后的第一句话PLIST_ENTRY nextEntry = entry->Flink;,就会出现异常.应该是内存访问的异常.我没有找到太多关于ReadQueue这个域的资料,有谁知道这个是因为什么?rn或者想要解决第一个按键拿不到的问题,是否有别的办法?望指教.

没有更多推荐了,返回首页