|
|
#include "ntapi.h"
#include "advpack.h"
#include "globals.h"
#include "crc32.h"
#include "resource.h"
// macro definitions
#define VDH_EXISTENCE_ONLY 0x01
#define VDH_GET_VALUE 0x02
#define VDH_DEL_VALUE 0x04
#define BIG_BUF_SIZE (1024 + 512) // 1.5K
// type definitions
typedef struct tagROOTKEY { PCSTR pcszRootKey; HKEY hkRootKey; } ROOTKEY;
// prototype declarations
VOID EnumerateSubKey(); BOOL RegSaveRestoreHelperWrapper(PCSTR pcszValueName, PCSTR pcszCRCValueName);
BOOL RegSaveHelper(HKEY hkBckupKey, HKEY hkRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, PCSTR pcszCRCValueName); BOOL RegRestoreHelper(HKEY hkBckupKey, HKEY hkRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, PCSTR pcszCRCValueName);
BOOL AddDelMapping(HKEY hkBckupKey, PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, DWORD dwFlags); BOOL MappingExists(HKEY hkBckupKey, PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName);
BOOL SetValueData(HKEY hkBckupKey, PCSTR pcszValueName, CONST BYTE *pcbValueData, DWORD dwValueDataLen); BOOL ValueDataExists(HKEY hkBckupKey, PCSTR pcszValueName); BOOL GetValueData(HKEY hkBckupKey, PCSTR pcszValueName, PBYTE *ppbValueData, PDWORD pdwValueDataLen); BOOL DelValueData(HKEY hkBckupKey, PCSTR pcszValueName); BOOL ValueDataHelper(HKEY hkBckupKey, PCSTR pcszValueName, PBYTE *ppbValueData, PDWORD pdwValueDataLen, DWORD dwFlags); VOID Convert2CRC(PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, PSTR pszCRCValueName);
BOOL MapRootRegStr2Key(PCSTR pcszRootKey, HKEY *phkRootKey); CHAR FindSeparator(PCSTR pcszSubKey, PCSTR pcszValueName); BOOL RegKeyEmpty(HKEY hkRootKey, PCSTR pcszSubKey);
PSTR GetNextToken(PSTR *ppszData, CHAR chDeLim);
BOOL FRunningOnNT();
// global variables
BOOL g_bRet, g_fRestore, g_fAtleastOneRegSaved, g_fRemovBkData; HKEY g_hkBckupKey, g_hkRootKey; PCSTR g_pcszRootKey, g_pcszValueName; PSTR g_pszCRCTempBuf = NULL, g_pszSubKey = NULL, g_pszCRCSubKey = NULL;
// related to logging
VOID StartLogging(PCSTR pcszLogFileSecName); VOID WriteToLog(PCSTR pcszFormatString, ...); VOID StopLogging();
HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
HRESULT WINAPI RegSaveRestore(HWND hWnd, PCSTR pszTitleString, HKEY hkBckupKey, PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, DWORD dwFlags) { HWND hSaveWnd = ctx.hWnd; WORD wSaveQuietMode = ctx.wQuietMode; LPSTR lpszSaveTitle = ctx.lpszTitle;
g_bRet = g_fAtleastOneRegSaved = FALSE;
if ( (hWnd == INVALID_HANDLE_VALUE) || (dwFlags & ARSR_NOMESSAGES) ) ctx.wQuietMode |= QUIETMODE_ALL; if ( hWnd != INVALID_HANDLE_VALUE ) ctx.hWnd = hWnd;
if (pszTitleString != NULL) ctx.lpszTitle = (PSTR)pszTitleString;
g_hkBckupKey = hkBckupKey; g_pcszRootKey = pcszRootKey; g_pcszValueName = pcszValueName;
g_fRestore = (dwFlags & IE4_RESTORE); g_fRemovBkData = (dwFlags & IE4_REMOVREGBKDATA) && g_fRestore;
StartLogging(g_fRestore ? REG_RESTORE_LOG_KEY : REG_SAVE_LOG_KEY);
if (!MapRootRegStr2Key(pcszRootKey, &g_hkRootKey)) { ErrorMsg1Param(ctx.hWnd, IDS_INVALID_ROOTKEY, pcszRootKey); goto ErrExit; }
// allocate a 1.5K buffer for g_pszCRCTempBuf
if ((g_pszCRCTempBuf = (PSTR) LocalAlloc(LPTR, BIG_BUF_SIZE)) == NULL) { ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); goto ErrExit; }
if (!g_fRestore && pcszValueName == NULL && !(dwFlags & IE4_NOENUMKEY)) { HKEY hk;
// check if pcszSubKey exits; if it doesn't and it has not been already backed up,
// set the IE4_NOENUMKEY flag so that an entry for this subkey is made in the backup branch
if (RegOpenKeyEx(g_hkRootKey, pcszSubKey, 0, KEY_READ, &hk) != ERROR_SUCCESS) { if (!MappingExists(hkBckupKey, pcszRootKey, pcszSubKey, pcszValueName)) dwFlags |= IE4_NOENUMKEY; } else RegCloseKey(hk); }
if (pcszValueName != NULL || (dwFlags & IE4_NOENUMKEY)) { g_pszSubKey = g_pszCRCSubKey = (PSTR) pcszSubKey; g_bRet = RegSaveRestoreHelperWrapper(g_pcszValueName, g_pcszValueName); if (!(dwFlags & IE4_NO_CRC_MAPPING) && g_bRet) { // store the RootKey, SubKey, Flags and ValueName in *.map.
// this info would be used by the caller during the restore phase.
g_bRet = AddDelMapping(g_hkBckupKey, g_pcszRootKey, g_pszSubKey, g_pcszValueName, dwFlags); } } else // save or restore pcszSubKey recursively
{ // allocate a 1K buffer for g_pszCRCSubKey
if ((g_pszCRCSubKey = (PSTR) LocalAlloc(LPTR, 1024)) == NULL) { ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); goto ErrExit; }
if (!g_fRestore) { // if backup info exists for pcszRootKey\pcszSubKey, then we won't re-backup
// the key recursively again; if we don't do this, then during an upgrade or reinstall
// over a build, we would backup potentially newer values that got added during the running
// of the program.
if (MappingExists(hkBckupKey, pcszRootKey, pcszSubKey, pcszValueName)) { g_bRet = TRUE;
LocalFree(g_pszCRCSubKey); g_pszCRCSubKey = NULL;
goto ErrExit; }
// allocate a 1K buffer for g_pszSubKey
if ((g_pszSubKey = (PSTR) LocalAlloc(LPTR, 1024)) == NULL) { ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); LocalFree(g_pszCRCSubKey); goto ErrExit; } } else g_pszSubKey = (PSTR) pcszSubKey;
g_bRet = TRUE; lstrcpy(g_pszCRCSubKey, pcszSubKey); if (!g_fRestore) lstrcpy(g_pszSubKey, pcszSubKey);
EnumerateSubKey(); if (!(dwFlags & IE4_NO_CRC_MAPPING)) { if (g_fRestore) { if (g_bRet) { // if we couldn't restore everything; then we shouldn't delete the mapping info.
g_bRet = AddDelMapping(g_hkBckupKey, g_pcszRootKey, g_pszSubKey, g_pcszValueName, dwFlags); } } else { if (g_fAtleastOneRegSaved) { // save the mapping info only if atleast one reg entry was saved
g_bRet = AddDelMapping(g_hkBckupKey, g_pcszRootKey, g_pszSubKey, g_pcszValueName, dwFlags); } } }
LocalFree(g_pszCRCSubKey); g_pszCRCSubKey = NULL; if (!g_fRestore) { LocalFree(g_pszSubKey); g_pszSubKey = NULL; }
}
ErrExit: StopLogging();
if (g_pszCRCTempBuf != NULL) { LocalFree(g_pszCRCTempBuf); g_pszCRCTempBuf = NULL; }
ctx.hWnd = hSaveWnd; ctx.wQuietMode = wSaveQuietMode; ctx.lpszTitle = lpszSaveTitle;
return g_bRet ? S_OK : E_FAIL; }
HRESULT WINAPI RegRestoreAll(HWND hWnd, PSTR pszTitleString, HKEY hkBckupKey) { HWND hSaveWnd = ctx.hWnd; WORD wSaveQuietMode = ctx.wQuietMode; LPSTR lpszSaveTitle = ctx.lpszTitle; HRESULT hRet;
if (hWnd != INVALID_HANDLE_VALUE) ctx.hWnd = hWnd; else ctx.wQuietMode |= QUIETMODE_ALL;
if (pszTitleString != NULL) ctx.lpszTitle = pszTitleString;
hRet = RegRestoreAllEx( hkBckupKey );
ctx.hWnd = hSaveWnd; ctx.wQuietMode = wSaveQuietMode; ctx.lpszTitle = lpszSaveTitle;
return hRet; }
HRESULT RegRestoreAllEx( HKEY hkBckupKey ) // In one shot restore all the reg entries by enumerating all the values under hkBckupKey\*.map keys
// and calling RegSaveRestore on each one of them.
{ BOOL bRet = TRUE; PSTR pszMappedValueData = NULL; CHAR szBuf[32]; CHAR szSubKey[32]; DWORD dwKeyIndex; HKEY hkSubKey; LONG lRetVal;
if ((pszMappedValueData = (PSTR) LocalAlloc(LPTR, BIG_BUF_SIZE)) == NULL) { ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); return E_FAIL; }
// enumerate all the sub-keys under hkBckupKey
for (dwKeyIndex = 0; ; dwKeyIndex++) { PSTR pszPtr;
lRetVal = RegEnumKey(hkBckupKey, dwKeyIndex, szSubKey, sizeof(szSubKey)); if (lRetVal != ERROR_SUCCESS) { if (lRetVal != ERROR_NO_MORE_ITEMS) bRet = FALSE; break; }
// check if the keyname is of the form *.map
if ((pszPtr = ANSIStrChr(szSubKey, '.')) != NULL && lstrcmpi(pszPtr, ".map") == 0) { if (RegOpenKeyEx(hkBckupKey, szSubKey, 0, KEY_READ, &hkSubKey) == ERROR_SUCCESS) { DWORD dwValIndex, dwValueLen, dwDataLen;
// enumerate all the values under this key and restore each one of them
dwValueLen = sizeof(szBuf); dwDataLen = BIG_BUF_SIZE; for (dwValIndex = 0; ; dwValIndex++) { CHAR chSeparator; PSTR pszFlags, pszRootKey, pszSubKey, pszValueName, pszPtr; DWORD dwMappedFlags;
lRetVal = RegEnumValue(hkSubKey, dwValIndex, szBuf, &dwValueLen, NULL, NULL, pszMappedValueData, &dwDataLen); if (lRetVal != ERROR_SUCCESS) { if (lRetVal != ERROR_NO_MORE_ITEMS) bRet = FALSE; break; }
// get the separator char first and then point to RootKey, SubKey and ValueName in pszMappedValueData
pszPtr = pszMappedValueData; chSeparator = *pszPtr++; pszFlags = GetNextToken(&pszPtr, chSeparator); pszRootKey = GetNextToken(&pszPtr, chSeparator); pszSubKey = GetNextToken(&pszPtr, chSeparator); pszValueName = GetNextToken(&pszPtr, chSeparator);
dwMappedFlags = (pszFlags != NULL) ? (DWORD) My_atoi(pszFlags) : 0;
if (SUCCEEDED(RegSaveRestore( ctx.hWnd, ctx.lpszTitle, hkBckupKey, pszRootKey, pszSubKey, pszValueName, dwMappedFlags))) dwValIndex--; // RegSaveRestore would delete this value
else bRet = FALSE;
dwValueLen = sizeof(szBuf); dwDataLen = BIG_BUF_SIZE; }
RegCloseKey(hkSubKey); } else bRet = FALSE; } }
LocalFree(pszMappedValueData);
// delete all the empty subkeys
for (dwKeyIndex = 0; ; dwKeyIndex++) { lRetVal = RegEnumKey(hkBckupKey, dwKeyIndex, szSubKey, sizeof(szSubKey)); if (lRetVal != ERROR_SUCCESS) { if (lRetVal != ERROR_NO_MORE_ITEMS) bRet = FALSE; break; }
if (RegKeyEmpty(hkBckupKey, szSubKey) && RegDeleteKey(hkBckupKey, szSubKey) == ERROR_SUCCESS) dwKeyIndex--; }
return bRet ? S_OK : E_FAIL; }
VOID EnumerateSubKey() // Recursively enumerate value names and sub-keys and call Save/Restore on each of them
{ HKEY hkSubKey; DWORD dwIndex; static DWORD dwLen; static PCSTR pcszSubKeyPrefix = "_$Sub#"; static PCSTR pcszValueNamePrefix = "_$Val#"; static PCSTR pcszValueNamePrefix0 = "_$Val#0"; static CHAR szValueName[MAX_PATH], szBckupCRCValueName[MAX_PATH]; static PSTR pszPtr;
if (g_fRestore) { // check if there is an entry in the back-up branch for just the g_pszCRCSubKey itself
Convert2CRC(g_pcszRootKey, g_pszCRCSubKey, NULL, szBckupCRCValueName); if (ValueDataExists(g_hkBckupKey, szBckupCRCValueName)) // restore the no value names sub-key
g_bRet = RegSaveRestoreHelperWrapper(NULL, NULL) && g_bRet; else { // enumerate values using the aliases
for (dwIndex = 0; ; dwIndex++) { wsprintf(szValueName, "%s%lu", pcszValueNamePrefix, dwIndex); Convert2CRC(g_pcszRootKey, g_pszCRCSubKey, szValueName, szBckupCRCValueName); if (ValueDataExists(g_hkBckupKey, szBckupCRCValueName)) g_bRet = RegSaveRestoreHelperWrapper(NULL, szValueName) && g_bRet; else break; // no more value names
} }
// enumerate sub-keys using the aliases
for (dwIndex = 0; ; dwIndex++) { // check if there is any sub-key under g_pszCRCSubKey; if none, this is our terminating condition.
// NOTE: a sub-key under g_pszCRCSubKey exists if:
// (1) the sub-key itself exists OR
// (2) the sub-key contains atleast one value name.
// check if a sub-key by itself exists
pszPtr = g_pszCRCSubKey + lstrlen(g_pszCRCSubKey); wsprintf(pszPtr, "\\%s%lu", pcszSubKeyPrefix, dwIndex); Convert2CRC(g_pcszRootKey, g_pszCRCSubKey, NULL, szBckupCRCValueName); if (ValueDataExists(g_hkBckupKey, szBckupCRCValueName)) EnumerateSubKey(); else { // check if the sub-key has the first value name alias - "_$Val#0"
Convert2CRC(g_pcszRootKey, g_pszCRCSubKey, pcszValueNamePrefix0, szBckupCRCValueName); if (ValueDataExists(g_hkBckupKey, szBckupCRCValueName)) EnumerateSubKey(); else { GetParentDir(g_pszCRCSubKey); break; // no more sub-keys
} }
GetParentDir(g_pszCRCSubKey); } } else // backup the key
{ if (RegOpenKeyEx(g_hkRootKey, g_pszSubKey, 0, KEY_READ, &hkSubKey) == ERROR_SUCCESS) { dwLen = sizeof(szValueName); if (RegEnumValue(hkSubKey, 0, szValueName, &dwLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) { // no value names; just save the key itself
g_bRet = g_bRet && RegSaveRestoreHelperWrapper(NULL, NULL); } else { // enumerate the values
dwIndex = 0; dwLen = sizeof(szValueName); while (RegEnumValue(hkSubKey, dwIndex, szValueName, &dwLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { // szBckupCRCValueName is really szCRCValueName
wsprintf(szBckupCRCValueName, "%s%lu", pcszValueNamePrefix, dwIndex); g_bRet = g_bRet && RegSaveRestoreHelperWrapper(szValueName, szBckupCRCValueName);
dwIndex++; dwLen = sizeof(szValueName); } }
// enumerate sub-keys
dwIndex = 0; // append '\\' to g_pszSubKey and make pszPtr point to the char after this last '\\' so that
// when RegEnumKey puts a sub-key name at pszPtr, g_pszSubKey would have the complete sub-key path.
dwLen = lstrlen(g_pszSubKey); pszPtr = g_pszSubKey + dwLen; *pszPtr++ = '\\'; while (RegEnumKey(hkSubKey, dwIndex, pszPtr, 1024 - dwLen - 1) == ERROR_SUCCESS) { // prepare the sub-key alias
pszPtr = g_pszCRCSubKey + lstrlen(g_pszCRCSubKey); wsprintf(pszPtr, "\\%s%lu", pcszSubKeyPrefix, dwIndex);
EnumerateSubKey();
GetParentDir(g_pszSubKey); GetParentDir(g_pszCRCSubKey);
dwIndex++;
// append '\\' to g_pszSubKey and make pszPtr point to the char after this last '\\' so that
// when RegEnumKey puts a sub-key name at pszPtr, g_pszSubKey would have the complete sub-key path.
dwLen = lstrlen(g_pszSubKey); pszPtr = g_pszSubKey + dwLen; *pszPtr++ = '\\'; }
*--pszPtr = '\0'; // chop the last '\\'; no DBCS clash because we added it
RegCloseKey(hkSubKey); } } }
BOOL RegSaveRestoreHelperWrapper(PCSTR pcszValueName, PCSTR pcszCRCValueName) { CHAR szBckupCRCValueName[32];
// a unique back-up value name is obtained by concatenating pcszRootKey, pcszSubKey and pcszValueName
// and the concatenated value name is stored as a 16-byte CRC value (space optimization)
Convert2CRC(g_pcszRootKey, g_pszCRCSubKey, pcszCRCValueName, szBckupCRCValueName);
WriteToLog("\r\nValueName = %1,%2", g_pcszRootKey, g_pszSubKey); if (pcszValueName != NULL) WriteToLog(",%1", pcszValueName); WriteToLog("\r\nCRCValueName = %1\r\n", szBckupCRCValueName);
return (g_fRestore) ? RegRestoreHelper(g_hkBckupKey, g_hkRootKey, g_pszSubKey, pcszValueName, szBckupCRCValueName) : RegSaveHelper(g_hkBckupKey, g_hkRootKey, g_pszSubKey, pcszValueName, szBckupCRCValueName); }
BOOL RegSaveHelper(HKEY hkBckupKey, HKEY hkRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, PCSTR pcszCRCValueName) // If pcszValueName exists in the registry, back-up its value data; otherwise, remember how much of pcszSubKey
// is present in the registry. This info would help during restoration.
{ HKEY hkSubKey = NULL; PSTR pszBckupData = NULL, pszCOSubKey = NULL, pszPtr; DWORD dwValueDataLen, dwValueType, dwBckupDataLen; CHAR chSeparator; BOOL fSubKeyValid;
// don't backup the value data of pcszCRCValueName if it has been already backed-up
if (ValueDataExists(hkBckupKey, pcszCRCValueName)) return TRUE;
// make a copy of pcszSubKey
if ((pszCOSubKey = (PSTR) LocalAlloc(LPTR, lstrlen(pcszSubKey) + 1)) == NULL) { ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); goto RegSaveHelperErr; } lstrcpy(pszCOSubKey, pcszSubKey);
// loop through each branch in pszCOSubKey to find out how much of it is already present in the registry.
// start with the whole sub key first and then chop one branch at a time from the end
fSubKeyValid = TRUE; do { if (RegOpenKeyEx(hkRootKey, pszCOSubKey, 0, KEY_READ, &hkSubKey) == ERROR_SUCCESS) break; } while (fSubKeyValid = GetParentDir(pszCOSubKey));
// NOTE: fSubKeyValid == FALSE here means that no branch of pcszSubKey is present
if (fSubKeyValid && lstrcmpi(pcszSubKey, pszCOSubKey) == 0) // entire subkey is present in the registry
{ if (pcszValueName != NULL) { if (*pcszValueName || FRunningOnNT()) { // check if pcszValueName is present in the registry
if (RegQueryValueEx(hkSubKey, pcszValueName, NULL, &dwValueType, NULL, &dwValueDataLen) != ERROR_SUCCESS) pcszValueName = NULL; } else { LONG lRetVal; CHAR szDummyBuf[1];
// On Win95, for the default value name, its existence is checked as follows:
// - pass in a dummy buffer for the value data but pass in the size of the buffer as 0
// - the query would succeed if and only if there is no value data set
// - for all other cases, including the case where the value data is just the empty string,
// the query would fail and dwValueDataLen would contain the no. of bytes needed to
// fit in the value data
// On NT4.0, if no value data is set, the query returns ERROR_FILE_NOT_FOUND
// NOTE: To minimize risk, we don't follow this code path if running on NT4.0
dwValueDataLen = 0; lRetVal = RegQueryValueEx(hkSubKey, pcszValueName, NULL, &dwValueType, (LPBYTE) szDummyBuf, &dwValueDataLen); if (lRetVal == ERROR_SUCCESS || lRetVal == ERROR_FILE_NOT_FOUND) pcszValueName = NULL; } } } else pcszValueName = NULL;
WriteToLog("BckupSubKey = ");
// compute the length required for pszBckupData
// format of pszBckupData is (assume that the separator char is ','):
// ,[<szSubKey>,[<szValueName>,\0<dwValueType><dwValueDataLen><ValueData>]]
dwBckupDataLen = 1 + 1; // the separator char + '\0'
if (fSubKeyValid) { WriteToLog("%1", pszCOSubKey); dwBckupDataLen += lstrlen(pszCOSubKey) + 1;
if (pcszValueName != NULL) { WriteToLog(", BckupValueName = %1", pcszValueName); dwBckupDataLen += lstrlen(pcszValueName) + 1 + 2 * sizeof(DWORD) + dwValueDataLen; // 2 * sizeof(DWORD) == sizeof(dwValueType) + sizeof(dwValueDataLen)
} }
WriteToLog("\r\n");
// determine a valid separator char that is not one of the chars in SubKey and ValueName
if ((chSeparator = FindSeparator(fSubKeyValid ? pszCOSubKey : NULL, pcszValueName)) == '\0') { ErrorMsg(ctx.hWnd, IDS_NO_SEPARATOR_CHAR); goto RegSaveHelperErr; }
// allocate memory for pszBckupData
if ((pszBckupData = (PSTR) LocalAlloc(LPTR, dwBckupDataLen)) == NULL) { ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); goto RegSaveHelperErr; }
// start building pszBckupData
// format of pszBckupData is (assume that the separator char is ','):
// ,[<szSubKey>,[<szValueName>,\0<dwValueType><dwValueDataLen><ValueData>]]
pszPtr = pszBckupData; *pszPtr++ = chSeparator; *pszPtr = '\0'; if (fSubKeyValid) { lstrcpy(pszPtr, pszCOSubKey); pszPtr += lstrlen(pszPtr); *pszPtr++ = chSeparator; *pszPtr = '\0';
if (pcszValueName != NULL) { lstrcpy(pszPtr, pcszValueName); pszPtr += lstrlen(pszPtr); *pszPtr++ = chSeparator; *pszPtr++ = '\0'; // include the '\0' char
*((DWORD UNALIGNED *) pszPtr)++ = dwValueType; *((DWORD UNALIGNED *) pszPtr)++ = dwValueDataLen;
// NOTE: pszPtr points to the start position of value data in pszBckupData
RegQueryValueEx(hkSubKey, pcszValueName, NULL, &dwValueType, (PBYTE) pszPtr, &dwValueDataLen); } }
if (!SetValueData(hkBckupKey, pcszCRCValueName, (CONST BYTE *) pszBckupData, dwBckupDataLen)) { ErrorMsg1Param(ctx.hWnd, IDS_ERR_REGSETVALUE, pcszCRCValueName); goto RegSaveHelperErr; } WriteToLog("Value backed-up\r\n");
g_fAtleastOneRegSaved = TRUE;
if (hkSubKey != NULL) RegCloseKey(hkSubKey); LocalFree(pszCOSubKey); LocalFree(pszBckupData);
return TRUE;
RegSaveHelperErr: if (hkSubKey != NULL) RegCloseKey(hkSubKey); if (pszCOSubKey != NULL) LocalFree(pszCOSubKey); if (pszBckupData != NULL) LocalFree(pszBckupData);
return FALSE; }
BOOL RegRestoreHelper(HKEY hkBckupKey, HKEY hkRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, PCSTR pcszCRCValueName) // (1) If the value name in the backed-up value data is not NULL, it means that pcszValueName existed during
// back-up time; so, restore the original value data.
// (2) If the value name in the backed-up value data is NULL and pcszValueName is not NULL, it means that
// pcszValueName didn't exist during the back-up time; so, delete it.
// (3) If the backed-up sub key is shorter than pcszSubKey, then delete one branch at a time, if it is empty,
// from the end in pcszSubKey till pcszSubKey becomes identical to the backed-up sub key.
{ HKEY hkSubKey = NULL; PSTR pszBckupData = NULL, pszCOSubKey, pszPtr, pszBckupSubKey, pszBckupValueName; DWORD dwValueDataLen, dwValueType, dwBckupDataLen, dwDisposition; CHAR chSeparator;
if (!GetValueData(hkBckupKey, pcszCRCValueName, &pszBckupData, &dwBckupDataLen)) { ErrorMsg1Param(ctx.hWnd, IDS_ERR_REGQUERYVALUE, pcszCRCValueName); goto RegRestoreHelperErr; }
// format of pszBckupData is (assume that the separator char is ','):
// ,[<szSubKey>,[<szValueName>,\0<dwValueType><dwValueDataLen><ValueData>]]
pszPtr = pszBckupData; chSeparator = *pszPtr++; // initialize the separator char; since it is not part of
// Leading or Trailing DBCS Character Set, pszPtr++ is fine
pszBckupSubKey = GetNextToken(&pszPtr, chSeparator); pszBckupValueName = GetNextToken(&pszPtr, chSeparator); pszPtr++; // skip '\0'
if (g_fRemovBkData) WriteToLog("RemoveRegistryBackupData: ");
WriteToLog("BckupSubKey = "); if (pszBckupSubKey != NULL) { WriteToLog("%1", pszBckupSubKey); if (pcszValueName == NULL && lstrlen(pszBckupSubKey) > lstrlen(pcszSubKey)) { // means that pcszSubKey was backed-up thru EnumerateSubKey
pcszSubKey = pszBckupSubKey; } }
// check to see if we want to restore the reg keys, values or remove reg backup data
if (g_fRemovBkData) { if (pszBckupValueName != NULL) // restore the backed-up value data -- case (1)
{ WriteToLog(", BckupValueName = %1", pcszValueName); } DelValueData(hkBckupKey, pcszCRCValueName); // delete the back-up value name
WriteToLog(" <Done>\r\n"); goto Done; }
if (RegCreateKeyEx(hkRootKey, pcszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkSubKey, &dwDisposition) == ERROR_SUCCESS) { if (pszBckupValueName != NULL) // restore the backed-up value data -- case (1)
{ WriteToLog(", BckupValueName = %1", pcszValueName); dwValueType = *((DWORD UNALIGNED *) pszPtr)++; dwValueDataLen = *((DWORD UNALIGNED *) pszPtr)++; if (RegSetValueEx(hkSubKey, pszBckupValueName, 0, dwValueType, (CONST BYTE *) pszPtr, dwValueDataLen) != ERROR_SUCCESS) { ErrorMsg1Param(ctx.hWnd, IDS_ERR_REGSETVALUE, pszBckupValueName); goto RegRestoreHelperErr; } } else if (pcszValueName != NULL) { // means that the value name didn't exist while backing-up; so delete it -- case (2)
RegDeleteValue(hkSubKey, pcszValueName); }
RegCloseKey(hkSubKey);
DelValueData(hkBckupKey, pcszCRCValueName); // delete the back-up value name
WriteToLog("\r\nBackup Value deleted"); }
WriteToLog("\r\n");
dwBckupDataLen = 0; if (pszBckupValueName == NULL && (pszBckupSubKey == NULL || (DWORD) lstrlen(pcszSubKey) > (dwBckupDataLen = lstrlen(pszBckupSubKey)))) { // only a part of the subkey was present in the registry during back-up;
// delete the remaining branches if they are empty -- case (3)
// make a copy of pcszSubKey
if ((pszCOSubKey = (PSTR) LocalAlloc(LPTR, lstrlen(pcszSubKey) + 1)) != NULL) { lstrcpy(pszCOSubKey, pcszSubKey);
// start processing one branch at a time from the end in pszCOSubKey;
// if the branch is empty, delete it;
// stop processing as soon as pszCOSubKey becomes identical to pszBckupSubKey
do { // NOTE: Need to delete a key only if it's empty; otherwise, we would delete
// more than what we backed up. For example, if component A wanted to backup
// HKLM,Software\Microsoft\Windows\CurrentVersion\Uninstall\InternetExplorer
// and the machine didn't have the Uninstall key, we should not blow away the
// entire Uninstall key when we uninstall A as other components might have added
// their uninstall strings there. So delete a key only if it's empty.
if (RegKeyEmpty(hkRootKey, pszCOSubKey)) RegDeleteKey(hkRootKey, pszCOSubKey); else break; } while (GetParentDir(pszCOSubKey) && (DWORD) lstrlen(pszCOSubKey) > dwBckupDataLen);
LocalFree(pszCOSubKey); } }
Done: LocalFree(pszBckupData);
return TRUE;
RegRestoreHelperErr: if (hkSubKey != NULL) RegCloseKey(hkSubKey); if (pszBckupData != NULL) LocalFree(pszBckupData);
return FALSE; }
BOOL AddDelMapping(HKEY hkBckupKey, PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, DWORD dwFlags) { CHAR szCRCValueName[32], szBuf[32]; DWORD dwIndex; BOOL bFound = FALSE; HKEY hkSubKey = NULL;
Convert2CRC(pcszRootKey, pcszSubKey, pcszValueName, szCRCValueName);
// enumerate all the sub-keys under hkBckupKey
for (dwIndex = 0; !bFound && RegEnumKey(hkBckupKey, dwIndex, szBuf, sizeof(szBuf)) == ERROR_SUCCESS; dwIndex++) { PSTR pszPtr;
// check if the keyname is of the form *.map
if ((pszPtr = ANSIStrChr(szBuf, '.')) != NULL && lstrcmpi(pszPtr, ".map") == 0) { if (RegOpenKeyEx(hkBckupKey, szBuf, 0, KEY_READ | KEY_WRITE, &hkSubKey) == ERROR_SUCCESS) { if (RegQueryValueEx(hkSubKey, szCRCValueName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) bFound = TRUE; else { RegCloseKey(hkSubKey); hkSubKey = NULL; } } } }
if (g_fRestore) { if (bFound) RegDeleteValue(hkSubKey, szCRCValueName); } else { if (!bFound) { DWORD dwMapKeyIndex = 0;
// add the quadruplet, i.e., ",Flags,RootKey,SubKey,ValueName" to hkBckupKey\*.map
wsprintf(szBuf, "%lu.map", dwMapKeyIndex); if (RegCreateKeyEx(hkBckupKey, szBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkSubKey, NULL) == ERROR_SUCCESS) { PSTR pszPtr; CHAR chSeparator;
// IMPORTANT: the global buffer g_pszCRCTempBuf is used in Convert2CRC;
// so be very careful if you want to call Convert2CRC after g_pszCRCTempBuf has been initialized here.
pszPtr = g_pszCRCTempBuf;
// determine a valid separator char that is not one of the chars in SubKey and ValueName
if ((chSeparator = FindSeparator(pcszSubKey, pcszValueName)) == '\0') { ErrorMsg(ctx.hWnd, IDS_NO_SEPARATOR_CHAR); } else { // reset the IE4_BACKNEW bit and set the IE4_RESTORE bit
dwFlags &= ~IE4_BACKNEW; dwFlags |= IE4_RESTORE; wsprintf(szBuf, "%lu", dwFlags);
// format of mapping data is (say ',' is chSeparator): ,<Flags>,<RootKey>,<SubKey>,[<ValueName>,]
{ *pszPtr++ = chSeparator;
lstrcpy(pszPtr, szBuf); pszPtr += lstrlen(pszPtr); *pszPtr++ = chSeparator;
lstrcpy(pszPtr, pcszRootKey); pszPtr += lstrlen(pszPtr); *pszPtr++ = chSeparator;
lstrcpy(pszPtr, pcszSubKey); pszPtr += lstrlen(pszPtr); *pszPtr++ = chSeparator;
if (pcszValueName != NULL) { lstrcpy(pszPtr, pcszValueName); pszPtr += lstrlen(pszPtr); *pszPtr++ = chSeparator; }
*pszPtr = '\0'; }
if (RegSetValueEx(hkSubKey, szCRCValueName, 0, REG_SZ, (CONST BYTE *) g_pszCRCTempBuf, lstrlen(g_pszCRCTempBuf) + 1) != ERROR_SUCCESS) { do { // hkBckupKey\<dwIndex>.map key may have reached the 64K limit; create another sub-key
RegCloseKey(hkSubKey); hkSubKey = NULL;
wsprintf(szBuf, "%lu.map", ++dwMapKeyIndex); if (RegCreateKeyEx(hkBckupKey, szBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkSubKey, NULL) == ERROR_SUCCESS) { bFound = RegSetValueEx(hkSubKey, szCRCValueName, 0, REG_SZ, (CONST BYTE *) g_pszCRCTempBuf, lstrlen(g_pszCRCTempBuf) + 1) == ERROR_SUCCESS; } } while (!bFound && dwMapKeyIndex < 64); } else bFound = TRUE; } } } }
if (hkSubKey != NULL) RegCloseKey(hkSubKey);
return bFound; }
BOOL MappingExists(HKEY hkBckupKey, PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName) { CHAR szCRCValueName[32], szBuf[32]; DWORD dwIndex; BOOL bFound = FALSE;
Convert2CRC(pcszRootKey, pcszSubKey, pcszValueName, szCRCValueName);
// enumerate all the sub-keys under hkBckupKey
for (dwIndex = 0; !bFound && RegEnumKey(hkBckupKey, dwIndex, szBuf, sizeof(szBuf)) == ERROR_SUCCESS; dwIndex++) { PSTR pszPtr;
// check if the keyname is of the form *.map
if ((pszPtr = ANSIStrChr(szBuf, '.')) != NULL && lstrcmpi(pszPtr, ".map") == 0) { HKEY hkSubKey;
if (RegOpenKeyEx(hkBckupKey, szBuf, 0, KEY_READ | KEY_WRITE, &hkSubKey) == ERROR_SUCCESS) { if (RegQueryValueEx(hkSubKey, szCRCValueName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) bFound = TRUE;
RegCloseKey(hkSubKey); } } }
return bFound; }
BOOL SetValueData(HKEY hkBckupKey, PCSTR pcszValueName, CONST BYTE *pcbValueData, DWORD dwValueDataLen) // Set the (pcszValueName, pcbValueData) pair in hkBckupKey
{ BOOL fDone = FALSE; HKEY hkSubKey; DWORD dwDisposition, dwSubKey; CHAR szSubKey[16];
// since a key has a size limit of 64K, automatically generate a new sub-key if the other ones are full
for (dwSubKey = 0; !fDone && dwSubKey < 64; dwSubKey++) { wsprintf(szSubKey, "%lu", dwSubKey); // sub-keys are named 0, 1, 2, etc.
if (RegCreateKeyEx(hkBckupKey, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkSubKey, &dwDisposition) == ERROR_SUCCESS) { if (RegSetValueEx(hkSubKey, pcszValueName, 0, REG_BINARY, pcbValueData, dwValueDataLen) == ERROR_SUCCESS) fDone = TRUE;
RegCloseKey(hkSubKey); } }
return fDone; }
BOOL ValueDataExists(HKEY hkBckupKey, PCSTR pcszValueName) // Return TRUE if pcszValueName exists in hkBckupKey; otherwise, return FALSE
{ return ValueDataHelper(hkBckupKey, pcszValueName, NULL, NULL, VDH_EXISTENCE_ONLY); }
BOOL GetValueData(HKEY hkBckupKey, PCSTR pcszValueName, PBYTE *ppbValueData, PDWORD pdwValueDataLen) // Allocate a buffer of required size and return the value data of pcszValueName in hkBckupKey
{ return ValueDataHelper(hkBckupKey, pcszValueName, ppbValueData, pdwValueDataLen, VDH_GET_VALUE); }
BOOL DelValueData(HKEY hkBckupKey, PCSTR pcszValueName) // Delete pcszValueName from hkBckupKey
{ return ValueDataHelper(hkBckupKey, pcszValueName, NULL, NULL, VDH_DEL_VALUE); }
BOOL ValueDataHelper(HKEY hkBckupKey, PCSTR pcszValueName, PBYTE *ppbValueData, PDWORD pdwValueDataLen, DWORD dwFlags) { BOOL fDone = FALSE; HKEY hkSubKey; CHAR szSubKey[16]; DWORD dwIndex, dwDataLen;
if (dwFlags == VDH_GET_VALUE && ppbValueData == NULL) return FALSE;
// search for pcszValueName in all the sub-keys
for (dwIndex = 0; !fDone && RegEnumKey(hkBckupKey, dwIndex, szSubKey, sizeof(szSubKey)) == ERROR_SUCCESS; dwIndex++) { if ( ANSIStrChr(szSubKey, '.') == NULL) // check only in non *.map keys
{ if (RegOpenKeyEx(hkBckupKey, szSubKey, 0, KEY_READ | KEY_WRITE, &hkSubKey) == ERROR_SUCCESS) { if (RegQueryValueEx(hkSubKey, pcszValueName, NULL, NULL, NULL, &dwDataLen) == ERROR_SUCCESS) { switch (dwFlags) { case VDH_DEL_VALUE: RegDeleteValue(hkSubKey, pcszValueName); break;
case VDH_GET_VALUE: if ((*ppbValueData = (PBYTE) LocalAlloc(LPTR, dwDataLen)) == NULL) { RegCloseKey(hkSubKey); ErrorMsg(ctx.hWnd, IDS_ERR_NO_MEMORY); return FALSE; }
*pdwValueDataLen = dwDataLen; RegQueryValueEx(hkSubKey, pcszValueName, NULL, NULL, *ppbValueData, &dwDataLen);
break;
case VDH_EXISTENCE_ONLY: break; }
fDone = TRUE; }
RegCloseKey(hkSubKey); } } }
return fDone; }
VOID Convert2CRC(PCSTR pcszRootKey, PCSTR pcszSubKey, PCSTR pcszValueName, PSTR pszCRCValueName) // Concatenate pcszRootKey, pcszSubKey and pcszValueName and convert the concatenated value name
// to a 16-byte CRC value.
{ PSTR pszPtr = g_pszCRCTempBuf; ULONG ulCRC = CRC32_INITIAL_VALUE; DWORD dwLen;
// concatenate pcszRootKey, pcszSubKey, pcszValueName
lstrcpy(pszPtr, pcszRootKey); lstrcat(pszPtr, pcszSubKey); if (pcszValueName != NULL) lstrcat(pszPtr, pcszValueName);
// call CRC32Compute on each half of szBuf and store the 2-DWORD result in ASCII form (16 bytes)
for (dwLen = lstrlen(pszPtr) / 2; dwLen; dwLen = lstrlen(pszPtr)) { ulCRC = CRC32Compute(pszPtr, dwLen, ulCRC);
wsprintf(pszCRCValueName, "%08x", ulCRC); pszCRCValueName += 8;
pszPtr += dwLen; // point to the beginning of the other half
} }
static ROOTKEY rkRoots[] = { {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE}, {"HKLM", HKEY_LOCAL_MACHINE}, {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT}, {"HKCR", HKEY_CLASSES_ROOT}, {"", HKEY_CLASSES_ROOT}, {"HKEY_CURRENT_USER", HKEY_CURRENT_USER}, {"HKCU", HKEY_CURRENT_USER}, {"HKEY_USERS", HKEY_USERS}, {"HKU", HKEY_USERS} };
BOOL MapRootRegStr2Key(PCSTR pcszRootKey, HKEY *phkRootKey) { INT iIndex;
for (iIndex = 0; iIndex < ARRAYSIZE(rkRoots); iIndex++) if (lstrcmpi(rkRoots[iIndex].pcszRootKey, pcszRootKey) == 0) { *phkRootKey = rkRoots[iIndex].hkRootKey; return TRUE; }
return FALSE; }
CHAR FindSeparator(PCSTR pcszSubKey, PCSTR pcszValueName) // Go through pcszSeparatorList and return the first char that doesn't appear in any of the parameters;
// if such a char is not found, return '\0'
{ PCSTR pcszSeparatorList = ",$'?%;:"; // since the separator chars are 'pure' ASCII chars, i.e.,
// they are not part of Leading or Trailing DBCS Character Set,
// IsSeparator(), which assumes a 'pure' ASCII ch to look for,
// can be used
CHAR ch;
while (ch = *pcszSeparatorList++) if (!IsSeparator(ch, pcszSubKey) && !IsSeparator(ch, pcszValueName)) break;
return ch; }
BOOL RegKeyEmpty(HKEY hkRootKey, PCSTR pcszSubKey) // Return TRUE if pcszSubKey is emtpy, i.e., no sub keys and value names; otherwise, return FALSE
{ HKEY hkKey; BOOL bRet = FALSE; CHAR szBuf[1]; DWORD dwBufLen = sizeof(szBuf);
if (RegOpenKeyEx(hkRootKey, pcszSubKey, 0, KEY_READ, &hkKey) == ERROR_SUCCESS) { if (RegEnumKey(hkKey, 0, szBuf, dwBufLen) == ERROR_NO_MORE_ITEMS && RegEnumValue(hkKey, 0, szBuf, &dwBufLen, NULL, NULL, NULL, NULL) == ERROR_NO_MORE_ITEMS) bRet = TRUE;
RegCloseKey(hkKey); }
return bRet; }
PSTR GetNextToken(PSTR *ppszData, CHAR chDeLim) // If the next token in *ppszData is delimited by the chDeLim char, replace chDeLim
// in *ppszData by '\0', set *ppszData to point to the char after '\0' and return
// ptr to the beginning of the token; otherwise, return NULL
{ PSTR pszPos;
if (ppszData == NULL || *ppszData == NULL || **ppszData == '\0') return NULL;
if ((pszPos = ANSIStrChr(*ppszData, chDeLim)) != NULL) { PSTR pszT = *ppszData;
*pszPos = '\0'; // replace chDeLim with '\0'
*ppszData = pszPos + 1; pszPos = pszT; } else // chDeLim not found; set *ppszData to point to
// to the end of szData; the next invocation
// of this function would return NULL
{ pszPos = *ppszData; *ppszData = pszPos + lstrlen(pszPos); }
return pszPos; }
BOOL FRunningOnNT() { static BOOL fIsNT4 = 2;
if (fIsNT4 == 2) { OSVERSIONINFO osviVerInfo;
osviVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osviVerInfo);
fIsNT4 = osviVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT; }
return fIsNT4; }
VOID StartLogging(PCSTR pcszLogFileSecName) { CHAR szBuf[MAX_PATH], szLogFileName[MAX_PATH]; HKEY hkSubKey;
szLogFileName[0] = '\0';
// check if logging is enabled
GetProfileString("RegBackup", pcszLogFileSecName, "", szLogFileName, sizeof(szLogFileName)); if (*szLogFileName == '\0') // check in the registry
{ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_SAVERESTORE, 0, KEY_READ, &hkSubKey) == ERROR_SUCCESS) { DWORD dwDataLen = sizeof(szLogFileName);
if (RegQueryValueEx(hkSubKey, pcszLogFileSecName, NULL, NULL, szLogFileName, &dwDataLen) != ERROR_SUCCESS) *szLogFileName = '\0';
RegCloseKey(hkSubKey); } }
if (*szLogFileName) { if (szLogFileName[1] != ':') // crude way of determining if fully qualified path is specified or not
{ GetWindowsDirectory(szBuf, sizeof(szBuf)); // default to windows dir
AddPath(szBuf, szLogFileName); } else lstrcpy(szBuf, szLogFileName);
if ((g_hLogFile = CreateFile(szBuf, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) SetFilePointer(g_hLogFile, 0, NULL, FILE_END); // append logging info to the file
} }
VOID WriteToLog(PCSTR pcszFormatString, ...) { va_list vaArgs; PSTR pszFullErrMsg = NULL; DWORD dwBytesWritten;
if (g_hLogFile != INVALID_HANDLE_VALUE) { va_start(vaArgs, pcszFormatString);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_STRING, (LPCVOID) pcszFormatString, 0, 0, (PSTR) &pszFullErrMsg, 0, &vaArgs);
if (pszFullErrMsg != NULL) { WriteFile(g_hLogFile, pszFullErrMsg, lstrlen(pszFullErrMsg), &dwBytesWritten, NULL); LocalFree(pszFullErrMsg); } } }
VOID StopLogging() { if (g_hLogFile != INVALID_HANDLE_VALUE) { CloseHandle(g_hLogFile); g_hLogFile = INVALID_HANDLE_VALUE; } }
|