mirror of https://github.com/tongzx/nt5src
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.
751 lines
23 KiB
751 lines
23 KiB
/*++
|
|
|
|
|
|
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Regqkey.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the server side implementation for the Win32
|
|
Registry query key API. That is:
|
|
|
|
- BaseRegQueryInfoKey
|
|
|
|
Author:
|
|
|
|
David J. Gilman (davegi) 27-Nov-1991
|
|
|
|
Notes:
|
|
|
|
See the Notes in Regkey.c.
|
|
|
|
--*/
|
|
|
|
#include <rpc.h>
|
|
#include "regrpc.h"
|
|
#include "localreg.h"
|
|
#include "regclass.h"
|
|
#include "regecls.h"
|
|
#include "regvcls.h"
|
|
#include <malloc.h>
|
|
|
|
#define DEFAULT_CLASS_SIZE 128
|
|
|
|
//
|
|
// Internal prototypes
|
|
//
|
|
|
|
NTSTATUS QueryKeyInfo(
|
|
HKEY hKey,
|
|
KEY_INFORMATION_CLASS KeyInformationClass,
|
|
PVOID *ppKeyInfo,
|
|
ULONG BufferLength,
|
|
BOOL fClass,
|
|
USHORT MaxClassLength);
|
|
|
|
void CombineKeyInfo(
|
|
PVOID KeyInfo,
|
|
PVOID MachineClassKeyInfo,
|
|
KEY_INFORMATION_CLASS KeyInformationClass,
|
|
DWORD dwTotalSubKeys,
|
|
DWORD dwTotalValues);
|
|
|
|
|
|
|
|
error_status_t
|
|
BaseRegQueryInfoKey(
|
|
IN HKEY hKey,
|
|
OUT PUNICODE_STRING lpClass,
|
|
OUT LPDWORD lpcSubKeys,
|
|
OUT LPDWORD lpcbMaxSubKeyLen,
|
|
OUT LPDWORD lpcbMaxClassLen,
|
|
OUT LPDWORD lpcValues,
|
|
OUT LPDWORD lpcbMaxValueNameLen,
|
|
OUT LPDWORD lpcbMaxValueLen,
|
|
OUT LPDWORD lpcbSecurityDescriptor,
|
|
OUT PFILETIME lpftLastWriteTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RegQueryInfoKey returns pertinent information about the key
|
|
corresponding to a given key handle.
|
|
|
|
Arguments:
|
|
|
|
hKey - A handle to an open key.
|
|
|
|
lpClass - Returns the Class string for the key.
|
|
|
|
lpcSubKeys - Returns the number of subkeys for this key .
|
|
|
|
lpcbMaxSubKeyLen - Returns the length of the longest subkey name.
|
|
|
|
lpcbMaxClassLen - Returns length of longest subkey class string.
|
|
|
|
lpcValues - Returns the number of ValueNames for this key.
|
|
|
|
lpcbMaxValueNameLen - Returns the length of the longest ValueName.
|
|
|
|
lpcbMaxValueLen - Returns the length of the longest value entry's data
|
|
field.
|
|
|
|
lpcbSecurityDescriptor - Returns the length of this key's
|
|
SECURITY_DESCRIPTOR.
|
|
|
|
lpftLastWriteTime - Returns the last time that the key or any of its
|
|
value entries was modified.
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS (0) for success; error-code for failure.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG BufferLength;
|
|
PVOID KeyInfo;
|
|
PVOID ClassKeyInfo;
|
|
KEY_INFORMATION_CLASS KeyInformationClass;
|
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|
ULONG SecurityDescriptorLength;
|
|
LONG Error;
|
|
PDWORD pCbMaxClassLen = NULL;
|
|
PDWORD pCbSecurityDescriptor = NULL;
|
|
|
|
BYTE PrivateKeyFullInfo[ sizeof( KEY_FULL_INFORMATION ) +
|
|
DEFAULT_CLASS_SIZE ];
|
|
|
|
BYTE PrivateClassKeyInfo[ sizeof( KEY_FULL_INFORMATION ) +
|
|
DEFAULT_CLASS_SIZE ];
|
|
|
|
ASSERT( lpClass != NULL );
|
|
ASSERT( lpcSubKeys != NULL );
|
|
ASSERT( lpcbMaxSubKeyLen != NULL );
|
|
ASSERT( lpcValues != NULL );
|
|
ASSERT( lpcbMaxValueNameLen != NULL );
|
|
ASSERT( lpcbMaxValueLen != NULL );
|
|
ASSERT( lpftLastWriteTime != NULL );
|
|
|
|
ASSERT( sizeof(KEY_FULL_INFORMATION) >= sizeof(KEY_CACHED_INFORMATION) );
|
|
|
|
if( lpcbMaxClassLen != NULL ) {
|
|
pCbMaxClassLen = lpcbMaxClassLen;
|
|
}
|
|
|
|
if( lpcbSecurityDescriptor != NULL ) {
|
|
pCbSecurityDescriptor = lpcbSecurityDescriptor;
|
|
}
|
|
|
|
//
|
|
// Call out to Perflib if the HKEY is HKEY_PERFORMANCE_DATA.
|
|
//
|
|
|
|
if(( hKey == HKEY_PERFORMANCE_DATA ) ||
|
|
( hKey == HKEY_PERFORMANCE_TEXT ) ||
|
|
( hKey == HKEY_PERFORMANCE_NLSTEXT )) {
|
|
|
|
DWORD cbMaxClassLen;
|
|
DWORD cbSecurityDescriptor;
|
|
//
|
|
// Impersonate the client.
|
|
//
|
|
|
|
RPC_IMPERSONATE_CLIENT( NULL );
|
|
|
|
//
|
|
// don't mess with Perf stuff
|
|
//
|
|
if( pCbMaxClassLen == NULL ) {
|
|
pCbMaxClassLen = &cbMaxClassLen;
|
|
}
|
|
if( pCbSecurityDescriptor == NULL ) {
|
|
pCbSecurityDescriptor = &cbSecurityDescriptor;
|
|
}
|
|
|
|
Error = PerfRegQueryInfoKey (
|
|
hKey,
|
|
lpClass,
|
|
NULL,
|
|
lpcSubKeys,
|
|
lpcbMaxSubKeyLen,
|
|
pCbMaxClassLen,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
pCbSecurityDescriptor,
|
|
lpftLastWriteTime
|
|
);
|
|
RPC_REVERT_TO_SELF();
|
|
|
|
return (error_status_t)Error;
|
|
|
|
}
|
|
|
|
ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
|
|
|
|
|
|
//
|
|
// First we assume that the information we want will fit on
|
|
// PrivateKeyFullInformattion
|
|
//
|
|
|
|
if( (lpClass->Buffer == NULL) && (pCbMaxClassLen == NULL) ) {
|
|
KeyInformationClass = KeyCachedInformation;
|
|
} else {
|
|
KeyInformationClass = KeyFullInformation;
|
|
}
|
|
|
|
ClassKeyInfo = (PVOID)PrivateClassKeyInfo;
|
|
KeyInfo = (PVOID)PrivateKeyFullInfo;
|
|
BufferLength = sizeof( PrivateKeyFullInfo );
|
|
|
|
|
|
//
|
|
// Ask Nt for all the meta information about this key.
|
|
//
|
|
|
|
Status = QueryKeyInfo(
|
|
hKey,
|
|
KeyInformationClass,
|
|
&KeyInfo,
|
|
BufferLength,
|
|
lpClass->Buffer ? TRUE : FALSE,
|
|
lpClass->MaximumLength
|
|
);
|
|
|
|
|
|
if( (NT_SUCCESS( Status ) ||
|
|
( Status == STATUS_BUFFER_OVERFLOW ) ) &&
|
|
(KeyInformationClass == KeyFullInformation)
|
|
) {
|
|
|
|
lpClass->Length = ( USHORT )
|
|
( (( PKEY_FULL_INFORMATION )KeyInfo)->ClassLength
|
|
+ sizeof( UNICODE_NULL )
|
|
);
|
|
}
|
|
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
#ifdef LOCAL
|
|
|
|
//
|
|
// For special keys in HKCR, we can't just take the information
|
|
// from the kernel -- these keys have properties that come from
|
|
// both the user and machine versions of their keys. To find out
|
|
// if it's a special key, we get more information below
|
|
//
|
|
if (REG_CLASS_IS_SPECIAL_KEY(hKey)) {
|
|
|
|
{
|
|
HKEY hkMachineClass;
|
|
HKEY hkUserClass;
|
|
|
|
BufferLength = sizeof( PrivateClassKeyInfo );
|
|
|
|
//
|
|
// we will now need information from both the user
|
|
// and machine locations to find the number of
|
|
// subkeys under this special key -- the machine
|
|
// key is not open yet, so we open it below
|
|
//
|
|
|
|
//
|
|
// Open the other key
|
|
//
|
|
Status = BaseRegGetUserAndMachineClass(
|
|
NULL,
|
|
hKey,
|
|
MAXIMUM_ALLOWED,
|
|
&hkMachineClass,
|
|
&hkUserClass);
|
|
|
|
if (NT_SUCCESS(Status) && (hkUserClass && hkMachineClass)) {
|
|
|
|
DWORD dwTotalSubKeys;
|
|
HKEY hkQuery;
|
|
|
|
if (hkUserClass == hKey) {
|
|
hkQuery = hkMachineClass;
|
|
} else {
|
|
hkQuery = hkUserClass;
|
|
}
|
|
|
|
//
|
|
// Still need to do this query to find out
|
|
// the largest subkey in the machine part
|
|
// as well as other information about the
|
|
// key such as its largest subkey
|
|
//
|
|
Status = QueryKeyInfo(
|
|
hkQuery,
|
|
KeyInformationClass,
|
|
&ClassKeyInfo,
|
|
BufferLength,
|
|
FALSE,
|
|
lpClass->MaximumLength);
|
|
|
|
//
|
|
// Now we will count the keys
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
Status = ClassKeyCountSubKeys(
|
|
hKey,
|
|
hkUserClass,
|
|
hkMachineClass,
|
|
0,
|
|
&dwTotalSubKeys);
|
|
}
|
|
|
|
NtClose(hkQuery);
|
|
|
|
//
|
|
// Do not let inability to query information for
|
|
// machine key cause a complete failure -- we'll
|
|
// just use the user key's information
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// this key may not exist in the machine hive
|
|
//
|
|
if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (STATUS_BUFFER_OVERFLOW == Status) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
|
|
ValueState* pValState;
|
|
|
|
//
|
|
// Find out how many values we have
|
|
//
|
|
Status = KeyStateGetValueState(
|
|
hKey,
|
|
&pValState);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Combine the information from the two
|
|
// trees
|
|
//
|
|
CombineKeyInfo(
|
|
KeyInfo,
|
|
ClassKeyInfo,
|
|
KeyInformationClass,
|
|
dwTotalSubKeys,
|
|
pValState ? pValState->cValues : 0);
|
|
|
|
ValStateRelease(pValState);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // LOCAL
|
|
}
|
|
|
|
if( NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// don't bother if the caller is not interested in this
|
|
//
|
|
if( pCbSecurityDescriptor != NULL ) {
|
|
ASSERT( lpcbSecurityDescriptor != NULL );
|
|
//
|
|
// Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP
|
|
// and DACL. These three are always accessible (or inaccesible)
|
|
// as a set.
|
|
//
|
|
|
|
Status = NtQuerySecurityObject(
|
|
hKey,
|
|
OWNER_SECURITY_INFORMATION
|
|
| GROUP_SECURITY_INFORMATION
|
|
| DACL_SECURITY_INFORMATION,
|
|
&SecurityDescriptor,
|
|
0,
|
|
lpcbSecurityDescriptor
|
|
);
|
|
|
|
//
|
|
// If getting the size of the SECURITY_DESCRIPTOR failed (probably
|
|
// due to the lack of READ_CONTROL access) return zero.
|
|
//
|
|
|
|
if( Status != STATUS_BUFFER_TOO_SMALL ) {
|
|
|
|
*lpcbSecurityDescriptor = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Try again to get the size of the key's SECURITY_DESCRIPTOR,
|
|
// this time asking for SACL as well. This should normally
|
|
// fail but may succeed if the caller has SACL access.
|
|
//
|
|
|
|
Status = NtQuerySecurityObject(
|
|
hKey,
|
|
OWNER_SECURITY_INFORMATION
|
|
| GROUP_SECURITY_INFORMATION
|
|
| DACL_SECURITY_INFORMATION
|
|
| SACL_SECURITY_INFORMATION,
|
|
&SecurityDescriptor,
|
|
0,
|
|
&SecurityDescriptorLength
|
|
);
|
|
|
|
|
|
if( Status == STATUS_BUFFER_TOO_SMALL ) {
|
|
|
|
//
|
|
// The caller had SACL access so update the returned
|
|
// length.
|
|
//
|
|
|
|
*lpcbSecurityDescriptor = SecurityDescriptorLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if( KeyInformationClass == KeyCachedInformation ) {
|
|
ASSERT( pCbMaxClassLen == NULL );
|
|
*lpcSubKeys = ((PKEY_CACHED_INFORMATION)KeyInfo)->SubKeys;
|
|
*lpcbMaxSubKeyLen = ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxNameLen;
|
|
*lpcValues = ((PKEY_CACHED_INFORMATION)KeyInfo)->Values;
|
|
*lpcbMaxValueNameLen = ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueNameLen;
|
|
*lpcbMaxValueLen = ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueDataLen;
|
|
*lpftLastWriteTime = *( PFILETIME ) &((PKEY_CACHED_INFORMATION)KeyInfo)->LastWriteTime;
|
|
Error = ERROR_SUCCESS;
|
|
} else {
|
|
*lpcSubKeys = ((PKEY_FULL_INFORMATION)KeyInfo)->SubKeys;
|
|
if( pCbMaxClassLen != NULL ) {
|
|
*lpcbMaxClassLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxClassLen;
|
|
ASSERT( pCbMaxClassLen == lpcbMaxClassLen );
|
|
}
|
|
*lpcbMaxSubKeyLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxNameLen;
|
|
*lpcValues = ((PKEY_FULL_INFORMATION)KeyInfo)->Values;
|
|
*lpcbMaxValueNameLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueNameLen;
|
|
*lpcbMaxValueLen = ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueDataLen;
|
|
*lpftLastWriteTime = *( PFILETIME ) &((PKEY_FULL_INFORMATION)KeyInfo)->LastWriteTime;
|
|
|
|
|
|
|
|
//
|
|
// Copy/assign remaining output parameters.
|
|
//
|
|
if ( lpClass->Length > lpClass->MaximumLength ) {
|
|
|
|
if( lpClass->Buffer != NULL ) {
|
|
lpClass->Buffer = NULL;
|
|
Error = (error_status_t)RtlNtStatusToDosError( STATUS_BUFFER_TOO_SMALL );
|
|
} else {
|
|
//
|
|
// Caller is not iterest in Class, so return its size only.
|
|
//
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
if( ((PKEY_FULL_INFORMATION)KeyInfo)->ClassLength != 0 ) {
|
|
|
|
ASSERT( lpClass->Buffer != NULL );
|
|
RtlMoveMemory(
|
|
lpClass->Buffer,
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->Class,
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->ClassLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// NUL terminate the class name.
|
|
//
|
|
|
|
lpClass->Buffer[ ((PKEY_FULL_INFORMATION)KeyInfo)->ClassLength >> 1 ] = UNICODE_NULL;
|
|
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
} else if( Status == STATUS_BUFFER_OVERFLOW ) {
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_OVERFLOW means that the user did
|
|
// not supply enough space for the class. The required space has
|
|
// already been assigned above.
|
|
//
|
|
lpClass->Buffer = NULL;
|
|
Error = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Some other error occurred.
|
|
//
|
|
|
|
Error = RtlNtStatusToDosError( Status );
|
|
}
|
|
|
|
if( KeyInfo != ( PVOID )PrivateKeyFullInfo ) {
|
|
|
|
//
|
|
// Free the buffer and return the Registry return value.
|
|
//
|
|
|
|
RtlFreeHeap( RtlProcessHeap( ), 0, KeyInfo );
|
|
}
|
|
|
|
if( ClassKeyInfo != ( PVOID )PrivateClassKeyInfo ) {
|
|
|
|
//
|
|
// Free the buffer and return the Registry return value.
|
|
//
|
|
|
|
RtlFreeHeap( RtlProcessHeap( ), 0, ClassKeyInfo );
|
|
}
|
|
|
|
return (error_status_t)Error;
|
|
}
|
|
|
|
NTSTATUS QueryKeyInfo(
|
|
HKEY hKey,
|
|
KEY_INFORMATION_CLASS KeyInformationClass,
|
|
PVOID *ppKeyInfo,
|
|
ULONG BufferLength,
|
|
BOOL fClass,
|
|
USHORT MaxClassLength)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries the kernel for key information.
|
|
|
|
Arguments:
|
|
|
|
hKey - handle of key for which to query info
|
|
|
|
KeyInformationClass - type of info required from the kernel
|
|
|
|
KeyInfo - pointer to address of
|
|
buffer for information about key
|
|
|
|
BufferLength - size of KeyFullInfo buffer
|
|
|
|
fClass - flag set to TRUE if the class for this
|
|
key should be rerieved
|
|
|
|
MaxClassLength - maximum size for class data that a caller
|
|
is willing to support. The ppKeyFullInfo buffer may
|
|
point to the address of a buffer that can handle the
|
|
class size of the key, but the caller may want the class
|
|
to fit in some smaller buffer later, so this param lets
|
|
the caller limit that size. It is ignored if fClass
|
|
is FALSE.
|
|
|
|
Return Value:
|
|
|
|
Returns NT_SUCCESS (0) for success; error-code for failure.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Result;
|
|
|
|
ASSERT( KeyInformationClass == KeyFullInformation ||
|
|
KeyInformationClass == KeyCachedInformation );
|
|
|
|
Status = NtQueryKey(
|
|
hKey,
|
|
KeyInformationClass,
|
|
*ppKeyInfo,
|
|
BufferLength,
|
|
&Result);
|
|
|
|
//
|
|
// A return value of STATUS_BUFFER_TOO_SMALL would mean that there
|
|
// was not enough room for even the fixed portion of the structure.
|
|
//
|
|
|
|
ASSERT( Status != STATUS_BUFFER_TOO_SMALL );
|
|
|
|
if ( Status == STATUS_BUFFER_OVERFLOW ) {
|
|
|
|
// we cannot hit this on a CachedInfo fixed structure
|
|
|
|
ASSERT( KeyInformationClass == KeyFullInformation );
|
|
//
|
|
// The buffer defined in the stack wasn't big enough to hold
|
|
// the Key Information.
|
|
//
|
|
// If the fClass flag is not set, then the caller does not do the
|
|
// check for the caller specified maximum class length below
|
|
// and requeries happily. If the flag is set, we do the check, and
|
|
// if the class size is bigger than what the caller specified as
|
|
// the max, we return STATUS_BUFFER_OVERFLOW.
|
|
//
|
|
|
|
if ( !fClass || ((ULONG)(MaxClassLength) >=
|
|
(( PKEY_FULL_INFORMATION )*ppKeyInfo )->ClassLength + sizeof( UNICODE_NULL )) ) {
|
|
|
|
//
|
|
// Required length is stored in Result -- set our length
|
|
// to the required length and allocate memory for it.
|
|
//
|
|
|
|
BufferLength = Result;
|
|
|
|
*ppKeyInfo = RtlAllocateHeap( RtlProcessHeap( ), 0,
|
|
BufferLength );
|
|
//
|
|
// If the memory allocation fails, return a Registry error.
|
|
//
|
|
|
|
if( ! *ppKeyInfo ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Query for the necessary information about the supplied key.
|
|
//
|
|
|
|
Status = NtQueryKey( hKey,
|
|
KeyFullInformation,
|
|
*ppKeyInfo,
|
|
BufferLength,
|
|
&Result
|
|
);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void CombineKeyInfo(
|
|
PVOID KeyInfo,
|
|
PVOID MachineClassKeyInfo,
|
|
KEY_INFORMATION_CLASS KeyInformationClass,
|
|
DWORD dwTotalSubKeys,
|
|
DWORD dwTotalValues)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Combine the information from the user and machine hives
|
|
for a special key
|
|
|
|
Arguments:
|
|
|
|
Status -
|
|
|
|
KeyInfo - buffer for information about user key
|
|
|
|
MachineClassKeyInfo - buffer for information about machine key
|
|
|
|
KeyClassInformation - type of information present in buffers
|
|
|
|
dwTotalSubKeys - total number of subkeys for the two
|
|
in each hive
|
|
|
|
Return Value:
|
|
|
|
Returns NT_SUCCESS (0) for success; error-code for failure.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
ASSERT( KeyInformationClass == KeyCachedInformation ||
|
|
KeyInformationClass == KeyFullInformation );
|
|
|
|
if( KeyInformationClass == KeyCachedInformation ) {
|
|
//
|
|
// Set the number of keys to be the total between the
|
|
// two versions in each hive
|
|
//
|
|
((PKEY_CACHED_INFORMATION)KeyInfo)->SubKeys = dwTotalSubKeys;
|
|
((PKEY_CACHED_INFORMATION)KeyInfo)->Values = dwTotalValues;
|
|
|
|
//
|
|
// Set our max namelen to the namelen of whichever is biggest
|
|
// between the two hives. Same for class.
|
|
//
|
|
|
|
if (((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxNameLen > ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxNameLen) {
|
|
((PKEY_CACHED_INFORMATION)KeyInfo)->MaxNameLen = ((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxNameLen;
|
|
}
|
|
|
|
//
|
|
// Since we also merge values, we must set the value information as well
|
|
//
|
|
if (((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen > ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueNameLen) {
|
|
((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueNameLen = ((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen;
|
|
}
|
|
|
|
if (((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen > ((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueDataLen) {
|
|
((PKEY_CACHED_INFORMATION)KeyInfo)->MaxValueDataLen = ((PKEY_CACHED_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen;
|
|
}
|
|
} else {
|
|
//
|
|
// Set the number of keys to be the total between the
|
|
// two versions in each hive
|
|
//
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->SubKeys = dwTotalSubKeys;
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->Values = dwTotalValues;
|
|
|
|
//
|
|
// Set our max namelen to the namelen of whichever is biggest
|
|
// between the two hives. Same for class.
|
|
//
|
|
|
|
if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxNameLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxNameLen) {
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->MaxNameLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxNameLen;
|
|
}
|
|
|
|
if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxClassLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxClassLen) {
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->MaxClassLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxClassLen;
|
|
}
|
|
|
|
//
|
|
// Since we also merge values, we must set the value information as well
|
|
//
|
|
if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueNameLen) {
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueNameLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueNameLen;
|
|
}
|
|
|
|
if (((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen > ((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueDataLen) {
|
|
((PKEY_FULL_INFORMATION)KeyInfo)->MaxValueDataLen = ((PKEY_FULL_INFORMATION)MachineClassKeyInfo)->MaxValueDataLen;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|