Each NT driver determines the size (StackSize in Figure 3.2) of the device extension when it calls IoCreateDevice to set up a device object.
For most NT drivers, this call is made only from the DriverEntry routine. For some NT drivers, such as disk drivers that must re-create logical device objects dynamically if the user repartitions a disk while the system is running, this call also can be made from a Dispatch routine.
However, every NT driver must call IoCreateDevice one or more times when it initializes to create a named device object to represent each physical, logical, or virtual device for which it handles I/O requests. Otherwise, the driver either will not be loaded if it creates no device object or it will not get IRPs for any target device for which it did not create a device object in its DriverEntry routine.
As shown in Figure 3.2, the caller also passes arguments that determine the following when IoCreateDevice sets up a device object:
For a list of the system-defined FILE_DEVICE_XXX constants, see the DDK master kernel-mode header file, ntddk.h.
Otherwise, the DeviceCharacteristics value passed to IoCreateDevice must be zero.
NT drivers for removable-media devices also must OR the device object’s Flags with DO_VERIFY_VOLUME if they detect (or suspect) that the media has changed during I/O operations. For more information about how to handle removable media, see Chapter 16.
For an explanation of exclusive devices, see the description of interactive device drivers in Chapter 2.
A DeviceName string must be provided when the driver creates a device object if the corresponding physical, logical, or virtual device must be made “visible” to user-mode callers as a file object, or if a higher-level driver must (or may) chain itself to the creating driver.
User-mode callers (protected subsystems) cannot carry out device I/O operations for their applications or user-mode drivers without obtaining a handle for a named file object associated with the device object. Higher-level NT drivers cannot chain themselves to the driver of an unnamed device by calling IoGetDeviceObjectPointer or IoAttachDevice. However, an NT file system driver can chain itself to an unnamed mass-storage device object during a mount operation through a volume parameter block (VPB).
For every NT driver except FSDs, the I/O Manager also sets up an associated device queue object for every successful call to IoCreateDevice. The device queue object associated with a device object represents a queue of IRPs bound for a driver’s StartIo routine after the driver is loaded. NT drivers that manage their own internal IRP queues, such as the system floppy controller driver described in Chapter 2, do not use the device queues associated with their device objects.
NT drivers can create additional device queue objects like the NT SCSI port driver, as already mentioned in Section 3.1. For more information about Kernel-defined device queue objects, see Section 3.8 later in this chapter.
If the call to IoCreateDevice succeeds, the I/O Manager provides storage for the device object itself and for all other data structures associated with the device object, including the driver’s device extension, which it initializes with zeros.