Let's suppose you have a device that doesn't fit into one of the device classes Microsoft has already defined. When you're initially testing your device and your driver, you can get away with using the Unknown class in your INF file. Production devices are not supposed to be in the Unknown class, however. You should instead place your custom device into a new device class that you define in the INF file. I'll explain how to create a custom class in this section.
The INF example I showed you earlier relied on a custom device class:
[Version] Signature=$CHICAGO$ Class=Sample ClassGuid={894A7460-A033-11d2-821E-444553540000} |
In fact, all of the samples in this book use the Sample class.
When you want to define a new class of device, you only need to do one task: run GUIDGEN to create a unique GUID for the class. You can add polish to the user interface for your device class by doing some additional tasks, such as writing a property page provider for use with the Device Manager and putting some special entries into the registry key your class uses. You can also provide filter drivers and parameter overrides that will be used for every device of your class. You control each of these additional features by statements in your INF file. For example:
[ClassInstall32] AddReg=SamclassAddReg CopyFiles=SamclassCopyFiles [SamclassAddReg] HKR,,,,"WDM Book Sample" ... [SamclassCopyFiles] ... |
The illustrated registry entry turns into the "friendly name" for the device class in the Device Manager and in the list of device types displayed by the add hardware wizard. I'll explain some of the additional registry entries you might want to add to the class key in the following sections.
NOTE
None of my INF files has a ClassInstall32 section. None is needed because the setup program for the sample disc puts the necessary class information directly into the registry. If you define your own device class as part of a production driver package, however, you will need this section. Note also that Microsoft discourages installing a new class without using an INF.
Way back in Chapter 1, "Introduction"—in Figure 1-6, to be precise—I showed you a screen shot of the property page I invented for use with the Sample device class. The SAMCLASS sample on the companion disc is the source code for the property page provider that produced that page, and I'm now going to explain how it works.
A property page provider for a device class is a 32-bit DLL with the following contents:
In general, a single DLL can provide property pages for several device classes. Microsoft supplies some DLLs with the operating system that do this, for example. SAMCLASS, however, provides only a single page for a single class of device. Its only exported entry point is the following function:
extern "C" BOOL CALLBACK EnumPropPages (PSP_PROPSHEETPAGE_REQUEST p, LPFNADDPROPSHEETPAGE AddPage, LPARAM lParam) { PROPSHEETPAGE page; HPROPSHEETPAGE hpage; memset(&page, 0, sizeof(page)); page.dwSize = sizeof(PROPSHEETPAGE); page.hInstance = hInst; page.pszTemplate = MAKEINTRESOURCE(IDD_SAMPAGE); page.pfnDlgProc = PageDlgProc; <some more stuff> hpage = CreatePropertySheetPage(&page); if (!hpage) return TRUE; if (!(*AddPage)(hpage, lParam)) DestroyPropertySheetPage(hpage); return TRUE; } |
When the Device Manager is about to construct the property sheet for a device, it consults the class registry key to see if there's a property page provider. You can designate a provider with a line like the following in your INF file:
[SamclassAddReg] HKR,,EnumPropPages32,,"samclass.dll,EnumPropPages" |
The Device Manager loads the DLL you specify (SAMCLASS.DLL) and calls the designated entry point (EnumPropPages). If the function returns TRUE, the Device Manager will display the property page; otherwise, it won't. The function can add zero or more pages by calling the AddPage function as shown in the preceding example.
Inside the SP_PROPSHEETPAGE_REQUEST structure your enumeration function receives as an argument, you'll find two very useful pieces of information: a handle to a device information set, and the address of an SP_DEVINFO_DATA structure that pertains to the device you're concerned with. These data items (but not, unfortunately, the SP_PROPSHEETPAGE_REQUEST structure that contains them) remain valid for as long as the property page is visible, and it would be useful for you to be able to access them inside the dialog procedure you write for your property page. Windows SDK Programming 101 (well, maybe 102, because this is a little obscure) taught you how to do this. First create an auxiliary structure whose address you pass to CreatePropertySheetPage as the lParam member of the PROPSHEETPAGE structure:
struct SETUPSTUFF { HDEVINFO info; PSP_DEVINFO_DATA did; }; BOOL EnumPropPages(...) { PROPSHEETPAGE page; ... SETUPSTUFF* stuff = new SETUPSTUFF; stuff->info = p->DeviceInfoSet; stuff->did = p->DeviceInfoData; page.lParam = (LPARAM) stuff; page.pfnCallback = PageCallbackProc; page.dwFlags = PSP_USECALLBACK; ... } UINT CALLBACK PageCallbackProc(HWND junk, UINT msg, LPPROPSHEETPAGE p) { if (msg == PSPCB_RELEASE && p->lParam) delete (SETUPSTUFF*) p->lParam; return TRUE; } |
The WM_INITDIALOG message that Windows sends to your dialog procedure gets an lParam value that's a pointer to the same PROPSHEETPAGE structure, so you can retrieve the stuff pointer there. You can then use SetWindowLong and GetWindowLong to save any desired information in the DWL_USER slot associated with the dialog object. In SAMCLASS, I chose to determine the name of a readme file that would describe the sample driver. I'll show you the code for doing that in a couple of paragraphs.
You also need to provide a way to delete the SETUPSTUFF structure when it's no longer needed. The easiest way, which works whether or not you ever get a WM_INITDIALOG message—you won't if there's an error constructing your property page—is to use a property page callback function as shown in the preceding fragment.
You can do all sorts of things in a custom property page. For the sample class, I wanted to provide a button that would bring up an explanation for each sample device. To keep things as general as possible, I decided to put a SampleInfo value naming the explanation file in the device's hardware registry key. To invoke a viewer for the explanation file, it suffices to call ShellExecute, which will interpret the file extension and locate an appropriate viewer application. For my book samples, the explanation files are HTML files, so the viewer in question will be your Web browser.
Most of the work in SAMCLASS occurs in the WM_INITDIALOG handler. (Error checking is again omitted.)
1 2 3 4 |
case WM_INITDIALOG: { SETUPSTUFF* stuff = (SETUPSTUFF*) ((LPPROPSHEETPAGE) lParam)->lParam; BOOL okay = FALSE; TCHAR name[256]; SetupDiGetDeviceRegistryProperty(stuff->info, stuff->did, SPDRP_FRIENDLYNAME, NULL, (PBYTE) name, sizeof(name), NULL); SetDlgItemText(hdlg, IDC_SAMNAME, name); HKEY hkey = SetupDiOpenDevRegKey(stuff->info, stuff->did, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); DWORD length = sizeof(name); RegQueryValueEx(hkey, "SampleInfo", NULL, NULL, (LPBYTE) name, &length); LPSTR infofile; DoEnvironmentSubst(name, sizeof(name)); infofile = (LPSTR) GlobalAlloc(GMEM_FIXED, strlen(name)+1); strcpy(infofile, name); SetWindowLong(hdlg, DWL_USER, (LONG) infofile); RegCloseKey(hkey); break; } |
When the end user—that would be you in this particular situation, I think—presses the More Information button on the property page, the dialog procedure receives a WM_COMMAND message, which it processes as shown below.
case WM_COMMAND: switch (LOWORD(wParam)) { case IDB_MOREINFO: { LPSTR infofile = (LPSTR) GetWindowLong(hdlg, DWL_USER); ShellExecute(hdlg, NULL, infofile, NULL, NULL, SW_SHOWNORMAL); return TRUE; } } break; |
ShellExecute will launch the application associated with the SampleInfo file—namely, your Web browser—whereupon you can view the file and find all sorts of interesting information.
In the preceding section, I showed you how an EnumPropPages32 registry entry controls the display of property pages for devices belonging to your custom class. Here are some other registry entries that you can use to tailor features of the class:
A class key may also specify DeviceCharacteristics, DeviceType, and/or Security properties that contain overriding values for certain device attributes. I discussed these values in Chapter 2, "Basic Structure of a WDM Driver," in the section "The Role of the Registry." The PnP Manager applies these overrides when it creates a physical device object (PDO). I'm guessing here, but I suspect that someday a system administrator will somehow be able to examine and change these properties.