One abstraction that should have been refactored away but was not is the EvtDriverUnload routine. Our initial thought was that since the KMDF driver must implement a DriverEntry routine, an optional driver unload routine should also be a part of the model. The problem is that if DriverEntry() returns !NT_SUCCESS, the WDM DriverUnload (and hence EvtDriverUnload) is not called. If you created global resources in DriverEntry and cleaned them up in (Evt)DriverUnload, they would not be cleaned up becuase the unload routine would never be called. Normally drivers do not create such global data, so we (the KMDF team) didn't realize that this design issue was a problem.
There is an easy fix though that we have been advocating. Instead of putting your cleanup code in EvtDriverUnload, put all cleanup code in the EvtObjectCleanup routine in the object attributes for the WDFDRIVER you create when calling WdfDriverCreate(). The cleanup routine is guaranteed to be called in both cases (DriverEntry() returning NT_SUCCESS() or !NT_SUCCESS()) once the WDFDRIVER has been created successfully. Note that important restriction, WdfDriverCreate() must return success for this pattern to work! That means that the allocation and initialization of all global resources should occur after WdfDriverCreate() has been called.
In hindsight we should have never had a EvtDriverUnload and should have had the WDFDRIVER's cleanup routine be the driver unload equivalent. Two things affected our decision to not make this change. First, we didn't realize the pattern was busted until late in the development process. By the time we did realize it, there were too many drivers in the wild and under developement to make such a significant change. Second, while EvtDriverUnload was created when KMDF development was first started, the standardized cleanup routine for each object was not introduced until much later. If both concepts were implemented at the same time in the development cycle, the unload routine would have been eliminated