A device in Windows can be thought to be representative of a piece of hardware such as a hard disk or a network card. But devices do not need to be hardware and can be a higher level software construct (such as a volume device that represents a volume such as C:\, D:\ etc) or may be a software-only construct (such as a virtual COM port).
Devices are target of I/O and are represented by a DEVICE_OBJECT structure. The device object structure has a ULONG ReferenceCount member that keeps the reference count on the device object. Reference counting helps solve DEVICE_OBJECT life-time issues (ie. a device object in use cannot be freed etc). The referencing and dereferencing of DEVICE_OBJECT is done internally by I/O manager routines but what if a driver needs to take a reference on a DEVICE_OBJECT or release a reference on a DEVICE_OBJECT ?
The KSPIN_LOCK_QUEUE_NUMBER parameter is an enum1 and has to be set to LockQueueIoDatabaseLock and you can change DEVICE_OBJECT.ReferenceCount and call KeReleaseQueuedSpinLock thereafter to release the lock2. KeReleaseQueuedSpinLock is declared in ntifs.h like thus
So what if Windows 2000 is a supported platform ? If one watches the I/O manager locks claimed on DEVICE_OBJECTs, the lock is maintained at IopDatabaseLock but there is no DDI to access that lock3. So you are out of luck in that operating system.4
1A total of eighteen locks are accessible via the KSPIN_LOCK_QUEUE_NUMBER enum and they include LockQueueDispatcherLock, presumably “the” dispatcher lock and LockQueueIoCancelLock, lock related to I/O cancellation.
2Changes to DEVICE_OBJECT do not have to be limited to changing the reference count
3KeAcquireQueuedSpinLock, KeReleaseQueuedSpinLock and KeTryToAcquireQueuedSpinLock DDIs are not available in Windows 2000 Service Pack 4 with Update Rollup 1 Version 2 either.
4On uniprocessor machines one could raise IRQL to DISPATCH_LEVEL as a workaround, but then there is no good solution for multiprocessor machines.