NdisMRegisterDevice was introduced back in the NDIS 5 days to allow for the creation of a control device in an NDIS miniport. The control device mechanism is used a lot by IM driver writers (configuring firewall rules, QoS, whatever). Some people also use them for miniports, such as virtual Ethernet miniports and NDIS-WDM drivers.
One thing that’s not clear from the documentation, though, is what sort of lifetime the device object has. Living in the nice safe world of the NDIS Miniport model, it’s easy to get lulled into a false sense of security regarding synchronization. NDIS is generally pretty good at managing device state, synchronizing with MiniportHalt(), and so on.
But when you create a device object with NdisMRegisterDevice(), NDIS basically turns all state management over to you. That means that you have to be cognizant of the different lifetime a device object has vs. a miniport object. In particular, a control device can indeed outlive a miniport!
For example, say you have a memory block that you allocate during MiniportInitialize() and free during MiniportHalt() – not an uncommon strategy. If your dispatch routines for your control device need to reference that memory, you have to be careful about how you handle it. MiniportHalt() is NOT synchronized with these control device dispatch routines, so your dispatch routines can indeed be called after MiniportHalt() is called. In fact, there’s no great way to tell when your dispatch routines will not be called any more, and there’s no way to tell if a thread is executing in a dispatch routine at the time you deregister the device.
All of this means that you have to be more clever in the way you manage that shared memory block. One possibility is reference counting the block itself – Reference the block on creation in MiniportInit(), dereference it on MiniportHalt(), and take/release references as necessary in your dispatch routines. This must be done in an interlocked and race-proof way; NdisInterlockedIncrement() and friends are helpful for this.
This is a subtle point, and one that is not well documented. It stems in large part from the absence of device extension access for control devices created this way; that’s the mechanism that the OS uses to tie a memory block’s lifetime to a device object’s (as opposed to a miniport’s). But, as long as you’re aware of the issue, it can be worked around.