[Previous] [Next]

The INF File

An INF file contains a collection of sections introduced by a section name in brackets. Most sections contain a series of directives of the form "keyword = value". The INF file begins with a Version section that identifies the type of device described by entries in the file:

[Version]
Signature=$CHICAGO$
Class=Sample
ClassGuid={894A7460-A033-11d2-821E-444553540000}

Signature can be one of the three magic values $Chicago$, $Windows NT$ (with one space), or $Windows 95$ (also with one space). Class identifies the class of device. Table 12-1 lists the predefined classes that Windows 2000 already supports. ClassGuid uniquely identifies the device class. The DDK header file DEVGUID.H defines the globally unique identifiers (GUIDs) for standard device classes, and the DDK documentation entry for the Version section documents them as well.

In a production INF file, you will also need to have DriverVer and CatalogFile statements in the Version section. You should also have a comment (that is, any line that starts with a semicolon) containing the word "copyright" to satisfy the CHKINF utility I'll describe in the section "Tools for INF Files" later in this chapter. The operating systems will accept INF files that lack these details, but Microsoft won't certify your driver package without them. Refer to the DDK documentation for more details about the required INF syntax.

I find it useful to think of the bulk of an INF file as the linear description of a tree structure. Each section is a node in the tree, and each directive is a pointer to another section. Figure 12-1 illustrates the concept.

Click to view at full size.

Figure 12-1. Tree structure of an INF file.

Table 12-1. Device classes for INF files.

INF Class NameDescription
1394IEEE 1394 host bus controllers (but not peripherals)
BatteryBattery devices
CDROMCD-ROM drives, including SCSI and IDE
DiskDriveHard disk drives
DisplayVideo adapters
FDCFloppy disk controllers
FloppyDiskFloppy disk drives
HDCHard disk controllers
HIDClassHuman input devices
ImageStill-image capture devices, including cameras and scanners
InfraredNDIS miniport drivers for Serial-IR and Fast-IR ports
KeyboardKeyboards
MediumChangerSCSI media changer devices
MediaMultimedia devices, including audio, DVD, joysticks, and full-motion video capture devices
ModemModems
MonitorDisplay monitors
MouseMouse and other pointing devices
MTDMemory technology driver for memory devices
MultifunctionCombination devices
MultiportSerialIntelligent multiport serial cards
NetNetwork adapter cards
NetClientNetwork file system and print providers (client side)
NetServiceServer-side support for network file systems
NetTransNetwork protocol drivers
PCMCIAPCMCIA and CardBus host controllers (but not peripherals)
PortsSerial and parallel ports
PrinterPrinters
SCSIAdapterSCSI and RAID controllers, host bus adapter miniports, and disk array controllers
SmartCardReaderSmart card readers
SystemSystem devices
TapeDriveTape drives
USBUSB host controllers and hubs (but not peripherals)
VolumeLogical storage volume drivers

At the apex of the tree is a Manufacturer section that lists all the companies with hardware described in the file. For example:

[manufacturer]
"Walter Oney Software"=DeviceList
"Finest Organization On Earth Yet"=FOOEY

[DeviceList]
...

[FOOEY]
...

Each individual manufacturer's model section (DeviceList and FOOEY in the example) describes one or more devices:

[DeviceList]
Description=InstallSectionName,DeviceId
...

where Description is a human-readable description of the device and DeviceId identifies a hardware device. The InstallSectionName parameter identifies (or points to, in my tree metaphor) another section of the INF file that contains instructions for installing the software for a particular device. An example of an entry for a single type of device might be this (drawn from the PKTDMA sample in Chapter 7, "Reading and Writing Data"):

[DeviceList]
"AMCC S5933 Development Board (DMA)"=DriverInstall,PCI\VEN_10E8&DEV_4750

The information in the Manufacturer section and in the model section(s) for individual manufacturers comes into play when the system needs to install a driver for a piece of hardware. A Plug and Play (PnP) device announces its presence and identity electronically. A bus driver detects it automatically and constructs a device identifier using onboard data. The system then attempts to locate preinstalled INF files that describe that particular device. INF files reside in the INF subdirectory of the Windows directory. If the system can't find a suitable INF file, it asks the end user to specify one.

A legacy device can't announce its own presence or identity. The end user therefore launches the add hardware wizard to install a legacy device and helps the wizard locate the right INF file. Key steps in this process include specifying the type of device being installed and the name of the manufacturer. See Figure 12-2.

The hardware wizard constructs dialogs such as Figure 12-2 by enumerating all the INF files for a particular type of device, all of the statements in their Manufacturer sections, and all of the model statements for each of the manufacturers. You can guess that the manufacturer names that appear in the left pane of the dialog come from the left sides of Manufacturer statements and that the device types that appear in the right pane come from the left sides of model statements.

Click to view at full size.

Figure 12-2. Selecting a device during installation.

Install Sections

An install section contains the actual instructions that the installer needs to install software for a device. We've been considering the PKTDMA sample. For that device, the DeviceList model section specifies the name DriverInstall. I find it useful to think of this name as identifying an array of sections, one for each Windows platform. The "zero" element in this array has the base name of the section (DriverInstall). You can have platform-specific array elements whose names start with the base name and contain one of the suffixes listed in Table 12-2. The device installer looks for the install section having the most specialized suffix. Suppose, for example, that you have install sections with no suffix, with the .NT suffix, and with the .NTx86 suffix. If you're installing into Windows 2000 on an Intel x86 platform, the installer will use the .NTx86 section. If you're installing into Windows 2000 on a non-Intel platform, it would use the .NT section. If you're installing into Windows 98, it would use the section without a suffix.

Table 12-2. Install section suffixes for each platform.

Platform Install Section Suffix
Any platform including Windows 98 [none]
Any Windows 2000 platform .NT
Windows 2000 on Intel x86 .NTx86

Because of the search rules I just outlined, all of the INF files for my sample drivers have the no-suffix and .NT-suffix install sections. That makes the INF files work fine on any Intel platform. (As you probably know by now, Microsoft and Compaq dropped support for the current 32-bit version of Windows 2000 on the Alpha platform just as this book was going to press. We therefore made no provision for testing my samples on the Alpha.)

Further along in this chapter, I'll be discussing other INF sections whose names begin with the name of the install section. If you have multiple install sections in your "array," these other sections have to include the platform-dependent suffix in their names, too. For example, I'll be discussing a Services section that you use to install a description of the driver into the registry. You would form the name of this section by taking the base name of the install section (for example, DriverInstall) plus the platform suffix (for example, NT) and adding the word Services, ending up with [DriverInstall.NT.Services].

A typical Windows 2000 install section would contain a CopyFiles directive and nothing else:

[DriverInstall.nt]
CopyFiles=DriverCopyFiles

This CopyFiles directive indicates that we want the installer to use the information in another INF section for copying files onto the end user hard disk. For the PKTDMA sample, the other section is named DriverCopyFiles:

[DriverCopyFiles]
pktdma.sys,,,2

This section directs the installer to copy PKTDMA.SYS to the end user's hard disk.

The statements in a CopyFiles section have this general form:

Destination,Source,Temporary,Flags

Destination is the name (without any directory name) of the file as it will eventually exist on the end user system. Source is the name of the file as it exists on the distribution media, if that name is different from the Destination name; otherwise, it's just blank as in the example. In Windows 98, if you might be installing a file that will be in use at the time of installation, you specify a temporary name in the Temporary parameter. Windows 98 will rename the temporary file to the Destination name on the next reboot. It's not necessary to use this parameter for Windows 2000 installs because the system automatically generates temporary names.

The Flags parameter contains a bit mask that governs whether the system will decompress a file and how the system deals with situations in which a file by the same name already exists. The interpretation of the flags depends in part on whether the INF and driver are part of a package that Microsoft has digitally signed after certification. Table 12-3 is a list of all these flag bits. The italicized flags in the table are ignored in a digitally signed package. I used a double line to delimit groups of mutually exclusive flags. Thus, in an unsigned package, you could specify one or the other of the NOSKIP or WARN_IF_SKIP flags, but not both.

The file name by itself is not sufficient to tell the installer what it needs to know to copy a file. It also needs to know which directory you want the file copied to. In addition, if you have multiple diskettes in the installation set, it needs to know which diskette contains the source file. These pieces of information come from other sections of the INF file, as suggested by Figure 12-3. In the PKTDMA example, these sections are as follows:

[DestinationDirs]
DefaultDestDir=10,System32\Drivers

[SourceDisksFiles]
pktdma.sys=1

[SourceDisksNames]
1="WDM Book Companion Disc",disk1

Click to view at full size.

Figure 12-3. Source and destination information for file copies.

Table 12-3. Flags in a CopyFile section directive.

Symbolic Name Numeric Value Description
COPYFLG_REPLACEONLY 0x00000400 Copy only if destination file already exists
COPYFLG_NODECOMP 0x00000800 Don’t decompress file
COPYFLG_FORCE_FILE_IN_USE 0x00000008 Always copy under temporary name and rename on next boot
COPYFLG_NO_OVERWRITE 0x00000010 Don’t overwrite an existing file (other flags can’t be used with this flag)
COPYFLG_REPLACE_BOOT_FILE 0x00001000 Replace boot file needed by the loader, which will prompt user to reboot
COPYFLG_NOPRUNE 0x00002000 Copy this file even if Setup thinks it’s already present
COPYFLG_NOVERSIONCHECK 0x00000004 Overwrite a file even if it’s a newer version than the source file
COPYFLG_NO_VERSION_DIALOG 0x00000020 Don’t present the dialog that allows the user to decide whether to overwrite a newer file
COPYFLG_OVERWRITE_OLDER_ONLY 0x00000040 Only overwrite an older version of the file
COPYFLG_NOSKIP 0x00000002 Don’t allow the user to skip this file
COPYFLG_WARN_IF_SKIP 0x00000001 Allow the user to skip this file and provide a warning

The SourceDisksFiles section indicates that the installer can find PKTDMA.SYS on disk number 1 of the set. The SourceDisksNames section indicates that disk number 1 has a human-readable label of "WDM Book Companion Disc" and contains a file named "disk1" that the installer can look for to verify that the correct diskette is in the drive. Note that these section names have an interior "s" that's very easy to miss.

The DestinationDirs section specifies the target directories for copy operations. DefaultDestDir is the target directory to use for any file whose target directory isn't otherwise specified. You use a numeric code to specify the target directory because the end user might choose to install Windows 2000 to a directory with a nonstandard name. Please refer to the DDK documentation entry for the DestinationDirs section for a complete list of the codes—only a few of them are in common use, as follows:

WDM drivers reside in the Drivers directory. If your CopyFiles section applies only to a Windows 2000 installation, you can just specify directory number 12. If you want to share a CopyFiles section between Windows 98 and Windows 2000 installs, however, I recommend that you specify "10,System32\Drivers" instead because it identifies the Drivers directory in both cases.

Defining the Driver Service

The INF syntax I've described so far is sufficient for your driver file(s) to be copied onto the end user's hard disk. You must also arrange for the PnP Manager to know which files to load. A .Services section accomplishes that goal, as in this example:

[DriverInstall.NT.Services]
AddService=PKTDMA,2,DriverService

[DriverService]
ServiceType=1
StartType=3
ErrorControl=1
ServiceBinary=%10%\system32\drivers\pktdma.sys

The 2 in the AddService directive indicates that the PKTDMA service will be the function driver for the device. You form the name of this section by appending the word "Services" to the name of the install section to which it applies.

The end result of these directives will be a key in the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services branch of the registry named PKTDMA (the first parameter in the AddService directive). It will define the service entry for the driver as a kernel-mode driver (ServiceType equal to 1) that should be demand-loaded by the PnP Manager (StartType equal to 3). Errors that occur during loading should be logged but should not by themselves prevent the system from starting (ErrorControl equal to 1). The executable image can be found in \Winnt\System32\Drivers\pktdma.sys (the value of ServiceBinary). By the way, when you look in the registry, you'll see that the name of the executable file is stored under the name ImagePath rather than ServiceBinary.

It's a good idea to make the name of the service (PKTDMA in this example) the same as the filename (PKTDMA.SYS in this example) of your driver binary file. Not only does this make it obvious which service name corresponds to which driver, but it also avoids a problem that can arise when two different service keys point to the same driver: any device that uses the same driver as a then-started device but under a different service name can't itself start.

Device Identifiers

For true Plug and Play devices, the device identifier that appears in a manufacturer's model section of an INF is very important. Plug and Play devices are those that can electronically announce their presence and identity. A bus enumerator can find these devices automatically, and it can read some sort of onboard information to find out what kind each device is. Universal serial bus (USB) devices, for example, include vendor and product identification codes in their device descriptors, and the configuration space of Peripheral Component Interconnect (PCI) devices includes vendor and product codes.

When an enumerator detects a device, it constructs a list of device identification strings. One entry in the list is a complete identification of the device. This entry will end up naming the hardware key in the registry. Additional entries in the list are "compatible" identifiers. The PnP Manager uses all of the identifiers in the list when it tries to match a device to an INF file. Enumerators place more specific identifiers ahead of less specific identifiers so that vendors can supply specific drivers that will be found in preference to more general drivers. The algorithm for constructing the strings depends on the enumerator, as follows:

PCI Devices

The full device identifier has the form

PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss&REV_rr

where vvvv is the vendor identifier that the PCI Special Interest Group assigned to the manufacturer of the card, dddd is the device identifier that the manufacturer assigned to the card, ssssssss is the subsystem id (often zero) reported by the card, and rr is the revision number.

For example, the display adapter on my current laptop computer (based on the Chips and Technologies 65550 chip) has this identifier:

PCI\VEN_102C&DEV_00E0&SUBSYS_00000000&REV_04

A device can also match an INF model with any of these identifiers:

PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss
PCI\VEN_vvvv&DEV_dddd&REV_rr
PCI\VEN_vvvv&DEV_dddd
PCI\VEN_vvvv&DEV_dddd&REV_rr&CC_ccss
PCI\VEN_vvvv&DEV_dddd&CC_ccsspp
PCI\VEN_vvvv&DEV_dddd&CC_ccss
PCI\VEN_vvvv&CC_ccsspp
PCI\VEN_vvvv&CC_ccss
PCI\VEN_vvvv
PCI\CC_ccsspp
PCI\CC_ccss

in which cc is the base class code from the configuration space, ss is the subclass code, and pp is the programming interface. For example, the following additional identifiers for my laptop's display adapter would have matched the information in an INF file:

PCI\VEN_102C&DEV_00E0&SUBSYS_00000000
PCI\VEN_102C&DEV_00E0&REV_04
PCI\VEN_102C&DEV_00E0
PCI\VEN_102C&DEV_00E0&REV_04&CC_0300
PCI\VEN_102C&DEV_00E0&CC_030000
PCI\VEN_102C&DEV_00E0&CC_0300
PCI\VEN_102C&CC_030000
PCI\VEN_102C&CC_0300
PCI\VEN_102C
PCI\CC_030000
PCI\CC_0300

The INF that the system actually used for driver installation was the third one, which includes just the vendor and device identifiers.

PCMCIA Devices

The device identifier for a simple device has the form

PCMCIA\Manufacturer-Product-Crc

For example, the device identifier for the 3Com network card on my current laptop computer is

PCMCIA\MEGAHERTZ-CC10BT/2-BF05

For an individual function on a multifunction device, the identifier has the form

PCMCIA\Manufacturer-Product-DEVdddd-Crc

where Manufacturer is the name of the manufacturer and Product is the name of the product. The PCMCIA enumerator retrieves these strings directly from tuples on the card. Crc is the 4-digit hexadecimal CRC checksum for the card. The child function number (dddd in the template) is a decimal number without leading zeros.

If the card doesn't have a manufacturer name, the identifier will have one of these three forms:

PCMCIA\UNKNOWN_MANUFACTURER-Crc
PCMCIA\UNKNOWN_MANUFACTURER-DEVdddd-Crc
PCMCIA\MTD-0002

(The last of these three alternatives is for a flash memory card with no manufacturer identifier on the card.)

In addition to the device identifier just described, an INF file's model section can also contain an identifier composed by replacing the 4-digit hexadecimal CRC with a string containing the 4-digit hexadecimal manufacturer code, a hyphen, and the 4-digit hexadecimal manufacturer information code (both from onboard tuples). For example:

PCMCIA\MEGAHERTZ-CC10BT/2-0128-0103

SCSI Devices

The complete device identifier is

SCSI\ttttvvvvvvvvpppppppppppppppprrrr

where tttt is a device type code, vvvvvvvv is an 8-character vendor identifier, pppppppppppppppp is a 16-character product identifier, and rrrr is a 4-character revision level value. The device type code is the only one of the identifier components that doesn't have a fixed length. The bus driver determines this portion of the device identifier by indexing an internal string table with the device type code from the device's inquiry data, as shown in Table 12-4. The remaining components are just the strings that appear in the device's inquiry data but with special characters (including space, comma, and any nonprinting graphic) replaced with an underscore.

Table 12-4. Type names for SCSI devices.

SCSI Type Code Device Type Generic Type
DIRECT_ACCESS_DEVICE (0) Disk GenDisk
SEQUENTIAL_ACCESS_DEVICE (1) Sequential
PRINTER_DEVICE (2) Printer GenPrinter
PROCESSOR_DEVICE (3) Processor
WRITE_ONCE_READ_MULTIPLE_DEVICE (4) Worm GenWorm
READ_ONLY_DIRECT_ACCESS_DEVICE (5) CdRom GenCdRom
SCANNER_DEVICE (6) Scanner GenScanner
OPTICAL_DEVICE (7) Optical GenOptical
MEDIUM_CHANGER (8) Changer ScsiChanger
COMMUNICATION_DEVICE (9) Net ScsiNet
Other ScsiOther

For example, a disk drive on one of my workstations has this identifier:

SCSI\DiskSEAGATE_ST39102LW_______0004

The bus driver also creates these additional identifiers:

SCSI\ttttvvvvvvvvpppppppppppppppp
SCSI\ttttvvvvvvvv
SCSI\vvvvvvvvppppppppppppppppr
vvvvvvvvppppppppppppppppr
gggg

In the third and fourth of these additional identifiers, r represents just the first character of the revision identifier. In the last identifier, gggg is the generic type code from Table 12-4.

To carry forward the example of my disk drive, the bus driver generated these additional device identifiers:

SCSI\DiskSEAGATE_ST39102LW_______
SCSI\DiskSEAGATE_
SCSI\DiskSEAGATE_ST39102LW_______0
SEAGATE_ST39102LW_______0
GenDisk

The last of these (GenDisk) is the one that appeared as the device identifier in the INF file that the PnP Manager actually used to install a driver for this disk. In fact, the generic identifier is usually the one that's in the INF file because SCSI drivers tend to be generic.

IDE Devices

IDE devices receive device identifiers that are very similar to SCSI identifiers:

IDE\ttttvpvprrrrrrrr
IDE\vpvprrrrrrrr
IDE\ttttvpvp
vpvprrrrrrrr
gggg

Here, tttt is a device type name (same as SCSI); vpvp is a string containing the vendor name, an underscore, the vendor's product name, and enough underscores to bring the total to 40 characters; rrrrrrrr is an 8-character revision number; and gggg is a generic type name (almost the same as SCSI type names in Table 12-4). For IDE changer devices, the generic type name is GenChanger instead of ScsiChanger; other IDE generic names are the same as SCSI.

For example, here are the device identifiers generated for an IDE hard drive on one of my desktop systems:

IDE\DiskMaxtor_91000D8_____________________SASX1B18
IDE\Maxtor_91000D8___________________________SASX1B18
IDE\DiskMaxtor_91000D8________________________
Maxtor_91000D8__________________________SASX1B18
GenDisk

ISAPNP Devices

The ISAPNP enumerator constructs two hardware identifiers:

ISAPNP\id
*altid

where id and altid are EISA-style identifiers for the device—three letters to identify the manufacturer and 4 hexadecimal digits to identify the particular device. If the device in question is one function of a multifunction card, the first identifier in the list takes this form:

ISAPNP\id_DEVnnnn

where nnnn is the decimal index (with leading zeros) of the function.

For example, the codec function of the Crystal Semiconductor audio card on one of my desktop machines has these two hardware identifiers:

ISAPNP\CSC6835_DEV0000
*CSC0000

The second of these identifiers is the one that matched the actual INF file.

USB Devices

The complete device identifier is

USB\VID_vvvv&PID_dddd&REV_rrrr

where vvvv is the 4-digit hexadecimal vendor code assigned by the USB committee to the vendor, dddd is the 4-digit hexadecimal product code assigned to the device by the vendor, and rrrr is the revision code. All three of these values appear in the device descriptor or interface descriptor for the device.

An INF model section can also specify these alternatives:

USB\VID_vvvv&PID_dddd
USB\CLASS_cc&SUBCLASS_ss&PROT_pp
USB\CLASS_cc&SUBCLASS_ss
USB\CLASS_cc
USB\COMPOSITE

where cc is the class code from the device or interface descriptor, ss is the subclass code, and pp is the protocol code. These values are in 2-digit hexadecimal format.

1394 Devices

The 1394 bus driver constructs these identifiers for a device:

1394\VendorName&ModelName
1394\UnitSpecId&UnitSwVersion

where VendorName is the name of the hardware vendor, ModelName identifies the device, UnitSpecId identifies the software specification authority, and UnitSwVersion identifies the software specification. The information used to construct these identifiers comes from the device's configuration ROM.

If a device has vendor and model name strings, the 1394 bus driver uses the first identifier as the hardware ID and the second identifier as the one and only compatible ID. If a device lacks a vendor or model name string, the bus driver uses the second identifier as the hardware ID.

Since I don't have a 1394 bus on any of my computers, I relied on fellow driver writer Jeff Kellam to provide me with two examples. The first example is for a Sony camera, for which the device identifier is

1394\SONY&CCM-DS250_1.08

The second example is for the 1394 bus itself operating in diagnostic mode; this device identifier is

1394\031887&040892

Identifiers for Generic Devices

The PnP Manager also works with device identifiers for generic devices that can appear on many different buses. These identifiers are of the form

*PNPdddd

where dddd is a 4-digit hexadecimal type identifier. At press time, the official list of these identifiers was at http://www.microsoft.com/hwdev/download/respec/devids.txt.

The Hardware Registry Key

The hardware registry key records information about a particular hardware instance your driver manages. Each enumerator of devices has its own registry key below HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum. When the enumerator finds a device with a particular identifier, it creates a key for the identifier and a subkey for each instance of the same device. For example, the PKTDMA device has the identifier PCI\VEN_10E8&DEV_4750. The first instance of this device in your system might have a hardware key named like this:

\Registry\Machine\System\CurrentControlSet\Enum\
  PCI\VEN_10E8&DEV_4750\BUS_00&DEV_04&FUNC_00

Standard Properties

The PnP Manager stores certain standard information about the device in the hardware key. You can retrieve this information in a WDM driver by calling IoGetDeviceProperty with one of the property codes listed in Table 12-5.

Table 12-5. Standard device properties in the hardware key.

Property Name Value Name Source Description
DevicePropertyDeviceDescription DeviceDesc First parameter in model statement Description of device
DevicePropertyHardwareId HardwareID Third parameter in model statement Identifies device
DevicePropertyCompatibleIDs CompatibleIDs Created by bus driver during detection Device types that can be considered to match
DevicePropertyClassName Class Class parameter in Version section of INF Name of device class
DevicePropertyClassGuid ClassGUID ClassGuid parameter in Version section of INF Unique identifier of device class
DevicePropertyDriverKeyName Driver Created automatically as part of installation process Name of service (software) key that specifies driver
DevicePropertyManufacturer 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));

Notice from Table 12-5 that the PnP Manager and bus driver together manage to create all of the standard device properties automatically except for the friendly name. You can supply a friendly name by an explicit statement in your INF file if you want:

[DriverInstall.NT.hw]
AddReg=DriverHwAddReg

[DriverHwAddReg]
HKR,,FriendlyName,,"Packet DMA Demonstration Device"

Mind you, every device of this particular type that is installed on a particular machine will end up with the same friendly name if you adopt this approach. The end user will obviously be confused if more than one device has the same friendly name. If you anticipate that there might be duplicate friendly names, you should provide a co-installer DLL to compute unique names.

User-mode applications can retrieve the standard device properties with SetupDiGetDeviceRegistryProperty. Use the following method within the context of an enumeration of registered interfaces using the setup APIs:

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.

As you can see, you must supply a device information set handle (an HDEVINFO) and an SP_DEVINFO_DATA structure as arguments to SetupDiGetDeviceRegistryProperty. That's easy to do if you're in the middle of a loop enumerating instances of a device interface. But suppose all you have is the symbolic name of the device? You can use the following trick, which I found to be pretty obscure when one of the Microsoft developers showed it to me, to construct these two crucial parameters:

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 and Windows 2000, but Microsoft discourages their use in new code. For that reason, I'm not even showing you examples of calling them. You might be tempted—as I initially was—to use them because they seem to be better documented. If you know where to look for the documentation, that is. Have patience: Microsoft will get around to documenting the SetupDiXxx functions in enough detail for us mortals to use them effectively.

Nonstandard Properties

The PnP Manager creates a subkey of the hardware key named Device Parameters. This subkey contains nonstandard properties of the device. You can initialize nonstandard properties in a hardware add registry section in your INF:

[DriverInstall.nt.hw]
AddReg=DriverHwAddReg

[DriverHwAddReg]
HKR,,SampleInfo,,"%wdmbook%\chap7\pktdma\pktdma.htm"

WDM drivers can easily open a handle to the device parameter key by calling IoOpenDeviceRegistryKey. Applications can access the key by using SetupDiOpenDevRegKey.

Tools for INF Files

If you look in the TOOLS subdirectory of the Windows 2000 DDK, you'll find two useful utilities for working with INF files. GENINF will help you build a new INF file, and CHKINF will help you validate an INF file. At the time I'm writing this, I'm using the RC1 release of the DDK, in which GENINF is still pretty rudimentary. By the time you read this, GENINF will either have grown to a robust tool with a completely different user interface than it now has, or else it will have been dropped from the kit. Either way, I can't give you any useful information about how to use it.

CHKINF is actually a BAT file that runs a PERL script to examine and validate an INF file. You'll obviously need a PERL implementation to use this tool. I got a copy from http://www.perl.com.

You can run CHKINF most easily from a command prompt. For example:

E:\Ntddk\tools\chkinf>chkinf C:\wdmbook\chap12\devprop\sys\device.inf

CHKINF generates HTML output files in an HTM subdirectory. Figure 12-4 shows the output I received when checking DEVICE.INF for DEVPROP sample.

Click to view at full size.

Figure 12-4. Example of CHKINF output.

In Windows 2000, the device installer logs various information about the operations it performs in a disk file named SETUPAPI.LOG in the Windows NT directory. You can control the verbosity of the log and the name of the log file by manually changing entries in the registry key named HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Setup. Please consult the DDK documentation for detailed information about these settings.