首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图

这个完成函数小弟我看不太明白

2014-03-14 来源:读书人网 【读书人网(Reader8.cn):综合教育门户网站】
这个完成函数我看不太明白这个完成函数我看不太明白,首先我不明白在什么时候会出现irp-CurrentLocation

这个完成函数我看不太明白
这个完成函数我看不太明白,首先我不明白在什么时候会出现irp->CurrentLocation <= 的情况,我发现将完成函数设置到了本层上。通过IoSkipCurrentIrpStackLocation + IoSetCompletionRoutine完成。在这个完成函数中做的一系列操作我看不明白,请大牛指点,谢谢

/*
 * completion routine for case if we use IoSkipCurrentIrpStackLocation way
 */
NTSTATUS
tdi_skip_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
  TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)Context;
  NTSTATUS status;
  PIO_STACK_LOCATION irps;

  if (Irp->IoStatus.Status != STATUS_SUCCESS)
    FilterDebugPrint(("%s: status 0x%x\n", __FUNCTION__, Irp->IoStatus.Status));

  // restore IRP for using in our completion

  Irp->CurrentLocation--;
  Irp->Tail.Overlay.CurrentStackLocation--;

  irps = IoGetCurrentIrpStackLocation(Irp);

  DeviceObject = irps->DeviceObject;

  if (ctx->new_cr != NULL) {
    // restore fileobject (it's NULL)
    irps->FileObject = ctx->fileobj;
    // set new device object in irps
    irps->DeviceObject = ctx->new_devobj;
    
    // call new completion 
    status = ctx->new_cr(ctx->new_devobj, Irp, ctx->new_context);//(C)ctx->new_context = context;(B)completion->context = query_irp;(A) query_irp = TdiBuildInternalDeviceControlIrp(TDI_QUERY_INFORMATION,//生成一个空的请求

  } 
  else
  {
    //i need to release the counter at here.((:(
    status = STATUS_SUCCESS;
  }
  /* patch IRP back */

  // restore routine and context (and even control!)
  irps->CompletionRoutine = ctx->old_cr;
  irps->Context = ctx->old_context;
  irps->Control = ctx->old_control;

  // restore device object
  irps->DeviceObject = DeviceObject;

  Irp->CurrentLocation++;
  Irp->Tail.Overlay.CurrentStackLocation++;

  if (ctx->old_cr != NULL) {

    if (status != STATUS_MORE_PROCESSING_REQUIRED) {
      // call old completion (see the old control)
      BOOLEAN b_call = FALSE;

      if (Irp->Cancel) {
        // cancel
        if (ctx->old_control & SL_INVOKE_ON_CANCEL)
          b_call = TRUE;
      } else {
        if (Irp->IoStatus.Status >= STATUS_SUCCESS) {
          // success
          if (ctx->old_control & SL_INVOKE_ON_SUCCESS)
            b_call = TRUE;
        } else {
          // error
          if (ctx->old_control & SL_INVOKE_ON_ERROR)
            b_call = TRUE;
        }
      }

      if (b_call)
        status = ctx->old_cr(DeviceObject, Irp, ctx->old_context);
    
    } else {

      /*
       * patch IRP to set IoManager to call completion next time
       */

      // restore Control
      irps->Control = ctx->old_control;

    }
  }

  ExFreePool(ctx);

  return status;
}

引用它的地方:

if (cr == NULL || irp->CurrentLocation <= 1/*前提建立在cr!=0时*/) // WINDBG时:irp->CurrentLocation = 2  一个是过滤设备,下一个是Tcp&Udp原设备
    {
      /*
       * we use _THIS_ way of sending IRP to old driver
       * a) to avoid NO_MORE_STACK_LOCATIONS


       * b) and if we haven't our completions - no need to copy stack locations!明白了
       */

      // stay on this location after IoCallDriver 这句话的意思是若irp->CurrentLocation == 1,然后调用该函数使irp->CurrentLocation = 2,这样在设置完成函数就可以设到本层了,然后调用IoCallDriver后,还是使用本层的堆栈。
      IoSkipCurrentIrpStackLocation(irp);//满足cr==NULL(说明不需要关心下层的处理结果)【或】满足cr!=NULL和irp->CurrentLocation <= 1(虽然想关心下层的处理结果,但是已经到最底层,这种情况可能是说过滤设备还没有附加好TCP&UDP设备)时: 跳过本层堆栈
    
      if (cr != NULL) //满足cr!=NULL和irp->CurrentLocation <= 1:虽然想关心下层的处理结果,但是已经到最底层
      {
        
        //set completion routine (this way is slow)
        // save old completion routine and context
        TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)ExAllocatePoolWithTag(NonPagedPool, sizeof(*ctx), POOL_TAG);
        if (ctx == NULL) {

          FilterDebugPrint(("%s: ExAllocatePoolWithTag error\n", __FUNCTION__));
          
          status = irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
          IoCompleteRequest(irp, IO_NO_INCREMENT);

          return status;
        }

        ctx->old_cr = irps->CompletionRoutine;//当前的完成函数
        ctx->old_context = irps->Context;//当前的完成函数的上下文
        ctx->new_cr = cr;//包含了完成例程的函数地址 completion->routine = tdi_create_addrobj_complete;
        ctx->new_context = context;//completion->context = query_irp;
        ctx->fileobj = irps->FileObject;
        ctx->new_devobj = devobj;

        ctx->old_control = irps->Control;

        IoSetCompletionRoutine(irp, tdi_skip_complete, ctx, TRUE, TRUE, TRUE);
        DbgPrint("in tdi_dispatch_complete,abnormal status irp->CurrentLocation <= 1:irp:0X%X.\n",irp);
        
        /*status = IoCallDriver(old_devobj, irp);
        return status;*/
      
      
      }
      else //cr == NULL 因为或运算是每个判断条件单线判断,如 IRP_MJ_CREATE时生成连接终端的IRP时,cr = null 就会执行这里
      {
        DbgPrint("in tdi_dispatch_complete,abnormal status,irp:0X%X.\n",irp);//当CR==null时就会执行这个,不涉及irp->CurrentLocation <= 1,相当于前面就调用了一句IoSkipCurrentIrpStackLocation(irp);当到底层TCP的设备栈时irp->CurrentLocation应该是2,因为这个函数给+1,调用IoCallDriver后就应该为2了
      }

    } 
    else //到这里必满足:(cr != NULL和 irp->CurrentLocation > 1,所以上方或运算的第一个条件已经判断判断cr是否等于空,这里又判断cr是否等于空,所以必是重复判断,也不会满足cr==null的情况,即也不会执行IoSetCompletionRoutine(irp, tdi_generic_complete, NULL, TRUE, TRUE, TRUE)
    {
      //构造这个IRP的内核函数本应该设置的是TCP设备对应的IRP堆栈,但是由于创建的过滤设备已经附加到了TCP设备上,所以内核设置下层堆栈时就设置到过滤设备的堆栈上了,所以这时在过滤设备的派遣函数中要把这些堆栈信息再设置到下层的TCP设备对应的堆栈单元上!
      IoCopyCurrentIrpStackLocationToNext(irp);//注意这里是拷贝而不是跳过IoSkipCurrentIrpStackLocation,上面的跳过用于生成连接终端的IRP,而这里用于生成传输层地址的IRP。
      
      if (cr != NULL) 
      {
        /*
         * this way for completion is more quicker than used above//这个完成的方式比上面使用的更快:明白了!只有一句话IoSetCompletionRoutine!
         */
        DbgPrint("in tdi_dispatch_complete,normal status,irp:0X%X.\n",irp);
        IoSetCompletionRoutine(irp, cr, context, TRUE, TRUE, TRUE);
      } 
      else
      {
        DbgPrint("in tdi_dispatch_complete,normal status,cr == NULL, irp:0X%X.\n",irp);
        IoSetCompletionRoutine(irp, tdi_generic_complete, NULL, TRUE, TRUE, TRUE);//这条指令不会被执行


      }
    }

    /* call original driver */
    status = IoCallDriver(old_devobj, irp);
  }
  return status;
  


[解决办法]
虽然不会,但是帮顶~