/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: Registry.cpp Abstract: Implementation of the registry wrapper class. Notes: ANSI & Unicode via TCHAR - runs on Win9x/NT/2K/XP etc. History: 01/29/2001 rparsons Created 03/02/2001 rparsons Major overhaul 12/16/2001 rparsons Cleanup 01/27/2002 rparsons Converted to TCHAR --*/ #include "registry.h" /*++ Routine Description: Allocates memory from the heap. Arguments: cbBytes - Count of bytes to allocate. Return Value: On success, a pointer to a block of memory. On failure, NULL. --*/ LPVOID CRegistry::Malloc( IN SIZE_T cbBytes ) { LPVOID pvReturn = NULL; pvReturn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbBytes); return pvReturn; } /*++ Routine Description: Frees memory from the heap. Arguments: lpMem - Pointer to a block of memory to free. Return Value: None. --*/ void CRegistry::Free( IN LPVOID pvMem ) { if (pvMem) { HeapFree(GetProcessHeap(), 0, pvMem); } } /*++ Routine Description: Creates the specified key. Arguments: hKey - Handle to a predefined key. pszSubKey - Path to the sub key to create. samDesired - The desired access rights. Return Value: A handle to the key on success, NULL otherwise. --*/ HKEY CRegistry::CreateKey( IN HKEY hKey, IN LPCTSTR pszSubKey, IN REGSAM samDesired ) { HKEY hKeyLocal = NULL; DWORD dwDisposition; if (!hKey) { return NULL; } RegCreateKeyEx(hKey, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKeyLocal, &dwDisposition); return hKeyLocal; } /*++ Routine Description: Creates the specified key. Arguments: hKey - Handle to a predefined key. pszSubKey - Path to the sub key to create. samDesired - The desired access rights. pdwDisposition - On return, the disposition. Return Value: A handle to the key on success, NULL otherwise. --*/ HKEY CRegistry::CreateKey( IN HKEY hKey, IN LPCTSTR pszSubKey, IN REGSAM samDesired, OUT LPDWORD pdwDisposition ) { HKEY hKeyLocal = NULL; if (!hKey || !pdwDisposition) { return NULL; } RegCreateKeyEx(hKey, pszSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKeyLocal, pdwDisposition); return hKeyLocal; } /*++ Routine Description: Opens the specified key. Arguments: hKey - Handle to a predefined key. pszSubKey - Path to the sub key to open. samDesired - The desired access rights. Return Value: A handle to the key on success, NULL otherwise. --*/ HKEY CRegistry::OpenKey( IN HKEY hKey, IN LPCTSTR pszSubKey, IN REGSAM samDesired ) { HKEY hReturnKey = NULL; if (!hKey || !samDesired) { return NULL; } RegOpenKeyEx(hKey, pszSubKey, 0, samDesired, &hReturnKey); return hReturnKey; } /*++ Routine Description: Gets a size for a specified value name. Arguments: hKey - Open key handle (not predefined). pszValueName - Name of data value. lpType - Receives the type of data. Return Value: Number of bytes the value occupies. --*/ DWORD CRegistry::GetStringSize( IN HKEY hKey, IN LPCTSTR pszValueName, OUT LPDWORD lpType OPTIONAL ) { DWORD cbSize = 0; if (!hKey) { return 0; } RegQueryValueEx(hKey, pszValueName, 0, lpType, NULL, &cbSize); return cbSize; } /*++ Routine Description: Closes the specified key. Arguments: hKey - Open key handle. Return Value: On success, ERROR_SUCCESS. --*/ LONG CRegistry::CloseKey( IN HKEY hKey ) { return RegCloseKey(hKey); } /*++ Routine Description: Retrieves a string value from the registry. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszValueName - Name of data value. Return Value: The requested value data on success, NULL otherwise. --*/ LPSTR CRegistry::GetString( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName ) { DWORD cbSize; BOOL fResult = FALSE; LONG lResult; LPTSTR pszReturn = NULL; HKEY hLocalKey = NULL; if (!hKey) { return NULL; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_QUERY_VALUE); if (!hLocalKey) { __leave; } } // // Get the required string size and allocate // memory for the actual call. // cbSize = this->GetStringSize(hLocalKey, pszValueName, NULL); if (0 == cbSize) { __leave; } pszReturn = (LPTSTR)this->Malloc(cbSize * sizeof(TCHAR)); if (!pszReturn) { __leave; } // // Make the actual call to get the data. // lResult = RegQueryValueEx(hLocalKey, pszValueName, 0, NULL, (LPBYTE)pszReturn, &cbSize); if (ERROR_SUCCESS != lResult) { __leave; } fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } if (!fResult) { this->Free(pszReturn); } } return (fResult ? pszReturn : NULL); } /*++ Routine Description: Retrieves a DWORD value from the registry. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszValueName - Name of data value. lpdwData - Pointer to store the value. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::GetDword( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName, IN OUT LPDWORD lpdwData ) { DWORD cbSize; BOOL fResult = FALSE; LONG lResult; HKEY hLocalKey = NULL; if (!hKey || !lpdwData) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_QUERY_VALUE); if (!hLocalKey) { __leave; } } // // Make the call to get the data. // cbSize = sizeof(DWORD); lResult = RegQueryValueEx(hLocalKey, pszValueName, 0, NULL, (LPBYTE)lpdwData, &cbSize); if (ERROR_SUCCESS != lResult) { __leave; } fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } } //finally return fResult; } /*++ Routine Description: Sets a DWORD value in the registry. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszValueName - Name of data value. dwData - Value to store. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::SetDword( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName, IN DWORD dwData ) { LONG lResult; BOOL fResult = FALSE; HKEY hLocalKey = NULL; if (!hKey) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_SET_VALUE); if (!hLocalKey) { __leave; } } // // Make the call to set the data. // lResult = RegSetValueEx(hLocalKey, pszValueName, 0, REG_DWORD, (const BYTE*)&dwData, sizeof(DWORD)); if (ERROR_SUCCESS != lResult) { __leave; } fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } } // finally return fResult; } /*++ Routine Description: Sets a string value in the registry. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszValueName - Name of data value. pszData - Value to store. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::SetString( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName, IN LPCTSTR pszData ) { HKEY hLocalKey = NULL; BOOL fResult = FALSE; LONG lResult; if (!hKey) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_SET_VALUE); if (!hLocalKey) { __leave; } } lResult = RegSetValueEx(hLocalKey, pszValueName, 0, REG_SZ, (const BYTE*)pszData, _tcslen(pszData) + 1); if (ERROR_SUCCESS != lResult) { __leave; } fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } } // finally return fResult; } /*++ Routine Description: Sets a MULTI_SZ string value in the registry. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszValueName - Name of data value. pszData - Value to store. cbSize - Size of the data to store. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::SetMultiSzString( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName, IN LPCTSTR pszData, IN DWORD cbSize ) { HKEY hLocalKey = NULL; BOOL fResult = FALSE; LONG lResult; if (!hKey) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_SET_VALUE); if (!hLocalKey) { __leave; } } lResult = RegSetValueEx(hLocalKey, pszValueName, 0, REG_MULTI_SZ, (const BYTE*)pszData, cbSize); if (ERROR_SUCCESS != lResult) { __leave; } fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } } // finally return fResult; } /*++ Routine Description: Deletes the specified value from the registry. Arguments: hKey - Handle to a predefined key. pszSubKey - Path to the subkey. pszValueName - Name of the value to delete. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::DeleteString( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszValueName ) { HKEY hLocalKey = NULL; BOOL fResult = FALSE; LONG lResult; if (!hKey) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_SET_VALUE); if (NULL == hLocalKey) { __leave; } } // // Delete the value. // lResult = RegDeleteValue(hLocalKey, pszValueName); if (ERROR_SUCCESS != lResult) { __leave; } fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } } // finally return fResult; } /*++ Routine Description: Adds a string to a REG_MULTI_SZ key. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszEntry - Name of entry to add. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::AddStringToMultiSz( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszEntry ) { int nLen = 0; HKEY hLocalKey = NULL; DWORD cbSize = 0, dwType = 0; LPTSTR pszNew = NULL, pszKey = NULL; BOOL fResult = FALSE; LONG lResult; if (!hKey || !pszEntry) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_QUERY_VALUE | KEY_SET_VALUE); if (NULL == hLocalKey) { __leave; } } // // Get the required string size and allocate // memory for the actual call. // cbSize = this->GetStringSize(hLocalKey, pszEntry, &dwType); if (0 == cbSize || dwType != REG_MULTI_SZ) { __leave; } pszKey = (LPSTR)this->Malloc(cbSize * sizeof(TCHAR)); if (!pszKey) { __leave; } // // Get the actual data. // lResult = RegQueryValueEx(hLocalKey, pszEntry, 0, 0, (LPBYTE)pszKey, &cbSize); if (ERROR_SUCCESS != lResult) { __leave; } pszNew = pszKey; while (*pszNew) { nLen = _tcslen(pszNew); // // Move to the next string. // pszNew += nLen + 1; // // At end of list of strings, append here. // if (!*pszNew) { StringCchCopy(pszNew, nLen, pszEntry); pszNew += _tcslen(pszEntry) + 1; *pszNew = 0; nLen = this->ListStoreLen(pszKey); lResult = RegSetValueEx(hKey, pszEntry, 0, REG_MULTI_SZ, (const BYTE*)pszKey, nLen); if (lResult != ERROR_SUCCESS) { __leave; } else { fResult = TRUE; } break; } } } // try __finally { if (pszKey) { this->Free(pszKey); } if (hLocalKey) { RegCloseKey(hKey); } } // finally return fResult; } /*++ Routine Description: Removes a string from a REG_MULTI_SZ key. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszEntry - Name of entry to remove. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::RemoveStringFromMultiSz( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszEntry ) { LPBYTE lpBuf = NULL; HKEY hLocalKey = NULL; TCHAR* pszFirst = NULL; TCHAR* pszSecond = NULL; DWORD dwType = 0, cbSize = 0; DWORD dwNameLen = 0, dwNameOffset = 0, dwSize = 0; BOOL fResult = FALSE; LONG lResult; if (!hKey || !pszEntry) { return FALSE; } __try { hLocalKey = hKey; if (IsPredefinedRegistryHandle(hKey)) { // // We'll need to open the key for them. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_QUERY_VALUE | KEY_SET_VALUE); if (!hLocalKey) { __leave; } } // // Get the required string size and allocate // memory for the actual call. // cbSize = this->GetStringSize(hLocalKey, pszEntry, &dwType); if (0 == cbSize || dwType != REG_MULTI_SZ) { __leave; } lpBuf = (LPBYTE)this->Malloc(cbSize * sizeof(TCHAR)); if (!lpBuf) { __leave; } // // Get the actual data. // lResult = RegQueryValueEx(hLocalKey, pszEntry, 0, 0, (LPBYTE)lpBuf, &cbSize); if (ERROR_SUCCESS != lResult) { __leave; } // // Attempt to find the string we're looking for. // for (pszFirst = (TCHAR*)lpBuf; *pszFirst; pszFirst += dwNameLen) { dwNameLen = _tcslen(pszFirst) + 1; // Length of name plus NULL dwNameOffset += dwNameLen; // // Check for a match. // if (!_tcsicmp(pszFirst, pszEntry)) { dwSize = _tcslen(pszFirst) + 1; // Length of name pszSecond = (TCHAR*)pszFirst + dwSize; while(*pszSecond) while(*pszSecond) *pszFirst++ = *pszSecond++; *pszFirst++ = *pszSecond++; *pszFirst = '\0'; // // Found a match - update the key. // lResult = RegSetValueEx(hLocalKey, pszEntry, 0, REG_MULTI_SZ, (const BYTE*)lpBuf, cbSize - dwSize); if (lResult != ERROR_SUCCESS) { __leave; } else { fResult = TRUE; } break; } } } // try __finally { if (lpBuf) { this->Free(lpBuf); } if (hLocalKey) { RegCloseKey(hLocalKey); } } // finally return fResult; } /*++ Routine Description: Determines if the specified subkey is present. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. Return Value: TRUE if it's present, FALSE otherwise. --*/ BOOL CRegistry::IsRegistryKeyPresent( IN HKEY hKey, IN LPCTSTR pszSubKey ) { BOOL fResult = FALSE; HKEY hLocalKey = NULL; if (!hKey || !pszSubKey) { return FALSE; } __try { hLocalKey = hKey; // // Check for the presence of the key. // hLocalKey = this->OpenKey(hKey, pszSubKey, KEY_QUERY_VALUE); if (NULL == hLocalKey) { __leave; } else { fResult = TRUE; } } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } } // finally return fResult; } /*++ Routine Description: Restores the specified registry key. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszFileName - Path & name of the file to restore. fGrantPrivs - Flag to indicate if we should grant privileges to the user. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::RestoreKey( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszFileName, IN BOOL fGrantPrivs ) { BOOL fResult = FALSE; HKEY hLocalKey = NULL; LONG lResult; if (!hKey || !pszSubKey || !pszFileName) { return FALSE; } __try { // // If necessary, grant privileges for the restore // if (fGrantPrivs) { this->ModifyTokenPrivilege(_T("SeRestorePrivilege"), TRUE); } lResult = RegCreateKeyEx(hKey, pszSubKey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hLocalKey, 0); if (ERROR_SUCCESS != lResult) { __leave; } // // Restore the key from the specified file. // lResult = RegRestoreKey(hLocalKey, pszFileName, REG_FORCE_RESTORE); if (ERROR_SUCCESS != lResult) { __leave; } RegFlushKey(hLocalKey); fResult = TRUE; } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } if (fGrantPrivs) { this->ModifyTokenPrivilege(_T("SeRestorePrivilege"), FALSE); } } // finally return fResult; } /*++ Routine Description: Makes a backup of the specified registry key. Arguments: hKey - Predefined or open key handle. pszSubKey - Path to the subkey. pszFileName - Path & name of the file to restore. fGrantPrivs - Flag to indicate if we should grant privileges to the user. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::BackupRegistryKey( IN HKEY hKey, IN LPCTSTR pszSubKey, IN LPCTSTR pszFileName, IN BOOL fGrantPrivs ) { BOOL fResult = FALSE; HKEY hLocalKey = NULL; DWORD dwDisposition; LONG lResult; if (!hKey || !pszSubKey || !pszFileName) { return FALSE; } __try { if (fGrantPrivs) { ModifyTokenPrivilege(_T("SeBackupPrivilege"), TRUE); } lResult = RegCreateKeyEx(hKey, pszSubKey, 0, NULL, REG_OPTION_BACKUP_RESTORE, KEY_QUERY_VALUE, // this argument is ignored NULL, &hLocalKey, &dwDisposition); if (ERROR_SUCCESS != lResult) { __leave; } // // Verify that we didn't create a new key. // if (REG_CREATED_NEW_KEY == dwDisposition) { __leave; } // // Save the key to the file. // lResult = RegSaveKey(hLocalKey, pszFileName, NULL); if (ERROR_SUCCESS != lResult) { __leave; } else { fResult = TRUE; } } // try __finally { if (hLocalKey) { RegCloseKey(hLocalKey); } if (fGrantPrivs) { this->ModifyTokenPrivilege(_T("SeBackupPrivilege"), FALSE); } } // finally return fResult; } /*++ Routine Description: Helper function that calculates the size of MULTI_SZ string. Arguments: pszList - MULTI_SZ string. Return Value: Size of the string. --*/ int CRegistry::ListStoreLen( IN LPTSTR pszList ) { int nStoreLen = 2, nLen = 0; if (!pszList) { return 0; } while (*pszList) { nLen = _tcslen(pszList) + 1; nStoreLen += nLen * 2; pszList += nLen; } return nStoreLen; } /*++ Routine Description: Enables or disables a specified privilege. Arguments: pszPrivilege - The name of the privilege. fEnable - A flag to indicate if the privilege should be enabled. Return Value: TRUE on success, FALSE otherwise. --*/ BOOL CRegistry::ModifyTokenPrivilege( IN LPCTSTR pszPrivilege, IN BOOL fEnable ) { HANDLE hToken = NULL; LUID luid; BOOL bResult = FALSE; BOOL bReturn; TOKEN_PRIVILEGES tp; if (!pszPrivilege) { return FALSE; } __try { // // Get a handle to the access token associated with the current process. // OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (!hToken) { __leave; } // // Obtain a LUID for the specified privilege. // if (!LookupPrivilegeValue(NULL, pszPrivilege, &luid)) { __leave; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0; // // Modify the access token. // bReturn = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); if (!bReturn || GetLastError() == ERROR_NOT_ALL_ASSIGNED) { __leave; } bResult = TRUE; } // try __finally { if (hToken) { CloseHandle(hToken); } } // finally return bResult; }