/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: CmRegUtil.c Abstract: This module contains registry utility functions. Author: Adrian J. Oney - April 21, 2002 Revision History: --*/ #include "WlDef.h" #include "CmpRegutil.h" #pragma hdrstop #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, CmRegUtilOpenExistingUcKey) #pragma alloc_text(PAGE, CmRegUtilCreateUcKey) #pragma alloc_text(PAGE, CmRegUtilUcValueGetDword) #pragma alloc_text(PAGE, CmRegUtilUcValueGetFullBuffer) #pragma alloc_text(PAGE, CmRegUtilUcValueSetFullBuffer) #pragma alloc_text(PAGE, CmRegUtilUcValueSetUcString) #pragma alloc_text(PAGE, CmRegUtilOpenExistingWstrKey) #pragma alloc_text(PAGE, CmRegUtilCreateWstrKey) #pragma alloc_text(PAGE, CmRegUtilWstrValueGetDword) #pragma alloc_text(PAGE, CmRegUtilWstrValueGetFullBuffer) #pragma alloc_text(PAGE, CmRegUtilWstrValueSetFullBuffer) #pragma alloc_text(PAGE, CmRegUtilWstrValueSetUcString) #pragma alloc_text(PAGE, CmRegUtilUcValueSetWstrString) #pragma alloc_text(PAGE, CmRegUtilWstrValueSetWstrString) #pragma alloc_text(PAGE, CmpRegUtilAllocateUnicodeString) #pragma alloc_text(PAGE, CmpRegUtilFreeAllocatedUnicodeString) #endif #define POOLTAG_REGBUFFER 'bRpP' #define POOLTAG_UCSTRING 'cUpP' // // FUTURE WORK: // - Add function to read strings from registry // - Add function to read multisz strings from registry // - Add function to write multisz strings from registry // - Add function to create key *path* (see IopCreateRegistryKeyEx, who's // code should be cleaned up first) // - Add function to recursively delete keys // // // Unicode primitives - these are the best functions to use. // NTSTATUS CmRegUtilOpenExistingUcKey( IN HANDLE BaseHandle OPTIONAL, IN PUNICODE_STRING KeyName, IN ACCESS_MASK DesiredAccess, OUT HANDLE *Handle ) /*++ Routine Description: Opens a registry key using the name passed in based at the BaseHandle node. This name may specify a key that is actually a registry path. Arguments: BaseHandle - Optional handle to the base path from which the key must be opened. If this parameter is specified, then KeyName must be a relative path. KeyName - UNICODE_STRING Name of the Key that must be opened (either a full registry path, or a relative path depending on whether BaseHandle is supplied) DesiredAccess - Specifies the desired access that the caller needs to the key (this isn't really used as the access-mode is KernelMode, but we specify it anyway). Handle - Recieves registry key handle upon success, NULL otherwise. Note that the handle is in the global kernel namespace (and not the current processes handle take). The handle should be released using ZwClose. Return Value: STATUS_SUCCESS if the key could be opened, in which case Handle receives the registry key. Otherwise, failure is returned, and handle receives NULL. --*/ { OBJECT_ATTRIBUTES objectAttributes; HANDLE newHandle; NTSTATUS status; PAGED_CODE(); *Handle = NULL; InitializeObjectAttributes( &objectAttributes, KeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, BaseHandle, (PSECURITY_DESCRIPTOR) NULL ); // // Simply attempt to open the path, as specified. // status = ZwOpenKey( &newHandle, DesiredAccess, &objectAttributes ); if (NT_SUCCESS(status)) { *Handle = newHandle; } return status; } NTSTATUS CmRegUtilCreateUcKey( IN HANDLE BaseHandle, IN PUNICODE_STRING KeyName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions, IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, OUT ULONG *Disposition OPTIONAL, OUT HANDLE *Handle ) /*++ Routine Description: Opens or creates a registry key using the name passed in based at the BaseHandle node. Arguments: BaseHandle - Handle to the base path under which the key must be opened. KeyName - UNICODE_STRING Key Name that must be opened/created. DesiredAccess - Specifies the desired access that the caller needs to the key (this isn't really used as the access-mode is KernelMode, but we specify it anyway). CreateOptions - Options passed to ZwCreateKey. Examples: REG_OPTION_VOLATILE - Key is not to be stored across boots. REG_OPTION_NON_VOLATILE - Key is preserved when the system is rebooted. SecurityDescriptor - Security to apply if the key is newly created. If NULL, the key will inherit settings as defined by the inheritable properties of its parent. Disposition - This optional pointer receives a ULONG indicating whether the key was newly created (0 on error): REG_CREATED_NEW_KEY - A new Registry Key was created. REG_OPENED_EXISTING_KEY - An existing Registry Key was opened. Handle - Recieves registry key handle upon success, NULL otherwise. Note that the handle is in the global kernel namespace (and not the current processes handle take). The handle should be released using ZwClose. Return Value: The function value is the final status of the operation. --*/ { OBJECT_ATTRIBUTES objectAttributes; ULONG disposition; HANDLE newHandle; NTSTATUS status; PAGED_CODE(); InitializeObjectAttributes( &objectAttributes, KeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, BaseHandle, SecurityDescriptor ); // // Attempt to create the path as specified. We have to try it this // way first, because it allows us to create a key without a BaseHandle // (if only the last component of the registry path is not present). // status = ZwCreateKey( &newHandle, DesiredAccess, &objectAttributes, 0, (PUNICODE_STRING) NULL, CreateOptions, &disposition ); // // Upon failure, populate the passed in parameters with consistant values // (this ensures determinisity if the calling code fails to properly check // the return value). // if (!NT_SUCCESS(status)) { newHandle = NULL; disposition = 0; } *Handle = newHandle; if (ARGUMENT_PRESENT(Disposition)) { *Disposition = disposition; } return status; } NTSTATUS CmRegUtilUcValueGetDword( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN ULONG DefaultValue, OUT ULONG *Value ) /*++ Routine Description: This routine reads a dword value from the registry. The value name is specified in UNICODE_STRING form. Arguments: KeyHandle - Points to key to read. ValueName - Points to the value to read. DefaultValue - Points to the default value to use in case of an absence or error. Value - Receives DefaultValue on error, otherwise the value stored in the registry. Return Value: STATUS_SUCCESS if the value was present in the registry, STATUS_OBJECT_NAME_NOT_FOUND if it was absent, STATUS_OBJECT_TYPE_MISMATCH if the value was not a dword, or some other error value. --*/ { UCHAR valueBuffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION keyInfo; ULONG keyValueLength; ULONG finalValue; NTSTATUS status; PAGED_CODE(); // // Preinit // finalValue = DefaultValue; keyInfo = (PKEY_VALUE_PARTIAL_INFORMATION) valueBuffer; // // Read in the value // status = ZwQueryValueKey( KeyHandle, ValueName, KeyValuePartialInformation, (PVOID) valueBuffer, sizeof(valueBuffer), &keyValueLength ); // // Fill in the output only as appropriate. // if (NT_SUCCESS(status)) { if (keyInfo->Type == REG_DWORD) { finalValue = *((PULONG) keyInfo->Data); } else { // // Closest error we can get... // status = STATUS_OBJECT_TYPE_MISMATCH; } } *Value = finalValue; return status; } NTSTATUS CmRegUtilUcValueGetFullBuffer( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN ULONG DataType OPTIONAL, IN ULONG LikelyDataLength OPTIONAL, OUT PKEY_VALUE_FULL_INFORMATION *Information ) /*++ Routine Description: This routine is invoked to retrieve the data for a registry key's value. This is done by querying the value of the key with a zero-length buffer to determine the size of the value, and then allocating a buffer and actually querying the value into the buffer. It is the responsibility of the caller to free the buffer. Arguments: KeyHandle - Supplies the key handle whose value is to be queried ValueName - Supplies the Unicode string name of the value. DataType - REG_NONE if any type is allowable, otherwise the specific type required. LikelyDataLength - An optional parameter to eliminate unneccessary allocations and reparses. Information - Receives a pointer to the allocated data buffer allocated from PagedPool, NULL on error. If successful, the buffer should be freed using ExFreePool. Note - the allocated memory is *not* charged against the calling process. Return Value: STATUS_SUCCESS if the information was retrievable, error otherwise (in which case Information will receive NULL). --*/ { PKEY_VALUE_FULL_INFORMATION infoBuffer; ULONG keyValueLength, guessSize; NTSTATUS status; PAGED_CODE(); // // Preinit for error // *Information = NULL; // // Set an initial size to try when loading a key. Note that // KeyValueFullInformation already comes with a single WCHAR of data. // guessSize = (ULONG)(sizeof(KEY_VALUE_FULL_INFORMATION) + ValueName->Length); // // Now round up to a natural alignment. This needs to be done because our // data member will naturally aligned as well. // guessSize = (ULONG) ALIGN_POINTER_OFFSET(guessSize); // // Adjust for the most likely size of the data. // guessSize += LikelyDataLength; infoBuffer = ExAllocatePoolWithTag( NonPagedPool, guessSize, POOLTAG_REGBUFFER ); if (infoBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Figure out how big the data value is so that a buffer of the // appropriate size can be allocated. // status = ZwQueryValueKey( KeyHandle, ValueName, KeyValueFullInformation, (PVOID) infoBuffer, guessSize, &keyValueLength ); if (NT_SUCCESS(status)) { // // First guess worked, bail! // goto Success; } ExFreePool(infoBuffer); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { ASSERT(!NT_SUCCESS(status)); return status; } // // Allocate a buffer large enough to contain the entire key data value. // infoBuffer = ExAllocatePoolWithTag( NonPagedPool, keyValueLength, POOLTAG_REGBUFFER ); if (infoBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Query the data for the key value. // status = ZwQueryValueKey( KeyHandle, ValueName, KeyValueFullInformation, infoBuffer, keyValueLength, &keyValueLength ); if (!NT_SUCCESS( status )) { ExFreePool(infoBuffer); return status; } Success: // // One last check - validate the type field // if ((DataType != REG_NONE) && (infoBuffer->Type != DataType)) { // // Mismatched type - bail. // ExFreePool(infoBuffer); // // Closest error we can get... // return STATUS_OBJECT_TYPE_MISMATCH; } // // Everything worked, so simply return the address of the allocated // buffer to the caller, who is now responsible for freeing it. // *Information = infoBuffer; return STATUS_SUCCESS; } NTSTATUS CmRegUtilUcValueSetFullBuffer( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN ULONG DataType, IN PVOID Buffer, IN ULONG BufferSize ) /*++ Routine Description: This function writes a buffer of information to a specific value key in the registry. Parameters: KeyHandle - A handle to the key under which the value is stored. ValueName - Supplies a pointer to the UNICODE_STRING name of the value key. DataType - Specifies the type of data to write. Buffer - Points to the buffer to write. BufferSize - Specifies the size of the buffer to write. Return Value: Status code that indicates whether or not the function was successful. --*/ { PAGED_CODE(); return ZwSetValueKey( KeyHandle, ValueName, 0, DataType, Buffer, BufferSize ); } NTSTATUS CmRegUtilUcValueSetUcString( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN PUNICODE_STRING ValueData ) /*++ Routine Description: Sets a value key in the registry to a specific value of string (REG_SZ) type. Parameters: KeyHandle - A handle to the key under which the value is stored. ValueName - Supplies a pointer to the UNICODE_STRING name of the value key ValueData - Supplies a pointer to the string to be stored in the key. The data will automatically be null terminated for storage in the registry. Return Value: Status code that indicates whether or not the function was successful. --*/ { UNICODE_STRING tempString; NTSTATUS status; PAGED_CODE(); ASSERT(ValueName); ASSERT(ValueData); ASSERT(ValueName->Buffer); ASSERT(ValueData->Buffer); // // Null terminate the string // if ((ValueData->MaximumLength - ValueData->Length) >= sizeof(UNICODE_NULL)) { // // There is room in the buffer so just append a null // ValueData->Buffer[(ValueData->Length / sizeof(WCHAR))] = UNICODE_NULL; // // Set the registry value // status = ZwSetValueKey( KeyHandle, ValueName, 0, REG_SZ, (PVOID) ValueData->Buffer, ValueData->Length + sizeof(UNICODE_NULL) ); } else { // // There is no room so allocate a new buffer and so we need to build // a new string with room // status = CmpRegUtilAllocateUnicodeString(&tempString, ValueData->Length); if (!NT_SUCCESS(status)) { goto clean0; } // // Copy the input string to the output string // tempString.Length = ValueData->Length; RtlCopyMemory(tempString.Buffer, ValueData->Buffer, ValueData->Length); // // Add the null termination // tempString.Buffer[tempString.Length / sizeof(WCHAR)] = UNICODE_NULL; // // Set the registry value // status = ZwSetValueKey( KeyHandle, ValueName, 0, REG_SZ, (PVOID) tempString.Buffer, tempString.Length + sizeof(UNICODE_NULL) ); // // Free the temporary string // CmpRegUtilFreeAllocatedUnicodeString(&tempString); } clean0: return status; } // // WSTR and mixed primitives // NTSTATUS CmRegUtilOpenExistingWstrKey( IN HANDLE BaseHandle OPTIONAL, IN PWSTR KeyName, IN ACCESS_MASK DesiredAccess, OUT HANDLE *Handle ) /*++ Routine Description: Opens a registry key using the name passed in based at the BaseHandle node. This name may specify a key that is actually a registry path. Arguments: BaseHandle - Optional handle to the base path from which the key must be opened. If this parameter is specified, then KeyName must be a relative path. KeyName - WSTR Name of the Key that must be opened (either a full registry path, or a relative path depending on whether BaseHandle is supplied) DesiredAccess - Specifies the desired access that the caller needs to the key (this isn't really used, as the access-mode is KernelMode, but we specify it anyway). Handle - Recieves registry key handle upon success, NULL otherwise. Note that the handle is in the global kernel namespace (and not the current processes handle take). The handle should be released using ZwClose. Return Value: STATUS_SUCCESS if the key could be opened, in which case Handle receives the registry key. Otherwise, failure is returned, and handle receives NULL. --*/ { UNICODE_STRING unicodeStringKeyName; NTSTATUS status; PAGED_CODE(); status = RtlInitUnicodeStringEx(&unicodeStringKeyName, KeyName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilOpenExistingUcKey( BaseHandle, &unicodeStringKeyName, DesiredAccess, Handle ); } NTSTATUS CmRegUtilCreateWstrKey( IN HANDLE BaseHandle, IN PWSTR KeyName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions, IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, OUT ULONG *Disposition OPTIONAL, OUT HANDLE *Handle ) /*++ Routine Description: Opens or creates a registry key using the name passed in based at the BaseHandle node. Arguments: BaseHandle - Handle to the base path under which the key must be opened. KeyName - WSTR Key Name that must be opened/created. DesiredAccess - Specifies the desired access that the caller needs to the key (this isn't really used as the access-mode is KernelMode, but we specify it anyway). CreateOptions - Options passed to ZwCreateKey. Examples: REG_OPTION_VOLATILE - Key is not to be stored across boots. REG_OPTION_NON_VOLATILE - Key is preserved when the system is rebooted. SecurityDescriptor - Security to apply if the key is newly created. If NULL, the key will inherit settings as defined by the inheritable properties of its parent. Disposition - This optional pointer receives a ULONG indicating whether the key was newly created (0 on error): REG_CREATED_NEW_KEY - A new Registry Key was created. REG_OPENED_EXISTING_KEY - An existing Registry Key was opened. Handle - Recieves registry key handle upon success, NULL otherwise. Note that the handle is in the global kernel namespace (and not the current processes handle take). The handle should be released using ZwClose. Return Value: The function value is the final status of the operation. --*/ { UNICODE_STRING unicodeStringKeyName; NTSTATUS status; PAGED_CODE(); status = RtlInitUnicodeStringEx(&unicodeStringKeyName, KeyName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilCreateUcKey( BaseHandle, &unicodeStringKeyName, DesiredAccess, CreateOptions, SecurityDescriptor, Disposition, Handle ); } NTSTATUS CmRegUtilWstrValueGetDword( IN HANDLE KeyHandle, IN PWSTR ValueName, IN ULONG DefaultValue, OUT ULONG *Value ) /*++ Routine Description: This routine reads a dword value from the registry. The value name is specified in WSTR form. Arguments: KeyHandle - Points to key to read. ValueName - Points to the value to read. DefaultValue - Points to the default value to use in case of an absence or error. Value - Receives DefaultValue on error, otherwise the value stored in the registry. Return Value: STATUS_SUCCESS if the value was present in the registry, STATUS_OBJECT_NAME_NOT_FOUND if it was absent, STATUS_OBJECT_TYPE_MISMATCH if the value was not a dword, or some other error value. --*/ { UNICODE_STRING unicodeStringValueName; NTSTATUS status; PAGED_CODE(); // // Construct the unicode name // status = RtlInitUnicodeStringEx(&unicodeStringValueName, ValueName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilUcValueGetDword( KeyHandle, &unicodeStringValueName, DefaultValue, Value ); } NTSTATUS CmRegUtilWstrValueGetFullBuffer( IN HANDLE KeyHandle, IN PWSTR ValueName, IN ULONG DataType OPTIONAL, IN ULONG LikelyDataLength OPTIONAL, OUT PKEY_VALUE_FULL_INFORMATION *Information ) /*++ Routine Description: This routine is invoked to retrieve the data for a registry key's value. This is done by querying the value of the key with a zero-length buffer to determine the size of the value, and then allocating a buffer and actually querying the value into the buffer. It is the responsibility of the caller to free the buffer. Arguments: KeyHandle - Supplies the key handle whose value is to be queried ValueName - Supplies the null-terminated WSTR name of the value. DataType - REG_NONE if any type is allowable, otherwise the specific type required. LikelyDataLength - Most likely size of the data to retrieve (used to optimize queries). Information - Receives a pointer to the allocated data buffer allocated from PagedPool, NULL on error. If successful, the buffer should be freed using ExFreePool. Note - the allocated memory is *not* charged against the calling process. Return Value: STATUS_SUCCESS if the information was retrievable, error otherwise (in which case Information will receive NULL). --*/ { UNICODE_STRING unicodeStringValueName; NTSTATUS status; PAGED_CODE(); // // Construct the unicode name // status = RtlInitUnicodeStringEx(&unicodeStringValueName, ValueName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilUcValueGetFullBuffer( KeyHandle, &unicodeStringValueName, DataType, LikelyDataLength, Information ); } NTSTATUS CmRegUtilWstrValueSetFullBuffer( IN HANDLE KeyHandle, IN PWSTR ValueName, IN ULONG DataType, IN PVOID Buffer, IN ULONG BufferSize ) /*++ Routine Description: This function writes a buffer of information to a specific value key in the registry. Parameters: KeyHandle - A handle to the key under which the value is stored. ValueName - Supplies a pointer to the WSTR name of the value key. DataType - Specifies the type of data to write. Buffer - Points to the buffer to write. BufferSize - Specifies the size of the buffer to write. Return Value: Status code that indicates whether or not the function was successful. --*/ { UNICODE_STRING unicodeStringValueName; NTSTATUS status; PAGED_CODE(); // // Construct the unicode name // status = RtlInitUnicodeStringEx(&unicodeStringValueName, ValueName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilUcValueSetFullBuffer( KeyHandle, &unicodeStringValueName, DataType, Buffer, BufferSize ); } NTSTATUS CmRegUtilWstrValueSetUcString( IN HANDLE KeyHandle, IN PWSTR ValueName, IN PUNICODE_STRING ValueData ) /*++ Routine Description: Sets a value key in the registry to a specific value of string (REG_SZ) type. The value name is specified in WSTR form, while the value data is in UNICODE_STRING format. Parameters: KeyHandle - A handle to the key under which the value is stored. ValueName - Supplies a WSTR pointer to the name of the value key ValueData - Supplies a pointer to the string to be stored in the key. The data will automatically be null terminated for storage in the registry. Return Value: Status code that indicates whether or not the function was successful. --*/ { UNICODE_STRING unicodeStringValueName; NTSTATUS status; PAGED_CODE(); ASSERT(ValueName); ASSERT(ValueData); ASSERT(ValueData->Buffer); // // Construct the unicode name // status = RtlInitUnicodeStringEx(&unicodeStringValueName, ValueName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilUcValueSetUcString( KeyHandle, &unicodeStringValueName, ValueData ); } NTSTATUS CmRegUtilUcValueSetWstrString( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN PWSTR ValueData ) /*++ Routine Description: Sets a value key in the registry to a specific value of string (REG_SZ) type. Parameters: KeyHandle - A handle to the key under which the value is stored. ValueName - Supplies a pointer to the UNICODE_STRING name of the value key ValueData - Supplies a pointer to the string to be stored in the key. The data will automatically be null terminated for storage in the registry. Return Value: Status code that indicates whether or not the function was successful. --*/ { UNICODE_STRING valueString; NTSTATUS status; PAGED_CODE(); ASSERT(ValueName); ASSERT(ValueData); ASSERT(ValueName->Buffer); // // Construct the unicode data // status = RtlInitUnicodeStringEx(&valueString, ValueData); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilUcValueSetUcString( KeyHandle, ValueName, &valueString ); } NTSTATUS CmRegUtilWstrValueSetWstrString( IN HANDLE KeyHandle, IN PWSTR ValueName, IN PWSTR ValueData ) /*++ Routine Description: Sets a value key in the registry to a specific value of string (REG_SZ) type. Parameters: KeyHandle - A handle to the key under which the value is stored. ValueName - Supplies a pointer to the WSTR name of the value key ValueData - Supplies a pointer to the string to be stored in the key. The data will automatically be null terminated for storage in the registry. Return Value: Status code that indicates whether or not the function was successful. --*/ { UNICODE_STRING unicodeStringValueName; UNICODE_STRING valueString; NTSTATUS status; PAGED_CODE(); ASSERT(ValueName); ASSERT(ValueData); // // Construct the unicode data // status = RtlInitUnicodeStringEx(&valueString, ValueData); if (!NT_SUCCESS(status)) { return status; } // // Construct the unicode name // status = RtlInitUnicodeStringEx(&unicodeStringValueName, ValueName); if (!NT_SUCCESS(status)) { return status; } return CmRegUtilUcValueSetUcString( KeyHandle, &unicodeStringValueName, &valueString ); } NTSTATUS CmpRegUtilAllocateUnicodeString( IN OUT PUNICODE_STRING String, IN USHORT Length ) /*++ Routine Description: This routine allocates a buffer for a unicode string of a given length and initialises the UNICODE_STRING structure appropriately. When the string is no longer required it can be freed using CmpRegUtilFreeAllocatedString. The buffer also can be directly deleted by ExFreePool and so can be handed back to a caller. Parameters: String - Supplies a pointer to an uninitialised unicode string which will be manipulated by the function. Length - The number of BYTES long that the string will be. Return Value: Either STATUS_INSUFFICIENT_RESOURCES indicating paged pool is exhausted or STATUS_SUCCESS. Remarks: The buffer allocated will be one character (2 bytes) more than length specified. This is to allow for easy null termination of the strings - eg for registry storage. --*/ { PAGED_CODE(); String->Length = 0; String->MaximumLength = Length + sizeof(UNICODE_NULL); String->Buffer = ExAllocatePoolWithTag( PagedPool, Length + sizeof(UNICODE_NULL), POOLTAG_UCSTRING ); if (String->Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } else { return STATUS_SUCCESS; } } VOID CmpRegUtilFreeAllocatedUnicodeString( IN PUNICODE_STRING String ) /*++ Routine Description: This routine frees a string previously allocated with CmpRegUtilAllocateUnicodeString. Parameters: String - Supplies a pointer to the string that has been previously allocated. Return Value: None --*/ { PAGED_CODE(); ASSERT(String); RtlFreeUnicodeString(String); }