// ========================================================================== // Class Implementation : COXRegistryItem // ========================================================================== // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXRegistryItem.h" #include "OXMainRes.h" #include "UTBStrOp.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNAMIC(COXRegistryItem, CObject) #define new DEBUG_NEW #ifdef _DEBUG // Trace a message when the RESULT specifies failure #define CONDITIONAL_TRACE_RESULT(TEXT, RESULT) \ { if (FAILED(RESULT)) { \ TRACE(_T("%s (%s): Failed (%u == 0x%X, Code : %u) :\n\t%s\n"), \ _T(TEXT), (LPCTSTR)GetFullRegistryItem(), RESULT, RESULT, \ HRESULT_CODE(RESULT), GetResultMessage(RESULT)); \ } } #else // Do not trace in Release build #define CONDITIONAL_TRACE_RESULT(TEXT, RESULT) #endif // _DEBUG ///////////////////////////////////////////////////////////////////////////// // Definition of static variables static const TCHAR szClassesRoot[] = _T("\\ClassesRoot"); static const TCHAR szCurrentUser[] = _T("\\CurrentUser"); static const TCHAR szLocalMachine[] = _T("\\LocalMachine"); static const TCHAR szUsers[] = _T("\\Users"); static const TCHAR szPerformanceData[] = _T("\\PerformanceData"); static const TCHAR szCurrentConfig[] = _T("\\CurrentConfig"); static const TCHAR szDynData[] = _T("\\DynData"); static const TCHAR szNoName[] = _T("NoName"); static const TCHAR szBackslash[] = _T("\\"); static const TCHAR szDoubleBackslash[] = _T("\\\\"); static const TCHAR szBar[] = _T("|"); static const TCHAR szSoftware[] = _T("Software"); static const TCHAR szUnknownCompanyName[] = _T("UnknownCompany"); static const TCHAR szUnknownApplicationName[] = _T("UnknownApplication"); ///////////////////////////////////////////////////////////////////////////// // Definition of static members const LPCTSTR COXRegistryItem::m_pszClassesRoot = szClassesRoot; const LPCTSTR COXRegistryItem::m_pszCurrentUser = szCurrentUser; const LPCTSTR COXRegistryItem::m_pszLocalMachine = szLocalMachine; const LPCTSTR COXRegistryItem::m_pszUsers = szUsers; const LPCTSTR COXRegistryItem::m_pszPerformanceData = szPerformanceData; const LPCTSTR COXRegistryItem::m_pszCurrentConfig = szCurrentConfig; const LPCTSTR COXRegistryItem::m_pszDynData = szDynData; const LPCTSTR COXRegistryItem::m_pszNoName = szNoName; const TCHAR COXRegistryItem::m_cNull = _T('\0'); const TCHAR COXRegistryItem::m_cBackslash = _T('\\'); const TCHAR COXRegistryItem::m_cBar = _T('|'); const LPCTSTR COXRegistryItem::m_pszBackslash = szBackslash; const LPCTSTR COXRegistryItem::m_pszDoubleBackslash = szDoubleBackslash; const LPCTSTR COXRegistryItem::m_pszBar = szBar; const LPCTSTR COXRegistryItem::m_pszSoftware = szSoftware; LPCTSTR COXRegistryItem::m_pszUnknownCompanyName = szUnknownCompanyName; LPCTSTR COXRegistryItem::m_pszUnknownApplicationName = szUnknownApplicationName; COXRegistryItem::COXRootRegistryKey COXRegistryItem::m_rgRootRegistryKey[]= { { szClassesRoot, HKEY_CLASSES_ROOT }, { szCurrentUser, HKEY_CURRENT_USER }, { szLocalMachine, HKEY_LOCAL_MACHINE }, { szUsers, HKEY_USERS }, { szPerformanceData, HKEY_PERFORMANCE_DATA }, { szCurrentConfig, HKEY_CURRENT_CONFIG }, { szDynData, HKEY_DYN_DATA }, { NULL, (HKEY)NULL } }; // Data members ------------------------------------------------------------- // protected: // CString m_sFullRegistryItem; // --- The full registry item specification (e.g. "\\Comp1\LocalMachine\Software\Test") // BOOL m_bMainBuilt; // --- Whether the value in m_sFullRegistryItem is valid // CString m_sMachineName; // --- The machine name part of the registry item // (empty or containing two leading backslashes) // CString m_sRegistryName; // --- The registry name part of the registry item // (empty or containing one leading backslash) // CString m_sKeyNames; // --- The key name part of the registry item // (empty or containing one leading and one trailing backslash) // CString m_sValueName; // --- The value name part of the registry item // (never containing any backslash) // BOOL m_bPartsBuilt; // --- Whether all the part (specified above) are valid // HKEY m_hRootKey; // --- Handle of the root of the registry (may be remote) // HKEY m_hKey; // --- Handle of the current key // HRESULT m_nLastError; // --- Error code of the last error of the object // static const TCHAR m_cNull; // static const TCHAR m_cBackslash; // static const TCHAR m_cBar; // static const LPCTSTR m_pszBackslash; // static const LPCTSTR m_pszDoubleBackslash; // static const LPCTSTR m_pszBar; // --- Constant strings and characters // struct COXRootRegistryKey // { // LPCTSTR m_pszRegistryName; // HKEY m_hRegistryKey; // }; // static COXRootRegistryKey m_rgRootRegistryKey[]; // --- Array of registry names and there handle // Always starts with a backslash // private: // Member functions --------------------------------------------------------- // public: COXRegistryItem::COXRegistryItem(LPCTSTR pszFullRegistryItem /* = NULL */) : m_sFullRegistryItem(pszFullRegistryItem), m_bMainBuilt(TRUE), m_sMachineName(), m_sRegistryName(), m_sKeyNames(), m_sValueName(), m_bPartsBuilt(FALSE), m_hRootKey(NULL), m_hKey(NULL), m_nLastError(NULL) { ASSERT_VALID(this); } COXRegistryItem::COXRegistryItem(const COXRegistryItem& registryItem) : m_sFullRegistryItem(registryItem.m_sFullRegistryItem), m_bMainBuilt( registryItem.m_bMainBuilt), m_sMachineName( registryItem.m_sMachineName), m_sRegistryName( registryItem.m_sRegistryName), m_sKeyNames( registryItem.m_sKeyNames), m_sValueName( registryItem.m_sValueName), m_bPartsBuilt( registryItem.m_bPartsBuilt), m_hRootKey( NULL), m_hKey( NULL) { ASSERT_VALID(®istryItem); // The registry key handles are set to NULL, so they will have to be opened // again before use ASSERT_VALID(this); } COXRegistryItem& COXRegistryItem::operator=(const COXRegistryItem& registryItem) { ASSERT_VALID(®istryItem); if(this==®istryItem) return *this; // First clean current settings (and close possible open handles) Empty(); // Copy simple data members m_sFullRegistryItem = registryItem.m_sFullRegistryItem; m_bMainBuilt = registryItem.m_bMainBuilt; m_sMachineName = registryItem.m_sMachineName; m_sRegistryName = registryItem.m_sRegistryName; m_sKeyNames = registryItem.m_sKeyNames; m_sValueName = registryItem.m_sValueName; m_bPartsBuilt = registryItem.m_bPartsBuilt; // The registry key handles are set to NULL, so they will have to be opened // again before use ASSERT(m_hRootKey == NULL); ASSERT(m_hKey == NULL); ASSERT_VALID(this); return *this; } CString COXRegistryItem::GetFullRegistryItem() { BuildMain(); ASSERT_VALID(this); return m_sFullRegistryItem; } CString COXRegistryItem::GetMachineName() { BuildParts(); ASSERT_VALID(this); return m_sMachineName; } CString COXRegistryItem::GetRegistryName() { BuildParts(); ASSERT_VALID(this); return m_sRegistryName; } CString COXRegistryItem::GetKeyNames() { BuildParts(); ASSERT_VALID(this); return m_sKeyNames; } CString COXRegistryItem::GetValueName() { BuildParts(); ASSERT_VALID(this); return m_sValueName; } void COXRegistryItem::SetFullRegistryItem(LPCTSTR pszFullRegistryItem) { ASSERT_VALID(this); m_sFullRegistryItem = pszFullRegistryItem; // ... We have changed main, so the parts are not up-to-date anymore DestroyParts(); AdjustMain(); // ... Close a possible open key Close(); ASSERT_VALID(this); } void COXRegistryItem::SetMachineName(LPCTSTR pszMachineName) { ASSERT_VALID(this); // ... Build the other parts before changing BuildParts(); m_sMachineName = pszMachineName; // ... We have changed a part, so main is not up-to-date anymore DestroyMain(); AdjustParts(); // ... Close a possible open key Close(); ASSERT_VALID(this); } void COXRegistryItem::SetRegistryName(LPCTSTR pszRegistryName) { ASSERT_VALID(this); // ... Build the other parts before changing BuildParts(); m_sRegistryName = pszRegistryName; // ... We have changed a part, so main is not up-to-date anymore DestroyMain(); AdjustParts(); // ... Close a possible open key Close(); ASSERT_VALID(this); } void COXRegistryItem::SetKeyNames(LPCTSTR pszKeyNames) { ASSERT_VALID(this); // ... Build the other parts before changing BuildParts(); m_sKeyNames = pszKeyNames; // ... We have changed a part, so main is not up-to-date anymore DestroyMain(); AdjustParts(); // ... Close a possible open key Close(); ASSERT_VALID(this); } void COXRegistryItem::SetValueName(LPCTSTR pszValueName) { ASSERT_VALID(this); // ... Build the other parts before changing BuildParts(); m_sValueName = pszValueName; // ... We have changed a part, so main is not up-to-date anymore DestroyMain(); AdjustParts(); // No need to close an open key, because changing the value name has no effect on the key ASSERT_VALID(this); } void COXRegistryItem::InitializeFromApplication(LPCTSTR pszSubkeyName /* = NULL */) { ASSERT_VALID(this); CString sCompany; CString sApplication; CString sSubkeyName = pszSubkeyName; // Get correct values from application object if (AfxGetApp() != NULL) { sCompany = AfxGetApp()->m_pszRegistryKey; sApplication = AfxGetApp()->m_pszProfileName; } // Use default values if empty if (sCompany.IsEmpty()) { TRACE1("COXRegistryItem::InitializeFromApplication : No company name is specified, using %s\n", m_pszUnknownCompanyName); sCompany = m_pszUnknownCompanyName; } if (sApplication.IsEmpty()) { TRACE1("COXRegistryItem::InitializeFromApplication : No application name is specified, using %s\n", m_pszUnknownApplicationName); sApplication = m_pszUnknownApplicationName; } // Remove possible leading or trailing back slash from subkey name if (!sSubkeyName.IsEmpty() && (sSubkeyName.GetAt(0) == m_cBackslash)) sSubkeyName = sSubkeyName.Mid(1); if (!sSubkeyName.IsEmpty() && (sSubkeyName.GetAt(sSubkeyName.GetLength() - 1) == m_cBackslash)) sSubkeyName = sSubkeyName.Left(sSubkeyName.GetLength() - 1); // Build complete keyname // \Software\\\ CString sKeyNames; if (sSubkeyName.IsEmpty()) sKeyNames.Format(_T("\\%s\\%s\\%s\\"), m_pszSoftware, sCompany, sApplication); else sKeyNames.Format(_T("\\%s\\%s\\%s\\%s\\"), m_pszSoftware, sCompany, sApplication, sSubkeyName); // Use only the key names to build a new registry item (rest is default) SetFullRegistryItem(sKeyNames); ASSERT_VALID(this); } void COXRegistryItem::Empty() { ASSERT_VALID(this); Close(); ASSERT(m_hRootKey == NULL); ASSERT(m_hKey == NULL); m_sFullRegistryItem.Empty(); m_bMainBuilt = FALSE; m_sMachineName.Empty(); m_sRegistryName.Empty(); m_sKeyNames.Empty(); m_sValueName.Empty(); m_bPartsBuilt = FALSE; ASSERT_VALID(this); } void COXRegistryItem::Serialize(CArchive& ar) { ASSERT_VALID(this); // Call base class implementation CObject::Serialize(ar); if (ar.IsStoring()) ar << GetFullRegistryItem(); else { CString sNewFullRegistryItem; ar >> sNewFullRegistryItem; SetFullRegistryItem(sNewFullRegistryItem); } ASSERT_VALID(this); } BOOL COXRegistryItem::Open(BOOL bCreate /* = TRUE */, REGSAM samDesired /* = KEY_ALL_ACCESS */, LPCTSTR pszClass /* = NULL */, DWORD dwOptions /* = REG_OPTION_NON_VOLATILE */, LPSECURITY_ATTRIBUTES lpSecurityAttributes /* = NULL */, LPDWORD pdwDisposition /* = NULL */) { ASSERT_VALID(this); // ... First close a possible open key Close(); // Get the key name CString sKeyName = GetKeyNames(); // Get the root key if (!OpenRootKey()) { ASSERT(!SUCCEEDED(m_nLastError)); return SUCCEEDED(m_nLastError); } // Remove possible leading and trailing backslash from key names if (!sKeyName.IsEmpty()) { // ... Non empty key name must begin with and end in back slash ASSERT(sKeyName.GetAt(0) == m_cBackslash); ASSERT(sKeyName.GetAt(sKeyName.GetLength() - 1) == m_cBackslash); sKeyName = sKeyName.Mid(1, sKeyName.GetLength() - 2); } // At the moment we are in an intermediate state (root already opened, // registry not yet) : ASSERT_VALID(this) does not hold at this position ASSERT(m_hRootKey != NULL); ASSERT(m_hKey == NULL); if (bCreate) { CString sClass(pszClass); m_nLastError = HResultFromWin32(::RegCreateKeyEx(m_hRootKey, sKeyName, 0, sClass.GetBuffer(0), dwOptions, samDesired, lpSecurityAttributes, &m_hKey, pdwDisposition)); sClass.ReleaseBuffer(); } else { m_nLastError = HResultFromWin32(::RegOpenKeyEx(m_hRootKey, sKeyName, dwOptions, samDesired, &m_hKey)); } if (FAILED(m_nLastError)) { // Close a possible open remote root key // ... Use the result of the failed Open (not the succeeded Close) HRESULT nLastError = m_nLastError; Close(); m_nLastError = nLastError; } #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::Open", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::Delete() { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; if (GetValueName().IsEmpty()) { // Delete key // Get the key name CString sKeyName = GetKeyNames(); // Get the root key if ((m_hRootKey == NULL) && !OpenRootKey()) { ASSERT(!SUCCEEDED(m_nLastError)); return SUCCEEDED(m_nLastError); } // Remove possible leading and trailing backslash from key names if (!sKeyName.IsEmpty()) { // ... Non empty key name must begin with and end in back slash ASSERT(sKeyName.GetAt(0) == m_cBackslash); ASSERT(sKeyName.GetAt(sKeyName.GetLength() - 1) == m_cBackslash); sKeyName = sKeyName.Mid(1, sKeyName.GetLength() - 2); } // Close the key (not the rootkey) before deleting it CloseKey(); HRESULT nLastError = HResultFromWin32(::RegDeleteKey(m_hRootKey, sKeyName)); // ... Close all keys now Close(); // ... Use the result of the failed Open (not the succeeded Close) m_nLastError = nLastError; } else { // Delete value CString sValueName = GetValueName(); // ... Use an empty string if the NoName constant is used if (sValueName.CompareNoCase(m_pszNoName) == 0) sValueName.Empty(); // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_SET_VALUE to delete the value) if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_SET_VALUE)) m_nLastError = HResultFromWin32(::RegDeleteValue(m_hKey, sValueName)); } #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::Close", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::GetSecurity(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, LPDWORD pcbSecurityDescriptor) { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_READ to read the security) if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_READ)) m_nLastError = HResultFromWin32(::RegGetKeySecurity(m_hKey, SecurityInformation, pSecurityDescriptor, pcbSecurityDescriptor)); #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetSecurity", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::SetSecurity(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor) { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_WRITE to set the security) if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_WRITE)) m_nLastError = HResultFromWin32(::RegSetKeySecurity(m_hKey, SecurityInformation, pSecurityDescriptor)); #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::SetSecurity", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::Flush() { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; if (m_hKey != NULL) m_nLastError = HResultFromWin32(::RegFlushKey(m_hKey)); #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::Flush", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::Close() { m_nLastError = ERROR_SUCCESS; CloseKey(); CloseRootKey(); ASSERT(m_hKey == NULL); ASSERT(m_hRootKey == NULL); #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::Close", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } CString COXRegistryItem::GetStringValue(LPCTSTR pszValueName /* = NULL */, BOOL bAllowUnexpanded /* = TRUE */, BOOL bAutoExpand /* = TRUE */, CString sDefault /* = _T("") */) { ASSERT_VALID(this); // ... Default result to empty string CString sResultValue; DWORD nType = 0; DWORD nSize = 0; // ... If unexpanded string are not allowed, we only accept plain string type DWORD nRequestedType = bAllowUnexpanded ? 0 : REG_SZ; LPBYTE pStringData = NULL; /// GetValue(&pStringData, pszValueName, nRequestedType, &nType, &nSize); if (GetValue(&pStringData, pszValueName, nRequestedType, &nType, &nSize) == 0) { sResultValue = sDefault; nType = REG_EXPAND_SZ; } // ... Use returned data only if it is not empty if ((nSize != 0) && (pStringData != NULL)) { if ((nType == REG_SZ) || (nType == REG_EXPAND_SZ)) sResultValue = (LPCTSTR)pStringData; else // ... Data type does not match m_nLastError = HResultFromWin32(ERROR_INVALID_DATA); delete[] pStringData; pStringData = NULL; } // ... Auto expand if requested and of correct type if (bAllowUnexpanded && bAutoExpand && (nType == REG_EXPAND_SZ)) sResultValue = ExpandString(sResultValue ); #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetStringValue", m_nLastError); #endif ASSERT_VALID(this); return sResultValue; } CString COXRegistryItem::GetMultiStringValue(LPCTSTR pszValueName /* = NULL */, CString sDefault /* = _T("") */) { ASSERT_VALID(this); // ... Default result to empty string CString sResultValue; LPBYTE pMultiStringData = NULL; DWORD nSize = 0; /// GetValue(&pMultiStringData, pszValueName, REG_MULTI_SZ, NULL, &nSize); if (GetValue(&pMultiStringData, pszValueName, REG_MULTI_SZ, NULL, &nSize) == 0) { sResultValue = sDefault; } // ... Parse returned data only if it is not empty if ((nSize != 0) && (pMultiStringData != NULL)) { NullToBarSeparator((LPTSTR)pMultiStringData); sResultValue = (LPCTSTR)pMultiStringData; delete[] pMultiStringData; pMultiStringData = NULL; } // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetMultiStringValue", m_nLastError); ASSERT_VALID(this); return sResultValue; } BOOL COXRegistryItem::GetMultiStringValue(CStringArray& stringArray, LPCTSTR pszValueName /* = NULL */) { ASSERT_VALID(this); CString sMultiStringValue = GetMultiStringValue(pszValueName); // Copy individual strings (delimited by a bar) to the string array // ... Remove possible previous contents stringArray.RemoveAll(); LPTSTR pszMultiStringValue = sMultiStringValue.GetBuffer(0); TCHAR * p; LPCTSTR pszStringValue = UTBStr::tcstok(pszMultiStringValue, m_pszBar, &p); while(pszStringValue != NULL) { stringArray.Add(pszStringValue); pszStringValue = UTBStr::tcstok(NULL, m_pszBar, &p); } sMultiStringValue.ReleaseBuffer(); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetMultiStringValue", m_nLastError); ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } DWORD COXRegistryItem::GetNumberValue(LPCTSTR pszValueName /* = NULL */, BOOL bLittleEndian /* = TRUE */, DWORD dwDefault /* = 0 */) { ASSERT_VALID(this); // ... Default result to 0 DWORD nResultValue = 0; DWORD nSize = 0; DWORD nRequestedType = bLittleEndian ? REG_DWORD_LITTLE_ENDIAN : REG_DWORD_BIG_ENDIAN; LPBYTE pNumberData = NULL; /// GetValue(&pNumberData, pszValueName, nRequestedType, NULL, &nSize); if (GetValue(&pNumberData, pszValueName, nRequestedType, NULL, &nSize) == 0) { nResultValue = dwDefault; } // ... Use returned data only if it is not empty if ((nSize != 0) && (pNumberData != NULL)) { nResultValue = *(LPDWORD)pNumberData; delete[] pNumberData; pNumberData = NULL; } // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetNumberValue", m_nLastError); ASSERT_VALID(this); return nResultValue; } BOOL COXRegistryItem::GetBinaryValue(CByteArray& binaryValue, LPCTSTR pszValueName /* = NULL */) { ASSERT_VALID(this); DWORD nSize = 0; LPBYTE pBinaryData = NULL; GetValue(&pBinaryData, pszValueName, REG_BINARY, NULL, &nSize); // ... Use returned data only if it is not empty if ((nSize != 0) && (pBinaryData != NULL)) { binaryValue.SetSize(nSize); ::CopyMemory(binaryValue.GetData(), pBinaryData, nSize); delete[] pBinaryData; pBinaryData = NULL; } // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetBinaryValue", m_nLastError); ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::SetStringValue(LPCTSTR pszNewStringValue, LPCTSTR pszValueName /* = NULL */, BOOL bUnexpanded /* = FALSE */) { ASSERT_VALID(this); DWORD nDataSize = (DWORD)(_tcslen(pszNewStringValue) + 1) * sizeof(TCHAR); const BYTE* pData = (const BYTE*)pszNewStringValue; DWORD nType = bUnexpanded ? REG_EXPAND_SZ : REG_SZ; SetValue(pszValueName, pData, nType, nDataSize); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::SetStringValue", m_nLastError); ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::SetMultiStringValue(LPCTSTR pszNewMultiStringValue, LPCTSTR pszValueName /* = NULL */) { ASSERT_VALID(this); if (!pszNewMultiStringValue) return FALSE; // ... Reserve space for double terminating null character in data copy DWORD nDataSize = (DWORD)(_tcslen(pszNewMultiStringValue) + 2) * sizeof(TCHAR); BYTE* pData = new BYTE[nDataSize]; ::CopyMemory(pData, pszNewMultiStringValue, nDataSize - 1); BarToNullSeparator((LPTSTR)pData); DWORD nType = REG_MULTI_SZ; SetValue(pszValueName, pData, nType, nDataSize); // ... Clean up copy of data delete[] pData; // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::SetMultiStringValue", m_nLastError); ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::SetMultiStringValue(const CStringArray& stringArray, LPCTSTR pszValueName /* = NULL */) { ASSERT_VALID(this); CString sNewMultiStringValue; for(int nIndex = 0; nIndex < stringArray.GetSize(); nIndex++) { sNewMultiStringValue += stringArray.GetAt(nIndex); if (nIndex0) { // ... Get the actual data pData = new BYTE[nDataSize]; m_nLastError = HResultFromWin32(::RegQueryValueEx(m_hKey, sValueName.GetBuffer(0), NULL, &nType, pData, &nDataSize)); sValueName.ReleaseBuffer(); // ... Discard the data if the call failed if (FAILED(m_nLastError)) { delete[] pData; pData = NULL; } } } // Assign the result values if (ppData != NULL) *ppData = pData; if (pnType != NULL) *pnType = nType; if (pnSize != NULL) *pnSize = nDataSize; #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetValue", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } BOOL COXRegistryItem::SetValue(LPCTSTR pszValueName, const BYTE* pData, DWORD nType, DWORD nSize) { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; // Get the name of the value CString sValueName = pszValueName; if (sValueName.IsEmpty()) sValueName = GetValueName(); // ... Use an empty string if the NoName constant is used if (sValueName.CompareNoCase(m_pszNoName) == 0) sValueName.Empty(); // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_SET_VALUE to set the value) // ... A key will be created if necessary if (IsOpen() || Open(TRUE) || Open(TRUE, KEY_SET_VALUE)) { // ... Get the value type and the needed buffer size m_nLastError = HResultFromWin32(::RegSetValueEx(m_hKey, sValueName, 0, nType, pData, nSize)); } #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::SetValue", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } CString COXRegistryItem::ExpandString(LPCTSTR pszUnexpanded) { DWORD nNeededSize = 0; nNeededSize = ::ExpandEnvironmentStrings(pszUnexpanded, NULL, nNeededSize); if (0 < nNeededSize) { CString sResult; TCHAR* buffer=new TCHAR[nNeededSize]; // ... Note that the length in GetBufferSetLength should not include the terminating null char if (nNeededSize>= ::ExpandEnvironmentStrings(pszUnexpanded,buffer,nNeededSize)) sResult=buffer; else { TRACE1("Error %d in COXRegistryItem::ExpandString()\r",::GetLastError()); sResult=_T(""); } delete[] buffer; return sResult; } else { // Error or empty string return pszUnexpanded; } } BOOL COXRegistryItem::GetInfo(LPTSTR pClass, LPDWORD pcbClass /* = NULL */, LPDWORD pcSubKeys /* = NULL */, LPDWORD pcbMaxSubKeyLen /* = NULL */, LPDWORD pcbMaxClassLen /* = NULL */, LPDWORD pcValues /* = NULL */, LPDWORD pcbMaxValueNameLen /* = NULL */, LPDWORD pcbMaxValueLen /* = NULL */, LPDWORD pcbSecurityDescriptor /* = NULL */, PFILETIME lpftLastWriteTime /* = NULL */) { ASSERT_VALID(this); // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_QUERY_VALUE to query the value) // ... A key will be created if necessary if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_QUERY_VALUE)) { // ... Get the value type and the needed buffer size m_nLastError = HResultFromWin32(::RegQueryInfoKey(m_hKey, pClass, pcbClass, NULL, pcSubKeys, pcbMaxSubKeyLen, pcbMaxClassLen, pcValues, pcbMaxValueNameLen, pcbMaxValueLen, pcbSecurityDescriptor, lpftLastWriteTime)); } // Do not TRACE a message when requesting more buffer space #ifndef OX_REGITEM_DONTTRACE if (HRESULT_CODE(m_nLastError) != ERROR_MORE_DATA) CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetInfo", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } CString COXRegistryItem::GetClassName() { ASSERT_VALID(this); CString sClassName; DWORD nClassNameLength = 0; GetInfo(NULL, &nClassNameLength); if (HRESULT_CODE(m_nLastError) == ERROR_MORE_DATA) GetInfo(sClassName.GetBufferSetLength(nClassNameLength)); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetClassName", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return sClassName; else return _T(""); } int COXRegistryItem::GetNumberOfSubkeys() { ASSERT_VALID(this); DWORD nNumberOfSubkeys = 0; GetInfo(NULL, NULL, &nNumberOfSubkeys); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetNumberOfSubkeys", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nNumberOfSubkeys; else return -1; } int COXRegistryItem::GetLongestSubkeyNameLength() { ASSERT_VALID(this); DWORD nLongestSubkeyNameLength = 0; GetInfo(NULL, NULL, NULL, &nLongestSubkeyNameLength); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetLongestSubkeyNameLength", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nLongestSubkeyNameLength; else return -1; } int COXRegistryItem::GetLongestClassNameLength() { ASSERT_VALID(this); DWORD nLongestClassNameLength = 0; GetInfo(NULL, NULL, NULL, NULL, &nLongestClassNameLength); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetLongestClassNameLength", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nLongestClassNameLength; else return -1; } int COXRegistryItem::GetNumberOfValues() { ASSERT_VALID(this); DWORD nNumberOfValues = 0; GetInfo(NULL, NULL, NULL, NULL, NULL, &nNumberOfValues); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetNumberOfValues", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nNumberOfValues; else return -1; } int COXRegistryItem::GetLongestValueNameLength() { ASSERT_VALID(this); DWORD nLongestValueNameLength = 0; GetInfo(NULL, NULL, NULL, NULL, NULL, NULL, &nLongestValueNameLength); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetLongestValueNameLength", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nLongestValueNameLength; else return -1; } int COXRegistryItem::GetLongestValueDataLength() { ASSERT_VALID(this); DWORD nLongestValueDataLength = 0; GetInfo(NULL, NULL, NULL, NULL, NULL, NULL, NULL, &nLongestValueDataLength); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetLongestValueDataLength", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nLongestValueDataLength; else return -1; } int COXRegistryItem::GetSecurityDescriptorLength() { ASSERT_VALID(this); DWORD nSecurityDescriptorLength = 0; GetInfo(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &nSecurityDescriptorLength); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetSecurityDescriptorLength", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return (int)nSecurityDescriptorLength; else return -1; } CTime COXRegistryItem::GetLastWriteTime() { ASSERT_VALID(this); FILETIME lastWriteTime; GetInfo(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &lastWriteTime); // ... Possible error condition has already been traced // CONDITIONAL_TRACE_RESULT("COXRegistryItem::GetLastWriteTime", m_nLastError); ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return CTime(lastWriteTime); else return CTime(); } BOOL COXRegistryItem::NotifyChange(BOOL bWatchSubtree /* = FALSE */, DWORD dwNotifyFilter /* = REG_NOTIFY_CHANGE_NAME */, HANDLE hEvent /* = NULL */) { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_NOTIFY to get notifications) if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_NOTIFY)) { // ... Get the value type and the needed buffer size m_nLastError = HResultFromWin32(::RegNotifyChangeKeyValue(m_hKey, bWatchSubtree, dwNotifyFilter, hEvent, (hEvent != NULL))); } #ifndef OX_REGITEM_DONTTRACE CONDITIONAL_TRACE_RESULT("COXRegistryItem::NotifyChange", m_nLastError); #endif ASSERT_VALID(this); return SUCCEEDED(m_nLastError); } CString COXRegistryItem::EnumerateSubkey(DWORD nIndex) { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; CString sSubkeyName; // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_ENUMERATE_SUB_KEYS to enumerate) if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_ENUMERATE_SUB_KEYS)) { // ... Start with a buffer that will satisfy most requests DWORD nSubkeyNameLength = 300; m_nLastError = HResultFromWin32(::RegEnumKeyEx(m_hKey, nIndex, sSubkeyName.GetBuffer(nSubkeyNameLength), &nSubkeyNameLength, NULL, NULL, NULL, NULL)); if (HRESULT_CODE(m_nLastError) == ERROR_MORE_DATA) { // Buffer was not large enough, try again with correct size sSubkeyName.ReleaseBuffer(0); // ... Note that the next function may fail because it is possible // that the key was not opened with KEY_QUERY_VALUE. // To resolve this open the key explicitly with KEY_QUERY_VALUE. nSubkeyNameLength = GetLongestSubkeyNameLength() + 1; m_nLastError = HResultFromWin32(::RegEnumKeyEx(m_hKey, nIndex, sSubkeyName.GetBuffer(nSubkeyNameLength), &nSubkeyNameLength, NULL, NULL, NULL, NULL)); } if (SUCCEEDED(m_nLastError)) sSubkeyName.ReleaseBuffer(); else sSubkeyName.ReleaseBuffer(0); } // ... Do not trace for end of iteration (expected condition) #ifndef OX_REGITEM_DONTTRACE if (HRESULT_CODE(m_nLastError) != ERROR_NO_MORE_ITEMS) CONDITIONAL_TRACE_RESULT("COXRegistryItem::EnumerateSubkey", m_nLastError); #endif ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return sSubkeyName; else return _T(""); } CString COXRegistryItem::EnumerateValue(DWORD nIndex) { ASSERT_VALID(this); m_nLastError = ERROR_SUCCESS; CString sValueName; // ... Open key if not yet open (first try to open with maximum // access, then only with KEY_ENUMERATE_SUB_KEYS and KEY_QUERY_VALUE to enumerate) if (IsOpen() || Open(FALSE) || Open(FALSE, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE)) { // ... Start with a buffer that will satisfy most requests DWORD nValueNameLength = 300; m_nLastError = HResultFromWin32(::RegEnumValue(m_hKey, nIndex, sValueName.GetBuffer(nValueNameLength), &nValueNameLength, NULL, NULL, NULL, NULL)); if (HRESULT_CODE(m_nLastError) == ERROR_MORE_DATA) { // Buffer was not large enough, try again with correct size sValueName.ReleaseBuffer(0); nValueNameLength = GetLongestValueNameLength() + 1; m_nLastError = HResultFromWin32(::RegEnumValue(m_hKey, nIndex, sValueName.GetBuffer(nValueNameLength), &nValueNameLength, NULL, NULL, NULL, NULL)); } if (SUCCEEDED(m_nLastError)) { sValueName.ReleaseBuffer(); // ... Replace an empty value name by the NoName constant if (sValueName.IsEmpty()) sValueName = m_pszNoName; } else sValueName.ReleaseBuffer(0); } // ... Do not trace for end of iteration (expected condition) #ifndef OX_REGITEM_DONTTRACE if (HRESULT_CODE(m_nLastError) != ERROR_NO_MORE_ITEMS) CONDITIONAL_TRACE_RESULT("COXRegistryItem::EnumerateValue", m_nLastError); #endif ASSERT_VALID(this); if (SUCCEEDED(m_nLastError)) return sValueName; else return _T(""); } BOOL COXRegistryItem::GetSortedValueList(CStringArray& arrValueName) { // delete all existing elements arrValueName.RemoveAll(); // find out how many values we'll be dealing with int nCount=GetNumberOfValues(); // enumerate the key's values using existing function CString sValueName; for (int nIndex=0; nIndexm_pszRegistryName != NULL)) { nRegistryNameLength = (int)_tcslen(pRootRegistryKey->m_pszRegistryName); if ((_tcsnicmp(pszRegistryName, pRootRegistryKey->m_pszRegistryName, nRegistryNameLength) == 0) && ((pszRegistryName[nRegistryNameLength] == m_cBackslash) || (pszRegistryName[nRegistryNameLength] == m_cNull)) ) nRootRegistryIndex = nRegistryIndex; pRootRegistryKey++; nRegistryIndex++; } return nRootRegistryIndex; } void COXRegistryItem::NullToBarSeparator(LPTSTR pStringData) // --- In : pStringData : Pointer to string with parts seperated by null chars // and concluded by two null chars // --- Out : pStringData : The same string but bars '|' used to seperate the parts // The string still end in a null char (without a bar) // --- Returns : // --- Effect : Converts the string { BOOL bPreviousWasNull = FALSE; BOOL bEnd = FALSE; while (!bEnd) { // ... String pointer should point to a valid string att all times ASSERT(AfxIsValidString(pStringData)); if (*pStringData == m_cNull) { if (bPreviousWasNull) { // ... This char is NULL and so was the previous : // zero-terminate the string and end the loop *(pStringData - 1) = m_cNull; bEnd = TRUE; } else { // ... This char is NULL but the previous was not: // replace the zero-char by a bar *pStringData = m_cBar; bPreviousWasNull = TRUE; } } else // ... Encountered a normal (non-NULL-char) bPreviousWasNull = FALSE; pStringData++; } } void COXRegistryItem::BarToNullSeparator(LPTSTR pStringData) // --- In : pStringData : A string with bars '|' used to seperate the parts // The string end in a null char (without a bar) // --- Out : pStringData : The same string but with parts seperated by null chars // and concluded by two null chars // --- Returns : // --- Effect : Converts the string // Note that the string will grow with one character. // Enough space should have been allocated before callinbg this function { // ... The pStringData should have enough space one additional (null) characters ASSERT(AfxIsValidAddress(pStringData, (_tcslen(pStringData) + 2) * sizeof(TCHAR))); // First add an extra NULL character at the end *(pStringData + _tcslen(pStringData) + 1) = m_cNull; // Now replace all bars by a NULL character (use _tcstok) TCHAR * p; LPCTSTR pNewStringData = UTBStr::tcstok(pStringData, m_pszBar, &p); while(pNewStringData != NULL) { pNewStringData = UTBStr::tcstok(NULL, m_pszBar, &p); } } // protected: #ifdef _DEBUG static TCHAR szUnknownError[] = _T("*** Unknown Error ***"); static DWORD dwLangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); CString COXRegistryItem::GetResultMessage(HRESULT hResult) // --- In : hResult : The result code // --- Out : // --- Returns : A string containg a message of the specified code // --- Effect : { CString sResultMessage; LPTSTR pszMsgBuf = NULL; BOOL bUnknown = FALSE; DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM; // ... Remove the facility part if FACILITY_WIN32 if (HRESULT_FACILITY(hResult) == FACILITY_WIN32) hResult = HRESULT_CODE(hResult); // ... Get the actual message if (::FormatMessage(dwFlags, NULL, hResult, dwLangID, (LPTSTR)&pszMsgBuf, 0, NULL) == 0) { TRACE2("COXRegistryItem::GetResultMessage : No message was found for result code %i == 0x%8.8X\n", hResult, hResult); //pszMsgBuf = szUnknownError; VERIFY(sResultMessage.LoadString(IDS_OX_REGITEMUNKERROR)); bUnknown = TRUE; } else sResultMessage = pszMsgBuf; // ... Clean up if (!bUnknown) LocalFree(pszMsgBuf); return sResultMessage; } #endif //_DEBUG // private: // ==========================================================================