We have a WDF based software-only driver that’s very similar to the “NOTHING” example at http://www.osronline.com/article.cfm^article=390.htm
The DriverEntry function is basically identical, and the only real difference in the DeviceAdd callback is that we change some init settings before calling WdfDeviceCreate
:
NTSTATUS MyEvtDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit)
{
DECLARE_CONST_UNICODE_STRING(deviceName, L"\\Device\\" MY_DEVICE_NAME);
DECLARE_CONST_UNICODE_STRING(dosDeviceName, L"\\DosDevices\\" MY_DEVICE_NAME);
WdfDeviceInitSetCharacteristics(DeviceInit, FILE_DEVICE_SECURE_OPEN, TRUE);
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_UNKNOWN);
NTSTATUS status = WdfDeviceInitAssignName(DeviceInit, &deviceName);
if (!NT_SUCCESS(status)) ...
status = WdfDeviceInitAssignSDDLString(DeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL);
if (!NT_SUCCESS(status)) ...
WDF_OBJECT_ATTRIBUTES deviceAttributes;
WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes);
deviceAttributes.EvtCleanupCallback = MyEvtDeviceCleanup;
deviceAttributes.EvtDestroyCallback = MyEvtDeviceDestroy;
WDFDEVICE hDevice = NULL;
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &hDevice);
if (!NT_SUCCESS(status)) ...
status = WdfDeviceCreateSymbolicLink(hDevice, &dosDeviceName);
if (!NT_SUCCESS(status)) ...
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential);
ioQueueConfig.EvtIoDeviceControl = DevIoCtrlCallback;
WDFQUEUE hQueue;
status = WdfIoQueueCreate(hDevice, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &hQueue);
if (!NT_SUCCESS(status)) ...
return status;
}
It’s part of a larger package, and the update mechanism for that package currently does a full uninstall of all components followed by an install of the new version.
That means when updating the package, the driver is uninstalled (via SetupDiCallClassInstaller(DIF_REMOVE)
) and after that the new driver is installed.
This works nicely as long as no application has an open HANDLE to the driver.
Now AFAIK it’s not possible to have the driver unload while an application still has a HANDLE open. (If I’m mistaken here please let me know, because allowing the driver to unload even if there are open HANDLEs would be the best option for us.)
The problem is: If the last HANDLE is not closed during the few seconds that DIF_REMOVE
will wait for just that to happen, the driver will stay loaded. I had expected that after such an “uninstall”, the driver would automatically unload when the last HANDLE to it is closed. Which unfortunately doesn’t happen, the driver stays loaded. At that point the system is in a rather unfortunate state: The device itself is no longer registered (because DIF_REMOVE
succeeded - after all it removed the registry keys for the device, and after e.g. a reboot it will be gone), so we cannot retry by running the uninstall process again. Yet the driver is still running, so when the installer tries to install the new version, the newly registered device will not be able to start because of a name collision (device name). Which is … bad
What I found out is that if I do a “scan for hardware changes” in device manager after the “uninstall without unload”, the driver will then finally unload (given that there are no more open HANDLES at that time of course). The same happens if I start or stop any other device.
First question: Is this a known problem, or is this likely caused by something that our driver does wrong?
Second question: How do we fix this?
The only way I have found so far is to add an IOCTL to our driver which simply makes it call WdfDeviceSetFailed(WdfIoQueueGetDevice(Queue), WdfDeviceFailedNoRestart)
. We could then send this IOCTL in the retry-loop of our driver-installer. But this seems like a rather ugly solution to me. Triggering whatever happens with every device enable/disable seems a bit cleaner to me – assuming that we can trigger it programmatically. Even better would be a solution where the “uninstalled but not unloaded” driver is automatically unloaded when the last HANDLE is closed.
Regards,
Paul Groke