Drivers can use the registry to obtain information about their devices and to store information that must persist from one system reboot to the next. UMDF and KMDF provide the following ways for a driver to read and write the registry:
A UMDF driver uses a device property store.
A KMDF driver uses a registry key object.
The device property store is an area in the registry where a UMDF driver can maintain information about the characteristics of its device, such as time-out values or device configuration settings for a particular device-in short, any device-specific information that the driver stores for use each time the system or device starts.
Each property store has a name, which is the same as that of the registry key that maintains the information. By default, the key has the same name as the driver. A property store contains one or more named string, integer, or binary values. To read information from the property store, a UMDF driver must have both the name of the property store and the name of the value that contains the data.
The property store provides a secure way for UMDF driver to write data to the registry and insulates drivers from the actual location of the data. A UMDF driver should not try to read or write property store data by accessing a specific registry location, and conversely, a UMDF driver cannot use the property store methods to read or write data such as device parameters that are recorded in other parts of the registry. To obtain information from elsewhere in the registry, drivers should use the Windows Registry API or Setup API. However, a UMDF driver runs in the LocalService security context, which restricts the areas of the registry that the driver can read and write.
A UMDF driver can create a new device property store or retrieve an existing property store by calling the RetrieveDevicePropertyStore method on either of the following interfaces:
IWDFDeviceInitialize during device initialization.
IWDFDevice after creation of the device object.
The RetrieveDevicePropertyStore method returns a pointer to an IWDFNamedPropertyStore helper interface through which the driver can set and get the values of device properties. This method has the following four parameters:
pcwszServiceName
A pointer to a NULL-terminated string that represents the name of the device property store, or NULL to use the name of the calling driver in the WUDF services list.
Flags
A WDF_PROPERTY_STORE_RETRIEVE_FLAGS value:
WdfPropertyStoreNormal does not create the property store if it does not already exist.
WdfPropertyStoreCreateIfMissing creates a property store if the requested property store does not exist.
ppPropStore
A pointer to a location in which the method returns a pointer to the IWDFNamedPropertyStore interface.
pDisposition
A pointer to a variable in which the method returns one of the following WDF_PROPERTY_STORE_DISPOSITION values:
CreatedNewStore means that the framework created a new store.
OpenedExistingStore means that the framework opened an existing store.
After the driver creates or retrieves the property store, it uses the IWDFNamedPropertyStore interface to get and set the values in the store. Table 12-3 summarizes the methods of the IWDFNamedPropertyStore interface.
Method | Description |
---|---|
GetNameAt | Retrieves the name of a property, given the index of the property. |
GetNameCount | Retrieves the number of properties in the property store. |
GetNamedValue | Retrieves the value of a property given the name of the value. |
SetNamedValue | Sets the value of a property. |
The driver defines the format and contents of the property store. The property store persists in the registry until the device is uninstalled from the system. The framework saves the property store under the devnode, so the property store is deleted when the device is uninstalled. The device vendor is not required to supply an uninstall procedure. Listing 12-3 shows how a driver creates a named property store after it creates a device object. This example is excerpted from Sideshow\WSSDevice.cpp.
Listing 12-3: Creating a named property store
hr = m_pWdfDevice->RetrieveDevicePropertyStore(NULL, WdfPropertyStoreCreateIfMissing, &pStore, NULL); if (SUCCEEDED(hr)) { hr = m_pBasicDriver->Initialize(pStore); }
In the example, the m_pWdfDevice variable is a pointer to the IWDFDevice interface that the driver received when it created the framework device object. The driver passes NULL as the property store name and WdfPropertyStoreCreateIfMissing to request that the framework create a new property store with the default name. The driver also passes NULL for the pDisposition parameter because it does not require this information. The method returns a pointer to the IWDFNamedPropertyStore interface in pStore.
Listing 12-4 shows how the same sample driver gets a named value from the property store. This code is derived from Sideshow\BasicDDI.cpp.
Listing 12-4: Retrieving information from a property store
PROPVARIANT pvBlob = {0}; PropVariantInit(&pvBlob); hr = m_pPropertyStore->GetNamedValue(wszKey, &pvBlob); if (SUCCEEDED(hr) && VT_BLOB == pvBlob.vt && 0 == (pvBlob.blob.cbSize % sizeof(APPLICATION_ID))) { *pcAppIds = pvBlob.blob.cbSize / sizeof(APPLICATION_ID); *ppAppIds = (APPLICATION_ID*)pvBlob.blob.pBlobData; }
The sample code calls IWDFNamedPropertyStore::GetNamedValue to retrieve the value of the key that the wszKey string describes. The value is returned as a binary value of variant type VT_BLOB, which the driver then parses and evaluates. Note that PropVariantInit is a COM function that initializes a variant property structure.
Tip See "PropVariantInit" on MSDN for more information-online at http://go.microsoft.com/fwlink/?LinkId=79586.
Listing 12-5 shows how the Sideshow sample driver sets a named value in the property store. This code is also derived from Sideshow\BasicDDI.cpp.
Listing 12-5: Setting information in a property store
PROPVARIANT pvBlob = {0}; PropVariantInit(&pvBlob); pvBlob.vt = VT_BLOB; pvBlob.blob.cbSize = cApps * sizeof(APPLICATION_ID); pvBlob.blob.pBlobData = (BYTE*)pApps; hr = m_pPropertyStore->SetNamedValue(wszKey, &pvBlob);
In Listing 12-5, the sample driver writes an application ID to the property store as binary data. The sample fills a PROPVARIANT variable with a value of variant type VT_BLOB and then calls the IWDFNamedPropertyStore::SetNamedValue method to save the value in the key named by the wszKey string.
KMDF includes numerous methods with which a driver can read and write the registry. These methods enable the driver to create, open, and close a registry key and to query, change, and delete the values of keys and individual data items within them.
A driver can access the registry entry for a driver or device either before or after creating the device object. If your driver requires information from the registry before it creates the device object, your driver can use the WdfFdoInitXxx methods to get the value of individual device properties or to retrieve the entire device hardware key or driver software key. After creating the device object, the driver uses WdfDeviceXxx methods. Table 12-4 summarizes the methods that query individual device properties and open registry keys.
Method | Description |
---|---|
WdfDeviceAllocAndQueryProperty | Allocates a buffer and retrieves a device property from the registry, given a handle to a WDFDEVICE object. |
WdfDeviceQueryProperty | Retrieves a device property from the registry, given a handle to a WDFDEVICE object. |
WdfDeviceOpenRegistryKey | Opens the registry hardware key for a device or the software key for a driver and creates a framework registry-key object that represents the key. |
WdfFdoInitAllocAndQueryProperty | Allocates a buffer and retrieves a device property from the registry, given a pointer to a WDFDEVICE_INIT structure. |
WdfFdoInitOpenRegistryKey | Opens a device's hardware key or a driver's software key in the registry and creates a registry-key object that represents the registry key, given a pointer to a WDFDEVICE_INIT structure. |
WdfFdoInitQueryProperty | Retrieves a device property from the registry, given a pointer to a WDFDEVICE_INIT structure. |
To read the value of a registry key, a driver opens the registry key and then calls a method that queries the registry for data. The WdfFdoInitOpenRegistryKey and WdfRegistryOpenKey methods open a registry key. Both of these methods have the following five parameters:
DeviceInit - or - Device
A pointer to a WDFDEVICE_INIT structure for WdfFdoInitOpenRegistryKey.
- Or -
A handle to a WDFDEVICE object for WdfRegistryOpenKey.
DeviceInstanceKeyType
A ULONG value that identifies the key to open.
DesiredAccess
A bit mask that indicates the type of required access.
KeyAttributes
An optional attribute structure.
Key
A location to receive a handle to a WDFKEY object.
To get or set the value of a single setting within the key, the driver must use one of the WdfRegistryXxx methods, which are listed in Table 12-5. When the driver has completed using the registry, the driver calls WdfRegistryClose to close and delete the key.
Method | Description |
---|---|
WdfRegistryAssignMemory | Assigns data from a memory buffer to a value name in the registry. |
WdfRegistryAssignMultiString | Assigns a set of strings from a collection of string objects to a value name in the registry. |
WdfRegistryAssignString | Assigns a string from a string object to a value name in the registry. |
WdfRegistryAssignULong | Assigns an unsigned long word value to a value name in the registry. |
WdfRegistryAssignUnicodeString | Assigns a Unicode string to a value name in the registry. |
WdfRegistryAssignValue | Assigns data to a value name in the registry. |
WdfRegistryClose | Closes the registry key that is associated with a registry-key object and then deletes the registry-key object. |
WdfRegistryCreateKey | Creates and opens a registry key, or just opens the key if it already exists, and creates a registry-key object that represents the registry key. |
WdfRegistryOpenKey | Opens a registry key and creates a registry-key object that represents the registry key. |
WdfRegistryQueryMemory | Retrieves the data that is currently assigned to a registry value, stores the data in a framework-allocated buffer, and creates a memory object to represent the buffer. |
WdfRegistryQueryMultiString | Retrieves the strings that are currently assigned to a multistring registry value, creates a framework string object for each string, and adds each string object to a collection. |
WdfRegistryQueryString | Retrieves the string data that is currently assigned to a registry string value and assigns the string to a string object. |
WdfRegistryQueryULong | Retrieves the unsigned long word (REG_DWORD) data that is currently assigned to a registry value and copies the data to a driver-specified location. |
WdfRegistryQueryUnicodeString | Retrieves the string data that is currently assigned to a registry string value and copies the string to a UNICODE_STRING structure. |
WdfRegistryQueryValue | Retrieves the data that is currently assigned to a registry value. |
WdfRegistryRemoveKey | Removes the registry key that is associated with a framework registry-key object and then deletes the registry-key object. |
WdfRegistryRemoveValue | Removes a value and its data from a registry key. |
WdfRegistryWdmGetHandle | Returns a WDM handle to the registry key that a framework registry-key object represents. |
Sample code to read and write the registry In the Pcidrv.c source file, the PCIDRV sample provides functions that:
Read a REG_DWORD registry value that was written by another user-mode or kernel-mode component.
Read a REG_DWORD registry value that was stored under the device key.
Write a REG_DWORD registry value that was stored under the device key.
The PCIDRV sample driver's PciDrvReadFdoRegistryKeyValue function is called from the EvtDriverDeviceAdd callback before the driver creates the device object. It reads a key, which the driver's INF wrote at installation, that indicates whether the driver was installed as an NDIS upper-edge miniport driver. This information is important because it determines whether the driver registers certain power policy and I/O event callbacks. If the driver was installed as an upper-edge miniport driver, it is not the power policy manager for its device; instead, NDIS manages power policy. Listing 12-6 shows the source code for this function.
Listing 12-6: Reading a registry key during device object initialization
BOOLEAN PciDrvReadFdoRegistryKeyValue( __in PWDFDEVICE_INIT DeviceInit, __in PWCHAR Name, __out PULONG Value ) { WDFKEY hKey = NULL; NTSTATUS status; BOOLEAN retValue = FALSE; UNICODE_STRING valueName; PAGED_CODE(); *Value = 0; status = WdfFdoInitOpenRegistryKey(DeviceInit, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_ALL, WDF_NO_OBJECT_ATTRIBUTES, &hKey); if (NT_SUCCESS (status)) { RtlInitUnicodeString (&valueName,Name); status = WdfRegistryQueryULong (hKey, &valueName, Value); if (NT_SUCCESS (status)) { retValue = TRUE; } WdfRegistryClose(hKey); } return retValue; }
First, the driver initializes the Value parameter that will receive the requested key value. Next, it opens the registry key by calling WdfFdoInitOpenRegistryKey, passing the five previously described parameters, and receiving a handle to the returned WDFKEY object in hKey. The PLUGPLAY_REGKEY_DEVICE constant specifies the device's hardware key. Although the sample requests all access rights to the registry by specifying STANDARD_RIGHTS_ALL, the driver only reads the key and does not write it, so STANDARD_RIGHTS_READ would also work.
If the driver successfully opens the hardware key, it queries the key for the requested value. The name of the value is passed into the PciDrvReadFdoRegistryKeyValue function as a pointer to a string. However, the KMDF query method requires the name in a counted Unicode string. Therefore, before querying for the value, the driver calls RtlInitUnicodeString to copy the input string into a string of the correct format.
The driver then calls WdfRegistryQueryUlong, which returns the ULONG value of the key in the Value parameter. The driver closes the key by calling WdfRegistryClose and the function returns.