1529 lines
52 KiB
C
1529 lines
52 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
restore.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the following Plug and Play registry merge-restore
|
|||
|
routines:
|
|||
|
|
|||
|
AsrRestorePlugPlayRegistryData
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Cavalaris (jamesca) 3-07-2000
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User-mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
07-March-2000 jamesca
|
|||
|
|
|||
|
Creation and initial implementation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// includes
|
|||
|
//
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#include "util.h"
|
|||
|
#include "debug.h"
|
|||
|
#include <regstr.h>
|
|||
|
#include <cfgmgr32.h>
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// private memory allocation definitions
|
|||
|
//
|
|||
|
|
|||
|
#define MyMalloc(size) LocalAlloc(0, size);
|
|||
|
#define MyFree(entry) LocalFree(entry);
|
|||
|
#define MyRealloc(entry,size) LocalReAlloc(entry, size, 0);
|
|||
|
|
|||
|
//
|
|||
|
// global variables to store the root keys we're working on.
|
|||
|
//
|
|||
|
// (we really shouldn't have to do this, but it's the easiest way for us to
|
|||
|
// reach into one set of keys while recursing through the other set.)
|
|||
|
//
|
|||
|
HKEY PnpSifRestoreSourceEnumKeyHandle = NULL;
|
|||
|
HKEY PnpSifRestoreTargetEnumKeyHandle = NULL;
|
|||
|
|
|||
|
HKEY PnpSifRestoreSourceClassKeyHandle = NULL;
|
|||
|
HKEY PnpSifRestoreTargetClassKeyHandle = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// private definitions
|
|||
|
//
|
|||
|
|
|||
|
// INVALID_FLAGS macro from pnp\inc\cfgmgrp.h
|
|||
|
#define INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
|
|||
|
|
|||
|
// private flags for internal RestoreSpecialRegistryData routine
|
|||
|
#define PNPSIF_RESTORE_TYPE_DEFAULT (0x00000000)
|
|||
|
#define PNPSIF_RESTORE_TYPE_ENUM (0x00000001)
|
|||
|
#define PNPSIF_RESTORE_TYPE_CLASS (0x00000002)
|
|||
|
#define PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION (0x00000010)
|
|||
|
#define PNPSIF_RESTORE_BITS (0x00000013)
|
|||
|
|
|||
|
// Enum level definitions describing the subkey types for the specified handles.
|
|||
|
#define ENUM_LEVEL_ENUMERATORS (0x0000003)
|
|||
|
#define ENUM_LEVEL_DEVICES (0x0000002)
|
|||
|
#define ENUM_LEVEL_INSTANCES (0x0000001)
|
|||
|
|
|||
|
// Class level definitions describing the subkey types for the specified handles.
|
|||
|
#define CLASS_LEVEL_CLASSGUIDS (0x0000002)
|
|||
|
#define CLASS_LEVEL_DRVINSTS (0x0000001)
|
|||
|
|
|||
|
//
|
|||
|
// private prototypes
|
|||
|
//
|
|||
|
|
|||
|
BOOL
|
|||
|
RestoreEnumKey(
|
|||
|
IN HKEY hSourceEnumKey,
|
|||
|
IN HKEY hTargetEnumKey
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
RestoreClassKey(
|
|||
|
IN HKEY hSourceClassKey,
|
|||
|
IN HKEY hTargetClassKey
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
RestoreSpecialRegistryData(
|
|||
|
IN HKEY hSourceKey,
|
|||
|
IN HKEY hTargetKey,
|
|||
|
IN ULONG ulLevel,
|
|||
|
IN OUT PVOID pContext,
|
|||
|
IN ULONG ulFlags
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
MyReplaceKey(
|
|||
|
IN HKEY hSourceKey,
|
|||
|
IN HKEY hTargetKey
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
IsString4DigitOrdinal(
|
|||
|
IN LPTSTR pszSubkeyName
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
IsDeviceConfigured(
|
|||
|
IN HKEY hInstanceKey
|
|||
|
);
|
|||
|
|
|||
|
BOOL
|
|||
|
ReplaceClassKeyForInstance(
|
|||
|
IN HKEY hSourceInstanceKey,
|
|||
|
IN HKEY hSourceRootClassKey,
|
|||
|
IN HKEY hTargetRootClassKey
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// routines
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
AsrRestorePlugPlayRegistryData(
|
|||
|
IN HKEY SourceSystemKey,
|
|||
|
IN HKEY TargetSystemKey,
|
|||
|
IN DWORD Flags,
|
|||
|
IN PVOID Reserved
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine restores plug and play data from the the specified source
|
|||
|
SYSTEM key to the specified target SYSTEM key, merging intermediate subkeys
|
|||
|
and values as appropriate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SourceSystemKey - Handle to the HKLM\SYSTEM key within the "source"
|
|||
|
registry, whose data is to be "merged" into the
|
|||
|
corresponding SYSTEM key of the "target" registry, as
|
|||
|
specified by TargetSystemKey.
|
|||
|
|
|||
|
TargetSystemKey - Handle to the HKLM\SYSTEM key within the "target"
|
|||
|
registry, that is to receive additional data from the
|
|||
|
corresponding SYSTEM key of the "source" registry, as
|
|||
|
specified by SourceSystemKey.
|
|||
|
|
|||
|
Flags - Not used, must be zero.
|
|||
|
|
|||
|
Reserved - Reserved for future use, must be NULL.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise. Upon failure, additional information
|
|||
|
can be retrieved by calling GetLastError().
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
This routine was written specifically to assist in the restoration of the
|
|||
|
Plug-and-Play specific registry keys that cannot simply be restored from
|
|||
|
backup. It is intended to be called within the context of a
|
|||
|
backup-and-restore application's "restore" phase.
|
|||
|
|
|||
|
During a backup-and-restore application's "restore" phase, the registry that
|
|||
|
has been backed-up onto backup medium is to become the new system registry.
|
|||
|
Certain Plug and Play values and registry keys, are actually copied into the
|
|||
|
backed-up registry from the current registry. Rather than using the backup
|
|||
|
registry, or the current registry exclusively, Plug and Play data contained
|
|||
|
in these keys should be merged from the current registry into the backup
|
|||
|
registry in a way that is appropriate for each key. The backup registry can
|
|||
|
then safely replace the current registry as the system registry.
|
|||
|
|
|||
|
In the context of a backup-and-restore application's "restore" phase, the
|
|||
|
"Source" registry key is the one contained in the current running system
|
|||
|
registry. The "Target" registry is that which has been backed-up, and will
|
|||
|
become the system registry, upon reboot.
|
|||
|
|
|||
|
The calling process must have both the SE_BACKUP_NAME and SE_RESTORE_NAME
|
|||
|
privileges.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONG result = ERROR_SUCCESS;
|
|||
|
HKEY hSystemSelectKey = NULL;
|
|||
|
TCHAR pszRegKeySelect[] = TEXT("Select");
|
|||
|
TCHAR pszRegValueCurrent[] = TEXT("Current");
|
|||
|
TCHAR szBuffer[128];
|
|||
|
DWORD dwType, dwSize;
|
|||
|
DWORD dwSourceCCS, dwTargetCCS;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the user didn't pass us anything in the Reserved parameter.
|
|||
|
//
|
|||
|
if (Reserved) {
|
|||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the user supplied us with valid flags.
|
|||
|
//
|
|||
|
if(INVALID_FLAGS(Flags, 0x0)) {
|
|||
|
SetLastError(ERROR_INVALID_FLAGS);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine the CurrentControlSet for the source.
|
|||
|
//
|
|||
|
result = RegOpenKeyEx(SourceSystemKey,
|
|||
|
pszRegKeySelect,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&hSystemSelectKey);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
hSystemSelectKey = NULL;
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
result = RegQueryValueEx(hSystemSelectKey,
|
|||
|
pszRegValueCurrent,
|
|||
|
0,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwSourceCCS,
|
|||
|
&dwSize);
|
|||
|
|
|||
|
RegCloseKey(hSystemSelectKey);
|
|||
|
hSystemSelectKey = NULL;
|
|||
|
|
|||
|
if ((result != ERROR_SUCCESS) ||
|
|||
|
(dwType != REG_DWORD)) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine the CurrentControlSet for the target.
|
|||
|
//
|
|||
|
result = RegOpenKeyEx(TargetSystemKey,
|
|||
|
pszRegKeySelect,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&hSystemSelectKey);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
hSystemSelectKey = NULL;
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
dwSize = sizeof(DWORD);
|
|||
|
result = RegQueryValueEx(hSystemSelectKey,
|
|||
|
pszRegValueCurrent,
|
|||
|
0,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwTargetCCS,
|
|||
|
&dwSize);
|
|||
|
|
|||
|
RegCloseKey(hSystemSelectKey);
|
|||
|
hSystemSelectKey = NULL;
|
|||
|
|
|||
|
if ((result != ERROR_SUCCESS) ||
|
|||
|
(dwType != REG_DWORD)) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the source CurrentControlSet\Enum key.
|
|||
|
//
|
|||
|
wsprintf(szBuffer, TEXT("ControlSet%03d\\Enum"), dwSourceCCS);
|
|||
|
result = RegOpenKeyEx(SourceSystemKey,
|
|||
|
szBuffer,
|
|||
|
0,
|
|||
|
KEY_READ, // only need to read from the source
|
|||
|
&PnpSifRestoreSourceEnumKeyHandle);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the target CurrentControlSet\Enum key.
|
|||
|
//
|
|||
|
wsprintf(szBuffer, TEXT("ControlSet%03d\\Enum"), dwTargetCCS);
|
|||
|
result = RegOpenKeyEx(TargetSystemKey,
|
|||
|
szBuffer,
|
|||
|
0,
|
|||
|
KEY_ALL_ACCESS, // need full access to the target
|
|||
|
&PnpSifRestoreTargetEnumKeyHandle);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the source CurrentControlSet\Control\Class key.
|
|||
|
//
|
|||
|
wsprintf(szBuffer, TEXT("ControlSet%03d\\Control\\Class"), dwSourceCCS);
|
|||
|
result = RegOpenKeyEx(SourceSystemKey,
|
|||
|
szBuffer,
|
|||
|
0,
|
|||
|
KEY_READ, // only need to read from the source
|
|||
|
&PnpSifRestoreSourceClassKeyHandle);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the target CurrentControlSet\Control\Class key.
|
|||
|
//
|
|||
|
wsprintf(szBuffer, TEXT("ControlSet%03d\\Control\\Class"), dwTargetCCS);
|
|||
|
result = RegOpenKeyEx(TargetSystemKey,
|
|||
|
szBuffer,
|
|||
|
0,
|
|||
|
KEY_ALL_ACCESS, // need full access to the target
|
|||
|
&PnpSifRestoreTargetClassKeyHandle);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// NOTE: We restore the Enum branch first, then restore the Class branch.
|
|||
|
// The code that restores these keys depends on it, so do not change this!!
|
|||
|
//
|
|||
|
// This order makes sense, because relevant Class keys correspond to exactly
|
|||
|
// one Enum instance key (but an Enum key may or may not have a Class key),
|
|||
|
// so Class keys are only really meaningful in the context of the instance
|
|||
|
// key they belong to. This behavior should NOT need to change.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Do the merge-restore for the Enum keys, ignore any errors.
|
|||
|
//
|
|||
|
if (!RestoreEnumKey(PnpSifRestoreSourceEnumKeyHandle,
|
|||
|
PnpSifRestoreTargetEnumKeyHandle)) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RestoreEnumKey failed with error == 0x%08lx\n"),
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the merge-restore for the Class keys, ignore any errors.
|
|||
|
//
|
|||
|
if (!RestoreClassKey(PnpSifRestoreSourceClassKeyHandle,
|
|||
|
PnpSifRestoreTargetClassKeyHandle)) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RestoreClassKey failed with error == 0x%08lx\n"),
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
Clean0:
|
|||
|
//
|
|||
|
// Close the open handles.
|
|||
|
//
|
|||
|
if (PnpSifRestoreSourceEnumKeyHandle) {
|
|||
|
RegCloseKey(PnpSifRestoreSourceEnumKeyHandle);
|
|||
|
PnpSifRestoreSourceEnumKeyHandle = NULL;
|
|||
|
}
|
|||
|
if (PnpSifRestoreTargetEnumKeyHandle) {
|
|||
|
RegCloseKey(PnpSifRestoreTargetEnumKeyHandle);
|
|||
|
PnpSifRestoreTargetEnumKeyHandle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (PnpSifRestoreSourceClassKeyHandle) {
|
|||
|
RegCloseKey(PnpSifRestoreSourceClassKeyHandle);
|
|||
|
PnpSifRestoreSourceClassKeyHandle = NULL;
|
|||
|
}
|
|||
|
if (PnpSifRestoreTargetClassKeyHandle) {
|
|||
|
RegCloseKey(PnpSifRestoreTargetClassKeyHandle);
|
|||
|
PnpSifRestoreTargetClassKeyHandle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
SetLastError(result);
|
|||
|
}
|
|||
|
return (result == ERROR_SUCCESS);
|
|||
|
|
|||
|
} // AsrRestorePlugPlayRegistryData()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// private worker routines for AsrRestorePlugPlayRegistryData
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
RestoreEnumKey(
|
|||
|
IN HKEY hSourceEnumKey,
|
|||
|
IN HKEY hTargetEnumKey
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Restores new device instances in the source (current) Enum key to the target
|
|||
|
Enum key, located in the backup registry to be restored. By doing this, the
|
|||
|
Enum key from the backup set can safely replace the current Enum key.
|
|||
|
|
|||
|
All intermediate values from the source (current) registry are restored to
|
|||
|
the target (backup), to account for the device instance hash values located
|
|||
|
at the root of the Enum key, that have been updated during setup.
|
|||
|
|
|||
|
During an ASR backup and restore operation, the hash values in the source
|
|||
|
(current) registry are propogated to the current registry during textmode
|
|||
|
setup, as they are stored the asrpnp.sif file. Since hash values may be
|
|||
|
modified by setup, the values in the source (current) registry will be more
|
|||
|
relevant than those in the target (backup), so source values should always
|
|||
|
be preseverd.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hSourceEnumKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Enum key within
|
|||
|
the "source" registry, whose data is to be "merged" into
|
|||
|
the corresponding SYSTEM key of the "target" registry, as
|
|||
|
specified by hTargetEnumKey.
|
|||
|
|
|||
|
hTargetEnumKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Enum key within
|
|||
|
the "target" registry, that is to receive additional data
|
|||
|
from the corresponding SYSTEM key of the "source" registry,
|
|||
|
as specified by hSourceEnumKey.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise. Upon failure, additional information
|
|||
|
can be retrieved by calling GetLastError().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOL bIsRootEnumerated = FALSE;
|
|||
|
|
|||
|
return RestoreSpecialRegistryData(hSourceEnumKey,
|
|||
|
hTargetEnumKey,
|
|||
|
ENUM_LEVEL_ENUMERATORS, // (0x0000003)
|
|||
|
(PVOID)&bIsRootEnumerated,
|
|||
|
PNPSIF_RESTORE_TYPE_ENUM |
|
|||
|
PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION
|
|||
|
);
|
|||
|
|
|||
|
} // RestoreEnumKey
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
RestoreClassKey(
|
|||
|
IN HKEY hSourceClassKey,
|
|||
|
IN HKEY hTargetClassKey
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Restores new elements of the source Class key to the target Class key,
|
|||
|
located in the backup registry to be restored.
|
|||
|
|
|||
|
Intermediate values from the source registry are coied to the the target
|
|||
|
registry only when they do not already exist in the target. Otherwise, the
|
|||
|
target values are preseved.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hSourceClassKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Control\Class
|
|||
|
key within the "source" registry, whose data is to be
|
|||
|
"merged" into the corresponding SYSTEM key of the "target"
|
|||
|
registry, as specified by hTargetClassKey.
|
|||
|
|
|||
|
hTargetClassKey - Handle to the HKLM\SYSTEM\CurrentControlSet\Control\Class
|
|||
|
key within the "target" registry, that is to receive
|
|||
|
additional data from the corresponding SYSTEM key of the
|
|||
|
"source" registry, as specified by hSourceClassKey.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise. Upon failure, additional information
|
|||
|
can be retrieved by calling GetLastError().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return RestoreSpecialRegistryData(hSourceClassKey,
|
|||
|
hTargetClassKey,
|
|||
|
CLASS_LEVEL_CLASSGUIDS, // (0x00000002)
|
|||
|
(PVOID)NULL,
|
|||
|
PNPSIF_RESTORE_TYPE_CLASS
|
|||
|
);
|
|||
|
|
|||
|
} // RestoreClassKey
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
RestoreSpecialRegistryData(
|
|||
|
IN HKEY hSourceKey,
|
|||
|
IN HKEY hTargetKey,
|
|||
|
IN ULONG ulLevel,
|
|||
|
IN OUT PVOID pContext,
|
|||
|
IN ULONG ulFlags
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine restores the specified source key to the specified target key,
|
|||
|
merging intermediate subkeys and values. Values in the subkeys located
|
|||
|
above the specified depth level are merged from the source into the target
|
|||
|
(with collisions handled according to the specified flags). Subkeys and
|
|||
|
values at and below the level specified are merged from the source key to
|
|||
|
the target key, with subkeys from the sources replacing any corresponding
|
|||
|
subkeys in the target.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hSourceKey - Handle to a key within the source registry, whose data is to be
|
|||
|
"merged" into the corresponding key of the target registry, as
|
|||
|
specified by hTargetKey.
|
|||
|
|
|||
|
hTargetKey - Handle to a key within the target registry, that is to receive
|
|||
|
data from the corresponding key of the source registry, as
|
|||
|
specified by hSourceKey.
|
|||
|
|
|||
|
ulLevel - Specifies the subkey depth at which "replacement" will
|
|||
|
take place. For subkeys above that depth, data in the target
|
|||
|
registry will be preserved if present, and copied otherwise.
|
|||
|
For keys at the specified level, data from the specified target
|
|||
|
key will be replaced by the source key.
|
|||
|
|
|||
|
pContext - Specifies caller-supplied context for the operation that is
|
|||
|
specific to the type of subkeys being restored (see ulFlags
|
|||
|
below), and the specified ulLevel parameter.
|
|||
|
|
|||
|
ulFlags - Supplies flags specifying options for the restoration of the
|
|||
|
registry keys. May be one of the following values:
|
|||
|
|
|||
|
PNPSIF_RESTORE_TYPE_ENUM:
|
|||
|
|
|||
|
Specifies that the subkeys being restored are subkeys of
|
|||
|
the SYSTEM\CurrentControlSet\Enum branch. Device hardware
|
|||
|
settings can be inspected at the appropriate ulLevel.
|
|||
|
|
|||
|
For Enum branch subkeys, the ulLevel parameter describes
|
|||
|
also the type of the subkeys contained under the hSourceKey
|
|||
|
and hTargetKey keys. May be one of:
|
|||
|
|
|||
|
ENUM_LEVEL_ENUMERATORS
|
|||
|
ENUM_LEVEL_DEVICES
|
|||
|
ENUM_LEVEL_INSTANCES
|
|||
|
|
|||
|
PNPSIF_RESTORE_TYPE_CLASS:
|
|||
|
|
|||
|
Specifies that the subkeys being restored are subkeys of
|
|||
|
the SYSTEM\CurrentControlSet\Control\Class branch. Setup
|
|||
|
class or device software settings can be inspected at the
|
|||
|
appropriate ulLevel.
|
|||
|
|
|||
|
For Class branch subkeys, the ulLevel parameter also
|
|||
|
describes the type of the subkeys contained under the
|
|||
|
hSourceKey and hTargetKey keys. May be one of:
|
|||
|
|
|||
|
CLASS_LEVEL_CLASSGUIDS
|
|||
|
CLASS_LEVEL_DRVINSTS
|
|||
|
|
|||
|
PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION:
|
|||
|
|
|||
|
When a collision occurs while merging values between the
|
|||
|
source and target at intermediate levels, replace the
|
|||
|
target value with that from the source (the default
|
|||
|
behavior is to preserve existing target values above
|
|||
|
'ulLevel' in depth from the supplied keys; below 'ulLevel',
|
|||
|
only source key values will be present.)
|
|||
|
|
|||
|
NOTE: it's probably worth noting that a corresponding flag for
|
|||
|
replace-target-keys-on-collision is not at all necessary, since
|
|||
|
such a behavior can already be implemented with the ulLevel
|
|||
|
parameter. (i.e. the ulLevel parameter actually specifies the
|
|||
|
level at which all source keys will replace target keys; to
|
|||
|
specify that this should ALWAYS be done is the same as
|
|||
|
specifying ulLevel == 0)
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise. Upon failure, additional information
|
|||
|
can be retrieved by calling GetLastError().
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
This routine was written specifically to assist in the restoration of the
|
|||
|
Plug-and-Play specific registry keys that cannot simply be restored from
|
|||
|
backup. It is intended to be called within the context of a
|
|||
|
backup-and-restore application's "restore" phase.
|
|||
|
|
|||
|
During a backup-and-restore application's "restore" phase, the registry that
|
|||
|
has been backed-up onto backup medium is to become the new system registry.
|
|||
|
Certain Plug and Play values and registry keys, are actually copied into the
|
|||
|
backed-up registry from the current registry. Rather than using the backup
|
|||
|
registry, or the current registry exclusively, Plug and Play data contained
|
|||
|
in these keys should be merged from the current registry into the backup
|
|||
|
registry in a way that is appropriate for each key. The backup registry can
|
|||
|
then safely replace the current registry as the system registry.
|
|||
|
|
|||
|
In the context of a backup-and-restore application's "restore" phase, the
|
|||
|
"Source" registry key is the one contained in the current running system
|
|||
|
registry. The "Target" registry is that which has been backed-up, and will
|
|||
|
become the system registry, upon reboot.
|
|||
|
|
|||
|
The different restore behaviors required for each of the different sets of
|
|||
|
keys can be specified with the appropriate ulLevel, and ulFlags.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONG result = ERROR_SUCCESS;
|
|||
|
DWORD dwIndex = 0;
|
|||
|
DWORD dwSubkeyCount, dwMaxSubkeyNameLength;
|
|||
|
DWORD dwValueCount, dwMaxValueNameLength, dwMaxValueDataLength;
|
|||
|
DWORD dwDisposition;
|
|||
|
LPTSTR pszSubkeyName = NULL, pszValueName = NULL;
|
|||
|
LPBYTE pValueData = NULL;
|
|||
|
BOOL bPossibleRedundantInstanceKeys = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate parameters
|
|||
|
//
|
|||
|
if (INVALID_FLAGS(ulFlags, PNPSIF_RESTORE_BITS)) {
|
|||
|
SetLastError(ERROR_INVALID_FLAGS);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if ((hTargetKey == NULL) ||
|
|||
|
(hTargetKey == INVALID_HANDLE_VALUE) ||
|
|||
|
(hSourceKey == NULL) ||
|
|||
|
(hSourceKey == INVALID_HANDLE_VALUE)) {
|
|||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (ulLevel == 0) {
|
|||
|
//
|
|||
|
// This is the level at which we should stop merging, so just replace
|
|||
|
// hTargetKey with hSourceKey, and we're done.
|
|||
|
//
|
|||
|
return MyReplaceKey(hSourceKey, hTargetKey);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// At levels above the replacement level, perform a non-destructive
|
|||
|
// merge of intermediate subkeys and values; that is, only copy keys and
|
|||
|
// values from the source to the target if they do not already exist at
|
|||
|
// the target. Otherwise, leave the target keys and values alone, and
|
|||
|
// keep traversing subkeys as necessary.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Find out information about the hSourceKey values and subkeys.
|
|||
|
//
|
|||
|
result = RegQueryInfoKey(hSourceKey,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&dwSubkeyCount,
|
|||
|
&dwMaxSubkeyNameLength,
|
|||
|
NULL,
|
|||
|
&dwValueCount,
|
|||
|
&dwMaxValueNameLength,
|
|||
|
&dwMaxValueDataLength,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegQueryInfoKey returned error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate space to fold the largest hSourceKey subkey, value and data.
|
|||
|
//
|
|||
|
dwMaxSubkeyNameLength++;
|
|||
|
pszSubkeyName = MyMalloc(dwMaxSubkeyNameLength * sizeof(TCHAR));
|
|||
|
if (pszSubkeyName == NULL) {
|
|||
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: MyMalloc failed allocating subkey name string, error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
dwMaxValueNameLength++;
|
|||
|
pszValueName = MyMalloc(dwMaxValueNameLength * sizeof(TCHAR));
|
|||
|
if (pszValueName == NULL) {
|
|||
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: MyMalloc failed allocating value name string, error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
pValueData = MyMalloc(dwMaxValueDataLength * sizeof(TCHAR));
|
|||
|
if (pValueData == NULL) {
|
|||
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: MyMalloc failed allocating value data buffer, error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate all hSourceKey values.
|
|||
|
//
|
|||
|
for (dwIndex = 0; dwIndex < dwValueCount; dwIndex++) {
|
|||
|
|
|||
|
DWORD dwValueNameLength = dwMaxValueNameLength;
|
|||
|
DWORD dwValueDataLength = dwMaxValueDataLength;
|
|||
|
DWORD dwType;
|
|||
|
|
|||
|
result = RegEnumValue(hSourceKey,
|
|||
|
dwIndex,
|
|||
|
pszValueName,
|
|||
|
&dwValueNameLength,
|
|||
|
0,
|
|||
|
&dwType,
|
|||
|
pValueData,
|
|||
|
&dwValueDataLength);
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Error enumerating values - whatchya gonna do?
|
|||
|
// Just move on and try to merge subkeys the best we can?
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegEnumValue returned error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
goto EnumSubkeys;
|
|||
|
}
|
|||
|
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: Enumerated value %d == '%ws' on hSourceKey.\n"),
|
|||
|
dwIndex, pszValueName));
|
|||
|
|
|||
|
//
|
|||
|
// Query to see if this value exists in the hTargetKey
|
|||
|
//
|
|||
|
result = RegQueryValueEx(hTargetKey,
|
|||
|
pszValueName,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
if ((result == ERROR_SUCCESS) &&
|
|||
|
!(ulFlags & PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION)) {
|
|||
|
//
|
|||
|
// The enumerated value already exists under the target key, and
|
|||
|
// we are NOT supposed to replace it.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: Value '%ws' already exists on hTargetKey.\n"),
|
|||
|
pszValueName));
|
|||
|
|
|||
|
} else if ((result == ERROR_FILE_NOT_FOUND) ||
|
|||
|
(ulFlags & PNPSIF_RESTORE_REPLACE_TARGET_VALUES_ON_COLLISION)){
|
|||
|
//
|
|||
|
// The enumerated value doesn't exist under the target key, or
|
|||
|
// it does and we're supposed to replace it.
|
|||
|
//
|
|||
|
result = RegSetValueEx(hTargetKey,
|
|||
|
pszValueName,
|
|||
|
0,
|
|||
|
dwType,
|
|||
|
pValueData,
|
|||
|
dwValueDataLength);
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Error setting value - whatchya gonna do?
|
|||
|
// Just move on to the next enumerated value.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegSetValueEx failed setting value '%ws', error == 0x%08lx\n"),
|
|||
|
pszValueName, result));
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// RegQueryValueEx returned some other error - weird?
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegQueryValueEx failed for value '%ws', error == 0x%08lx\n"),
|
|||
|
pszValueName, result));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
EnumSubkeys:
|
|||
|
|
|||
|
//
|
|||
|
// Perform special processing of the device instance subkeys.
|
|||
|
//
|
|||
|
if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
|
|||
|
(ARGUMENT_PRESENT(pContext)) &&
|
|||
|
(ulLevel == ENUM_LEVEL_INSTANCES)) {
|
|||
|
|
|||
|
if (*((PBOOL)pContext)) {
|
|||
|
//
|
|||
|
// For root enumerated devices, check for the possibility that
|
|||
|
// redundant instance keys of a device might have been created
|
|||
|
// in the current registry, in which case we should ignore them
|
|||
|
// when migrating to the target registry.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// The possibility of redundant root-enumerated device
|
|||
|
// instance keys exists when:
|
|||
|
//
|
|||
|
// - the device is root-enumerated
|
|||
|
//
|
|||
|
// - instances of this device already exist in the target hive
|
|||
|
//
|
|||
|
// a particular device instance is redundant if it is a newly
|
|||
|
// created instance in the target registry for a device where
|
|||
|
// other instances already exist.
|
|||
|
//
|
|||
|
DWORD dwTargetSubkeyCount = 0;
|
|||
|
if (RegQueryInfoKey(hTargetKey,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&dwTargetSubkeyCount,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL) != ERROR_SUCCESS) {
|
|||
|
dwTargetSubkeyCount = 0;
|
|||
|
}
|
|||
|
bPossibleRedundantInstanceKeys = (dwTargetSubkeyCount > 0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enumerate all hSourceKey subkeys.
|
|||
|
//
|
|||
|
for (dwIndex = 0; dwIndex < dwSubkeyCount; dwIndex++) {
|
|||
|
|
|||
|
HKEY hTargetSubkey = NULL, hSourceSubkey = NULL;
|
|||
|
DWORD dwSubkeyNameLength = dwMaxSubkeyNameLength;
|
|||
|
|
|||
|
result = RegEnumKeyEx(hSourceKey,
|
|||
|
dwIndex,
|
|||
|
pszSubkeyName,
|
|||
|
&dwSubkeyNameLength,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Error enumerating subkeys - whatchya gonna do?
|
|||
|
// There is nothing left to do, so just exit.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegEnumKeyEx returned error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: enumerated subkey %d == '%ws' on hSourceKey.\n"),
|
|||
|
dwIndex, pszSubkeyName));
|
|||
|
|
|||
|
//
|
|||
|
// Perform special processing of the Enum subkeys.
|
|||
|
//
|
|||
|
if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
|
|||
|
(ARGUMENT_PRESENT(pContext)) &&
|
|||
|
(ulLevel == ENUM_LEVEL_ENUMERATORS)) {
|
|||
|
//
|
|||
|
// At the start of the recursive enumeration for each
|
|||
|
// enumerator subkey, reset our (global) BOOL context
|
|||
|
// variable to keep track of whether subsequent device
|
|||
|
// subkeys represent "ROOT" enumerated devices.
|
|||
|
//
|
|||
|
PBOOL pbIsRootEnumerated = (PBOOL)pContext;
|
|||
|
|
|||
|
*pbIsRootEnumerated =
|
|||
|
(BOOL)(lstrcmpi((LPTSTR)pszSubkeyName,
|
|||
|
REGSTR_KEY_ROOTENUM) == 0);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open this subkey in hSourceKey
|
|||
|
//
|
|||
|
result = RegOpenKeyEx(hSourceKey,
|
|||
|
pszSubkeyName,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&hSourceSubkey);
|
|||
|
|
|||
|
if (result == ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Attempt to open this subkey in the hTargetKey
|
|||
|
//
|
|||
|
result = RegOpenKeyEx(hTargetKey,
|
|||
|
pszSubkeyName,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&hTargetSubkey);
|
|||
|
|
|||
|
if ((result != ERROR_SUCCESS) &&
|
|||
|
(bPossibleRedundantInstanceKeys) &&
|
|||
|
(IsString4DigitOrdinal(pszSubkeyName))) {
|
|||
|
//
|
|||
|
// The subkey is a root-enumerated instance key with a
|
|||
|
// 4-digit ordinal name (most-likely autogenerated), where
|
|||
|
// we may have redundant keys.
|
|||
|
//
|
|||
|
// If this key doesn't exist in the target registry, it was
|
|||
|
// likely a redundant, autogenerated instance created during
|
|||
|
// setup because a previous autogenerated key (i.e. one we
|
|||
|
// just migrated) already existed, we just won't migrate it.
|
|||
|
//
|
|||
|
ASSERT(ulFlags & PNPSIF_RESTORE_TYPE_ENUM);
|
|||
|
ASSERT(ulLevel == ENUM_LEVEL_INSTANCES);
|
|||
|
ASSERT((ARGUMENT_PRESENT(pContext)) && (*((PBOOL)pContext)));
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (result == ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// We successfully opened the target subkey above (we
|
|||
|
// just weren't supposed to delete it) so we should have
|
|||
|
// a valid handle.
|
|||
|
//
|
|||
|
ASSERT(hTargetSubkey != NULL);
|
|||
|
dwDisposition = REG_OPENED_EXISTING_KEY;
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Create the target subkey.
|
|||
|
//
|
|||
|
ASSERT(hTargetSubkey == NULL);
|
|||
|
|
|||
|
result = RegCreateKeyEx(hTargetKey,
|
|||
|
pszSubkeyName,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
REG_OPTION_NON_VOLATILE,
|
|||
|
KEY_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
&hTargetSubkey,
|
|||
|
&dwDisposition);
|
|||
|
|
|||
|
if (result == ERROR_SUCCESS) {
|
|||
|
ASSERT(dwDisposition == REG_CREATED_NEW_KEY);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (result == ERROR_SUCCESS) {
|
|||
|
|
|||
|
//
|
|||
|
// Check if the key had already existed.
|
|||
|
//
|
|||
|
|
|||
|
if (dwDisposition == REG_CREATED_NEW_KEY) {
|
|||
|
//
|
|||
|
// We just created this key in the target to replace it
|
|||
|
// with the corresponding key in the source.
|
|||
|
//
|
|||
|
if (!MyReplaceKey(hSourceSubkey, hTargetSubkey)) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
} else if ((ulFlags & PNPSIF_RESTORE_TYPE_ENUM) &&
|
|||
|
(ulLevel == ENUM_LEVEL_INSTANCES) &&
|
|||
|
(!IsDeviceConfigured(hTargetSubkey)) &&
|
|||
|
(IsDeviceConfigured(hSourceSubkey))) {
|
|||
|
//
|
|||
|
// If we are enumerating instances, check if the
|
|||
|
// instance keys in the target and source are
|
|||
|
// properly configured.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// If it is not configured in the target, but is
|
|||
|
// configured in the source - then we DO want to
|
|||
|
// replace the target instance key, because it might
|
|||
|
// be some sort of critical device - like a boot
|
|||
|
// device. Even if it's not critical, if the device
|
|||
|
// has not been seen since before the last upgrade
|
|||
|
// *prior* to backup, we can't really have much
|
|||
|
// confidence in those settings anyways. If neither
|
|||
|
// the target or source are configured, we may as
|
|||
|
// well just keep the target, like we do for
|
|||
|
// everything else.
|
|||
|
//
|
|||
|
if (MyReplaceKey(hSourceSubkey, hTargetSubkey)) {
|
|||
|
//
|
|||
|
// If we successfully replaced the target instance
|
|||
|
// key for this device with the source, then also
|
|||
|
// replace the corresponding Class key for this
|
|||
|
// instance from the target with the Class key from
|
|||
|
// the source.
|
|||
|
//
|
|||
|
// NOTE: this works because we always restore the
|
|||
|
// Enum branch before retoring the Class branch, so
|
|||
|
// the Class keys haven't been touched yet.
|
|||
|
//
|
|||
|
|
|||
|
if (!ReplaceClassKeyForInstance(hSourceSubkey,
|
|||
|
PnpSifRestoreSourceClassKeyHandle,
|
|||
|
PnpSifRestoreTargetClassKeyHandle)) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: ReplaceClassKeyForInstance failed with error == 0x%08lx\n"),
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
} else if ((ulLevel-1) != 0) {
|
|||
|
//
|
|||
|
// We're still above the replacement level, and the key
|
|||
|
// previously existed in the target, so we're not
|
|||
|
// supposed to replace it. Since the next level is NOT
|
|||
|
// the replacement level, just follow the keys down
|
|||
|
// recursively.
|
|||
|
//
|
|||
|
ASSERT(dwDisposition == REG_OPENED_EXISTING_KEY);
|
|||
|
|
|||
|
if (!RestoreSpecialRegistryData(hSourceSubkey,
|
|||
|
hTargetSubkey,
|
|||
|
ulLevel-1,
|
|||
|
(PVOID)pContext,
|
|||
|
ulFlags)) {
|
|||
|
//
|
|||
|
// Error handling the subkeys - whatcha gonna do?
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RestoreSpecialRegistryData failed for subkey %ws at level %d, error == 0x%08lx\n"),
|
|||
|
pszSubkeyName, ulLevel-1,
|
|||
|
GetLastError()));
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// The key already exists, so don't replace it. Also,
|
|||
|
// the next level is the replacement level, so don't
|
|||
|
// recurse either (else we'd replace it). Basically,
|
|||
|
// just do nothing here.
|
|||
|
//
|
|||
|
ASSERT(ulLevel == 1);
|
|||
|
ASSERT(dwDisposition == REG_OPENED_EXISTING_KEY);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Close the open target subkey.
|
|||
|
//
|
|||
|
RegCloseKey(hTargetSubkey);
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// could not open/create subkey in taget registry.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegCreateKey failed to create target subkey %s with error == 0x%08lx\n"),
|
|||
|
pszSubkeyName, result));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Close the enumerated hSourceKey subkey.
|
|||
|
//
|
|||
|
RegCloseKey(hSourceSubkey);
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Could not open the enumerated hSourceKey subkey.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegOpenKey failed to open existing subkey %s with error == 0x%08lx\n"),
|
|||
|
pszSubkeyName, result));
|
|||
|
}
|
|||
|
|
|||
|
} // for (dwIndex = 0; dwIndex < dwSubkeyCount; dwIndex++)
|
|||
|
|
|||
|
} // (ulLevel != 0)
|
|||
|
|
|||
|
|
|||
|
Clean0:
|
|||
|
//
|
|||
|
// Free any allocated buffers
|
|||
|
//
|
|||
|
if (pszSubkeyName != NULL) {
|
|||
|
MyFree(pszSubkeyName);
|
|||
|
}
|
|||
|
if (pszValueName != NULL) {
|
|||
|
MyFree(pszValueName);
|
|||
|
}
|
|||
|
if (pValueData != NULL) {
|
|||
|
MyFree(pValueData);
|
|||
|
}
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
SetLastError(result);
|
|||
|
}
|
|||
|
return (result == ERROR_SUCCESS);
|
|||
|
|
|||
|
} // RestoreSpecialRegistryData
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
MyReplaceKey(
|
|||
|
IN HKEY hSourceKey,
|
|||
|
IN HKEY hTargetKey
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine replaces the target registry key with the source registry key.
|
|||
|
This is done by performing a RegSaveKey on the source registry key to a
|
|||
|
temporary file, and restoring that file to the target registry key. All
|
|||
|
data contained below the target registry key is lost. The source registry
|
|||
|
key is not modified by this routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hSourceKey - Handle to the key that is the source of the restore operation.
|
|||
|
All values and subkeys of the source key will be restored on
|
|||
|
top of the target key.
|
|||
|
|
|||
|
hTargetKey - Handle to the key that is the target of the restore operation.
|
|||
|
All values and subkeys of the source key will be restored on
|
|||
|
top of this key, and all existing values and data underneath
|
|||
|
this key will be lost.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise. Upon failure, additional information
|
|||
|
can be retrieved by calling GetLastError().
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Since this routine uses the RegSaveKey and RegRestoreKey registry APIs, it
|
|||
|
is expected that the calling process have both the SE_BACKUP_NAME and
|
|||
|
SE_RESTORE_NAME privileges.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONG result = ERROR_SUCCESS;
|
|||
|
TCHAR szTempFilePath[MAX_PATH];
|
|||
|
TCHAR szTempFileName[MAX_PATH];
|
|||
|
DWORD dwTemp;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Use the temporary directory to store the saved registry key.
|
|||
|
//
|
|||
|
dwTemp = GetTempPath(MAX_PATH, szTempFilePath);
|
|||
|
if ((dwTemp == 0) || (dwTemp > MAX_PATH)) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: GetTempPath failed, current directory will be specified.\n")));
|
|||
|
// current path specified with trailing '\', as GetTempPath would have.
|
|||
|
lstrcpy(szTempFileName, TEXT(".\\"));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Assign the saved registry key a temporary, unique filename.
|
|||
|
//
|
|||
|
if (!GetTempFileName(szTempFilePath,
|
|||
|
TEXT("PNP"),
|
|||
|
0, // make sure it's unique
|
|||
|
szTempFileName)) {
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: GetTempFileName failed with error == 0x%08lx, using hardcoded temp file name!\n"),
|
|||
|
GetLastError()));
|
|||
|
lstrcpy(szTempFileName, szTempFilePath);
|
|||
|
lstrcat(szTempFileName, TEXT("~pnpreg.tmp"));
|
|||
|
}
|
|||
|
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: Using temporary filename: %ws\n"),
|
|||
|
szTempFileName));
|
|||
|
|
|||
|
//
|
|||
|
// A side effect of requesting a "unique" filename from GetTempFileName is
|
|||
|
// that it automatically creates the file. Unfortunately, RegSaveKey will
|
|||
|
// fail if the specified file already exists, so delete the file now.
|
|||
|
//
|
|||
|
if(pSifUtilFileExists(szTempFileName,NULL)) {
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: Temporary file %s exists, deleting.\n"),
|
|||
|
szTempFileName));
|
|||
|
SetFileAttributes(szTempFileName, FILE_ATTRIBUTE_NORMAL);
|
|||
|
DeleteFile(szTempFileName);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the source key to a file using the temporary file name.
|
|||
|
// (calling process must have the SE_BACKUP_NAME privilege)
|
|||
|
//
|
|||
|
result = RegSaveKey(hSourceKey,
|
|||
|
szTempFileName,
|
|||
|
NULL);
|
|||
|
if (result == ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Restore the file to the target key.
|
|||
|
// (calling process must have the SE_RESTORE_NAME privilege)
|
|||
|
//
|
|||
|
result = RegRestoreKey(hTargetKey,
|
|||
|
szTempFileName,
|
|||
|
REG_FORCE_RESTORE);
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// Failed to restore the file to the target key!
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegRestoreKey from %ws failed with error == 0x%08lx\n"),
|
|||
|
szTempFileName, result));
|
|||
|
} else {
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: Key replacement successful.\n")));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Delete the temporary file we created, now that we're done with it.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_INFO,
|
|||
|
(TEXT("PNPSIF: Deleting temporary file %s.\n"),
|
|||
|
szTempFileName));
|
|||
|
ASSERT(pSifUtilFileExists(szTempFileName,NULL));
|
|||
|
SetFileAttributes(szTempFileName, FILE_ATTRIBUTE_NORMAL);
|
|||
|
DeleteFile(szTempFileName);
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Failed to save the source key.
|
|||
|
//
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: RegSaveKey to %ws failed with error == 0x%08lx\n"),
|
|||
|
szTempFileName, result));
|
|||
|
}
|
|||
|
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
SetLastError(result);
|
|||
|
}
|
|||
|
return (result == ERROR_SUCCESS);
|
|||
|
|
|||
|
} // MyReplaceKey
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
IsString4DigitOrdinal(
|
|||
|
IN LPTSTR pszSubkeyName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine checks if a subkey name has the form of a 4-digit decimal
|
|||
|
ordinal (e.g. "0000", "0001", ... , "9999"), that are typically given to
|
|||
|
auto-generated root-enumerated device instance ids - i.e. TEXT("%04u").
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszSubkeyName - Subkey name to check.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the string has the form of a 4-digit ordinal string, FALSE
|
|||
|
otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LPTSTR p;
|
|||
|
ULONG ulTotalLength = 0;
|
|||
|
|
|||
|
if ((!ARGUMENT_PRESENT(pszSubkeyName)) ||
|
|||
|
(pszSubkeyName[0] == TEXT('\0'))) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
for (p = pszSubkeyName; *p; p++) {
|
|||
|
//
|
|||
|
// Count the caharcters in the string, and make sure its not longer than
|
|||
|
// 4 characters.
|
|||
|
//
|
|||
|
ulTotalLength++;
|
|||
|
if (ulTotalLength > 4) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check if the character is non-numeric, non-decimal.
|
|||
|
//
|
|||
|
if ((*p < TEXT('0')) || (*p > TEXT('9'))) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ulTotalLength != 4) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} // IsString4DigitOrdinal
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
IsDeviceConfigured(
|
|||
|
IN HKEY hInstanceKey
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine determines whether the device instance specified by the
|
|||
|
supplied registry key is configured or not. If a device has configflags, and
|
|||
|
CONFIGFLAG_REINSTALL or CONFIGFLAG_FAILEDINSTALL are not set then the
|
|||
|
device is considered configured.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hInstanceKey - Handle to a device instance registry key.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise. Upon failure, additional information
|
|||
|
can be retrieved by calling GetLastError().
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOL bDeviceConfigured = FALSE;
|
|||
|
DWORD dwSize, dwType, dwConfigFlags;
|
|||
|
|
|||
|
if ((hInstanceKey == NULL) ||
|
|||
|
(hInstanceKey == INVALID_HANDLE_VALUE)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
dwSize = sizeof(dwConfigFlags);
|
|||
|
|
|||
|
if ((RegQueryValueEx(hInstanceKey,
|
|||
|
REGSTR_VAL_CONFIGFLAGS,
|
|||
|
0,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)&dwConfigFlags,
|
|||
|
&dwSize) == ERROR_SUCCESS) &&
|
|||
|
(dwType == REG_DWORD) &&
|
|||
|
!(dwConfigFlags & CONFIGFLAG_REINSTALL) &&
|
|||
|
!(dwConfigFlags & CONFIGFLAG_FAILEDINSTALL)) {
|
|||
|
|
|||
|
bDeviceConfigured = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
return bDeviceConfigured;
|
|||
|
|
|||
|
} // IsDeviceConfigured
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
ReplaceClassKeyForInstance(
|
|||
|
IN HKEY hSourceInstanceKey,
|
|||
|
IN HKEY hSourceRootClassKey,
|
|||
|
IN HKEY hTargetRootClassKey
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine replaces the class key corresponding to the specified device
|
|||
|
instance key (as specified by the "Driver" value in the instance key) in the
|
|||
|
target hive with the class key from the source.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hSourceInstanceKey - Handle to a device instance registry key in the source
|
|||
|
hive.
|
|||
|
|
|||
|
hSourceRootClassKey - Handle to the root of the Class branch in the source
|
|||
|
hive - the same hive as the specified instance key.
|
|||
|
|
|||
|
hTargetRootClassKey - Handle to the root of the Class branch in the target
|
|||
|
hive.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful, FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONG result = ERROR_SUCCESS;
|
|||
|
TCHAR szDriverKeyName[MAX_GUID_STRING_LEN + 5]; // "{ClassGUID}\XXXX"
|
|||
|
DWORD dwSize, dwType, dwDisposition;
|
|||
|
HKEY hSourceClassSubkey = NULL, hTargetClassSubkey = NULL;
|
|||
|
|
|||
|
if ((hSourceInstanceKey == NULL) ||
|
|||
|
(hSourceInstanceKey == INVALID_HANDLE_VALUE) ||
|
|||
|
(hSourceRootClassKey == NULL) ||
|
|||
|
(hSourceRootClassKey == INVALID_HANDLE_VALUE) ||
|
|||
|
(hTargetRootClassKey == NULL) ||
|
|||
|
(hTargetRootClassKey == INVALID_HANDLE_VALUE)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Read the REGSTR_VAL_DRIVER REG_SZ "Driver" value from the instance key.
|
|||
|
//
|
|||
|
szDriverKeyName[0] = TEXT('\0');
|
|||
|
dwSize = sizeof(szDriverKeyName);
|
|||
|
|
|||
|
result = RegQueryValueEx(hSourceInstanceKey,
|
|||
|
REGSTR_VAL_DRIVER,
|
|||
|
0,
|
|||
|
&dwType,
|
|||
|
(LPBYTE)szDriverKeyName,
|
|||
|
&dwSize);
|
|||
|
if (result == ERROR_FILE_NOT_FOUND) {
|
|||
|
//
|
|||
|
// No "Driver" value, so there's no class key to migrate, which is fine.
|
|||
|
//
|
|||
|
result = ERROR_SUCCESS;
|
|||
|
goto Clean0;
|
|||
|
|
|||
|
} else if ((result != ERROR_SUCCESS) ||
|
|||
|
(dwType != REG_SZ)) {
|
|||
|
//
|
|||
|
// Any other error is a failure to read the value.
|
|||
|
//
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open the "Driver" key in the source Class branch.
|
|||
|
//
|
|||
|
result = RegOpenKeyEx(hSourceRootClassKey,
|
|||
|
szDriverKeyName,
|
|||
|
0,
|
|||
|
KEY_READ,
|
|||
|
&hSourceClassSubkey);
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
//
|
|||
|
// The instance key had a "Driver" value, so a corresponding key
|
|||
|
// *should* exist. If we couldn't open it, it's from some failure
|
|||
|
// besides that.
|
|||
|
//
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open / create the corresponding key in the target Class branch.
|
|||
|
//
|
|||
|
result = RegCreateKeyEx(hTargetRootClassKey,
|
|||
|
szDriverKeyName,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
REG_OPTION_NON_VOLATILE,
|
|||
|
KEY_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
&hTargetClassSubkey,
|
|||
|
&dwDisposition);
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
goto Clean0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Replace the target class subkey with the source.
|
|||
|
//
|
|||
|
if (!MyReplaceKey(hSourceClassSubkey, hTargetClassSubkey)) {
|
|||
|
result = GetLastError();
|
|||
|
DBGTRACE( DBGF_ERRORS,
|
|||
|
(TEXT("PNPSIF: MyReplaceKey failed with error == 0x%08lx\n"),
|
|||
|
result));
|
|||
|
}
|
|||
|
|
|||
|
Clean0:
|
|||
|
|
|||
|
if (hTargetClassSubkey) {
|
|||
|
RegCloseKey(hTargetClassSubkey);
|
|||
|
}
|
|||
|
if (hSourceClassSubkey) {
|
|||
|
RegCloseKey(hSourceClassSubkey);
|
|||
|
}
|
|||
|
if (result != ERROR_SUCCESS) {
|
|||
|
SetLastError(result);
|
|||
|
}
|
|||
|
return (result == ERROR_SUCCESS);
|
|||
|
|
|||
|
} // ReplaceClassKeyForInstance
|
|||
|
|
|||
|
|
|||
|
|