在设备驱动中添加属性页
2011-09-20 14:50
295 查看
Click
Here to Download: Code Associated With This Article Zip Archive, 45KB
In this article, I am going to show you how to create a custom property page for your driver that displays in Device Manager. In case you don?t know which ?property page? I?m talking about, check out the picture in
Figure 1. The example driver I?ll use is for a device that uses a custom setup class.
![](http://www.osronline.com/images/default/articles/27/noth_driver_prop.gif)
Figure 1 ? WDM Nothing Driver Property Page
First of all you might ask: Why would I want a Device Property Page? According to the Windows 2000 DDK, ?A co installer or class installer should supply a custom device property page if its device or class has any individual properties that a user can
set?. But even if you don?t create a co-installer or class installer, you still might want to have custom properties for your device or driver. Maybe you need to set the speed of your device, a buffer size for incoming data, or provide a license or security
key. Or, you might want to provide means to selectively set some debug parameters. Whatever your reasons, creating a property page can make setting changeable device parameters easy, and creates a nice, finished, look to your driver. And it?s actually
pretty easy, once you know the tricks I?ll describe in this article.
We?ll start with OSR?s implementation of the WDM NOTHING Driver. What does the driver do? Well, exactly what the name states, Nothing! Using this driver, we will take create an ?.INF? file for it, specifying a new setup class for the device (So, we?ll
be talking about the CLASS32INSTALL section). Then, we?ll talk about what is needed in the DLL and how to implement it. Finally, we?ll talk about what is needed in the WDM NOTHING Driver so that the DLL can find it.
Getting Started
In the example to be used for this article, we create an INF file for the WDM NOTHING driver. As a part of creating this INF file, we decide that the device is a member of a unique setup class.. In order to define this new setup class, we add some information
to the INF file. In the [Version] section of the file, we add the following 2 lines:
Class=OsrNothingClass
ClassGuid={0c833dae-8679-11d3-b19b-0060b0efd4aa}
The first line declares that the WDM NOTHING driver is part of the setup class named ?OsrNothingClass? (The currently support list of Device Setup Classes in located in ?Chapter 8 Device Setup Classes? of the DDK). Since this class is not a currently known
class, we must create a Globally Unique Identifier (GUID) using the GUIDGEN.EXE utility, available with VC++ or the SDK. This will uniquely identify our new setup class to Win2K/XP. Once this is done, we must create a new section in our INF file, the
[ClassInstall32] section, which will add a new setup device class (and possibly a class installer) for some number of devices of the same type. For our example, we add the following lines to our INF file:
[ClassInstall32]
Addreg=OsrNothingClass
[OsrNothingClass]
HKR,,,,%ClassName%
HKR,,Icon,,103 ; Empty
HKR,,EnumPropPages32,,"nothingproppage.dll,NOTHINGPropPageProvider"
In the [ClassInstall32] section, the ?AddReg? directive references another named section in our INF file, which contains class-specific value entries to be written into the registry when our INF file is processed. In our
case, the [OsrNothingClass] is the only named section that will be processed.
Our [OsrNothingClass] section contains 3 lines. The Registry values created by these lines will be created under the ?HKLM\System\CurrentControlSet\Control\Class\{X} key in the registry (where X is the GUID, expressed in hex digits, that
we generated for our new class). The first line:
HKR,,,,%ClassName%
creates an unnamed value in the Registry which defines the device-class friendly name that will be displayed by the Device Manager (See
Figure 2), in our Example %ClassName% is defined in the [Strings] as
ClassName = "OSR Nothing Devices?.
![](http://www.osronline.com/images/default/articles/27/device_manager.gif)
Figure 2 ? WDM Nothing Driver Shown in Device Manager
The second line in our INF file
HKR,,Icon,,103
creates a named value Icon which contains the string 103. What is 103? Well, that happens to correspond to a numbered resource in our property page DLL that will represent the icon displayed by the Device Manager. In our case this translates
into a bucket icon (no smirks allowed! I can program, I never said I could draw). Of course, you could use one of the ?standard? icons for your device. But what fun is
that? By the way, the icon should be something that represents your device to the user. It shouldn?t be a little miniature version of you company?s logo (which doesn?t necessary denote anything about your device other than who made it). The third
and final line
HKR,,EnumPropPages32,,"nothingproppage.dll,NOTHINGPropPageProvider"
creates a named value EnumPropPages32. This value indicates that
the property page provider for this class is called nothingproppage.dll (which must be located in %SystemRoot%\System32) and its main entry point is
NOTHINGPropPageProvider.
Whew! Now that we?ve got this all out of the way, let?s get to the good stuff?.
The Property Page DLL and the NothingPropPageProvider Interface
A property page DLL is a standard Win32 DLL. It contains a ?DllMain? entry point that gets called when the DLL gets loaded. As you can see below our DllMain, named NOTHINGPropPageProvider, is pretty simple. It just saves away the Handle to the DLL module,
which is the base address of the DLL. The HINSTANCE of a DLL is the same as the
HMODULE of the DLL, so hinstDLL can be used in calls to functions that require a module handle. After the DLL is loaded the first routine called by the Device Manager is the entry point
NOTHINGPropPageProvider that we defined in our EnumPropPages32 key. The definition of the
NothingPropPageProvider entry point (as shown in Figure 3)is as follows:
· pPropPageRequest ? Which is used to retrieve a handle (or, potentially, multiple handles) to property pages for a specified property page type
· fAddFunc ? Is the address of a callback routine to be called when any pages defined in the
pPropPageRequest structure (defined above) are needed.
· lParam ? Is the add sheet functions private data handle
The purpose of this routine is to respond to any calls for additional property sheets (other than the property sheets that the Device Manager handles) that our class of device might handle. In our case, we handle the request for the
SPPSR_ENUM_ADV_DEVICE_PROPERTIES property sheet for our device. In our implementation, once we receive this type of request, we need to determine which of our devices information is being requested. We do this by calling our routine
GetDeviceInterfaceDetail.
The GetDeviceInterfaceDetail routines? responsibility is to retrieve Device Interface Details. In other words, it must get the information necessary to open a handle to the correct instance of a WDM NOTHING device so that we may communicate
with it. To do this, we use the SetupDiXxx interface routines. Our first call to
SetupDiGetDevice InstanceId retrieves the device instance ID (a string) associated with a device information element, contained within the
PSP_PROPSHEETPAGE_REQUEST that was passed into GetDeviceInterfaceDetail. As you can see in the code in
Figure 3, we make two calls to this routine.
BOOL APIENTRY NOTHINGPropPageProvider(PSP_PROPSHEETPAGE_REQUEST pPropPageRequest, LPFNADDPROPSHEETPAGE fAddFunc, LPARAM lParam)
{
PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetailData;
PNOTHING_PROP_INTF pNothingFeatures;
PROPSHEETPAGE PropSheetPage;
HPROPSHEETPAGE hPropSheetPage;
NothingOutputDebugString("NOTHINGPropPageProvider Enter\n");
// Check page requested
if( pPropPageRequest->PageRequested != SPPSR_ENUM_ADV_DEVICE_PROPERTIES ) {
return FALSE;
}
// Check device info set and data
if ((!pPropPageRequest->DeviceInfoSet) || (!pPropPageRequest->DeviceInfoData)) {
return FALSE;
}
// Allocate the memory for the Nothing features.
pNothingFeatures = (PNOTHING_PROP_INTF) LocalAlloc (LPTR, sizeof (NOTHING_PROP_INTF));
if (!pNothingFeatures) {
DbgError ("NOTHINGPropPageProvider: LocalAlloc: No Features");
return FALSE;
}
memset(pNothingFeatures,0,sizeof(NOTHING_PROP_INTF));
// Get the device interface detail which return a path to the device
// driver that we need to open the device.
if (!GetDeviceInterfaceDetail (pPropPageRequest, &pDeviceInterfaceDetailData)) {
return FALSE;
}
// Get the Nothing features through the private property call.
if (!GetNothingFeatures (pDeviceInterfaceDetailData, pNothingFeatures)) {
return FALSE;
}
pNothingFeatures->PIntfDetail = pDeviceInterfaceDetailData;
// We don't need the device interface details any more, get rid of it now!
// LocalFree (pDeviceInterfaceDetailData);
// initialize the property page
PropSheetPage.dwSize = sizeof(PROPSHEETPAGE);
PropSheetPage.dwFlags = PSP_USECALLBACK | PSP_HASHELP;
PropSheetPage.hInstance = ghInstance;
PropSheetPage.pszTemplate = MAKEINTRESOURCE(DLG_NOTHINGFEATURES);
PropSheetPage.pfnDlgProc = NothingDlgProc;
PropSheetPage.lParam = (LPARAM) pNothingFeatures;
PropSheetPage.pfnCallback = NothingSettingsDlgCallback;
// create the page and get back a handle
hPropSheetPage = CreatePropertySheetPage( &PropSheetPage );
if( !hPropSheetPage ) {
return FALSE;
}
// add the property page
if( !(*fAddFunc)(hPropSheetPage, lParam)) {
DestroyPropertySheetPage (hPropSheetPage);
return FALSE;
}
NothingOutputDebugString("NOTHINGPropPageProvider Exit\n");
return TRUE;
}
Figure 3
The first call is expected to fail, since all we really want here is to figure out the number of bytes that will have to be allocated in order to retrieve the device instance ID. Our second call to
SetupDiGetDeviceInstanceId will pass this allocated buffer as a parameter, so that we can retrieve the string. You could eliminate the 2 calls and just allocate a buffer of fixed length, but that is a waste of memory (perhaps a topic on which
Peter can pontificate!).
Now that we have the device instance ID, we want to verify that the device we?re going to talk to supports the Device Interface that the WDM NOTHING driver supports (technically, we already know the answer to this, given we have correctly implemented
IoRegisterDeviceInterface as described in the DDK). To do this we call
SetupDiGetClassDevs passing in the Device Interface GUID that we?re interested in, the device instance ID that we got previously, and finally the
DIGCF_DEVICEINTERFACE flag which tells SetupDiGetClassDevs that it should only examine this particular device (specified by the device instance ID) for the requested interface class. Once we have this information, we must
call SetupDiEnumDevice Interfaces that returns a context structure for a device interface element of a device information set. In other words it returns us information about the
NOTHINGID_PRIVATE interface, so that we may retrieve the interface information required to do a
CreateFile to our device. Finally, we call SetupDiGetInterfaceDetail that will return us all the information necessary to communicate with our device (Note: we actually call this function twice to ensure that we allocate the
correct amount of memory for the returned information).
While painful, this is the Microsoft preferred way to get a handle to the device you want to communicate with. Using pre-defined device names i.e. COM1, COM2?COM99, is a thing of the past. With plug and play, devices may not be named in numerical order;
it is much simpler to just say that your device supports the GUID_CLASS_COMPORT interface.
Once we successfully return from GetDeviceInterfaceDetail we have enough information to be able to communicate with the correct device instance. So, the next thing that our
NothingPropPageProvider routine does is call GetNothingFeatures.
This is just a simple routine which opens our WDM NOTHING device instance, and issues a private IOCTL we define (IOCTL_GET_NOTHING_ PROPERTY) in order to receive our
NOTHING_ FEATURES information (used to update our property sheet).
Once we have the NOTHING_FEATURES information we must define our property sheet to the Device Manager. To do this we must fill in a
PROPSHEETPAGE structure with information about our sheet and then add it to the Device Manager?s list of sheets. The code that does this (as shown in
Figure 2) has fields defined as follows:
· dwSize ? Receives the size of the PROPSHEETPAGE structure
· dwFlags ? This field instructs the property page manager that this property page supports help (PSP_HASHELP) and wants to be called back (using PSP_USECALLBACK) at the routine specified in PfnCallBack.
· hInstance ? This field is where we store the location of ghInstance (the filed that we set in DLLMain).
· pszTemplate ? This field is the storage location for the resourceID for our property page (which is how the device manager knows what information to display).
· pfnDlgProc ? This field receives a pointer to our NothingDlgProc routine that is the dialog box procedure for our property page (i.e., the routine that processes Windows messages targeted at our dialog box).
· lParam ? This field receives a pointer to our NOTHING_PROP_INTF structure, which contains context for our WDM NOTHING driver features, and information on which device instance to communicate with.
· pfnCallBack ? This field contains the address of NothingSettingsDlgCallback, which gets called when the property sheet is created or destroyed (if destroyed, we clean up any memory we allocated).
Once this structure is initialized, we call the Win32 API CreatePropertSheetPage routine, which creates the property page (for clarity, property
sheets contain property pages) and then, if successful, we call the
AddPropertySheet routine whose address was passed in to our
NothingPropPage Provider routine.
NothingDlgProc
The NothingDlgProc routine handles Windows messages targeted at our Property Page Dialog box. As you can see in the code shown in
Figure 4, we handle very few messages, only WM_INITDIALOG
and WM_COMMAND.
int APIENTRY NothingDlgProc (HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
switch (uMessage) {
// We don't do anything for these messages.
case WM_COMMAND:
NothingOutputDebugString("NothingDlgProc WM_COMMAND\n");
switch(LOWORD(wParam)) {
case IDC_CHECK_DO_NOTHING: {
PNOTHING_PROP_INTF pNothingFeatures = (PNOTHING_PROP_INTF)GetWindowLong(hDlg, DWL_USER);
UINT val = IsDlgButtonChecked(hDlg,IDC_CHECK_DO_NOTHING);
//
// Something has happened to our control. See if the state of the button
// has changed.
//
switch(val) {
case BST_CHECKED:
pNothingFeatures->NothingFeatures.DoNothing = TRUE;
NothingOutputDebugString("NothingDlgProc Checked\n");
break;
case BST_UNCHECKED:
pNothingFeatures->NothingFeatures.DoNothing = FALSE;
NothingOutputDebugString("NothingDlgProc Unchecked\n");
break;
default:
NothingOutputDebugString("NothingDlgProc Indeterminate\n");
break;
}
SetNothingFeatures (pNothingFeatures->PIntfDetail,pNothingFeatures);
}
break;
default:
break;
}
NothingOutputDebugString("NothingDlgProc WM_COMMAND exit\n");
break;
case WM_CONTEXTMENU:
case WM_HELP:
case WM_NOTIFY:
case WM_DESTROY:
break;
case WM_INITDIALOG:
return NothingPropPage_OnInitDialog (hDlg, (HWND) wParam, lParam);
}
return FALSE;
}
Figure 4 ? NothingDlgProc
Let?s start with WM_INITDIALOG, when we get this message, Windows is asking us to initialize our dialog box, so we call our
NothingPropPage_OnInitDialog routine (oh, don?t start up with me about these long routine names, OK? I happen to have to support the code that I write). This routine is simple; It just initializes the dialog box fields. In our case, it calls
the WDM NOTHING device instance to get information about its features, and initializes the Check Box in the dialog with the correct information. The one interesting thing is that we save a pointer to our
NOTHING_PROP_INTF structure in the window memory of the dialog (using
SetWindowLong) so that we have a context value to work with when subsequent
WM_COMMANDs come to this routine.
When the NothingDlgProc routine receives WM_ COMMAND messages, these messages are targeted at controls in the window (in our case the Check Box) so it is this routines responsibility to determine which control is affected
by this message and how it is affected. In order to figure out which control is affected, the code calls LOWORD(wParam). This macro returns the resource ID of the control affected by the message. We only have one control
IDC_CHECK_DO_NOTHING and the only thing that could have happened to the control is that it was either checked or unchecked. Once we determine which one it was, we call the call our
SetNothingFeatures routine which calls the driver with the IOCTL we defined to set features (IOCTL_SET_NOTHING_PROPERTY).
Summary
As you can see writing a Property Page DLL is not that difficult. It can be time consuming, and you do have to know something about Windows GUI programming (I only know enough to be dangerous). You also need to understand how your driver works, but if
you?re reading The NT Insider, I sure hope you already know that. In fact probably the hardest thing to get correct is the
SetupDi? calls and using IoRegisterDeviceInterface
and IoSetDeviceInterfaceState.
转自: http://www.osronline.com/custom.cfm?name=articlePrint.cfm&id=27
Here to Download: Code Associated With This Article Zip Archive, 45KB
In this article, I am going to show you how to create a custom property page for your driver that displays in Device Manager. In case you don?t know which ?property page? I?m talking about, check out the picture in
Figure 1. The example driver I?ll use is for a device that uses a custom setup class.
![](http://www.osronline.com/images/default/articles/27/noth_driver_prop.gif)
Figure 1 ? WDM Nothing Driver Property Page
First of all you might ask: Why would I want a Device Property Page? According to the Windows 2000 DDK, ?A co installer or class installer should supply a custom device property page if its device or class has any individual properties that a user can
set?. But even if you don?t create a co-installer or class installer, you still might want to have custom properties for your device or driver. Maybe you need to set the speed of your device, a buffer size for incoming data, or provide a license or security
key. Or, you might want to provide means to selectively set some debug parameters. Whatever your reasons, creating a property page can make setting changeable device parameters easy, and creates a nice, finished, look to your driver. And it?s actually
pretty easy, once you know the tricks I?ll describe in this article.
We?ll start with OSR?s implementation of the WDM NOTHING Driver. What does the driver do? Well, exactly what the name states, Nothing! Using this driver, we will take create an ?.INF? file for it, specifying a new setup class for the device (So, we?ll
be talking about the CLASS32INSTALL section). Then, we?ll talk about what is needed in the DLL and how to implement it. Finally, we?ll talk about what is needed in the WDM NOTHING Driver so that the DLL can find it.
Getting Started
In the example to be used for this article, we create an INF file for the WDM NOTHING driver. As a part of creating this INF file, we decide that the device is a member of a unique setup class.. In order to define this new setup class, we add some information
to the INF file. In the [Version] section of the file, we add the following 2 lines:
Class=OsrNothingClass
ClassGuid={0c833dae-8679-11d3-b19b-0060b0efd4aa}
The first line declares that the WDM NOTHING driver is part of the setup class named ?OsrNothingClass? (The currently support list of Device Setup Classes in located in ?Chapter 8 Device Setup Classes? of the DDK). Since this class is not a currently known
class, we must create a Globally Unique Identifier (GUID) using the GUIDGEN.EXE utility, available with VC++ or the SDK. This will uniquely identify our new setup class to Win2K/XP. Once this is done, we must create a new section in our INF file, the
[ClassInstall32] section, which will add a new setup device class (and possibly a class installer) for some number of devices of the same type. For our example, we add the following lines to our INF file:
[ClassInstall32]
Addreg=OsrNothingClass
[OsrNothingClass]
HKR,,,,%ClassName%
HKR,,Icon,,103 ; Empty
HKR,,EnumPropPages32,,"nothingproppage.dll,NOTHINGPropPageProvider"
In the [ClassInstall32] section, the ?AddReg? directive references another named section in our INF file, which contains class-specific value entries to be written into the registry when our INF file is processed. In our
case, the [OsrNothingClass] is the only named section that will be processed.
Our [OsrNothingClass] section contains 3 lines. The Registry values created by these lines will be created under the ?HKLM\System\CurrentControlSet\Control\Class\{X} key in the registry (where X is the GUID, expressed in hex digits, that
we generated for our new class). The first line:
HKR,,,,%ClassName%
creates an unnamed value in the Registry which defines the device-class friendly name that will be displayed by the Device Manager (See
Figure 2), in our Example %ClassName% is defined in the [Strings] as
ClassName = "OSR Nothing Devices?.
![](http://www.osronline.com/images/default/articles/27/device_manager.gif)
Figure 2 ? WDM Nothing Driver Shown in Device Manager
The second line in our INF file
HKR,,Icon,,103
creates a named value Icon which contains the string 103. What is 103? Well, that happens to correspond to a numbered resource in our property page DLL that will represent the icon displayed by the Device Manager. In our case this translates
into a bucket icon (no smirks allowed! I can program, I never said I could draw). Of course, you could use one of the ?standard? icons for your device. But what fun is
that? By the way, the icon should be something that represents your device to the user. It shouldn?t be a little miniature version of you company?s logo (which doesn?t necessary denote anything about your device other than who made it). The third
and final line
HKR,,EnumPropPages32,,"nothingproppage.dll,NOTHINGPropPageProvider"
creates a named value EnumPropPages32. This value indicates that
the property page provider for this class is called nothingproppage.dll (which must be located in %SystemRoot%\System32) and its main entry point is
NOTHINGPropPageProvider.
Whew! Now that we?ve got this all out of the way, let?s get to the good stuff?.
The Property Page DLL and the NothingPropPageProvider Interface
A property page DLL is a standard Win32 DLL. It contains a ?DllMain? entry point that gets called when the DLL gets loaded. As you can see below our DllMain, named NOTHINGPropPageProvider, is pretty simple. It just saves away the Handle to the DLL module,
which is the base address of the DLL. The HINSTANCE of a DLL is the same as the
HMODULE of the DLL, so hinstDLL can be used in calls to functions that require a module handle. After the DLL is loaded the first routine called by the Device Manager is the entry point
NOTHINGPropPageProvider that we defined in our EnumPropPages32 key. The definition of the
NothingPropPageProvider entry point (as shown in Figure 3)is as follows:
· pPropPageRequest ? Which is used to retrieve a handle (or, potentially, multiple handles) to property pages for a specified property page type
· fAddFunc ? Is the address of a callback routine to be called when any pages defined in the
pPropPageRequest structure (defined above) are needed.
· lParam ? Is the add sheet functions private data handle
The purpose of this routine is to respond to any calls for additional property sheets (other than the property sheets that the Device Manager handles) that our class of device might handle. In our case, we handle the request for the
SPPSR_ENUM_ADV_DEVICE_PROPERTIES property sheet for our device. In our implementation, once we receive this type of request, we need to determine which of our devices information is being requested. We do this by calling our routine
GetDeviceInterfaceDetail.
The GetDeviceInterfaceDetail routines? responsibility is to retrieve Device Interface Details. In other words, it must get the information necessary to open a handle to the correct instance of a WDM NOTHING device so that we may communicate
with it. To do this, we use the SetupDiXxx interface routines. Our first call to
SetupDiGetDevice InstanceId retrieves the device instance ID (a string) associated with a device information element, contained within the
PSP_PROPSHEETPAGE_REQUEST that was passed into GetDeviceInterfaceDetail. As you can see in the code in
Figure 3, we make two calls to this routine.
BOOL APIENTRY NOTHINGPropPageProvider(PSP_PROPSHEETPAGE_REQUEST pPropPageRequest, LPFNADDPROPSHEETPAGE fAddFunc, LPARAM lParam)
{
PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetailData;
PNOTHING_PROP_INTF pNothingFeatures;
PROPSHEETPAGE PropSheetPage;
HPROPSHEETPAGE hPropSheetPage;
NothingOutputDebugString("NOTHINGPropPageProvider Enter\n");
// Check page requested
if( pPropPageRequest->PageRequested != SPPSR_ENUM_ADV_DEVICE_PROPERTIES ) {
return FALSE;
}
// Check device info set and data
if ((!pPropPageRequest->DeviceInfoSet) || (!pPropPageRequest->DeviceInfoData)) {
return FALSE;
}
// Allocate the memory for the Nothing features.
pNothingFeatures = (PNOTHING_PROP_INTF) LocalAlloc (LPTR, sizeof (NOTHING_PROP_INTF));
if (!pNothingFeatures) {
DbgError ("NOTHINGPropPageProvider: LocalAlloc: No Features");
return FALSE;
}
memset(pNothingFeatures,0,sizeof(NOTHING_PROP_INTF));
// Get the device interface detail which return a path to the device
// driver that we need to open the device.
if (!GetDeviceInterfaceDetail (pPropPageRequest, &pDeviceInterfaceDetailData)) {
return FALSE;
}
// Get the Nothing features through the private property call.
if (!GetNothingFeatures (pDeviceInterfaceDetailData, pNothingFeatures)) {
return FALSE;
}
pNothingFeatures->PIntfDetail = pDeviceInterfaceDetailData;
// We don't need the device interface details any more, get rid of it now!
// LocalFree (pDeviceInterfaceDetailData);
// initialize the property page
PropSheetPage.dwSize = sizeof(PROPSHEETPAGE);
PropSheetPage.dwFlags = PSP_USECALLBACK | PSP_HASHELP;
PropSheetPage.hInstance = ghInstance;
PropSheetPage.pszTemplate = MAKEINTRESOURCE(DLG_NOTHINGFEATURES);
PropSheetPage.pfnDlgProc = NothingDlgProc;
PropSheetPage.lParam = (LPARAM) pNothingFeatures;
PropSheetPage.pfnCallback = NothingSettingsDlgCallback;
// create the page and get back a handle
hPropSheetPage = CreatePropertySheetPage( &PropSheetPage );
if( !hPropSheetPage ) {
return FALSE;
}
// add the property page
if( !(*fAddFunc)(hPropSheetPage, lParam)) {
DestroyPropertySheetPage (hPropSheetPage);
return FALSE;
}
NothingOutputDebugString("NOTHINGPropPageProvider Exit\n");
return TRUE;
}
Figure 3
The first call is expected to fail, since all we really want here is to figure out the number of bytes that will have to be allocated in order to retrieve the device instance ID. Our second call to
SetupDiGetDeviceInstanceId will pass this allocated buffer as a parameter, so that we can retrieve the string. You could eliminate the 2 calls and just allocate a buffer of fixed length, but that is a waste of memory (perhaps a topic on which
Peter can pontificate!).
Now that we have the device instance ID, we want to verify that the device we?re going to talk to supports the Device Interface that the WDM NOTHING driver supports (technically, we already know the answer to this, given we have correctly implemented
IoRegisterDeviceInterface as described in the DDK). To do this we call
SetupDiGetClassDevs passing in the Device Interface GUID that we?re interested in, the device instance ID that we got previously, and finally the
DIGCF_DEVICEINTERFACE flag which tells SetupDiGetClassDevs that it should only examine this particular device (specified by the device instance ID) for the requested interface class. Once we have this information, we must
call SetupDiEnumDevice Interfaces that returns a context structure for a device interface element of a device information set. In other words it returns us information about the
NOTHINGID_PRIVATE interface, so that we may retrieve the interface information required to do a
CreateFile to our device. Finally, we call SetupDiGetInterfaceDetail that will return us all the information necessary to communicate with our device (Note: we actually call this function twice to ensure that we allocate the
correct amount of memory for the returned information).
While painful, this is the Microsoft preferred way to get a handle to the device you want to communicate with. Using pre-defined device names i.e. COM1, COM2?COM99, is a thing of the past. With plug and play, devices may not be named in numerical order;
it is much simpler to just say that your device supports the GUID_CLASS_COMPORT interface.
Once we successfully return from GetDeviceInterfaceDetail we have enough information to be able to communicate with the correct device instance. So, the next thing that our
NothingPropPageProvider routine does is call GetNothingFeatures.
This is just a simple routine which opens our WDM NOTHING device instance, and issues a private IOCTL we define (IOCTL_GET_NOTHING_ PROPERTY) in order to receive our
NOTHING_ FEATURES information (used to update our property sheet).
Once we have the NOTHING_FEATURES information we must define our property sheet to the Device Manager. To do this we must fill in a
PROPSHEETPAGE structure with information about our sheet and then add it to the Device Manager?s list of sheets. The code that does this (as shown in
Figure 2) has fields defined as follows:
· dwSize ? Receives the size of the PROPSHEETPAGE structure
· dwFlags ? This field instructs the property page manager that this property page supports help (PSP_HASHELP) and wants to be called back (using PSP_USECALLBACK) at the routine specified in PfnCallBack.
· hInstance ? This field is where we store the location of ghInstance (the filed that we set in DLLMain).
· pszTemplate ? This field is the storage location for the resourceID for our property page (which is how the device manager knows what information to display).
· pfnDlgProc ? This field receives a pointer to our NothingDlgProc routine that is the dialog box procedure for our property page (i.e., the routine that processes Windows messages targeted at our dialog box).
· lParam ? This field receives a pointer to our NOTHING_PROP_INTF structure, which contains context for our WDM NOTHING driver features, and information on which device instance to communicate with.
· pfnCallBack ? This field contains the address of NothingSettingsDlgCallback, which gets called when the property sheet is created or destroyed (if destroyed, we clean up any memory we allocated).
Once this structure is initialized, we call the Win32 API CreatePropertSheetPage routine, which creates the property page (for clarity, property
sheets contain property pages) and then, if successful, we call the
AddPropertySheet routine whose address was passed in to our
NothingPropPage Provider routine.
NothingDlgProc
The NothingDlgProc routine handles Windows messages targeted at our Property Page Dialog box. As you can see in the code shown in
Figure 4, we handle very few messages, only WM_INITDIALOG
and WM_COMMAND.
int APIENTRY NothingDlgProc (HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
switch (uMessage) {
// We don't do anything for these messages.
case WM_COMMAND:
NothingOutputDebugString("NothingDlgProc WM_COMMAND\n");
switch(LOWORD(wParam)) {
case IDC_CHECK_DO_NOTHING: {
PNOTHING_PROP_INTF pNothingFeatures = (PNOTHING_PROP_INTF)GetWindowLong(hDlg, DWL_USER);
UINT val = IsDlgButtonChecked(hDlg,IDC_CHECK_DO_NOTHING);
//
// Something has happened to our control. See if the state of the button
// has changed.
//
switch(val) {
case BST_CHECKED:
pNothingFeatures->NothingFeatures.DoNothing = TRUE;
NothingOutputDebugString("NothingDlgProc Checked\n");
break;
case BST_UNCHECKED:
pNothingFeatures->NothingFeatures.DoNothing = FALSE;
NothingOutputDebugString("NothingDlgProc Unchecked\n");
break;
default:
NothingOutputDebugString("NothingDlgProc Indeterminate\n");
break;
}
SetNothingFeatures (pNothingFeatures->PIntfDetail,pNothingFeatures);
}
break;
default:
break;
}
NothingOutputDebugString("NothingDlgProc WM_COMMAND exit\n");
break;
case WM_CONTEXTMENU:
case WM_HELP:
case WM_NOTIFY:
case WM_DESTROY:
break;
case WM_INITDIALOG:
return NothingPropPage_OnInitDialog (hDlg, (HWND) wParam, lParam);
}
return FALSE;
}
Figure 4 ? NothingDlgProc
Let?s start with WM_INITDIALOG, when we get this message, Windows is asking us to initialize our dialog box, so we call our
NothingPropPage_OnInitDialog routine (oh, don?t start up with me about these long routine names, OK? I happen to have to support the code that I write). This routine is simple; It just initializes the dialog box fields. In our case, it calls
the WDM NOTHING device instance to get information about its features, and initializes the Check Box in the dialog with the correct information. The one interesting thing is that we save a pointer to our
NOTHING_PROP_INTF structure in the window memory of the dialog (using
SetWindowLong) so that we have a context value to work with when subsequent
WM_COMMANDs come to this routine.
When the NothingDlgProc routine receives WM_ COMMAND messages, these messages are targeted at controls in the window (in our case the Check Box) so it is this routines responsibility to determine which control is affected
by this message and how it is affected. In order to figure out which control is affected, the code calls LOWORD(wParam). This macro returns the resource ID of the control affected by the message. We only have one control
IDC_CHECK_DO_NOTHING and the only thing that could have happened to the control is that it was either checked or unchecked. Once we determine which one it was, we call the call our
SetNothingFeatures routine which calls the driver with the IOCTL we defined to set features (IOCTL_SET_NOTHING_PROPERTY).
Summary
As you can see writing a Property Page DLL is not that difficult. It can be time consuming, and you do have to know something about Windows GUI programming (I only know enough to be dangerous). You also need to understand how your driver works, but if
you?re reading The NT Insider, I sure hope you already know that. In fact probably the hardest thing to get correct is the
SetupDi? calls and using IoRegisterDeviceInterface
and IoSetDeviceInterfaceState.
转自: http://www.osronline.com/custom.cfm?name=articlePrint.cfm&id=27
相关文章推荐
- nandflash驱动之添加MTD设备与测试
- 在menuconfig中添加编译驱动设备
- 在uclinux添加设备驱动的完整过程
- 在uclinux添加设备驱动的完整过程(ZT)
- Linux添加设备驱动步骤和实例
- Linux之设备驱动学习简过程<三>(不断添加中)
- 向内核里面添加触摸屏驱动(方法适合其他设备)
- Linux Kernel设备驱动模型之总线添加设备
- 向内核里面添加触摸屏驱动(方法适合其他设备)[zt]
- WinCE流设备驱动简介及往BSP添加GPIO驱动
- WINCE--流设备驱动简介及往BSP添加
- 如何在linux下添加自己的设备驱动
- linux驱动自动添加设备节点
- 如何把设备驱动文件添加到Linux内核编译选项配置的menuconfig中
- 驱动中添加参数进行调试和控制设备驱动
- Linux设备驱动之s3c2440添加LCD驱动
- Linux下SPI和IIC驱动免在设备树上添加设备信息的编写方法
- Linux Kernel设备驱动模型之总线添加驱动
- WINCE--流设备驱动简介及往BSP添加
- Linux Kernel设备驱动模型之 设备添加