DeviceIoControl 用于和应用层和驱动层之间的数据传送。是应用层调用驱动文件(SYS)中的控制请求的重要方法。
今天在使用DeviceIoControl 调用一个自行编写的驱动程序的使用返回值总是FALSE,使用GetLastError获得错误码为87
这个错误码对应的错误是:参数不正确。可是应用层的参数怎么看都没有问题。最终发现是驱动层的编码出现了问题。
在驱动层的分发函数中,在指定的控制请求分支,完成自定义的请求业务逻辑之后应该调用:
status=irp->IoStatus.Status;
来返回一个错误码,然后调用
IoCompleteRequest(irp,IO_NO_INCREMENT);
来结束对控制请求的处理
而发生错误的处理方法使用了
IoCallDriver(s_nextobj,irp);
将请求继续传递给了后续的驱动对象,这是系统的驱动对象
由于这个控制请求是自定义的,系统的驱动对象中没有这个处理分支,而一般的处理方法就是,对没有的分支返回参数错误。即是控制请求这个参数传递错误。
由此可以总结出在驱动层编写自定义的消息请求时,处理完成之后不能再路由到系统的驱动中,因为对控制请求这个参数的判断是以最后一个处理来判断的。使用IoCallDriver(s_nextobj,irp);不但在自己编写的代码中处理白做了,返回了错误码,同时也接受不到返回信息。因为这里对irpsp的处理被后续的处理覆盖了。
在来重新审视:
BOOL WINAPI DeviceIoControl(
__in HANDLE hDevice,
__in DWORD dwIoControlCode,//自定义的控制码就是这一项
__in LPVOID lpInBuffer,
__in DWORD nInBufferSize,
__out LPVOID lpOutBuffer,
__in DWORD nOutBufferSize,
__out LPDWORD lpBytesReturned,
__in LPOVERLAPPED lpOverlapped
);
这里遇到的问题只是针对参数dwIoControlCode的错误的解决,这个问题也比较隐蔽。
正确的调用当然还要保证其它的所有的参数都是正确的。在调试的时候要特别关注错误码,和对代码进行例行的分析。
附,我遇到这个问题的驱动程序的分发函数的代码,是正在调试中的代码,所以比较乱:
这里是一个串口的Filter驱动,对读写请求都要路由到系统,而对于自定义的消息一定要调用IoCompleteRequest完成请求
NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp) { PIO_STACK_LOCATION irpsp=IoGetCurrentIrpStackLocation(irp); NTSTATUS status; ULONG len; ULONG j; PUCHAR buffer=NULL; ULONG dwInputBufferLength; ULONG dwOutputBufferLength; PVOID pvIOBuffer; pvIOBuffer = irp->AssociatedIrp.SystemBuffer; dwInputBufferLength = irpsp->Parameters.DeviceIoControl.InputBufferLength; dwOutputBufferLength = irpsp->Parameters.DeviceIoControl.OutputBufferLength; switch (irpsp->MajorFunction) { case IRP_MJ_POWER: PoStartNextPowerIrp(irp); IoSkipCurrentIrpStackLocation(irp); return PoCallDriver(s_nextobj,irp); case IRP_MJ_WRITE: len=irpsp->Parameters.Write.Length; if (irp->MdlAddress!=NULL) { buffer=(PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority); } else { buffer=(PUCHAR)irp->UserBuffer; } if (buffer==NULL) { buffer=(PUCHAR)irp->AssociatedIrp.SystemBuffer; } for(j=0;j<len;++j) { DbgPrint("comcap:Send Data:%2x/r/n",buffer[j]); } break; case IRP_MJ_READ: IoCopyCurrentIrpStackLocationToNext(irp); IoSetCompletionRoutine(irp,ReadComplete,device,TRUE,TRUE,TRUE); return IoCallDriver(s_nextobj,irp); case IRP_MJ_DEVICE_CONTROL: DbgPrint("IRP_MJ_DEVICE_CONTROL/n"); switch(irpsp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_WINIO_SETDEVICE: DbgPrint("IOCTL_WINIO_SETDEVICE/n"); DbgPrint("Bytes Received:%d/n",dwInputBufferLength); DbgPrint("Received:%ws/n",pvIOBuffer); //if (dwInputBufferLength!=0) //{ // ccpAttachCom(s_driver,L"//Device//com0com11"); //} status=irp->IoStatus.Status; IoCompleteRequest(irp,IO_NO_INCREMENT); return status; case IOCTL_WINIO_READ://这个是自定义的,不要再路由给系统驱动 DbgPrint("IOCTL_WINIO_READ/n"); memcpy(pvIOBuffer,L"aaa",sizeof(L"aaa")); irp->IoStatus.Information=sizeof(L"aaa"); status=irp->IoStatus.Status; IoCompleteRequest(irp,IO_NO_INCREMENT); return status; default: DbgPrint("Invalid Request:%0xx/n",irpsp->Parameters.DeviceIoControl.IoControlCode); } break; } IoSkipCurrentIrpStackLocation(irp); status=IoCallDriver(s_nextobj,irp); DbgPrint("Dispath Result:%d/n",status); return status; //err irp->IoStatus.Information=0; irp->IoStatus.Status=STATUS_INVALID_PARAMETER; IoCompleteRequest(irp,IO_NO_INCREMENT); return status; }