This column is dedicated to disseminating information on the various Gotchas and annoyances that crop up during Windows driver development. Where possible we'll provide a workaround, as well as identify which versions of the DDK and the operating systems exhibit these problems. Also, some Gotchas are restricted to particular driver models, such as Network, Video, Audio, etc., which will be identified. In addition to Gotchas, we'll also periodically cover some advanced techniques that do not require an entire article to describe, as well as the occasional pearl of wisdom.
If you have any Gotchas that you would like to share, please send them to Gotchas@wd-3.com. We don't promise to use anything you send us -- or even to read everything you send -- but if we do use your suggestion, we’ll give you credit (if we didn’t think of it first, or if you slip Brian a sawbuck) making you instantly famous and the object of adoration by millions (well OK, maybe you’ll still just be adored only by your mother -- but you’ve got to start somewhere).
Driver Model |
All |
Title |
RtlDeleteRegistryValue incorrectly documented |
Annoyance |
High |
Description |
RtlDeleteRegistryValue is used to delete a key in the registry. The DDK states the following: Note that if RelativeTo is set to RTL_REGISTRY_HANDLE, the following occurs:
This is incorrect; Windows 2000 will close the handle, but Windows XP and later will not |
Workaround |
Call ZwClose to close the handle if running on Windows 2000 |
Versions |
Win2K |
Driver Model |
All |
Title |
RtlQueryRegistryValues may bugcheck |
Annoyance |
High |
Description |
If RtlQueryRegistryValues is called with a table entry whose Flags field does not have RTL_QUERY_REGISTRY_REQUIRED set AND whose DefaultType is not 0 (REG_NONE) AND the value being queried does not exist in the registry, the system will bugcheck. |
Workaround |
Set DefaultType to be 0 (REG_NONE) |
Versions |
Win2K, WinXP, WS2003 |
Driver Model |
Application |
Title |
DLL and Thread Local Storage |
Annoyance |
Low |
Description |
If a DLL implements one or more thread local storage variables by using __declspec (thread) AND the DLL is loaded using LoadLibrary, then any attempt to access any of the thread local storage variables will cause an access violation. If the same DLL is loaded as a result of some program (or another DLL for that matter) being linked to it, then all is OK. |
Workaround |
To get around this problem, use the TlsAlloc, TlsFree, TlsGetValue, and TlsSetValue functions from within the DllMain; this is not as clean but will work when the DLL is loaded using LoadLibrary. |
Versions |
Win2K, WinXP, WS2003 |
Driver Model |
All |
Title |
Writing drivers using C++ |
Annoyance |
Low |
Description |
It is possible to write device drivers using C++, but with the following restrictions:
|
Workaround |
You’ll have to write it all yourself. Here is an article on how you might go about supporting static and global initializers: http://msdn.microsoft.com/msdnmag/issues/01/01/hood/ |
Versions |
Win2K, WinXP, WS2003 |
Driver Model |
All |
Title |
Including firmware in driver executable |
Annoyance |
Low |
Description |
More and more devices require firmware to be downloaded by its device driver. Storing the firmware in an external file (in the \Windows\System32\Drivers directory!) allows for the firmware to be easily upgraded. However, if the firmware file is missing, then the device is unusable. Also, some devices rarely – if ever – have their firmware updated. In both cases, it would be nice to include the firmware in the driver’s executable – but without having to convert the binary into some hokey ASCII representation. Surprisingly enough, this is supported – but with one catch. |
Workaround |
To include a firmware file (or any other type of file) into your driver’s executable, specify the name of the file in your resources.rc file, in the following format: resource-name resource-type file-name, for example: MyWidget Firmware \firmware\WidgetFirmware.bin The resource compiler will grab the firmware file and place it in the resource PSECT (Program Section), which the linker will combine with other resource PSECTs into the .rsrc image section in the executable. All the image sections in the driver’s executable are loaded into kernel memory when the driver is loaded, so the driver will then have to search through its .rsrc (resource) image section for the firmware resource. Remember the ‘catch’ I mentioned earlier? Well, the catch is that the .rsrc image section has the Discardable bit set, which, like the INIT image section (containing DriverEntry), will be deallocated after DriverEntry returns back to the I/O manager. This leaves you with two options:
Clearly, changing the Discardable bit is the simplest choice. This requires a change to your linker options. Ever notice the following linker option: -SECTION:INIT,d Ever wonder what it does? It sets the Discardable bit on the INIT image section (and clears all other bits). Well, if you were to add the following line to your linker options, the resource section would not be deallocated when DriverEntry returned: -SECTION:.rsrc,r Now, all you have to do is locate your firmware resource in the resource section. The beginning of the driver in memory is Driver_obj->DriverStart. From there, locate the offset to the resource section in the image directory. Once you have the resource section, search the resource tree for your named resource. |
Versions |
Win2K, WinXP, WS2003 |
Driver Model |
All |
Title |
Messing with system data structures |
Annoyance |
Low |
Description |
More and more, I see driver writers messing around with undocumented system data structures. While I don’t advocate this, if you’re going to do it, then at least do it safely. The problem is synchronizing access to these structures safely – especially on a multiprocessor system. At any time, the system may be “in the middle” of its own access to these structures. So, what we need is guaranteed atomic access to these structures – but we generally don’t have access to the spinlock protecting them. |
Workaround |
Well, there are no guarantees in life, but in this case, we can come pretty close. In a nutshell, we want to prevent all the CPUs on the system from doing anything until we finish accessing the system data structure. To do this, allocate an array of DPC objects (one for each CPU on the system), specify the CPU that the DPC is to run on using KeSetTargetProcessorDpc, and queue up the DPCs using KeInsertQueueDpc. When a DPC runs, raise IRQL to 31, and spin while waiting for all the other CPUs to run their DPC. When all CPUs are spinning at IRQL 31, one CPU will perform the access. When the access is complete, the other CPUs are notified, causing them to exit their spin loops, go back to IRQL 2, and then exit the DPC. |
Versions |
All |