/***********************************************************************/ /* Copyright (C) 2002 Definitive Solutions, Inc. All Rights Reserved. */ /* THIS COMPUTER PROGRAM IS PROPRIETARY AND CONFIDENTIAL TO DEFINITIVE */ /* SOLUTIONS, INC. AND ITS LICENSORS AND CONTAINS TRADE SECRETS OF */ /* DEFINITIVE SOLUTIONS, INC. THAT ARE PROVIDED PURSUANT TO A WRITTEN */ /* AGREEMENT CONTAINING RESTRICTIONS ON USE AND DISCLOSURE. ANY USE, */ /* REPRODUCTION, OR TRANSFER EXCEPT AS PROVIDED IN SUCH AGREEMENT */ /* IS STRICTLY PROHIBITED. */ /***********************************************************************/ #include "stdafx.h" #include "MyRegistry.h" #include "Generic.h" #include "MyApp.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // MyRegistry // Default constructor. MyRegistry::MyRegistry() : m_hRootKey(NULL) , m_hSubKey(NULL) { // Be sure that the random number generator has been seeded. (If the // calling app has already done this, this does no harm.) srand((unsigned) time(NULL)); } // Constructor. MyRegistry::MyRegistry(HKEY hRootKey, const CString& sSubKey, bool bCreate /* = true */ ) : m_hRootKey(hRootKey) , m_sSubKey(sSubKey) , m_hSubKey(NULL) { _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); HRESULT hr(S_OK); // Special handling for this case. bool bReturn(Open(m_hRootKey, m_sSubKey, bCreate)); _ASSERTE(bReturn && "This will always fail the first time a 'MyDialog'-derived class is popped - in that case, this assert (and the next one) can be safely ignored"); EC_B(bReturn); EC_FAIL(s, "Open(m_hRootKey, m_sSubKey, bCreate)"); EC_FAIL(x, m_hRootKey); EC_FAIL(s, m_sSubKey); } // Destructor. /* virtual */ MyRegistry::~MyRegistry() { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); // Could have failed to find a subkey ---> _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); HRESULT hr(S_OK); EC_B(Close()); } // Open the Registry key. We virtually always use the constructor instead of // this function. However, you would use thie function if you don't want to // log or assert when the key is not found - i.e., for stealthy things. bool MyRegistry::Open(HKEY hRootKey, const CString& sSubKey, bool bCreate /* = true */ ) { VALIDATE; _ASSERTE(hRootKey && "hRootKey is NULL"); _ASSERTE(! sSubKey.IsEmpty() && "sSubKey is empty"); _ASSERTE('\\' != sSubKey.GetAt(0) && "Should not have a leading slash"); // No matter which choice the user made, clear the Registry. bool bReturn(false); LONG lnReturn(ERROR_SUCCESS); DWORD dwDisp(0L); // These values wouldn't get set if this method is being called after the // default constructor. m_hRootKey = hRootKey; m_sSubKey = sSubKey; // If it's not there, create it. if (bCreate) { bReturn = (ERROR_SUCCESS == (lnReturn = ::RegCreateKeyEx( hRootKey, // Root sSubKey, // "Software\\Your Company Name\\Your Product\\Find", for example. NULL, // Reserved "MyClass", // Class 0L, // Special Options Flag KEY_ALL_ACCESS, // Desired security access NULL, // Address of key security structure &m_hSubKey, // Returned handle to opened/created key &dwDisp))); // Returned disposition value - we don't care what it is. if (! bReturn) { m_hSubKey = NULL; DOMYLOGST(API_FAILURE), "::RegCreateKeyEx: " + sSubKey, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { // Don't create it if it's not already there. bReturn = (ERROR_SUCCESS == (lnReturn = ::RegOpenKeyEx( hRootKey, // Root sSubKey, // "Software\\Your Company Name\\Your Product\\Find", for example. NULL, // Reserved KEY_ALL_ACCESS, // Security access mask &m_hSubKey))); // Returned handle to opened key // Not finding it is okay if we aren't creating. if (! bReturn && ERROR_FILE_NOT_FOUND != lnReturn) { m_hSubKey = NULL; DOMYLOGST(API_FAILURE), "::RegOpenKeyEx: " + sSubKey, lnReturn, Generic::GetSysErrorString(lnReturn)); } } // Opening the Registry is not something you'd want to do in a tight loop, // so this is a good way to catch that error. if (bReturn) { //DOMYLOGT ("Opened Registry on hive <%x>,\n", hRootKey); //DOMYLOGT (" key <%s>.\n", sSubKey); } return bReturn; } // Close the Registry key. We usually let the dtor handle this. bool MyRegistry::Close() { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); // Could have failed to find a subkey ---> __ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); bool bReturn(true); HRESULT hr(S_OK); // If not found, nothing to close. if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); EC_B(bReturn = (ERROR_SUCCESS == (lnReturn = ::RegCloseKey(m_hSubKey)))); EC_FAIL(s, m_sSubKey); EC_FAIL(x, lnReturn); } m_hRootKey = NULL; m_hSubKey = NULL; m_sSubKey.Empty(); return bReturn; } // Write a DWORD to the Registry. bool MyRegistry::SaveValueString(UINT uiValueID, const CString& sData) { VALIDATE; _ASSERTE(0U < uiValueID); CString sValueID(Generic::LoadString(uiValueID)); return SaveValueString(sValueID, sData); } // Write a string to the registry. bool MyRegistry::SaveValueString(const CString& sValueName, const CString& sData) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); bool bReturn(false); if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); // Build the buffer. int nDataLen(sData.GetLength() + 1); const BYTE* pData = reinterpret_cast (static_cast (sData)); _ASSERTE(pData && "One of the casts failed"); // Write out the value. bReturn = (ERROR_SUCCESS == (lnReturn = ::RegSetValueEx( m_hSubKey, // Handle of key to set value for sValueName, // Address of value to set NULL, // Reserved REG_SZ, // Type pData, // Value data nDataLen))); // Size of value data // Done. Did we succeed? if (! bReturn) { DOMYLOGST(API_FAILURE), "::RegSetValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Write a DWORD to the Registry. bool MyRegistry::SaveValueDWord(UINT uiValueID, DWORD dwData) { VALIDATE; _ASSERTE(0U < uiValueID); CString sValue(Generic::LoadString(uiValueID)); return SaveValueDWord(sValue, dwData); } // Write a bool to the registry. bool MyRegistry::SaveValueBool(UINT uiValueID, bool bValue) { VALIDATE; return SaveValueDWord(uiValueID, bValue); } // Write a bool to the registry. bool MyRegistry::SaveValueBool(const CString& sValueName, bool bData) { VALIDATE; return SaveValueDWord(sValueName, bData); } // Write an int to the registry. bool MyRegistry::SaveValueInt(const CString& sValueName, int nData) { VALIDATE; return SaveValueDWord(sValueName, nData); } // Write an int to the registry. bool MyRegistry::SaveValueInt(UINT uiValueID, int nData) { VALIDATE; return SaveValueDWord(uiValueID, nData); } // Write a UINT to the registry. bool MyRegistry::SaveValueUINT(const CString& sValueName, UINT uiValue) { VALIDATE; return SaveValueDWord(sValueName, uiValue); } // Write a UINT to the registry. bool MyRegistry::SaveValueUINT(UINT uiValueID, UINT uiValue) { VALIDATE; return SaveValueDWord(uiValueID, uiValue); } // Write a DWORD to the registry. bool MyRegistry::SaveValueDWord(const CString& sValueName, DWORD dwData) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); bool bReturn(false); if (m_hSubKey) { // Build the buffer. LONG lnReturn(ERROR_SUCCESS); const BYTE* pData = reinterpret_cast (&dwData); // Write out the value. bReturn = (ERROR_SUCCESS == (lnReturn = ::RegSetValueEx( m_hSubKey, // Handle of key to set value for sValueName, // Address of value to set NULL, // Reserved REG_DWORD, // Type pData, // Value data sizeof(DWORD)))); // Size of value data // Done. Did we succeed? if (! bReturn) { DOMYLOGST(API_FAILURE), "::RegSetValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Write a binary thing (like a structure) to the Registry. bool MyRegistry::SaveValueBinary(const CString& sValueName, BYTE* pValue, DWORD dwValueLenInBytes) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); _ASSERTE(pValue && "NULL pValue pointer sent"); _ASSERTE(0L <= dwValueLenInBytes && "Length may be zero"); bool bReturn(false); if (m_hSubKey) { // Build the buffer. LONG lnReturn(ERROR_SUCCESS); // Write out the value. bReturn = (ERROR_SUCCESS == (lnReturn = ::RegSetValueEx( m_hSubKey, // Handle of key to set value for sValueName, // Address of value to set NULL, // Reserved REG_BINARY, // Type pValue, // Value data dwValueLenInBytes))); // Size of value data // Done. Did we succeed? if (! bReturn) { DOMYLOGST(API_FAILURE), "::RegSetValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Save this bit to the sent value. bool MyRegistry::SaveValueBit(const CString& sValueName, BYTE bit, bool bData) { VALIDATE; _ASSERTE(! sValueName.IsEmpty()); DWORD dwOldValue(0); DWORD dwNewValue(0); DWORD dwValueLen(4); ReadValueBinary(sValueName, (BYTE*) &dwOldValue, dwValueLen); // First do the bits above the target bit. dwNewValue = dwOldValue >> (bit + 1); dwNewValue = dwNewValue << 1; // Now add the target bit. dwNewValue += bData; // Now do the bits below the target bit. dwNewValue = dwNewValue << bit; dwOldValue = dwOldValue << (32 - bit); dwOldValue = dwOldValue >> (32 - bit); dwNewValue += dwOldValue; return SaveValueBinary(sValueName, (BYTE*) &dwNewValue, /* dwValueLenInBytes */ 4); } // bool MyRegistry::ReadValueString(UINT uiValueID, CString& sData) const { VALIDATE; _ASSERTE(0U < uiValueID); CString sValueID(Generic::LoadString(uiValueID)); return ReadValueString(sValueID, sData); } // Read a string from the Registry. Returns true to mean the string was there. bool MyRegistry::ReadValueString(const CString& sValueName, CString& sData) const { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); bool bReturn(false); if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); DWORD dwType(REG_NONE); DWORD dwDataSize(0); // First we have to get the size of the string so we know how much // space to allocate. if (bReturn = (ERROR_SUCCESS == (lnReturn = ::RegQueryValueEx( m_hSubKey, // Handle of key to query sValueName, // Name of value to query NULL, // Reserved &dwType, // Type NULL, // Data buffer &dwDataSize)))) { // Size of value data _ASSERTE(REG_SZ == dwType && "Did you use ReadValueString() to read in a non-string?"); BYTE* pData = reinterpret_cast (sData.GetBuffer(dwDataSize)); // Now actually get the string. bReturn = (ERROR_SUCCESS == (lnReturn = ::RegQueryValueEx( m_hSubKey, // Handle of key to query sValueName, // Name of value to query NULL, // Reserved &dwType, // Type pData, // Data buffer &dwDataSize))); // Size of value data sData.ReleaseBuffer(); // Not finding the value is not a serious error. if (! bReturn && ERROR_FILE_NOT_FOUND != lnReturn) { DOMYLOGST(API_FAILURE), "::RegQueryValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else if (ERROR_FILE_NOT_FOUND != lnReturn) { // Not finding the value is not a serious error. DOMYLOGST(API_FAILURE), "::RegQueryValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Read an array of NULL-terinated strings that are terminated with a double- // NULL from the Registry. Returns true to mean the multi-string was there. // The call should not allocate space for pszData, but it *is* responsible for // delete [ ] -ing it. // // ?? THIS METHOD HAS NOT BEEN TESTED! bool MyRegistry::ReadValueMultiString(const CString& sValueName, char * pszData) const { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); _ASSERTE(NULL == pszData && "Should be sent as NULL - caller must call delete [], too"); bool bReturn(false); if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); DWORD dwType(REG_NONE); DWORD dwDataSize(0); // First we have to get the size of the string so we know how much // space to allocate. if (bReturn = (ERROR_SUCCESS == (lnReturn = ::RegQueryValueEx( m_hSubKey, // Handle of key to query sValueName, // Name of value to query NULL, // Reserved &dwType, // Type NULL, // Data buffer &dwDataSize)))) { // Size of value data _ASSERTE(REG_SZ == dwType && "Did you use ReadValueString() to read in a non-string?"); PBYTE pbyData= new BYTE[dwDataSize]; // Now actually get the string. bReturn = (ERROR_SUCCESS == (lnReturn = ::RegQueryValueEx( m_hSubKey, // Handle of key to query sValueName, // Name of value to query NULL, // Reserved &dwType, // Type pbyData, // Data buffer &dwDataSize))); // Size of value data // Not finding the value is not a serious error. if (! bReturn && ERROR_FILE_NOT_FOUND != lnReturn) { DOMYLOGST(API_FAILURE), "::RegQueryValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } else { // Success! pszData = reinterpret_cast (pbyData); } } else if (ERROR_FILE_NOT_FOUND != lnReturn) { // Not finding the value is not a serious error. DOMYLOGST(API_FAILURE), "::RegQueryValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Read a bool from the Registry. A bool is a 1-byte object! bool MyRegistry::ReadValueBool(UINT uiValueID, bool& bData) const { VALIDATE; DWORD dwData(bData); bool bReturn(ReadValueDWord(uiValueID, dwData)); bData = dwData; return bReturn; } // Read a bool from the Registry. A bool is a 1-byte object! bool MyRegistry::ReadValueBool(const CString& sValueName, bool& bData) const { VALIDATE; DWORD dwData(bData); bool bReturn(ReadValueDWord(sValueName, dwData)); bData = dwData; return bReturn; } // Read an int from the Registry. bool MyRegistry::ReadValueInt(UINT uiValueID, int& nData) const { VALIDATE; return ReadValueDWord(uiValueID, reinterpret_cast (nData)); } // Read an int from the Registry. bool MyRegistry::ReadValueInt(const CString& sValueName, int& nData) const { VALIDATE; return ReadValueDWord(sValueName, reinterpret_cast (nData)); } // Read a DWORD from the Registry. bool MyRegistry::ReadValueDWord(UINT uiValueID, DWORD& dwData) const { VALIDATE; _ASSERTE(0U < uiValueID); CString sValue(Generic::LoadString(uiValueID)); return ReadValueDWord(sValue, dwData); } // Read a DWORD from the Registry. bool MyRegistry::ReadValueDWord(const CString& sValueName, DWORD& dwData) const { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); bool bReturn(false); if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); DWORD dwType(0L); DWORD dwDataSize(sizeof(DWORD)); BYTE* pData = reinterpret_cast (&dwData); bReturn = (ERROR_SUCCESS == (lnReturn = ::RegQueryValueEx( m_hSubKey, // Handle of key to query sValueName, // Name of value to query NULL, // Reserved &dwType, // Type pData, // Data buffer &dwDataSize))); // Size of value data // Not finding the value is not a serious error. if (! bReturn && ERROR_FILE_NOT_FOUND != lnReturn) { DOMYLOGST(API_FAILURE), "::RegQueryValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Read a binary structure from the Registry. bool MyRegistry::ReadValueBinary(const CString& sValueName, BYTE* pValue, DWORD& dwValueLen) const { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); // Send NULL to find put size in dwValueLen. --> _ASSERTE(pValue && "NULL pValue pointer sent"); _ASSERTE(NULL == pValue || 0L < dwValueLen && "Length must be gt zero in bytes (unless you're trying to find the size to use) - it is used in input and output!"); bool bReturn(false); if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); DWORD dwType(0L); bReturn = (ERROR_SUCCESS == (lnReturn = ::RegQueryValueEx( m_hSubKey, // Handle of key to query sValueName, // Name of value to query NULL, // Reserved &dwType, // Type pValue, // Data buffer &dwValueLen))); // Size of value data // Not finding the value is not a serious error. if (! bReturn && ERROR_FILE_NOT_FOUND != lnReturn) { DOMYLOGST(API_FAILURE), "::RegQueryValueEx: " + sValueName, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValueName); } return bReturn; } // Read this bit from this DWORD in the Registry. bool MyRegistry::ReadValueBit(const CString& sValueName, BYTE bit, bool& bData) const { VALIDATE; _ASSERTE(! sValueName.IsEmpty()); bool bReturn(false); DWORD dwData(0); DWORD dwValueLenInBytes(4); bData = false; if (ReadValueBinary(sValueName, (BYTE*) &dwData, dwValueLenInBytes)) { dwData = dwData << (32 - bit - 1); dwData = dwData >> 31; bData = dwData; bReturn = true; } return bReturn; } // Read a UINT from the Registry. bool MyRegistry::ReadValueUINT(const CString& sValueName, UINT& uiValue) const { VALIDATE; return ReadValueDWord(sValueName, reinterpret_cast (uiValue)); } // Read a UINT from the Registry. bool MyRegistry::ReadValueUINT(UINT uiValueID, UINT& uiValue) const { VALIDATE; return ReadValueDWord(uiValueID, reinterpret_cast (uiValue)); } // Delete a value. bool MyRegistry::DeleteValue(const CString& sValue) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); _ASSERTE(! sValue.IsEmpty() && "sValue is empty"); HRESULT hr(S_OK); bool bReturn(false); if (m_hSubKey) { LONG lnReturn(ERROR_SUCCESS); EC_B(bReturn = (ERROR_SUCCESS == (lnReturn = ::RegDeleteValue(m_hSubKey, sValue)))); EC_FAIL(s, sValue); EC_FAIL(d, lnReturn); } else { DOMYLOGST(REGISTRY_NOT_OPEN), sValue); } return bReturn; } // Delete a Key, and its whole tree. bool MyRegistry::DeleteKey(const CString& sSubKey) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); _ASSERTE(! sSubKey.IsEmpty() && "sSubKey is empty"); _ASSERTE(-1 == sSubKey.Find("\\") && "Must not send a Registry path, just a key - use Generic::FileFromPath()"); return (ERROR_SUCCESS == RecursiveDelete(m_hSubKey, sSubKey)); } // NOTE: This is private implementation! What you really want to do is call // "Delete Key()" above! // // Adapted from MSJ 9/96 "Under the Hood". Recursive routine to delete a key // and all of its subkeys. We can`t just use RegDeleteKey, since Windows NT // doesn`t do recursive deletions. (For once, Windows 95 did something better // than Windows NT!) Based on PSS article Q142491. LONG MyRegistry::RecursiveDelete(HKEY hKeyRecurse, const CString& sSubKey) { _ASSERTE(hKeyRecurse && "NULL hKeyRecurse passed in"); _ASSERTE(! sSubKey.IsEmpty() && "Empty subkey passed in"); _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); HKEY hSubKey(NULL); LONG lnReturn(::RegOpenKeyEx(hKeyRecurse, sSubKey, /* dwReserved */ 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &hSubKey)); // Might not be there, and that's... okay. _ASSERTE(ERROR_SUCCESS == lnReturn && "Registry failure"); while (ERROR_SUCCESS == lnReturn) { TCHAR szSubKey[MAX_PATH + 1] = { 0 }; DWORD cbSubkeySize(sizeof(szSubKey)); lnReturn = ::RegEnumKeyEx(hSubKey, /* dwIndex */ 0, szSubKey, &cbSubkeySize, 0, 0, 0, 0); if (ERROR_NO_MORE_ITEMS == lnReturn) { break; } else if (ERROR_SUCCESS != lnReturn) { DOMYLOGST(API_FAILURE), "::RegEnumKeyEx: " + sSubKey, lnReturn, Generic::GetSysErrorString(lnReturn)); return lnReturn; } lnReturn = RecursiveDelete(hSubKey, szSubKey); if (ERROR_SUCCESS != lnReturn) { DOMYLOGST(API_FAILURE), "MyRegistry::RecursiveDelete: " + sSubKey, lnReturn, Generic::GetSysErrorString(lnReturn)); break; } } // Close (but may not have been there). if (hSubKey) { if (ERROR_SUCCESS == (lnReturn = ::RegCloseKey(hSubKey))) { lnReturn = ::RegDeleteKey(hKeyRecurse, sSubKey); if (ERROR_SUCCESS != lnReturn) { DOMYLOGST(API_FAILURE), "::RegDeleteKey: " + sSubKey, lnReturn, Generic::GetSysErrorString(lnReturn)); } } else { DOMYLOGST(API_FAILURE), "::RegCloseKey: " + sSubKey, lnReturn, Generic::GetSysErrorString(lnReturn)); } } return lnReturn; } // Return a list of all the subkeys under this key. bool MyRegistry::EnumSubKeys(CStringList& slSubKeys) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); LONG lnReturn(ERROR_SUCCESS); if (m_hSubKey) { TCHAR szSubKey[MAX_PATH + 1] = { 0 }; DWORD cbSubkeySize(0); HKEY hSubKey(m_hSubKey); for (int nIndex = 0; ERROR_SUCCESS == lnReturn; nIndex++) { cbSubkeySize = sizeof(szSubKey); lnReturn = ::RegEnumKeyEx( hSubKey, // handle of key to enumerate nIndex, // index of subkey to enumerate szSubKey, // address of buffer for subkey name &cbSubkeySize, // address for size of subkey buffer 0, // reserved NULL, // address of buffer for class string NULL, // address for size of class buffer NULL); // address for time key last written to if (ERROR_SUCCESS == lnReturn) { slSubKeys.AddTail(szSubKey); } } } else { DOMYLOGST(REGISTRY_NOT_OPEN), "MyRegistry::EnumSubKeys"); } return (ERROR_NO_MORE_ITEMS == lnReturn); } // Return a list of all the values under this key. bool MyRegistry::EnumValues(CStringList& slValues, CDWordArray& dwaTypes) { VALIDATE; _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); LONG lnReturn(ERROR_SUCCESS); if (m_hSubKey) { TCHAR szValue[MAX_PATH + 1] = { 0 }; DWORD cbValueSize(0); HKEY hSubKey(m_hSubKey); DWORD dwType(-1); for (int nIndex = 0; ERROR_SUCCESS == lnReturn; ++nIndex) { cbValueSize = sizeof(szValue); lnReturn = ::RegEnumValue( hSubKey, // handle of key to enumerate nIndex, // index of subkey to enumerate szValue, // address of buffer for subkey name &cbValueSize, // address for size of subkey buffer 0, // reserved &dwType, // address of buffer for class string (REG_DWORD, et al.) NULL, // address for size of class buffer NULL); // address for time key last written to if (ERROR_SUCCESS == lnReturn) { slValues.AddTail(szValue); dwaTypes.SetAtGrow(nIndex, dwType); } } } else { DOMYLOGST(REGISTRY_NOT_OPEN), "MyRegistry::EnumValues"); } return (ERROR_NO_MORE_ITEMS == lnReturn); } // bool MyRegistry::SaveValueDWordEncrypt(UINT uiValueID, DWORD dwData) { VALIDATE; CString sValue(Generic::LoadString(uiValueID)); return SaveValueDWordEncrypt(sValue, dwData); } // Write out a DWORD in a slightly encrypted way, to confound hackers. We // can't using cloaking because that would reduce the max size of a DWORD we // can store, which is a big can of worms. bool MyRegistry::SaveValueDWordEncrypt(const CString& sValue, DWORD dwData) { VALIDATE; _ASSERTE(! sValue.IsEmpty()); _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); HRESULT hr(S_OK); // Flip bits. FlipBit(dwData, 4); FlipBit(dwData, 9); FlipBit(dwData, 17); FlipBit(dwData, 22); FlipBit(dwData, 28); // Now write the dword. EC_B(SaveValueDWord(sValue, dwData)); return SUCCEEDED(hr); } // bool MyRegistry::ReadValueDWordEncrypt(UINT uiValueID, DWORD& dwData) { VALIDATE; CString sValue(Generic::LoadString(uiValueID)); return ReadValueDWordEncrypt(sValue, dwData); } // Read in a DWORD in a slightly encrypted way, to confound hackers. We can't // using cloaking because that would reduce the max size of a DWORD we can // store, which is a big can of worms. bool MyRegistry::ReadValueDWordEncrypt(const CString& sValue, DWORD& dwData) { VALIDATE; _ASSERTE(! sValue.IsEmpty()); _ASSERTE(m_hRootKey && "m_hRootKey is NULL"); _ASSERTE(m_hSubKey && "m_hSubKey is NULL"); _ASSERTE(! m_sSubKey.IsEmpty() && "m_sSubKey is empty"); bool bReturn(false); // Read the dword. Be careful that it might not exist yet. if (ReadValueDWord(sValue, dwData)) { // Flip bits. FlipBit(dwData, 4); FlipBit(dwData, 9); FlipBit(dwData, 17); FlipBit(dwData, 22); FlipBit(dwData, 28); bReturn = true; } return bReturn; } // Flip the bit in place. void MyRegistry::FlipBit(DWORD& dwData, int nBit) const { VALIDATE; DWORD dwBit(dwData << (31 - nBit)); dwBit = dwBit >> 31; bool bWasOn(dwBit); dwBit = 1 << nBit; if (bWasOn) { dwData = dwData & ~ dwBit; } else { dwData = dwData | dwBit; } } // All "cloaked" bits reside in a series of DWORDs starting with "BufferSize-0". // Each such DWORD has two bits that flip on each write (6 and 13), two bits // that are set randomly on each write (17 and 21), one bit that must be zero // (23), and one bit that must be one (25). bool MyRegistry::SaveValueBitCloak(BYTE bitValue, bool bData) { VALIDATE; _ASSERTE(6 != bitValue && 13 != bitValue && 17 != bitValue && 21 != bitValue && 23 != bitValue && 24 != bitValue && "Cannot use a reserved bit place!"); bool bReturn(false); // Make sure it's not one of the reserved ones. if (6 != bitValue && 13 != bitValue && 17 != bitValue && 21 != bitValue && 23 != bitValue && 24 != bitValue) { // Figure out which DWORD this is in. DWORD dwKey(bitValue / 32); CString sValue; sValue.Format("BufferSize-%d", dwKey); // Write that bit to the Registry. if (SaveValueBit(sValue, bitValue, bData)) { // Now do the cloaking part. bool b(false); // Flip two. ReadValueBit(sValue, 6, b); SaveValueBit(sValue, 6, ! b); ReadValueBit(sValue, 13, b); SaveValueBit(sValue, 13, ! b); // Set two randomly. SaveValueBit(sValue, 17, rand() % 2 ? false : true); SaveValueBit(sValue, 21, rand() % 2 ? false : true); // Set two on/off. SaveValueBit(sValue, 23, false); SaveValueBit(sValue, 25, true); // Double-check our work. bReturn = ValidateBitCloak(sValue); } } else { DOMYLOGST(API_FAILURE), "::SaveValueBitCloak", 666, "666"); } return bReturn; } // Returns true on success. bool MyRegistry::ReadValueBitCloak(BYTE bitValue, bool& bData) const { VALIDATE; _ASSERTE(6 != bitValue && 13 != bitValue && 17 != bitValue && 21 != bitValue && 23 != bitValue && 24 != bitValue && "Cannot use a reserved bit place!"); bool bReturn(false); // Initialize in case of failure. bData = false; // Make sure it's not one of the reserved ones. if (6 != bitValue && 13 != bitValue && 17 != bitValue && 21 != bitValue && 23 != bitValue && 24 != bitValue) { // Figure out which DWORD this is in. DWORD dwKey(bitValue / 32); CString sValue; sValue.Format("BufferSize-%d", dwKey); // See if someone's been hacking. if (ValidateBitCloak(sValue)) { bReturn = ReadValueBit(sValue, bitValue, bData); } } else { DOMYLOGST(API_FAILURE), "::ReadValueBitCloak", 666, "666"); } return bReturn; } // Has someone been fiddling with the Registry? bool MyRegistry::ValidateBitCloak(const CString& sValue) const { VALIDATE; _ASSERTE(! sValue.IsEmpty()); bool bReturn(true); bool bValueMustBeZero(false); bool bValueMustBeOne(false); // Note that if the value hasn't been written yet, that's okay. if (ReadValueBit(sValue, 23, bValueMustBeZero)) { if (ReadValueBit(sValue, 25, bValueMustBeOne)) { if (bValueMustBeZero || ! bValueMustBeOne) { bReturn = false; } } } // This means that the key is there, but does not have the expected bits // turned on and off as required. This implies someone has tried to hack // the Registry to set this cloaked value. if (! bReturn) { DOMYLOGST(API_FAILURE), "::ValidateValueBitCloak", 666, sValue); } return bReturn; }