闲暇时写个WDM驱动:CharSample。加载驱动后,在设备管理器中显示工作正常,甚是欣喜;然而这种欣喜没有持续几分钟就被冲散了:停用/再启用设备后,设备图标上有yellow bang,显示设备状态为:"This device is not working properly because Windows cannot load the drivers required for this device. (Code 31)"。看设备管理的提示,感觉这是重启设备后驱动文件丢失似得。
初次加载时:
停用启用后:
本想到c:\windows\inf\setupapi.dev.log中查找线索,可是任我怎么重启设备,日志文件中就是没有任何记录...
我又猜想在设备管理器中启用设备,应该会响应_IRP_MJ_PNP/IRP_MN_START_DEVICE事件,于是又在"case IRP_MN_START_DEVICE"处下断点,重启了几次设备,windbg都没有在case IRP_MN_START_DEVICE处停下。但重启系统,又能正常工作,难道重启设备后都不会响应IRP_MJ_PNP/IRP_MN_START_DEVICE事件了,不太可能啊?各种办法都尝试了,还没有解决办法,这就很尴尬了。最后在MSDN上找到Code 31对应的链接,MS给出的解释是:
CM_PROB_FAILED_ADD
A driver's attempt to add a device failed.
Error Code
31
Display Message (Windows 2000 and later versions of Windows)
"This device is not working properly because Windows cannot load the drivers required for this device. (Code 31)"
Recommended Resolution (Windows 2000 and later versions of Windows)
Update the device driver.
Starting with Windows XP, this problem can only occur if the driver's AddDevice routine fails.
最后一行提示说,仅当AddDevice函数失败时,才会出现这样的问题。按它的提示,我在AddDevice函数入口处下断点,再次重启设备,这次如期在AddDevice处停下。单步跟踪程序的执行发现驱动在IoCreateDevice时失败,之后就退出AddDevice,再往后就没有其他PNP事件产生了:
执行IoCreateDevice失败,不得不怀疑是不是因为系统中存在同名设备导致:
kd> !drvobj SampleChar
Driver object (852be230) is for:
\Driver\SampleChar
Driver Extension List: (id , addr)
Device Object list:
85203158
kd> !devstack 85203158
!DevObj !DrvObj !DevExt ObjectName
> 85203158 \Driver\SampleChar 85203210 SampleChar
8671d9a8 \Driver\PnpManager 00000000 000000b6
!DevNode 8519c3e8 :
DeviceInst is "ROOT\SYSTEM\0002"
ServiceName is "SampleChar"
如windbg显示SampleChar驱动有一个设备,并且附加在ROOT\System\002上。这就奇怪了,我在IRP_MJ_PNP\IRP_MN_STOP_DEVICE中的确有处理移除设备啊,为什么会没有把设备移除?
//这是我Pnp函数的代码片
NTSTATUS SampleCharPnp(PDEVICE_OBJECT devObj, PIRP irp)
{
...
case IRP_MN_STOP_DEVICE:
IoSetDeviceInterfaceState(&devCtx->symLinkName, FALSE);
IoSkipCurrentIrpStackLocation(irp);
status = IoCallDriver(devCtx->lowerDev, irp);
IoDetachDevice(devCtx->lowerDev);
IoDeleteDevice(devObj);
return status;
带着这个疑问,我安装了ddk的toaster样例,按部就班的重启设备,发现当停用设备时,
会依次响应下列事件:IRP_MN_REMOVE_DEVICE/ToasterUnload,并没有包含IRP_MN_STOP_DEVICE事件。Toaster在IRP_MN_REMOVE_DEVICE事件中将设备从设备栈中分离并移除,而我处理IRP_MN_REMOVE_DEVICE事件时采用默认的方式:将irp下发给Root总线(其实就是什么都没做),因此错失了移除设备的机会:
default:
IoSkipCurrentIrpStackLocation(irp);
status = IoCallDriver(devCtx->lowerDev, irp);
return status;
break;
原因找到了,最后修改代码如下:
NTSTATUS SampleCharPnp(PDEVICE_OBJECT devObj, PIRP irp)
{
NTSTATUS status = STATUS_SUCCESS;
SampleCharDevContext* devCtx = (SampleCharDevContext*)devObj->DeviceExtension;
KEVENT completeEvt;
IO_STACK_LOCATION* curStack = IoGetCurrentIrpStackLocation(irp);
if ((devCtx == NULL) || \
(devCtx->lowerDev == NULL))
{
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return status;
}
switch (curStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
ForwardIrpAndWait(devCtx->lowerDev,irp);
IoSetDeviceInterfaceState(&devCtx->symLinkName, TRUE);
status = irp->IoStatus.Status = status;
IoCompleteRequest(irp, IO_NO_INCREMENT);
break;
#if 0
case IRP_MN_STOP_DEVICE:
IoSetDeviceInterfaceState(&devCtx->symLinkName, FALSE);
IoSkipCurrentIrpStackLocation(irp);
status = IoCallDriver(devCtx->lowerDev, irp);
IoDetachDevice(devCtx->lowerDev);
IoDeleteDevice(devObj);
return status;
break;
#endif
case IRP_MN_REMOVE_DEVICE:
IoSetDeviceInterfaceState(&devCtx->symLinkName, FALSE);
IoSkipCurrentIrpStackLocation(irp);
status = IoCallDriver(devCtx->lowerDev, irp);
IoDetachDevice(devCtx->lowerDev);
IoDeleteDevice(devObj);
return status;
default:
IoSkipCurrentIrpStackLocation(irp);
status = IoCallDriver(devCtx->lowerDev, irp);
return status;
break;
}
return status;
}
再次重启设备,YB终于没有了,大功告成