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.
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. A driver can read the registry either before or after creating
its device object.
To read the registry before creating the device object, a driver calls the WdfFdoInitOpenRegistryKey method with the following parameters:
A handle to the WDFDEVICE_INIT structure that was passed to its EvtDriverDeviceAdd function.
A ULONG value that identifies the key to open.
A bit mask that indicates the type of required access.
An optional attribute structure.
A location to receive a handle to a WDFKEY object.
The WDFDEVICE_INIT structure contains settings that the framework requires before the device object has been created. WdfFdoInitOpenRegisterKey requires some of this information so that it can find the requested key.
To read the registry after creating the device object, a driver calls WdfDeviceOpenRegistryKey with a handle to the device object instead of the WDFDEVICE_INIT structure.
To get the value of a single setting within the key,
the driver must query the key. KMDF provides several query methods,
each of which returns a different type of value. For example, if the
information is stored as a ULONG, the driver calls WdfRegisteryQueryUlong with the handle to the keys, a pointer to the name of the value, and a pointer to a ULONG variable to receive the value. After completing its query, the driver closes the registry key by calling WdfRegistryClose.
1. Code to Read and Write the Registry
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.
These functions are in the pcidrv.c source file.
The PCIDRV sample driver’s PciDrvReadFdoRegistryKeyValue function is called from the EvtDriverDeviceAdd callback, before the driver creates the device object. It reads a key that the driver’s INF wrote at installation, which 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; NDIS manages power policy.
The following is the source code for this function:
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. The WDF_DEVICEINIT object provides information that is required to identify the driver-specific key. The next parameter is a ULONG that contains flags that identify the key to open; the constant PLUGPLAY_REGKEY_DEVICE indicates the device’s hardware key. Although the sample requests all access rights to the registry (STANDARD_RIGHTS_ALL), the driver only reads the key and does not write it, so STANDARD_RIGHTS_READ would also work. Finally, the driver specifies WDF_NO_OBJECT_ATTRIBUTES to indicate that it is not passing an attribute structure for the key object. The output parameter hKey receives a handle to the returned WDFKEY object.
If the driver successfully opens the hardware key,
it can query the key for the requested value. The name of the value is
passed into the current 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 RtInitUnicodeString to copy the input string into a string of the correct format.
The driver then queries for the value by calling WdfRegistryQueryUlong, which returns the ULONG value of the key in the Value parameter. The driver then closes the key by calling WdfRegisterClose and the function returns.
The driver’s other function to read the registry is similar. The only difference is that PciDrvReadRegistryValue is always called after the WDFDEVICE object has been created and therefore uses a handle to the WDFDEVICE object instead of a handle to the WDFDEVICE_INIT object.
The following is the code for this function:
BOOLEAN
PciDrvReadRegistryValue (
__in PFDO_DATA FdoData,
__in PWCHAR Name,
__out PULONG Value
)
{
WDFKEY hKey = NULL;
NTSTATUS status;
BOOLEAN retValue = FALSE;
UNICODE_STRING valueName;
PAGED_CODE ();
*Value = 0;
status = WdfDeviceOpenRegistryKey (FdoData->WdfDevice,
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;
}
WdfRegistryClost (hKey);
}
return retValue;
}
This function is passed a pointer to the device context area (FdoData), where the driver keeps a handle to the device object. It passes the device object handle to the WdfDeviceOpenRegistryKey method. The driver then proceeds in exactly the same way as the previously discussed PciDrvReadFdoRegisterKeyValue function: It calls RtlUnicode-StringCopyString to build a Unicode string that holds the requested value name, calls WdfRegistryQueryUlong to get the ULONG value of the key, and calls WdfRegistryClose when it is finished.
The third registry function in the driver is PciDrvWriteRegistry-Value, which writes a ULONG value to the registry. This function differs from PciDrvReadRegistryValue in only two aspects:
The value of the key is an input parameter to PciDrvWriteRegistryValue but an output parameter to PciDrvReadRegistryValue.
PciDrvWriteRegistryValue calls WdfRegistryAssignULong to write a new value for the key, whereas PciDrvReadRegistryValue calls WdfRegistryQueryULong to read the current value of the key.
Otherwise, the functions are identical. The following statement shows how the driver writes the new value for the key:
status = WdfREgistryAssignULong (hKey, &valueName, Value);
Currently, the PCIDRV sample driver does not call PciDrvWriteRegistryValue; this function is included only for demonstration.