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

1237 lines
43 KiB

#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;
}
}