Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

988 lines
27 KiB

//+----------------------------------------------------------------------------
//
// Copyright (C) 1992, Microsoft Corporation
//
// File: registry.c
//
// Contents: Module to read in registry parameters.
//
// This module is intended as a simple, reusable, interface to the
// registry. Among its features are:
//
// o Kernel Callable.
//
// o Intuitive (it is to me!) - To read the value for /a/b/c/d,
// you say KRegGetValue("/a/b/c/d"), instead of calling at
// least 3 different Nt APIs. Any and all strings returned are
// guaranteed to be NULL terminated.
//
// o Allocates memory on behalf of caller - so we don't waste any
// by allocating max amounts.
//
// o Completely non-reentrant - 'cause it maintains state across
// calls. (It would be simple to make it multi-threaded tho)
//
//
// Classes:
//
// Functions: KRegSetRoot
// KRegCloseRoot
// KRegGetValue
// KRegGetNumValuesAndSubKeys
// KRegEnumValueSet
// KRegEnumSubKeySet
//
//
// History: 18 Sep 92 Milans created
//
//-----------------------------------------------------------------------------
#include "registry.h"
#include "regsups.h"
//
// If we ever needed to go multithreaded, we should return HKEY_ROOT to caller
// instead of keeping it here.
//
static HKEY HKEY_ROOT = NULL; // Handle to root key
#define KREG_SIZE_THRESHOLD 5 // For allocation optimization
//
// Some commonly used error format strings.
//
#if DBG == 1
static char *szErrorOpen = "Error opening %ws section.\n";
static char *szErrorRead = "Error reading %ws section.\n";
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, KRegInit )
#pragma alloc_text( PAGE, KRegCloseRoot )
#pragma alloc_text( PAGE, KRegSetRoot )
#pragma alloc_text( PAGE, KRegCreateKey )
#pragma alloc_text( PAGE, KRegGetValue )
#pragma alloc_text( PAGE, KRegSetValue )
#pragma alloc_text( PAGE, KRegDeleteValue )
#pragma alloc_text( PAGE, KRegGetNumValuesAndSubKeys )
#pragma alloc_text( PAGE, KRegEnumValueSet )
#pragma alloc_text( PAGE, KRegEnumSubKeySet )
#pragma alloc_text( PAGE, KRegFreeArray )
#endif // ALLOC_PRAGMA
//+----------------------------------------------------------------------------
//
// Function: KREG_CHECK_HARD_STATUS, macro
//
// Synopsis: If Status is not STATUS_SUCCESS, print out debug msg and return.
//
// Arguments:
//
// Returns:
//
//-----------------------------------------------------------------------------
#define KREG_CHECK_HARD_STATUS(Status, M1, M2) \
if (!NT_SUCCESS(Status)) { \
kreg_debug_out(M1, M2); \
return(Status); \
}
//+----------------------------------------------------------------------------
//
// Function: KRegInit
//
// Synopsis:
//
// Arguments: None
//
// Returns: STATUS_SUCCESS
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegInit(void)
{
return(STATUS_SUCCESS);
}
//+----------------------------------------------------------------------------void
//
// Function: KRegCloseRoot
//
// Synopsis: Close the Root opened by KRegSetRoot.
//
// Arguments: None
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
void
KRegCloseRoot()
{
if (HKEY_ROOT) {
NtClose(HKEY_ROOT);
HKEY_ROOT = NULL;
}
}
//+----------------------------------------------------------------------------
//
// Function: KRegSetRoot
//
// Synopsis: Sets a particular key as the "root" key for subsequent calls
// to registry routines.
//
// Arguments: wszRootName - Name of root key
//
// Returns: Result of opening the key.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegSetRoot(
IN PWSTR wszRootName
)
{
NTSTATUS Status;
if (HKEY_ROOT != NULL) {
NtClose(HKEY_ROOT);
}
Status = NtOpenKey(
&HKEY_ROOT, // Handle to DFS root key
KEY_READ | KEY_WRITE, // Only need to read
KRegpAttributes( // Name of Key
NULL,
wszRootName
)
);
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegDeleteKey
//
// Synopsis: Deletes a key from the registry.
//
// Arguments: [wszKey] -- name of key relative to the current root.
//
// Returns: Status from deleting the key.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegDeleteKey(
IN PWSTR wszKey)
{
NTSTATUS Status;
APWSTR awszSubKeys;
ULONG cSubKeys, i;
HKEY hkey;
//
// NtDeleteKey won't delete key's which have subkeys. So, we first
// enumerate all the subkeys and delete them, before deleting the key.
//
Status = KRegEnumSubKeySet(wszKey, &cSubKeys, &awszSubKeys);
if (!NT_SUCCESS(Status)) {
return( Status );
}
for (i = 0; i < cSubKeys && NT_SUCCESS(Status); i++) {
Status = NtOpenKey(
&hkey,
KEY_ALL_ACCESS,
KRegpAttributes(HKEY_ROOT, awszSubKeys[i])
);
if (NT_SUCCESS(Status)) {
Status = NtDeleteKey( hkey );
NtClose(hkey);
}
}
//
// If we were able to delete all the subkeys, we can delete the
// key itself.
//
if (NT_SUCCESS(Status)) {
Status = NtOpenKey(
&hkey,
KEY_ALL_ACCESS,
KRegpAttributes(HKEY_ROOT, wszKey)
);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
Status = STATUS_SUCCESS;
} else if (NT_SUCCESS(Status)) {
Status = NtDeleteKey( hkey );
NtClose(hkey);
}
}
KRegFreeArray( cSubKeys, (APBYTE) awszSubKeys );
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegCreateKey
//
// Synopsis: Creates a new key in the registry under the subkey given
// in wszSubKey. wszSubKey may be NULL, in which case the
// new key is created directly under the current root.
//
// Arguments: [wszSubKey] -- Name of subkey relative to root key under
// which new key will be created.
// [wszNewKey] -- Name of new key to be created.
//
// Returns:
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegCreateKey(
IN PWSTR wszSubKey,
IN PWSTR wszNewKey)
{
HKEY hkeySubKey, hkeyNewKey;
NTSTATUS Status;
UNICODE_STRING ustrValueName;
if (wszSubKey != NULL) {
Status = NtOpenKey(
&hkeySubKey,
KEY_WRITE,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
} else {
hkeySubKey = HKEY_ROOT;
}
Status = NtCreateKey(
&hkeyNewKey,
KEY_WRITE,
KRegpAttributes(hkeySubKey, wszNewKey),
0L, // TitleIndex
NULL, // Class
REG_OPTION_NON_VOLATILE, // Create option
NULL); // Create disposition
if (wszSubKey != NULL) {
NtClose(hkeySubKey);
}
if (!NT_SUCCESS(Status)) {
kreg_debug_out(szErrorOpen, wszNewKey);
NtClose(hkeyNewKey);
return(Status);
}
NtClose(hkeyNewKey);
return( Status );
}
//+----------------------------------------------------------------------------
//
// Function: KRegGetValue
//
// Synopsis: Given a Value Name and a handle, will allocate memory for the
// handle and fill it with the Value Data for the value name.
//
// Arguments: [wszSubKey] - Name of subkey relative to root key
// [wszValueName] - name of value data
// [ppValueData] - pointer to pointer to byte data.
//
// Returns: STATUS_SUCCESS, STATUS_NO_MEMORY, Status from NtReg api.
//
// Notes: It will allocate memory for the data, and return a pointer to
// it in ppValueData. Caller must free it.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegGetValue(
IN PWSTR wszSubKey,
IN PWSTR wszValueName,
OUT PBYTE *ppValueData
)
{
HKEY hkeySubKey;
ULONG dwUnused, cbMaxDataSize;
ULONG cbActualSize;
PBYTE pbData = NULL;
NTSTATUS Status;
Status = NtOpenKey(
&hkeySubKey,
KEY_READ,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
Status = KRegpGetKeyInfo(
hkeySubKey,
&dwUnused, // Number of Subkeys
&dwUnused, // Max size of subkey length
&dwUnused, // # of Values
&dwUnused, // Max size of value Name
&cbMaxDataSize // Max size of value Data
);
if (!NT_SUCCESS(Status)) {
kreg_debug_out(szErrorRead, wszSubKey);
NtClose(hkeySubKey);
return(Status);
}
//
// Just in case the value is of type REG_SZ, provide for inserting a
// UNICODE_NULL at the end.
//
cbMaxDataSize += sizeof(UNICODE_NULL);
pbData = kreg_alloc(cbMaxDataSize);
if (pbData == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
cbActualSize = cbMaxDataSize;
Status = KRegpGetValueByName(
hkeySubKey,
wszValueName,
pbData,
&cbActualSize);
if (NT_SUCCESS(Status)) {
if ((cbMaxDataSize - cbActualSize) < KREG_SIZE_THRESHOLD) {
//
// Optimization - no need to double allocate if actual size and max
// size are pretty close.
//
*ppValueData = pbData;
pbData = NULL; // "deallocate" pbData
} else {
//
// Big enough difference between actual and max size, warrants
// allocating a smaller buffer.
//
*ppValueData = (PBYTE) kreg_alloc(cbActualSize);
if (*ppValueData == NULL) {
//
// Well, we couldn't "reallocate" a smaller chunk, we'll just
// live on the edge and return the original buffer.
//
*ppValueData = pbData;
pbData = NULL;
} else {
RtlMoveMemory(*ppValueData, pbData, cbActualSize);
}
}
}
Cleanup:
NtClose(hkeySubKey);
if (pbData != NULL) {
kreg_free(pbData);
}
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegSetValue
//
// Synopsis: Given a Key Name, Value Name, and type, size and data, this
// routine will update the registry's value to the type and data.
// The valuename will be created if it doesn't exist. The key
// must already exist.
//
// Arguments: [wszSubKey] - Name of subkey relative to root key
// [wszValueName] - name of value data
// [ulType] - as in REG_DWORD, REG_SZ, REG_MULTI_SZ, etc.
// [cbSize] - size in bytes of data. If type == REG_SZ, data need
// !not! include the terminating NULL. One will be
// appended as needed.
// [pValueData] - pointer to pointer to byte data.
//
// Returns: STATUS_SUCCESS, STATUS_NO_MEMORY, Status from NtReg api.
//
// Notes: It will allocate memory for the data, and return a pointer to
// it in ppValueData. Caller must free it.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegSetValue(
IN PWSTR wszSubKey,
IN PWSTR wszValueName,
IN ULONG ulType,
IN ULONG cbSize,
IN PBYTE pValueData
)
{
HKEY hkeySubKey;
NTSTATUS Status;
UNICODE_STRING ustrValueName;
PWSTR pwszValueData;
Status = NtOpenKey(
&hkeySubKey,
KEY_WRITE,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
RtlInitUnicodeString(&ustrValueName, wszValueName);
if (ulType == REG_SZ) {
pwszValueData = (PWSTR) kreg_alloc(cbSize+sizeof(UNICODE_NULL));
if (pwszValueData == NULL) {
NtClose(hkeySubKey);
return(STATUS_INSUFFICIENT_RESOURCES);
}
RtlMoveMemory((PVOID) pwszValueData, (PVOID) pValueData, cbSize);
pwszValueData[cbSize/sizeof(WCHAR)] = UNICODE_NULL;
cbSize += sizeof(UNICODE_NULL);
} else {
pwszValueData = (PWSTR) pValueData;
}
Status = NtSetValueKey(
hkeySubKey,
&ustrValueName,
0L, // TitleIndex
ulType,
pwszValueData,
cbSize);
if (ulType == REG_SZ) {
kreg_free(pwszValueData);
}
if (NT_SUCCESS(Status)) {
NtFlushKey(hkeySubKey);
}
NtClose(hkeySubKey);
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegDeleteValue
//
// Synopsis: Deletes a value name
//
// Arguments: [wszSubKey] -- Name of subkey under which wszValueName exists.
// [wszValueName] -- Name of Value to delete.
//
// Returns:
//
//-----------------------------------------------------------------------------
NTSTATUS KRegDeleteValue(
IN PWSTR wszSubKey,
IN PWSTR wszValueName)
{
HKEY hkeySubKey;
NTSTATUS Status;
UNICODE_STRING ustrValueName;
Status = NtOpenKey(
&hkeySubKey,
KEY_WRITE,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
RtlInitUnicodeString(&ustrValueName, wszValueName);
Status = NtDeleteValueKey(
hkeySubKey,
&ustrValueName);
if (NT_SUCCESS(Status)) {
NtFlushKey(hkeySubKey);
}
NtClose(hkeySubKey);
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegGetNumValuesAndSubKeys
//
// Synopsis: Given a Subkey, return how many subkeys and values it has.
//
// Arguments: [wszSubKey] - for which info is required.
// [plNumValues] - receives number of values this subkey has.
// [plNumSubKeys] - receives number of subkeys under this subkey.
//
// Returns: STATUS_SUCCESS, or Status from NtRegAPI.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegGetNumValuesAndSubKeys(
IN PWSTR wszSubKey,
OUT PULONG pcNumValues,
OUT PULONG pcNumSubKeys
)
{
HKEY hKeySubKey;
ULONG dwUnused;
NTSTATUS Status;
Status = NtOpenKey(
&hKeySubKey,
KEY_READ,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
Status = KRegpGetKeyInfo(
hKeySubKey,
pcNumSubKeys, // Number of Subkeys
&dwUnused, // Max size of subkey length
pcNumValues, // # of Values
&dwUnused, // Max size of value Name
&dwUnused // Max size of value Data
);
if (!NT_SUCCESS(Status)) {
kreg_debug_out(szErrorRead, wszSubKey);
NtClose(hKeySubKey);
return(Status);
}
NtClose(hKeySubKey);
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegEnumValueSet
//
// Synopsis: Given a subkey, return the names and data for all its values.
//
// Arguments: [wszSubKey] - Name of subkey to read.
// [pcMaxElements] - On return, contains number of names and
// data actually read.
// [pawszValueNames] - Pointer to array of PWSTRS. This routine
// will allocate memory for the array and the
// strings.
// [papbValueData] - pointer to array of PBYTES. This routine
// will allocate the array and the memory for the
// data, stuffing the pointers to data into this
// array.
// [paValueStatus] - Status of reading the corresponding Value. The
// array will be allocated here.
//
// Returns: STATUS_SUCCESS, STATUS_NO_MEMORY, or Status from NtRegAPI.
//
// Notes: Returned Value Names are always NULL terminated.
// If the Value Data is of type REG_SZ, it, too, is NULL terminated
// Caller frees the last three OUT params (and the contents of
// awszValueNames and apbValueData!) only on SUCCESSFUL return.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegEnumValueSet(
IN PWSTR wszSubKey,
OUT PULONG pcMaxElements,
OUT APWSTR *pawszValueNames,
OUT APBYTE *papbValueData,
OUT ANTSTATUS *paValueStatus
)
{
ULONG i = 0;
HKEY hKeySubKey;
ULONG dwUnused, cbMaxNameSize, cbMaxDataSize;
PWSTR wszName = NULL;
PBYTE pbData = NULL;
NTSTATUS Status;
APWSTR awszValueNames;
APBYTE apbValueData;
ANTSTATUS aValueStatus;
ULONG cMaxElements;
awszValueNames = *pawszValueNames = NULL;
apbValueData = *papbValueData = NULL;
aValueStatus = *paValueStatus = NULL;
Status = NtOpenKey(
&hKeySubKey,
KEY_READ,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
Status = KRegpGetKeyInfo(
hKeySubKey,
&dwUnused, // Number of Subkeys
&dwUnused, // Max size of subkey length
&cMaxElements, // # of Values
&cbMaxNameSize, // Max size of value Name
&cbMaxDataSize // Max size of value Data
);
if (!NT_SUCCESS(Status)) {
kreg_debug_out(szErrorRead, wszSubKey);
NtClose(hKeySubKey);
return(Status);
}
//
// Allocate memory for the arrays.
//
awszValueNames = *pawszValueNames = kreg_alloc(cMaxElements * sizeof(PWSTR));
apbValueData = *papbValueData = kreg_alloc(cMaxElements * sizeof(PBYTE));
aValueStatus = *paValueStatus = kreg_alloc(cMaxElements * sizeof(NTSTATUS));
if (awszValueNames == NULL || apbValueData == NULL || aValueStatus == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Initialize the arrays
//
RtlZeroMemory(awszValueNames, cMaxElements * sizeof(PWSTR));
RtlZeroMemory(apbValueData, cMaxElements * sizeof(PBYTE));
RtlZeroMemory(aValueStatus, cMaxElements * sizeof(NTSTATUS));
//
// For name, we need an extra spot for the terminating NULL
//
wszName = kreg_alloc(cbMaxNameSize + sizeof(WCHAR));
pbData = kreg_alloc(cbMaxDataSize);
if (pbData == NULL || wszName == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
for (i = 0; i < cMaxElements; i++) {
ULONG cbActualNameSize, cbActualDataSize;
cbActualNameSize = cbMaxNameSize;
cbActualDataSize = cbMaxDataSize;
Status = KRegpEnumKeyValues(
hKeySubKey,
i,
wszName,
&cbActualNameSize,
pbData,
&cbActualDataSize
);
if (Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
break;
} else if (!NT_SUCCESS(Status)) {
aValueStatus[i] = Status;
continue;
} else {
aValueStatus[i] = Status;
}
cbActualNameSize += sizeof(WCHAR); // For terminating NULL
awszValueNames[i] = (PWSTR) kreg_alloc(cbActualNameSize);
apbValueData[i] = (PBYTE) kreg_alloc(cbActualDataSize);
if (apbValueData[i] == NULL || awszValueNames[i] == NULL) {
Status = aValueStatus[i] = STATUS_NO_MEMORY;
goto Cleanup;
}
RtlMoveMemory(awszValueNames[i], wszName, cbActualNameSize);
RtlMoveMemory(apbValueData[i], pbData, cbActualDataSize);
}
Cleanup:
NtClose(hKeySubKey);
if (wszName != NULL) {
kreg_free(wszName);
}
if (pbData != NULL) {
kreg_free(pbData);
}
if (!NT_SUCCESS(Status)) {
*pcMaxElements = 0;
KRegFreeArray(i+1, (APBYTE) awszValueNames);
KRegFreeArray(i+1, apbValueData);
if (aValueStatus != NULL) {
kreg_free(aValueStatus);
}
} else { // Status success
*pcMaxElements = i;
}
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegEnumSubKeySet
//
// Synopsis: This one's slightly trick. Given a Subkey, it returns an array
// of its subkey-names. The following is true - say the root is
// /a/b/c. Say wszSubKey is d. Say d has subkeys s1, s2, and s3.
// Then the array of subkey-names returned will contain the names
// d/s1, d/s2, d/s3.
//
// Arguments: [wszSubKey] - the Subkey relative to the root.
// [pcMaxElements] - On return, # subkeys actually being returned.
// [awszValueNames] - Unitialized array of PWSTR (room for at least
// *plMaxElements). This routine will allocate
// memory for the subkey names, and fill in this
// array with pointers to them.
//
// Returns:
//
// Notes: Caller must free at most *plMaxElements members of
// awszSubKeyNames. See synopsis above for form of subkey names.
//
//-----------------------------------------------------------------------------
NTSTATUS
KRegEnumSubKeySet(
IN PWSTR wszSubKey,
IN OUT PULONG pcMaxElements,
OUT APWSTR *pawszSubKeyNames
)
{
ULONG i, j = 0;
APWSTR awszSubKeyNames = NULL;
PWSTR wszBuffer = NULL;
HKEY hKeySubKey;
ULONG dwUnused, cbMaxNameSize, cwszSubKey;
NTSTATUS Status;
cwszSubKey = wcslen(wszSubKey);
*pawszSubKeyNames = NULL;
Status = NtOpenKey(
&hKeySubKey,
KEY_READ,
KRegpAttributes(HKEY_ROOT, wszSubKey)
);
KREG_CHECK_HARD_STATUS(Status, szErrorOpen, wszSubKey);
Status = KRegpGetKeyInfo(
hKeySubKey,
pcMaxElements, // Number of Subkeys
&cbMaxNameSize, // Max size of subkey name
&dwUnused, // # of Values
&dwUnused, // Max size of value Name
&dwUnused // Max size of value Data
);
if (pcMaxElements == 0) {
NtClose(hKeySubKey);
*pawszSubKeyNames = NULL;
return(STATUS_SUCCESS);
}
if (!NT_SUCCESS(Status)) {
kreg_debug_out(szErrorRead, wszSubKey);
goto Cleanup;
}
*pawszSubKeyNames = kreg_alloc(
(*pcMaxElements + 1) * sizeof(PWSTR)
);
awszSubKeyNames = *pawszSubKeyNames;
wszBuffer = kreg_alloc(cbMaxNameSize + sizeof(WCHAR));
if (wszBuffer == NULL || awszSubKeyNames == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
} else {
RtlZeroMemory(awszSubKeyNames, (*pcMaxElements + 1) * sizeof(PWSTR));
}
for (i = j = 0; j < *pcMaxElements; i++) {
ULONG cbActualNameSize, cNameIndex;
cbActualNameSize = cbMaxNameSize;
Status = KRegpEnumSubKeys(
hKeySubKey,
i,
wszBuffer,
&cbActualNameSize
);
if (Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
break;
} else if (!NT_SUCCESS(Status)) {
continue;
}
cbActualNameSize += sizeof(WCHAR); // for terminating NULL
awszSubKeyNames[j] = (PWSTR) kreg_alloc(
cwszSubKey * sizeof(WCHAR) +
sizeof(WCHAR) + // for backslash
cbActualNameSize
);
if (awszSubKeyNames[j] == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
} else {
RtlZeroMemory(
awszSubKeyNames[j],
cwszSubKey * sizeof(WCHAR) +
sizeof(WCHAR) + // for backslash
cbActualNameSize);
if (wszSubKey[0] != UNICODE_NULL) {
wcscpy(awszSubKeyNames[j], wszSubKey);
wcscat(awszSubKeyNames[j], UNICODE_PATH_SEP_STR);
cNameIndex = cwszSubKey + 1;
} else {
cNameIndex = 0;
}
RtlMoveMemory(
&awszSubKeyNames[j][cNameIndex],
wszBuffer,
cbActualNameSize);
j++;
}
}
Cleanup:
NtClose(hKeySubKey);
if (wszBuffer != NULL) {
kreg_free(wszBuffer);
}
if (!NT_SUCCESS(Status)) {
*pcMaxElements = 0;
KRegFreeArray(j, (APBYTE) awszSubKeyNames);
} else { // status success
*pcMaxElements = j;
}
return(Status);
}
//+----------------------------------------------------------------------------
//
// Function: KRegFreeArray
//
// Synopsis: Given an array of pointers, frees the pointers and the array.
//
// Arguments: [cElements] - Number of elements to free. Some elements can be
// NULL.
// [pa] - pointer to the array.
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
VOID
KRegFreeArray(
IN ULONG cElements,
IN APBYTE pa)
{
ULONG i;
if (pa != NULL) {
for (i = 0; i < cElements; i++) {
if (pa[i] != NULL) {
kreg_free(pa[i]);
}
}
kreg_free(pa);
}
}