The Role of the Registry
The PnP Manager and setup subsystems rely heavily on four keys in the HKEY_LOCAL_MACHINE branch of the registry. These are called the hardware key, the class key, the driver key, and the service key. (See Figure 15-1.) To be clear, these are not the proper names of specific subkeys: they are generic names of four keys whose pathnames depend on the device to which they belong. Broadly speaking, the hardware and driver keys contain information about a single device, the class key concerns all devices of the same type, and the service key contains information about the driver. People sometimes use the name instance key to refer to the hardware key and software key to refer to the driver key. The multiplicity of names derives from the fact that Windows 95/98/Me and Windows XP were written (mostly) by different people. A fifth key, the hardware parameters key, might also exist; it contains nonstandard parameter information about the device.
Figure 15-1. Meaningful registry keys for a device.
In this section, I ll describe the contents of these registry keys. You ll never alter these keys directly on an end user system. The keys and the values in them are created or maintained automatically by the setup program, the Device Manager, and the PnP Manager. I ll show you later how you can inspect and modify these values using user-mode and kernel-mode APIs. You should plan to use those APIs instead of directly tampering with the registry. In fact, even administrator accounts lack permission to write to some of these keys.
When you re developing and debugging drivers, though, and especially when you re debugging your installation procedures, you often need to directly muck about with registry settings using REGEDIT. In Windows XP, REGEDIT allows you to alter the security permissions in the registry. (In Windows 2000, you would use REGEDT32 for this purpose. In Windows 98/Me, of course, the registry isn t secured in the first place.)
The Hardware (Instance) Key
Device hardware keys appear in the \System\CurrentControlSet\Enum subkey of the local machine branch of the registry. Figure 15-2 illustrates the hardware key for a sample device (namely, the USB42 sample from Chapter 12). The subkeys on the first level below the Enum key correspond to the different bus enumerators in the system. The description of all past or present USB devices is in the \Enum\USB subkey. I ve expanded the key for the USB42 sample to show you how the device s hardware ID (vendor 0547, product 102A) has turned into the name of a key (Vid_0547&Pid_102A) and how a particular instance of the device that has that ID appears as a further subkey named 6&16f0a439&0&2. The 6&16f0a439&0&2 key is the hardware, or instance, key for this device.
Figure 15-2. A hardware key in the registry.
Some of the values in the hardware key provide descriptive information that user-mode components such as the Device Manager can use. Figure 15-3 shows how the Device Manager portrays the properties of USB42. If you compare Figures 15-2 and 15-3, you ll notice some common things. In particular, the DeviceDesc string in the hardware key is the title of the device (unless there happens to be a FriendlyName property in the registry, which isn t the case here), and the Mfg property appears as the Manufacturer name in the property page. I ll explain later where some of the other property information comes from. I ll also explain later how you can access these properties from a user-mode application or from a WDM driver.
TIP
You can open the Device Manager from the Hardware page of My Computer properties or the System control panel applet, or from the Computer Management console under Administrative Tools. Since I use the Device Manager so frequently, I created a desktop shortcut to devmgmt.msc, which is normally in the \windows\system32 directory.
Figure 15-3. Device Manager properties for the USB42 device.
The hardware key also contains several values that identify the class of device to which the device belongs and the drivers for the device. ClassGUID is the ASCII representation of a globally unique identifier (GUID) that uniquely identifies a device setup class; in effect, it s a pointer to the class key for this device. Class is the name of the setup class. Driver names the driver key, which is a subkey of the class key. Service is a pointer to the service key in HKLM\System\CurrentControlSet\Services. Optional values (which USB42 doesn t have) named LowerFilters and UpperFilters, if present, would identify the service names for any lower or upper filter drivers.
A hardware key might have overriding values named Security, Exclusive, DeviceType, and DeviceCharacteristics that force the device object the driver will create to have certain attributes. USB42 doesn t have these overrides.
Finally, the hardware key might contain a subkey named Device Parameters, which contains nonstandard configuration information about the hardware. See Figure 15-4. SampleInfo, the only property in the figure for USB42, specifies the help file for the sample driver. (The other values in the figure are artifacts of a failed run of one of the Hardware Compatibility Tests. Their presence does no harm.)
Figure 15-4. A device parameters key in the registry.
The Class Key
The class keys for all classes of device appear in the HKLM\System\CurrentControlSet\Control\Class key. Figure 15-5 illustrates the class key for SAMPLE devices, which is the class to which the USB42 sample and all the other sample drivers in this book belong. The values in this key serve these purposes:
Figure 15-5. A class key in the registry.
(Default) specifies the friendly name of the class. This is the class title that the Device Manager uses. (Refer back to Figure 15-3.)
Class is the name of the class. The class name and the GUID go together here and in the hardware keys for devices belonging to this class.
EnumPropPages32 specifies a property-page provider DLL that provides custom property pages for the Device Manager to use when displaying properties for this class of device. The provider for this class, samclass.dll, presents the page labeled Sample Information. In general, this value can include a DLL name and the name of an entry point. If the entry point name is omitted, as in this example, the system assumes it is EnumPropPages.
Install32 specifies the class installer DLL that the setup system uses whenever it performs setup actions on devices belonging to the class. This value can include a DLL name and the name of an entry point. If the DLL name is omitted, as in this example, the system assumes it is the same as the property-page DLL.
Icon specifies the resource identifier for an icon in the class installer DLL. The Device Manager and the setup system use this icon whenever they display information about the class. The DDK suggests that an icon will be taken from the property page DLL if no class installer is present, but that was not the case in Windows 2000, and I ve been in the habit of providing at least a degenerate class installer entry point just so I can have a custom icon.
The SAMPLE class lacks some of the optional values that might be present, such as the following:
NoInstallClass, if present and not equal to 0, indicates that some enumerator will automatically detect any device belonging to this class. If the class has this attribute, the hardware wizard won t include this class in the list of device classes it presents to the end user.
SilentInstall, if present and not equal to 0, causes the PnP manager to install devices of this class without presenting any dialog boxes to the end user.
UpperFilters and LowerFilters specify service names for filter drivers. The PnP Manager loads these filters for every device belonging to the class. (You specify filter drivers that apply to just one device in the device s hardware key.)
NoDisplayClass, if present and not equal to 0, suppresses devices of this class from the Device Manager display.
There can be a Properties subkey of the class key, which can contain values named Security, Exclusive, DeviceType, and DeviceCharacteristics. These values override default settings of certain device object parameters for all devices of this class. Refer to the subsection Device Object Properties a bit further on for information about these settings.
The Driver Key
Each device also has its own subkey below the class key. The name of this key (actually, the name of this key relative to CurrentControlSet\Control\Class) is the Driver value in the device s hardware key. Refer to Figure 15-6 for an illustration of the contents of this subkey, the purpose of which is to correlate all these registry entries with the INF file used to install the device and to provide a repository for driver-specific configuration information that concerns this device.
Figure 15-6. A driver key in the registry.
Both the driver key and the hardware parameters key can contain parameter information about a device. The difference between the two keys is a bit subtle. The DDK says that the driver key contains driver-specific information, whereas the hardware parameters key contains device-specific information. In both cases, the information in question pertains to a particular instance of the device. Microsoft s concept is that the driver-specific information would be peculiar to a given driver and not relevant to some other driver for the same hardware. I confess that this distinction pretty much escapes me, inasmuch as I m used to thinking that any given device will have just one driver.
The Service (Software) Key
The last key that s important for a device driver is the service key. It indicates where the driver s executable file is on disk and contains some other parameters that govern the way the driver is loaded. Service keys appear in the HKLM\System\CurrentControlSet\Services key. Refer to Figure 15-7 for USB42 s service key.
Figure 15-7. A service key in the registry.
I won t rehash all the possible settings in the service key, which is splendidly documented in several places, including under the heading Service Install in the Platform Software Development Kit (Platform SDK). In this particular case, the values have the following significance:
ImagePath indicates that the executable file for the driver is named USB42.SYS and can be found in %SystemRoot%\system32\drivers. Note that the registry setting in this case is a relative pathname starting from the system root directory.
Type (1) indicates that this entry describes a kernel-mode driver.
Start (3) indicates that the system should load this driver when it s needed to support a newly arrived device. (This numeric value corresponds to the SERVICE_DEMAND_START constant in a call to CreateService. When applied to a kernel-mode driver, it has the meaning I just described it s not necessary to explicitly call StartService or issue a NET START command to start the driver.)
ErrorControl (1) indicates that a failure to load this driver should cause the system to log the error and display a message box.
Accessing the Registry from a Program
As I said earlier, you should plan to use a set of kernel-mode and user-mode APIs for accessing the registry rather than accessing it directly. In this subsection, I ll discuss the relevant APIs.
Accessing the Registry from a Driver
Table 15-1 shows which kernel-mode functions you should use to access information in the various registry keys I ve just discussed. In most cases, you only need to use a special way to open the key. Thereafter, you use regular ZwXxx functions to read and write values and ZwClose to close the key. Refer to the examples in Chapter 3 for full details.
Registry Key | Function to Use for Access |
Hardware | Read individual standard properties via IoGetDeviceProperty. You can t change these properties from a driver, and you shouldn t try to figure out the name of this key in order to open it directly. |
Hardware Parameters | IoOpenDeviceRegistryKey (PLUGPLAY_REGKEY_DEVICE option) |
Driver | IoOpenDeviceRegistryKey (PLUGPLAY_REGKEY_DRIVER option) |
Class | No access method provided, and you shouldn t try to figure out the name of this key in order to open it directly. |
Service | ZwOpenKey using RegistryPath parameter to DriverEntry. |
To access standard parameters kept in the hardware key, you call IoGetDeviceProperty with one of the property codes listed in Table 15-2. The entries in the Source column refer to items in the INF file, which I ll discuss in the next major section of this chapter. Note that this function has still more property codes than shown in the table, but they refer to property values that the PnP Manager maintains elsewhere than in the registry.
Property Name | Value Name | Source | Description |
DeviceProperty DeviceDescription | DeviceDesc | First parameter in model statement | Description of device |
DeviceProperty HardwareId | HardwareID | Second parameter in model statement | Identifies device |
DeviceProperty CompatibleIDs | CompatibleIDs | Created by bus driver during detection | Device types that can be considered to match |
DeviceProperty ClassName | Class | Class parameter in Version section of INF | Name of device class |
DeviceProperty ClassGuid | ClassGUID | ClassGuid parameter in Version section of INF | Unique identifier of device class |
DevicePropertyDriverKeyName | Driver | First parameter in AddService statement | Name of service key that specifies driver |
DeviceProperty Manufacturer | Mfg | Manufacturer in whose model section device was found | Name of hardware manufacturer |
DevicePropertyFriendlyName | FriendlyName | Explicit AddReg in INF file, or class installer | Friendly name suitable for presentation to the user |
For example, to retrieve the description of a device, use the following code. (See the AddDevice function in the DEVPROP sample.)
WCHAR name[256]; ULONG junk; status = IoGetDeviceProperty(pdo, DevicePropertyDeviceDescription, sizeof(name), name, &junk); KdPrint((DRIVERNAME " - AddDevice has succeeded for '%ws' device\n", name));
On the CD The DEVPROP sample in the companion content illustrates how to obtain standard property information from kernel mode and user mode.
Accessing the Registry from User Mode
Table 15-3 lists the user-mode APIs you would use to access the registry keys we ve just discussed. Most of these functions are oriented toward setup programs, including class installers and co-installers. To use them successfully, you need to have an HDEVINFO handle and an SP_DEVINFO_DATA structure that refers to the specific device you re interested in.
Registry Key | Function to Use for Access |
Hardware | Read or write individual standard properties via Setup DiGetDeviceRegistryProperty and SetupDiSetDeviceRegistryProperty. |
Hardware Parameters | SetupDiOpenDevRegKey (DIREG_DEV option). |
Driver | SetupDiOpenDevRegKey (DIREG_DRV option). |
Class | SetupDiOpenClassRegKey. Starting in Windows XP, read or write device object properties via SetupDiGetClassRegistryProperty and SetupDiSetClassRegistryProperty. |
Service | QueryServiceConfig, ChangeServiceConfig. |
For example, within the context of an enumeration of registered interfaces using the setup APIs, you can retrieve the friendly name of a device:
HDEVINFO info = SetupDiGetClassDevs(...); SP_DEVINFO_DATA did = {sizeof(SP_DEVINFO_DATA)}; SetupDiGetDeviceInterfaceDetail(info, ..., &did); TCHAR fname[256]; SetupDiGetDeviceRegistryProperty(info, &did, SPDRP_FRIENDLYNAME, NULL, (PBYTE) fname, sizeof(fname), NULL);
Refer to the DDK documentation of SetupDiGetDeviceRegistryProperty for a list of the SPDRP_XXX values you can specify to retrieve the various properties.
If all you have is the symbolic name of the device, you can use the following trick:
LPCTSTR devname; // <== someone gives you this HDEVINFO info = SetupDiCreateDeviceInfoList(NULL, NULL); SP_DEVICE_INTERFACE_DATA ifdata = {sizeof(SP_DEVICE_INTERFACE_DATA)}; SetupDiOpenDeviceInterface(info, devname, 0, &ifdata); SP_DEVINFO_DATA did = {sizeof(SP_DEVINFO_DATA)}; SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, NULL, &did);
You can go on to call routines such as SetupDiGetDeviceRegistryProperty in the normal way at this point.
NOTE
In Windows 98 and Windows NT version 4, application programs used the CFGMGR32 set of APIs to obtain information about devices and to interact with the PnP Manager. These APIs continue to be supported for purposes of compatibility in Windows 98/Me and Windows XP, but Microsoft discourages their use in new code. For that reason, I m not even showing you examples of calling them.
Device Object Properties
As you know, you call IoCreateDevice to create a device object. In a WDM driver, your AddDevice function ordinarily creates a single device object and links it into the PnP driver stack by calling IoAttachDeviceToDeviceStack. Once the function driver and all filter drivers have finished these steps, the PnP Manager consults the registry to apply optional overrides to some of the settings in the device objects. The settings in question are these few:
The security descriptor attached to the physical device object (PDO), which can be overridden by a Security value in the registry.
The device type (FILE_DEVICE_XXX) for the PDO, which can be overridden by a DeviceType value in the registry.
The device characteristics flags, which can be overridden by a DeviceCharacteristics value.
The exclusivity option for the PDO, which can be overridden by an Exclusive value.
The PnP Manager looks first in the hardware key and then in the Properties subkey of the class key to find these overrides. After modifying the PDO, the PnP Manager then merges the characteristics flags from all device objects in the stack and sets certain ones (selected by the FILE_CHARACTERIS TICS_PROPAGATED mask in ntddk.h) to be the same in all the device objects. At the present time, the characteristics flags that are propagated are these:
FILE_REMOVABLE_MEDIA
FILE_READ_ONLY_DEVICE
FILE_FLOPPY_DISKETTE
FILE_WRITE_ONCE_MEDIA
FILE_DEVICE_SECURE_OPEN
For the security and exclusivity overrides to be effective, none of the filter or function drivers in the PnP stack should name their device objects. They should instead use IoRegisterDeviceInterface as the only method of establishing a symbolic link. The registered interface approach forces the I/O and Object Managers to refer to the PDO when opening a handle to the device, thereby giving effect to these two overrides.
The overriding values come to exist in the registry in one of two ways. You can specify them with special syntax in your INF file. Alternatively, you or some standard management application can use the SetupDiSetXxxRegistryProperty functions to change them in the hardware or class keys.