Driver Language and Libraries
A driver is a Dynamic Link Library (DLL) with the file extension .sys. It is usually written in C or C++ and can include resources, such as a version block, event messages, and Windows Management Instrumentation (WMI) class definitions. In Windows 98, a driver executable must have an 8.3 filename.
Although drivers were traditionally written just in C, it is quite straightforward to use C++. The main requirement is to use the extern 'C' directive in a couple of important places. However, do not use the new keyword in C++. The new keyword may be implemented using malloc, which is not available to kernel mode drivers.
In fact, most standard libraries and classes are not available to driver writers in either language, because they make inappropriate use of memory. If you are using one of the proprietary driver development kits, these provide various useful classes, including safe memory allocators.
Instead, you can use any of the routines provided by the operating system to kernel mode devices, as described in Chapter 3. While these are useful, it takes a while to get used to the different set of routines that are available.
Assembly code can be used if absolutely necessary. Obviously, this makes for more work if you port the driver to the Windows 2000 Alpha platform.
A resource .rc file should include a standard version block. Increment the version numbers as new builds are released. Make sure that you keep a full source backup of each version you release. Many version control packages can help you manage this task.
If you generate NT or Windows 2000 events, you should write these in an .mc file that is compiled using the
Similarly, if you generate custom WMI classes, you need to write a .mof file that is compiled using the
A sophisticated driver might need to download microcode to its device and so would include the microcode as a binary resource in its executable.
Good Code
A driver is an integral part of the operating system, so it can easily crash Windows if it goes wrong. You do not have the protection of a Win32 address space to stop you from overwriting memory that does not belong to you.
Please be especially careful when you write your driver. Keep it as simple as possible and document it well.
Treat all compiler warnings as errors that need to be fixed. For example, whether an integer is signed or not can make all the difference.
Make sure that you check the return values of all kernel functions that you call, and act on them accordingly. For example, if you get an error after you create a device, make sure that the clean-up code deletes the device.
Make sure that you use the kernel resources carefully, particularly memory. Some resources are scarce and overuse may degrade system functioning.
The DDK
The next section describes how to invoke
Younger readers may not have come across makefiles. In the days before Integrated Development Environments (IDEs) such as Visual Studio, you had to use makefiles to determine which files in a project needed recompiling. If you changed only one module in a project consisting of eight modules, you want to recompile only that one module and then link the whole lot together.
The
haggis.exe: haggis.obj
link –o haggis.exe haggis.obj
haggis.obj: haggis.cpp
cl haggis
Most makefiles are a good deal more complicated than this, which is why they were happily forgotten when IDEs came along. However, setting up the compiler and linker settings for drivers is quite a complicated task. Therefore, Microsoft has stuck with makefiles. See the Visual Studio
Listing 4.1 shows the SOURCES file for the Wdm1 project. It specifies that the driver target name is Wdm1.sys, that it is a WDM driver, and that it should be built in the OBJ subdirectory. Source browser information should be generated. The DDK inc directory is added to the search list for header files. The SOURCES macro specifies a list of files to compile. NTTARGETFILES specifies some post build steps, as described in the following text.
Other less common SOURCES macro definitions can be found in the DDK documentation. Note that there must be no spaces between the SOURCES macro and its equal sign.
Listing 4.1 Wdm1 project SOURCES file
TARGETNAME=Wdm1
TARGETTYPE=DRIVER
DRIVERTYPE=WDM
TARGETPATH=OBJ
BROWSER_INFO=1
INCLUDES=$(BASEDIR)inc;
SOURCES=init.cpp
dispatch.cpp
pnp.cpp
DebugPrint.c
version.rc
NTTARGETFILES=PostBuildSteps
You must provide a standard file called makefile as shown in Listing 4.2. This invokes the standard make file makefile.def in the DDK inc directory.
As it says, do not edit this file at all. If you want to add to the list of files to compile, add them to the SOURCES macro in the SOURCES file.
Listing 4.2 Wdm1 project makefile
#
# DO NOT EDIT THIS FILE!!! Edit .sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#
!INCLUDE ${NTMAKEENV)makefile.def
Windows 98 and NT
Both the free and checked builds put their object files in the same directory. If you switch between these build types, make sure that you rebuild all the files in the project (by putting '-nmake /a' on the
The checked build output file OBJi386checkedWdm1.sys contains the debug symbols.