(转)驱动开发之五 --- TDI之八 【译文】

http://hi.baidu.com/combojiang/item/fe7ee5147f64a621f6625c0e

步骤8:关闭句柄
这个函数被两个句柄调用,传输句柄和上下文句柄。

NTSTATUS TdiFuncs_CloseTdiOpenHandle(HANDLE hTdiHandle,

                                  PFILE_OBJECT pfoTdiFileObject)

{

    NTSTATUS NtStatus = STATUS_SUCCESS;

 

    /*

     * De-Reference the FILE_OBJECT and Close The Handle

     */

 

    ObDereferenceObject(pfoTdiFileObject);

    ZwClose(hTdiHandle);

 

    return NtStatus;

}

 

其他资源

 

一旦你熟悉了,TDI接口就变得简单了。当写驱动时,最大的事情就是处理IRP。TDI似乎比socket有些复杂,但是它是内核接口。

 

如果曾经研究过TDI或者NDIS,你大概进入过Thomas Divine. 如果你想购买复杂的TDI或者NDIS示例,你可以找他们,或者他公司网站上的其他资源。在上面你也可以找到其他不同网站的指南。

 

IRP处理

 

下篇文章将粗微的谈到一些IRP的基本概念和怎样处理他们。实际上那里描述的东西有一些大的缝隙,为了保持下篇文章简单,所以在本篇我们将加快步伐,尽可能多的去填充这些缝隙。在这时,正面的揭示这些给驱动开发者,我们能非常简单的做这个。然而这里有大量的信息,并不是所有的都在示例代码中体现出来了。你需要自己试验这些IRP的处理,这是开发驱动的根本部分。

驱动请求

 

当写驱动的时候,有两次不同的揭示IRP. 即:发给你自己驱动的请求和你创建的IRP,发送给在其他驱动的请求两种。我记得,有一个驱动栈,栈里的每一个驱动在IRP中都有自己的栈区域。每次一个IRP沿着栈向下发送,IRP的当前栈区域是优先的。当它到达你的驱动时,你可以有少量的选择。

前进和遗忘

 

你可以使用IoCallDriver使IRP向前到栈中的下一个驱动。这是我们在其它驱动指南中的做法。我们向前了IRP并且忘记它。可是这里有一个问题,我们并没有考虑到STATUS_PENDING的情况。STATUS_PENDING是实现异步操作的一个方法。由底层驱动通知调用者,他们没有完成这个IRP。或许他们也正在一个单独的线程中完成这个IRP. 它的规则是如果你返回STATUS_PENDING,你必须在返回之前调用IoMarkIrpPending。如果你已经向前把IRP传给了下一个驱动,现在这仍然是个问题。在调用后,不允许你接触它。所以实际上你有两个选择。

   IoMarkIrpPending(Irp);

   IoCallDriver(pDeviceObject, Irp);

 

   return STATUS_PENDING;

 

 

第二个选择是设置一个完成例程。你应该记得在第四篇文章中的代码,我们使用完成例程通过返回STATUS_MORE_PROCESSING_REQUIRED而不是STATUS_SUCCESS,停止了一个IRP的完成。,

   IoSetCompletionRoutine(Irp, CompletionRoutine, NULL, TRUE, TRUE, TRUE);

 

   return IoCallDriver(pDeviceObject, Irp);

 

...

 

   NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject,

                             PIRP Irp, PVOID Context)

   {

      if(Irp->PendingReturned)

      {

        IoMarkIrpPending(Irp);

      }

 

      return STATUS_SUCCESS;

   }

你可以在这里再一次的停止处理过程,如果你这么做,你需要使用IoMarkIrpPending。这里有个循环逻辑,如果你调用IoMarkIrpPending,那么你必须从你的驱动中返回STATUS_PENDING,如果你从你的驱动中返回STATUS_PENDING,你必须调用IoMarkIrpPending。记住,如果你停止一个完成的处理,那么这就意味着你必须完成它。我们在第四篇就是这么做的。

 

需要注意一点,如果一个完成例程不提供这些,I/O管理器会把"IoMarkIrpPending"告诉给你。在这个或许你不想去相信的主题中,无论信息是多么分散,你要确信你所做的任何事情都是对的。

   向前和投递处理

 

这和我们在第四篇的处理有些细微的差别。我们需要考虑pending的情况,如果IRP从底层驱动中返回pending的状态,我们需要等待,直到底层驱动完成它。一旦驱动完成,我们需要唤醒原始线程,这样我们就可以处理和完成IRP. 作为一个最佳方式,如果pending状态返回,我们只想设置事件。如果任何事情被同步处理,就不需要增加前面的设置和等待事件。下面是代码示例。

 

IoSetCompletionRoutine(Irp, CompletionRoutine,

                  &kCompleteEvent, TRUE, TRUE, TRUE);

 

   NtStatus = IoCallDriver(pDeviceObject, Irp);

 

 

   if(NtStatus == STATUS_PENDING)

   {

        KeWaitForSingleObject(&kCompleteEvent,

                     Executive, KernelMode, FALSE, NULL);

 

        /*

         * Find the Status of the completed IRP

         */

 

       NtStatus = IoStatusBlock.Status;

   }

 

   /*

    * Do Post Processing

    */

 

   IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    

   return NtStatus;

   

   ...

 

   NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject,

                                     PIRP Irp, PVOID Context)

   {

      if(Irp->PendingReturned)

      {

         KeSetEvent(Context, IO_NO_INCREMENT, FALSE);

      }

 

      return STATUS_MORE_PROCESSING_REQUIRED;

   }

posted @ 2013-01-15 11:10  himessage  阅读(724)  评论(0编辑  收藏  举报