Last reviewed and updated: 11 May 2020
Best Practice: A procedure that has been shown by research and experience to produce optimal results and that is established or proposed as a standard suitable for widespread adoption. (Merriam-Webster)
We spend a lot of time here at OSR, both in our offices and in our classes, discussing what we believe to be Best Practices for Windows driver development. We thought it would be a good idea to try to enumerate these best practices.
We realize it won’t be possible for every project to follow every one of these suggestions. That’s fine. These are meant to be aspirational guidelines. Sometimes it will make the most sense – and even be a best practice – to not follow some of the listed practices, depending on the details of your project. Remember, these are guidelines. They’re not meant to substitute for your good engineering judgement.
Use the most up-to-date version of the WDK, and of Visual Studio supported by that WDK.
Why: Every version of the WDK brings new features, and includes fixes from previous versions.
When possible, use the most modern driver model that applies to your project. For example, use WDF (KMDF or UMDF) instead of WDM. Use the file system Mini-Filter model, instead of creating yet another legacy file system filter.
Why: In this case, newer really is better. The newer driver models will help you avoid lots of problems that some of the older models entail.
Even if you write in C, use the C++ compiler.
Why: It’s worth it to use the C++ compiler, even if you just use the strong type-checking alone. Later, you may find other modern features of the C++ language that make sense to use and that are compatible with the C-language interfaces provided by Windows.
Use the role-type annotations for your drivers, where provided. By these, we mean the declarations such as EVT_WDF_DRIVER_DEVICE_ADD in WDF, or DRIVER_INITIALIZE in WDM.
Why: These annotations provide additional useful information to both Code Analysis and Static Driver Verifier. They help those tools better understand your code.
Use SAL Annotations for your internal driver functions – At Least for locking and IRQL checking.
Why: Concurrency problems, and IRQL violations, are some of the most common – and the most difficult to diagnose – problems that people have in writing Windows drivers. We have found that using annotations for at least these items can be invaluable in identifying bugs early.
Build your solution with warning level /W4 /WX
Why: Sure, there are warnings that you might want to globally suppress. But the header files should all build cleanly with /W4 now, and the more checking your code gets, the less likely you are to have latent errors. And if you’re going to jump in and build with all warnings, you might as well treat warnings as errors to keep your build clean.
Enable Code Analysis (CA) for every build of your driver project. Enable All Rules, or at least “Microsoft Native Recommended Rules” (depending on the version of VS you’re using).
Why: We like to think of Code Analysis as a super-smart extension of the compiler warnings. Again, anything that helps file bugs for you is good.
Enable Windows Driver Verifier for your driver during all testing. Just turn it on and leave it on. On your test machine in your office, and on your test machine in the lab. Enable everything except the various Low Resource features.
Why: Being able to run your driver successfully under Driver Verifier is the only way to have confidence that your driver is properly written. Because it never (well, almost never) causes noise or false-positives, it’s the kind of thing you can just enable and forget about.
Enable Windows Driver Verifier for any wrapper/library modules you use. For example, if you’re writing a KMDF driver, be sure to enable to enable Driver Verifier on the Framework. If you’re writing a StorPort MiniPort, enable Driver Verifier on StorPort as well as your own driver.
Why: When you use a “wrapper” or “support library” much of the work your driver does is done on your behalf by that “wrapper” or “library” – you absolutely need that work monitored by Driver Verifier just as if your driver had done it. Note that you can also enable Driver Verifier on the kernel itself. This, for example, causes allocations made by the kernel to be subject to Special Pool.
Test on the checked build of the OS. Test on a fully checked build if you can find it (and you can successfully get it to install), test on a partially checked build otherwise. Checked kernel and HAL images are provided as part of the WDK.
Why: Checked build asserts are added by the developers of the OS code to validate the assumptions they are making in their code. The checks find unique problems that you would never find otherwise.
Run the Hardware Lab Kit (HLK) tests on your driver. Even if you don’t currently need logo certification, just get the environment set up and start running the tests periodically.
Why: The HLK tests can either be very minimal (e.g. for Unclassified devices and drivers) to intensive and sometimes seemingly arbitrary (e.g. the File System Filter driver tests). The tests attempt to determine if the system behaves differently with your driver present than without. Better to get a feel for this periodically than to stay blissfully unaware and get a rude awakening in the future.
Run SDV Periodically Before Release, if your driver model is supported. Enable All Rules.
Why: It took us years to be able to say this but… SDV actually finds bugs. There. It took years, but SDV is actually a damn useful tool, especially when coupled with SAL.
Test Exactly What You Ship. This should go without saying, but… Before releasing your product to the world, be sure that you replicate the “real world” scenarios of how your product will be built and installed. For example, before shipping your driver, you’ll want to test at least once without Windows Driver Verifier enabled and without WinDbg attached to the system. Test what your customers will use.
Why: If you don’t test what your customers will ultimately use, you run the very real risk for shipping something that does not work. We learned this the hard way.