#include "priv.h" #include #ifdef _X86_ #include #endif // These private helper functions below call ANSI versions of the // registry functions so they will run on Win95. STDAPI_(DWORD) RegData_AtoW( IN LPCVOID pvData, IN DWORD dwSize, IN DWORD dwType, /*INOUT*/ LPDWORD pcbData ) { DWORD dwRet = NO_ERROR; if (REG_SZ == dwType || REG_EXPAND_SZ == dwType || REG_MULTI_SZ == dwType) { DWORD cbData = pcbData ? *pcbData : -1; if (pvData) { // Allocate a temporary buffer to work with int cch = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pvData, cbData, NULL, 0); LPWSTR pwszT = (LPWSTR)LocalAlloc(LPTR, CbFromCchW(cch)); if (!pwszT) dwRet = ERROR_NOT_ENOUGH_MEMORY; else { if (CbFromCchW(cch) > dwSize) dwRet = ERROR_MORE_DATA; else { // Convert it to the temporary buffer MultiByteToWideChar(CP_ACP, 0, (LPSTR)pvData, cbData, pwszT, cch); // Copy it back to the output buffer // pwszT can be REG_MULTI_SZ, so we can't use StrCpyW memcpy((LPVOID)pvData, (LPVOID)pwszT, CbFromCchW(cch)); } LocalFree(pwszT); } // If string data, make room for unicode if (pcbData) { (*pcbData) = cch * sizeof(WCHAR); } } else if (pcbData) { // We don't have the data so guess (actual value may be less) // Does this need to be exact? For now this seem sufficient. (Stevepro) (*pcbData) *= sizeof(WCHAR); } } return dwRet; } /*---------------------------------------------------------- Purpose: Helper function to delete a key that has no subkeys and no values. Otherwise does nothing. Mimics what RegDeleteKey does on NT. Returns: Cond: -- */ DWORD DeleteEmptyKey( IN HKEY hkey, IN LPCSTR pszSubKey) { DWORD dwRet; HKEY hkeyNew; dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, KEY_READ | KEY_SET_VALUE, &hkeyNew); if (NO_ERROR == dwRet) { DWORD ckeys; DWORD cvalues; // Are there any subkeys or values? dwRet = RegQueryInfoKey(hkeyNew, NULL, NULL, NULL, &ckeys, NULL, NULL, &cvalues, NULL, NULL, NULL, NULL); if (NO_ERROR == dwRet && 0 == cvalues && 0 == ckeys) { // No; delete the subkey dwRet = RegDeleteKeyA(hkey, pszSubKey); } else { dwRet = ERROR_DIR_NOT_EMPTY; } RegCloseKey(hkeyNew); } return dwRet; } /*---------------------------------------------------------- Purpose: Recursively delete the key, including all child values and keys. Mimics what RegDeleteKey does in Win95. Returns: Cond: -- */ DWORD DeleteKeyRecursively( IN HKEY hkey, IN LPCSTR pszSubKey) { DWORD dwRet; HKEY hkSubKey; // Open the subkey so we can enumerate any children dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, MAXIMUM_ALLOWED, &hkSubKey); if (ERROR_SUCCESS == dwRet) { DWORD dwIndex; CHAR szSubKeyName[MAX_PATH + 1]; DWORD cchSubKeyName = ARRAYSIZE(szSubKeyName); // I can't just call RegEnumKey with an ever-increasing index, because // I'm deleting the subkeys as I go, which alters the indices of the // remaining subkeys in an implementation-dependent way. In order to // be safe, I have to count backwards while deleting the subkeys. // Find out how many subkeys there are dwRet = RegQueryInfoKeyA(hkSubKey, NULL, NULL, NULL, &dwIndex, // The # of subkeys -- all we need NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (NO_ERROR == dwRet) { // dwIndex is now the count of subkeys, but it needs to be // zero-based for RegEnumKey, so I'll pre-decrement, rather // than post-decrement. while (ERROR_SUCCESS == RegEnumKeyA(hkSubKey, --dwIndex, szSubKeyName, cchSubKeyName)) { DeleteKeyRecursively(hkSubKey, szSubKeyName); } } RegCloseKey(hkSubKey); if (pszSubKey) { dwRet = RegDeleteKeyA(hkey, pszSubKey); } else { // we want to delete all the values by hand cchSubKeyName = ARRAYSIZE(szSubKeyName); while (ERROR_SUCCESS == RegEnumValueA(hkey, 0, szSubKeyName, &cchSubKeyName, NULL, NULL, NULL, NULL)) { // avoid looping infinitely when we cant delete the value if (RegDeleteValueA(hkey, szSubKeyName)) break; cchSubKeyName = ARRAYSIZE(szSubKeyName); } } } return dwRet; } /*---------------------------------------------------------- Purpose: Gets a registry value. This opens and closes the key in which the value resides. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and set/get the values with the regular Win32 registry functions, rather than using this function repeatedly. Returns: Cond: -- */ STDAPI_(DWORD) SHGetValueA( IN HKEY hkey, IN LPCSTR pszSubKey, OPTIONAL IN LPCSTR pszValue, OPTIONAL OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData) OPTIONAL { DWORD dwRet = ERROR_INVALID_PARAMETER; HKEY hkClose = NULL; if (pvData) { RIPMSG(pcbData != NULL, "SHGetValueA: caller passed pvData output buffer but not size of buffer (pcbData)!"); } if (pszSubKey && *pszSubKey) { dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, KEY_QUERY_VALUE, &hkClose); hkey = hkClose; ASSERT(NO_ERROR == dwRet || !hkey); } if (hkey) { dwRet = SHQueryValueExA(hkey, pszValue, NULL, pdwType, pvData, pcbData); if (hkClose) RegCloseKey(hkClose); } else if (pcbData) *pcbData = 0; return dwRet; } /*---------------------------------------------------------- Purpose: Gets a registry value. This opens and closes the key in which the value resides. On Win95, this function thunks and calls the ansi version. On NT, this function calls the unicode registry APIs. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and set/get the values with the regular Win32 registry functions, rather than using this function repeatedly. Returns: Cond: -- */ STDAPI_(DWORD) SHGetValueW( IN HKEY hkey, IN LPCWSTR pwszSubKey, OPTIONAL IN LPCWSTR pwszValue, OPTIONAL OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData) OPTIONAL { DWORD dwRet = ERROR_INVALID_PARAMETER; if (pvData) { RIPMSG(pcbData != NULL, "SHGetValueW: caller passed pvData output buffer but not size of buffer (pcbData)!"); } if (g_bRunningOnNT) { HKEY hkClose = NULL; if (pwszSubKey && *pwszSubKey) { dwRet = RegOpenKeyExW(hkey, pwszSubKey, 0, KEY_QUERY_VALUE, &hkClose); hkey = hkClose; ASSERT(NO_ERROR == dwRet || !hkey); } if (hkey) { dwRet = SHQueryValueExW(hkey, pwszValue, NULL, pdwType, pvData, pcbData); if (hkClose) RegCloseKey(hkClose); } else if (pcbData) *pcbData = 0; } else { CHAR szSubKey[MAX_PATH]; CHAR szValue[MAX_PATH]; LPSTR pszSubKey = NULL; LPSTR pszValue = NULL; DWORD dwType; DWORD cbSizeSav = 0; // Thunk the values if (pwszSubKey) { WideCharToMultiByte(CP_ACP, 0, pwszSubKey, -1, szSubKey, SIZECHARS(szSubKey), NULL, NULL); pszSubKey = szSubKey; } if (pwszValue) { WideCharToMultiByte(CP_ACP, 0, pwszValue, -1, szValue, SIZECHARS(szValue), NULL, NULL); pszValue = szValue; } if (pcbData) cbSizeSav = *pcbData; // Save this size for later dwRet = SHGetValueA(hkey, pszSubKey, pszValue, &dwType, pvData, pcbData); if (pdwType) *pdwType = dwType; if (NO_ERROR == dwRet) { dwRet = RegData_AtoW(pvData, cbSizeSav, dwType, pcbData); // Thunk data from ANSI->UNICODE if needed. } } return dwRet; } /*---------------------------------------------------------- Purpose: Sets a registry value. This opens and closes the key in which the value resides. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and set/get the values with the regular Win32 registry functions, rather than using this function repeatedly. Returns: Cond: -- */ STDAPI_(DWORD) SHSetValueA( IN HKEY hkey, IN OPTIONAL LPCSTR pszSubKey, IN LPCSTR pszValue, IN DWORD dwType, IN LPCVOID pvData, IN DWORD cbData) { DWORD dwRet = NO_ERROR; HKEY hkeyNew; DWORD dwDisp; if (pszSubKey && pszSubKey[0]) dwRet = RegCreateKeyExA(hkey, pszSubKey, 0, "", 0, KEY_SET_VALUE, NULL, &hkeyNew, &dwDisp); else hkeyNew = hkey; if (NO_ERROR == dwRet) { dwRet = RegSetValueExA(hkeyNew, pszValue, 0, dwType, pvData, cbData); if (hkeyNew != hkey) RegCloseKey(hkeyNew); } return dwRet; } /*---------------------------------------------------------- Purpose: Sets a registry value. This opens and closes the key in which the value resides. On Win95, this function thunks and calls the ansi version. On NT, this function calls the unicode registry APIs directly. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and set/get the values with the regular Win32 registry functions, rather than using this function repeatedly. Returns: Cond: -- */ STDAPI_(DWORD) SHSetValueW( IN HKEY hkey, IN OPTIONAL LPCWSTR pwszSubKey, IN LPCWSTR pwszValue, IN DWORD dwType, IN LPCVOID pvData, IN DWORD cbData) { DWORD dwRet = NO_ERROR; HKEY hkeyNew; DWORD dwDisp; if (pwszSubKey && pwszSubKey[0]) { if (g_bRunningOnNT) { dwRet = RegCreateKeyExW(hkey, pwszSubKey, 0, TEXTW(""), 0, KEY_SET_VALUE, NULL, &hkeyNew, &dwDisp); } else { CHAR szSubKey[MAX_PATH]; LPSTR pszSubKey = NULL; if (pwszSubKey) { WideCharToMultiByte(CP_ACP, 0, pwszSubKey, -1, szSubKey, SIZECHARS(szSubKey), NULL, NULL); pszSubKey = szSubKey; } dwRet = RegCreateKeyExA(hkey, pszSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkeyNew, &dwDisp); } } else hkeyNew = hkey; if (NO_ERROR == dwRet) { // RegSetValueExW is not supported on Win95 but we have a thunking function. dwRet = RegSetValueExW(hkeyNew, pwszValue, 0, dwType, pvData, cbData); if (hkeyNew != hkey) RegCloseKey(hkeyNew); } return dwRet; } /*---------------------------------------------------------- Purpose: Deletes a registry value. This opens and closes the key in which the value resides. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and set/get the values with the regular Win32 registry functions, rather than using this function repeatedly. Returns: Cond: -- */ STDAPI_(DWORD) SHDeleteValueA( IN HKEY hkey, IN LPCSTR pszSubKey, IN LPCSTR pszValue) { DWORD dwRet; HKEY hkeyNew; dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, KEY_SET_VALUE, &hkeyNew); if (NO_ERROR == dwRet) { dwRet = RegDeleteValueA(hkeyNew, pszValue); RegCloseKey(hkeyNew); } return dwRet; } /*---------------------------------------------------------- Purpose: Deletes a registry value. This opens and closes the key in which the value resides. On Win95, this function thunks and calls the ansi version. On NT, this function calls the unicode registry APIs directly. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and set/get the values with the regular Win32 registry functions, rather than using this function repeatedly. Returns: Cond: -- */ STDAPI_(DWORD) SHDeleteValueW( IN HKEY hkey, IN LPCWSTR pwszSubKey, IN LPCWSTR pwszValue) { DWORD dwRet; HKEY hkeyNew; if (g_bRunningOnNT) { dwRet = RegOpenKeyExW(hkey, pwszSubKey, 0, KEY_SET_VALUE, &hkeyNew); if (NO_ERROR == dwRet) { dwRet = RegDeleteValueW(hkeyNew, pwszValue); RegCloseKey(hkeyNew); } } else { CHAR szSubKey[MAX_PATH]; CHAR szValue[MAX_PATH]; LPSTR pszSubKey = NULL; LPSTR pszValue = NULL; if (pwszSubKey) { WideCharToMultiByte(CP_ACP, 0, pwszSubKey, -1, szSubKey, SIZECHARS(szSubKey), NULL, NULL); pszSubKey = szSubKey; } if (pwszValue) { WideCharToMultiByte(CP_ACP, 0, pwszValue, -1, szValue, SIZECHARS(szValue), NULL, NULL); pszValue = szValue; } dwRet = SHDeleteValueA(hkey, pszSubKey, pszValue); } return dwRet; } // purpose: recursively copy subkeys and values of hkeySrc\pszSrcSubKey to hkeyDest // e.g. hkeyExplorer = HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ // SHCopyKey(HKEY_CURRENT_USER, "Software\\Classes\\", hkeyExplorer, 0) // results in // ...\\CurrentVersion\\Explorer\\ // Appid // CLSID\\ // {xxxx yyyyy ...} // Interface // ... // TO DO: currently we are not copying the ACL's but in the future we should do that // upon request that's what fReserved is for // NOTE that there is no hkeyDest, pszDestSubKey pair like src one, because in case // pszDestSubKey did not exist we would have to create it and deal with Class name // which would just clober the parameter list STDAPI_(DWORD) SHCopyKeyA(HKEY hkeySrc, LPCSTR pszSrcSubKey, HKEY hkeyDest, DWORD fReserved) { HKEY hkeyFrom; DWORD dwRet; if (pszSrcSubKey) dwRet = RegOpenKeyExA(hkeySrc, pszSrcSubKey, 0, MAXIMUM_ALLOWED, &hkeyFrom); else if (hkeySrc) { dwRet = ERROR_SUCCESS; hkeyFrom = hkeySrc; } else dwRet = ERROR_INVALID_PARAMETER; if (dwRet == ERROR_SUCCESS) { DWORD dwIndex; DWORD cchValueSize; DWORD cchClassSize; DWORD dwType; CHAR szValue[MAX_PATH]; //NOTE:szValue is also used to store subkey name when enumerating keys CHAR szClass[MAX_PATH]; cchValueSize = ARRAYSIZE(szValue); cchClassSize = ARRAYSIZE(szClass); for (dwIndex=0; dwRet == ERROR_SUCCESS && (dwRet = RegEnumKeyExA(hkeyFrom, dwIndex, szValue, &cchValueSize, NULL, szClass, &cchClassSize, NULL)) == ERROR_SUCCESS; dwIndex++, cchValueSize = ARRAYSIZE(szValue), cchClassSize = ARRAYSIZE(szClass)) { HKEY hkeyTo; DWORD dwDisp; // create new key dwRet = RegCreateKeyExA(hkeyDest, szValue, 0, szClass, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hkeyTo, &dwDisp); if (dwRet != ERROR_SUCCESS) break; dwRet = SHCopyKeyA(hkeyFrom, szValue, hkeyTo, fReserved); //if not error_success we break out RegCloseKey(hkeyTo); } // copied all the sub keys, now copy all the values if (dwRet == ERROR_NO_MORE_ITEMS) { DWORD cb, cbBufferSize; LPBYTE lpbyBuffer; // get the max value size dwRet = RegQueryInfoKey(hkeyFrom, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cb, NULL, NULL); if (dwRet == ERROR_SUCCESS) { // allocate buffer cb++; // add 1 just in case of a string lpbyBuffer = (LPBYTE)LocalAlloc(LPTR, cb); if (lpbyBuffer) cbBufferSize = cb; else dwRet = ERROR_OUTOFMEMORY; cchValueSize = ARRAYSIZE(szValue); for (dwIndex=0; dwRet == ERROR_SUCCESS && (dwRet = RegEnumValueA(hkeyFrom, dwIndex, szValue, &cchValueSize, NULL, &dwType, lpbyBuffer, &cb)) == ERROR_SUCCESS; dwIndex++, cchValueSize = ARRAYSIZE(szValue), cb = cbBufferSize) { // cb has the size of the value so use it rather than cbBufferSize which is just max size dwRet = RegSetValueExA(hkeyDest, szValue, 0, dwType, lpbyBuffer, cb); if (dwRet != ERROR_SUCCESS) break; } if (lpbyBuffer != NULL) LocalFree(lpbyBuffer); } } if (dwRet == ERROR_NO_MORE_ITEMS) dwRet = ERROR_SUCCESS; if (pszSrcSubKey) RegCloseKey(hkeyFrom); } return dwRet; } STDAPI_(DWORD) SHCopyKeyW(HKEY hkeySrc, LPCWSTR pwszSrcSubKey, HKEY hkeyDest, DWORD fReserved) { CHAR sz[MAX_PATH]; if (pwszSrcSubKey) WideCharToMultiByte(CP_ACP, 0, pwszSrcSubKey, -1, sz, SIZECHARS(sz), NULL, NULL); return SHCopyKeyA(hkeySrc, pwszSrcSubKey ? sz : NULL, hkeyDest, fReserved); } /*---------------------------------------------------------- Purpose: Delete a key only if there are no subkeys or values. It comes close to mimicking the behavior of RegDeleteKey as it works on NT, except the NT version ignores values. */ STDAPI_(DWORD) SHDeleteEmptyKeyA( IN HKEY hkey, IN LPCSTR pszSubKey) { return DeleteEmptyKey(hkey, pszSubKey); } /*---------------------------------------------------------- Purpose: Delete a key only if there are no subkeys or values. It comes close to mimicking the behavior of RegDeleteKey as it works on NT, except the NT version ignores values. */ STDAPI_(DWORD) SHDeleteEmptyKeyW( IN HKEY hkey, IN LPCWSTR pwszSubKey) { CHAR sz[MAX_PATH]; WideCharToMultiByte(CP_ACP, 0, pwszSubKey, -1, sz, SIZECHARS(sz), NULL, NULL); return DeleteEmptyKey(hkey, sz); } /*---------------------------------------------------------- Purpose: Recursively delete the key, including all child values and keys. Returns: Cond: -- */ STDAPI_(DWORD) SHDeleteKeyA( IN HKEY hkey, IN LPCSTR pszSubKey) { DWORD dwRet; if (g_bRunningOnNT || !pszSubKey) { dwRet = DeleteKeyRecursively(hkey, pszSubKey); } else { // On Win95, RegDeleteKey does what we want dwRet = RegDeleteKeyA(hkey, pszSubKey); } return dwRet; } /*---------------------------------------------------------- Purpose: Recursively delete the key, including all child values and keys. Returns: Cond: -- */ STDAPI_(DWORD) SHDeleteKeyW( IN HKEY hkey, IN LPCWSTR pwszSubKey) { DWORD dwRet; CHAR sz[MAX_PATH]; WideCharToMultiByte(CP_ACP, 0, pwszSubKey, -1, sz, SIZECHARS(sz), NULL, NULL); if (g_bRunningOnNT) { dwRet = DeleteKeyRecursively(hkey, sz); } else { // On Win95, RegDeleteKey does what we want dwRet = RegDeleteKeyA(hkey, sz); } return dwRet; } STDAPI_(DWORD) FixupRegStringA(HKEY hk, PCSTR pszValue, BOOL fExpand, DWORD err, void *pvData, DWORD *pcbData, DWORD *pcbSize); STDAPI_(DWORD) FixupRegStringW(HKEY hk, PCWSTR pszValue, BOOL fExpand, DWORD err, void *pvData, DWORD *pcbData, DWORD *pcbSize); /*---------------------------------------------------------- Purpose: Behaves just like RegQueryValueEx, except if the data type is REG_EXPAND_SZ, then this goes ahead and expands out the string. *pdwType will always be massaged to REG_SZ if this happens. Returns: Cond: -- */ STDAPI_(DWORD) SHQueryValueExA( IN HKEY hkey, IN LPCSTR pszValue, IN LPDWORD lpReserved, OUT LPDWORD pdwType, OUT LPVOID pvData, IN OUT LPDWORD pcbData) { DWORD dwRet; DWORD cbSize = 0; DWORD dwType = REG_NONE; DWORD cbOriginal = 0; if (pvData) { RIPMSG(pcbData != NULL, "SHQueryValuegExW: caller passed pvData output buffer but not size of buffer (pcbData)!"); } // Trying to get back data if (pcbData) { cbOriginal = cbSize = *pcbData; // Size of output buffer } dwRet = RegQueryValueExA(hkey, pszValue, lpReserved, &dwType, pvData, &cbSize); // Normally, we'd be done with this. But do some extra work // if this is an expandable string (something that has system // variables in it), or if we need to pad the buffer. if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) { dwRet = FixupRegStringA(hkey, pszValue, REG_EXPAND_SZ == dwType, dwRet, pvData, pcbData, &cbSize); // Massage dwType so that callers always see REG_SZ dwType = REG_SZ; } if (pdwType) *pdwType = dwType; if (pcbData) *pcbData = cbSize; return dwRet; } /*---------------------------------------------------------- Purpose: Behaves just like RegQueryValueEx, except if the data type is REG_EXPAND_SZ, then this goes ahead and expands out the string. *pdwType will always be massaged to REG_SZ if this happens. Returns: Cond: -- */ STDAPI_(DWORD) SHQueryValueExW( IN HKEY hkey, IN LPCWSTR pwszValue, IN LPDWORD lpReserved, OUT LPDWORD pdwType, OUT LPVOID pvData, IN OUT LPDWORD pcbData) { DWORD dwRet; DWORD cbSize = 0; DWORD dwType = REG_NONE; if (pvData) { RIPMSG(pcbData != NULL, "SHQueryValueExW: caller passed pvData output buffer but not size of buffer (pcbData)!"); } if ( !g_bRunningOnNT ) { CHAR szValue[MAX_PATH]; LPSTR pszValue = NULL; DWORD dwOriginalSize = 0; if (pvData && pcbData) dwOriginalSize = *pcbData; if (pwszValue) { WideCharToMultiByte(CP_ACP, 0, pwszValue, -1, szValue, SIZECHARS(szValue), NULL, NULL); pszValue = szValue; } if (!pdwType) pdwType = &dwType; dwRet = SHQueryValueExA(hkey, pszValue, lpReserved, pdwType, pvData, pcbData); if (NO_ERROR == dwRet) dwRet = RegData_AtoW(pvData, dwOriginalSize, *pdwType, pcbData); // Thunk data from ANSI->UNICODE if needed. } else { // Running on NT // Trying to get back data if (pvData && pcbData) { cbSize = *pcbData; } dwRet = RegQueryValueExW(hkey, pwszValue, lpReserved, &dwType, pvData, &cbSize); // Normally, we'd be done with this. But do some extra work // if this is an expandable string (something that has system // variables in it), or if we need to pad the buffer. if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) { dwRet = FixupRegStringW(hkey, pwszValue, REG_EXPAND_SZ == dwType, dwRet, pvData, pcbData, &cbSize); // Massage dwType so that callers always see REG_SZ dwType = REG_SZ; } if (pdwType) *pdwType = dwType; if (pcbData) *pcbData = cbSize; } return dwRet; } /*---------------------------------------------------------- Purpose: Behaves just like RegEnumKeyExA, except it does not let you look at the class and timestamp of the sub-key. Written to provide equivalent for SHEnumKeyExW which is useful on Win95. Returns: Cond: -- */ STDAPI_(LONG) SHEnumKeyExA ( IN HKEY hkey, IN DWORD dwIndex, OUT LPSTR pszName, IN OUT LPDWORD pcchName ) { return RegEnumKeyExA(hkey, dwIndex, pszName, pcchName, NULL, NULL, NULL, NULL); } /*---------------------------------------------------------- Purpose: Behaves just like RegEnumKeyExW, except it does not let you look at the class and timestamp of the sub-key. Wide char version supported under Win95. Returns: Cond: -- */ STDAPI_(LONG) SHEnumKeyExW ( IN HKEY hkey, IN DWORD dwIndex, OUT LPWSTR pszName, IN OUT LPDWORD pcchName ) { LONG lRet = NO_ERROR; if ( !g_bRunningOnNT ) { // We are reading in sub-key names. regapix.h suggests MAXIMUM_SUB_KEY_LENGTH = 256, // so MAX_PATH should be enough. Is this the official limit. CHAR sz[MAX_PATH]; DWORD dwSize = sizeof(sz); lRet = RegEnumKeyExA(hkey, dwIndex, sz, &dwSize, NULL, NULL, NULL, NULL); if (NO_ERROR != lRet) { ASSERT(ERROR_MORE_DATA != lRet); } else { int cch = MultiByteToWideChar(CP_ACP, 0, sz, -1, NULL, 0); if (NULL == pcchName) { lRet = ERROR_INVALID_PARAMETER; } else if (NULL == pszName || *pcchName < (DWORD)cch ) { *pcchName = cch; lRet = (NULL == pszName) ? ERROR_SUCCESS : ERROR_MORE_DATA ; } else { // Not supposed to include null terminator in count if successful. *pcchName = cch - 1; MultiByteToWideChar(CP_ACP, 0, sz, -1, pszName, cch); ASSERT(NO_ERROR == lRet); } } } else { lRet = RegEnumKeyExW(hkey, dwIndex, pszName, pcchName, NULL, NULL, NULL, NULL); } return lRet; } /*---------------------------------------------------------- Purpose: Behaves just like RegEnumValueA. Written to provide equivalent for SHEnumKeyExW which is useful on Win95. Environment vars in a string are NOT expanded. Returns: Cond: -- */ STDAPI_(LONG) SHEnumValueA ( IN HKEY hkey, IN DWORD dwIndex, OUT LPSTR pszValueName, OPTIONAL IN OUT LPDWORD pcchValueName, OPTIONAL OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL IN OUT LPDWORD pcbData OPTIONAL ) { return RegEnumValueA(hkey, dwIndex, pszValueName, pcchValueName, NULL, pdwType, pvData, pcbData); } /*---------------------------------------------------------- Purpose: Behaves just like RegEnumValueW. Wide char version works on Win95. Environment vars in a string are NOT expanded. Returns: Cond: -- */ STDAPI_(LONG) SHEnumValueW ( IN HKEY hkey, IN DWORD dwIndex, OUT LPWSTR pszValueName, OPTIONAL IN OUT LPDWORD pcchValueName, OPTIONAL OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL IN OUT LPDWORD pcbData OPTIONAL ) { LONG lRet; if ( !g_bRunningOnNT ) { CHAR sz[MAX_PATH]; // This should be sufficient to read the value name. DWORD dwSize = sizeof(sz); DWORD dwType; DWORD cbSizeSav = 0; if (pcbData) cbSizeSav = *pcbData; lRet = RegEnumValueA(hkey, dwIndex, sz, &dwSize, NULL, &dwType, pvData, pcbData); if (pdwType) *pdwType = dwType; if (NO_ERROR == lRet) { // Convert the ValueName to unicode. if (pcchValueName) { int cch = MultiByteToWideChar(CP_ACP, 0, sz, -1, NULL, 0); if (NULL == pszValueName || *pcchValueName < (DWORD)cch ) { *pcchValueName = cch; lRet = (NULL == pszValueName) ? ERROR_SUCCESS : ERROR_MORE_DATA ; } else { // Not supposed to include null terminator in count if returning // back name succesfully. *pcchValueName = cch - 1; // cch should be sufficient MultiByteToWideChar(CP_ACP, 0, sz, -1, pszValueName, cch); } } // Next convert the data to unicode if it is a string. if ((NOERROR == lRet) && pcbData) lRet = RegData_AtoW(pvData, cbSizeSav, dwType, pcbData); } } else { lRet = RegEnumValueW(hkey, dwIndex, pszValueName, pcchValueName, NULL, pdwType, pvData, pcbData); } return lRet; } /*---------------------------------------------------------- Purpose: Behaves just like RegQueryInfoKeyA. Written to provide equivalent for W version. Returns: Cond: -- */ STDAPI_(LONG) SHQueryInfoKeyA ( IN HKEY hkey, OUT LPDWORD pcSubKeys, OPTIONAL OUT LPDWORD pcchMaxSubKeyLen, OPTIONAL OUT LPDWORD pcValues, OPTIONAL OUT LPDWORD pcchMaxValueNameLen OPTIONAL ) { return RegQueryInfoKeyA(hkey, NULL, NULL, NULL, pcSubKeys, pcchMaxSubKeyLen, NULL, pcValues, pcchMaxValueNameLen, NULL, NULL, NULL); } /*---------------------------------------------------------- Purpose: Behaves just like RegQueryInfoKeyW. Works on Win95. Returns: Cond: -- */ STDAPI_(LONG) SHQueryInfoKeyW ( IN HKEY hkey, OUT LPDWORD pcSubKeys, OPTIONAL OUT LPDWORD pcchMaxSubKeyLen, OPTIONAL OUT LPDWORD pcValues, OPTIONAL OUT LPDWORD pcchMaxValueNameLen OPTIONAL ) { LONG lRet; if (!g_bRunningOnNT) { lRet = RegQueryInfoKeyA(hkey, NULL, NULL, NULL, pcSubKeys, pcchMaxSubKeyLen, NULL, pcValues, pcchMaxValueNameLen, NULL, NULL, NULL); if (NO_ERROR == lRet) { // *pcchMaxSubKeyLen has the number of single byte chars reqd for the // KeyName. We return back the same value which is an upper limit on // the number of Unicode characters needed. We will ask for more // Unicode chars than required if the string has DBCS characters. // Without knowing the actual string this is the best guess we can take. } } else { lRet = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, pcSubKeys, pcchMaxSubKeyLen, NULL, pcValues, pcchMaxValueNameLen, NULL, NULL, NULL); } return lRet; } /*----------------------------------------------------------*\ USER SPECIFC SETTINGS DESCRIPTION: These functions will be used to query User Specific settings correctly. The installer needs to populate HKLM with User specific settings, because that's the only part of the registry that is shared between all users. Code will then read values from HKCU, and if that's empty, it will look in HKLM. The only exception is that if TRUE is passed in for the fIgnore parameter, then the HKLM version will be used instead of HKCU. This is the way that an admin can specify that they doesn't want users to be able to use their User Specific values (HKCU). \*----------------------------------------------------------*/ typedef struct tagUSKEY { HKEY hkeyCurrentUser; HKEY hkeyCurrentUserRelative; HKEY hkeyLocalMachine; HKEY hkeyLocalMachineRelative; CHAR szSubPath[MAXIMUM_SUB_KEY_LENGTH]; REGSAM samDesired; } USKEY; typedef USKEY * PUSKEY; typedef PUSKEY * PPUSKEY; #define IS_HUSKEY_VALID(pUSKey) (((pUSKey) && IS_VALID_WRITE_PTR((pUSKey), USKEY) && ((pUSKey)->hkeyCurrentUser || (pUSKey)->hkeyLocalMachine))) // Private Helper Function // Bring the out of date key up to date. LONG PrivFullOpen(PUSKEY pUSKey) { LONG lRet = ERROR_SUCCESS; HKEY *phkey = NULL; HKEY *phkeyRel = NULL; ASSERT(IS_HUSKEY_VALID(pUSKey)); // Will always be true, but assert against maintainence mistakes if (!pUSKey->hkeyCurrentUser) // Do we need to open HKCU? { phkey = &(pUSKey->hkeyCurrentUser); phkeyRel = &(pUSKey->hkeyCurrentUserRelative); } if (!pUSKey->hkeyLocalMachine) // Do we need to open HKLM? { phkey = &(pUSKey->hkeyLocalMachine); phkeyRel = &(pUSKey->hkeyLocalMachineRelative); } if ((phkeyRel) && (*phkeyRel)) { ASSERT(phkey); // Will always be true, but assert against maintainence mistakes lRet = RegOpenKeyExA(*phkeyRel, pUSKey->szSubPath, 0, pUSKey->samDesired, phkey); // If we need to bring the out of date key, up to date, we need to free the old one. if ((HKEY_CURRENT_USER != *phkeyRel) && (HKEY_LOCAL_MACHINE != *phkeyRel)) RegCloseKey(*phkeyRel); *phkeyRel = NULL; pUSKey->szSubPath[0] = '\0'; } return lRet; } // Private Helper Function // Bring the out of date key up to date. LONG PrivFullCreate(PUSKEY pUSKey) { LONG lRet = ERROR_SUCCESS; HKEY *phkey = NULL; HKEY *phkeyRel = NULL; ASSERT(IS_HUSKEY_VALID(pUSKey)); // Will always be true, but assert against maintainence mistakes if (!pUSKey->hkeyCurrentUser) // Do we need to open HKCU? { phkey = &(pUSKey->hkeyCurrentUser); phkeyRel = &(pUSKey->hkeyCurrentUserRelative); } if (!pUSKey->hkeyLocalMachine) // Do we need to open HKLM? { phkey = &(pUSKey->hkeyLocalMachine); phkeyRel = &(pUSKey->hkeyLocalMachineRelative); } if ((phkeyRel) && (*phkeyRel)) { ASSERT(phkey); // Will always be true, but assert against maintainence mistakes lRet = RegCreateKeyExA(*phkeyRel, pUSKey->szSubPath, 0, NULL, REG_OPTION_NON_VOLATILE, pUSKey->samDesired, NULL, phkey, NULL); // If we need to bring the out of date key, up to date, we need to free the old one. if ((HKEY_CURRENT_USER != *phkeyRel) && (HKEY_LOCAL_MACHINE != *phkeyRel)) RegCloseKey(*phkeyRel); *phkeyRel = NULL; pUSKey->szSubPath[0] = '\0'; } return lRet; } // Private Helper Function // Create one of the keys (Called for both HKLM and HKCU) LONG PrivCreateKey(LPHKEY lphkey, LPHKEY lphkeyRelative, LPCSTR lpSubPath, REGSAM samDesired) { LONG lRet = ERROR_SUCCESS; if (*lphkeyRelative) { lRet = RegCreateKeyExA(*lphkeyRelative, lpSubPath, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, lphkey, NULL); *lphkeyRelative = NULL; } else { // If the relative key == NULL, then we don't have enough of the path to // create this key. return(ERROR_INVALID_PARAMETER); } return(lRet); } // Private Helper Function // Query for the specific value. LONG PrivRegQueryValue( IN PUSKEY pUSKey, IN HKEY *phkey, IN LPCWSTR pwzValue, // May have been an ANSI String type case. Use fWideChar to determine if so. IN BOOL fWideChar, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData) OPTIONAL { LONG lRet = ERROR_SUCCESS; ASSERT(IS_HUSKEY_VALID(pUSKey)); // Will always be true, but assert against maintainence mistakes // It may be necessary to open the key if (NULL == *phkey) lRet = PrivFullOpen(pUSKey); if ((ERROR_SUCCESS == lRet) && (*phkey)) { if (fWideChar) lRet = SHQueryValueExW(*phkey, pwzValue, NULL, pdwType, pvData, pcbData); else lRet = SHQueryValueExA(*phkey, (LPCSTR)pwzValue, NULL, pdwType, pvData, pcbData); } else lRet = ERROR_INVALID_PARAMETER; return lRet; } // Private Helper Function // Query for the specific value. LONG PrivRegWriteValue( IN PUSKEY pUSKey, IN HKEY *phkey, IN LPCWSTR pwzValue, // May have been an ANSI String type case. Use fWideChar to determine if so. IN BOOL bWideChar, IN BOOL bForceWrite, IN DWORD dwType, OPTIONAL IN LPCVOID pvData, OPTIONAL IN DWORD cbData) OPTIONAL { LONG lRet = ERROR_SUCCESS; ASSERT(IS_HUSKEY_VALID(pUSKey)); // Will always be true, but assert against maintainence mistakes // It may be necessary to open the key if (NULL == *phkey) lRet = PrivFullCreate(pUSKey); // Check if the caller only want's to write value if it's empty if (!bForceWrite) { // Yes we need to check before we write. if (bWideChar) bForceWrite = !(ERROR_SUCCESS == SHQueryValueExW(*phkey, pwzValue, NULL, NULL, NULL, NULL)); else bForceWrite = !(ERROR_SUCCESS == SHQueryValueExA(*phkey, (LPCSTR)pwzValue, NULL, NULL, NULL, NULL)); } if ((ERROR_SUCCESS == lRet) && (*phkey) && bForceWrite) { if (bWideChar) // RegSetValueExW is not supported on Win95 but we have a thunking function. lRet = RegSetValueExW(*phkey, pwzValue, 0, dwType, pvData, cbData); else lRet = RegSetValueExA(*phkey, (LPCSTR)pwzValue, 0, dwType, pvData, cbData); } return lRet; } // Private helper function // Enum sub-keys of a key. LONG PrivRegEnumKey( IN PUSKEY pUSKey, IN HKEY *phkey, IN DWORD dwIndex, IN LPWSTR pwzName, // May have been an ANSI String type case. Use fWideChar to determine if so. IN BOOL fWideChar, IN OUT LPDWORD pcchName ) { LONG lRet = ERROR_SUCCESS; ASSERT(IS_HUSKEY_VALID(pUSKey)); // It may be necessary to open the key if (NULL == *phkey) lRet = PrivFullOpen(pUSKey); if ((ERROR_SUCCESS == lRet) && (*phkey)) { if (fWideChar) lRet = SHEnumKeyExW(*phkey, dwIndex, pwzName, pcchName); else lRet = SHEnumKeyExA(*phkey, dwIndex, (LPSTR)pwzName, pcchName); } else lRet = ERROR_INVALID_PARAMETER; return lRet; } // Private helper function // Enum values of a key. LONG PrivRegEnumValue( IN PUSKEY pUSKey, IN HKEY *phkey, IN DWORD dwIndex, IN LPWSTR pwzValueName, // May have been an ANSI String type case. Use fWideChar to determine if so. IN BOOL fWideChar, IN OUT LPDWORD pcchValueName, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL IN OUT LPDWORD pcbData OPTIONAL ) { LONG lRet = ERROR_SUCCESS; ASSERT(IS_HUSKEY_VALID(pUSKey)); // It may be necessary to open the key if (NULL == *phkey) lRet = PrivFullOpen(pUSKey); if ((ERROR_SUCCESS == lRet) && (*phkey)) { if (fWideChar) lRet = SHEnumValueW(*phkey, dwIndex, pwzValueName, pcchValueName, pdwType, pvData, pcbData); else lRet = SHEnumValueA(*phkey, dwIndex, (LPSTR)pwzValueName, pcchValueName, pdwType, pvData, pcbData); } else lRet = ERROR_INVALID_PARAMETER; return lRet; } // Query the Key information. LONG PrivRegQueryInfoKey( IN PUSKEY pUSKey, IN HKEY *phkey, IN BOOL fWideChar, OUT LPDWORD pcSubKeys, OPTIONAL OUT LPDWORD pcchMaxSubKeyLen, OPTIONAL OUT LPDWORD pcValues, OPTIONAL OUT LPDWORD pcchMaxValueNameLen OPTIONAL ) { LONG lRet = ERROR_SUCCESS; ASSERT(IS_HUSKEY_VALID(pUSKey)); if (NULL == *phkey) lRet = PrivFullOpen(pUSKey); if ((ERROR_SUCCESS == lRet) && (*phkey)) { if (fWideChar) lRet = SHQueryInfoKeyW(*phkey, pcSubKeys, pcchMaxSubKeyLen, pcValues, pcchMaxValueNameLen); else lRet = SHQueryInfoKeyA(*phkey, pcSubKeys, pcchMaxSubKeyLen, pcValues, pcchMaxValueNameLen); } else lRet = ERROR_INVALID_PARAMETER; return lRet; } /*---------------------------------------------------------- Purpose: Create or open a user specifc registry key (HUSKEY). Description: This function will: 1. Allocate a new USKEY structure. 2. Initialize the structure. 3. Create/Open HKLM if that flag is set. 4. Create/Open HKCU if that flag is set. Note that there is no difference between FORCE and don't force in the dwFlags parameter. The hUSKeyRelative parameter should have also been opened by a call to SHRegCreateUSKey. If SHRegOpenUSKey was called, it could have returned ERROR_SUCCESS but still be invalid for calling this function. This will occur if: 1) the parameter fIgnoreHKCU was FALSE, 2) it was a relative open, 3) the HKCU branch could not be opened because it didn't exist, and 4) HKLM opened successfully. This situation renders the HUSKEY valid for reading but not writing. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegCreateUSKeyA( IN LPCSTR pszPath, IN REGSAM samDesired, // security access mask IN HUSKEY hUSKeyRelative, OPTIONAL OUT PHUSKEY phUSKey, IN DWORD dwFlags) // Indicates whether to create/open HKCU, HKLM, or both { PUSKEY pUSKeyRelative = (PUSKEY) hUSKeyRelative; PPUSKEY ppUSKey = (PPUSKEY) phUSKey; PUSKEY pUSKey; LONG lRet = ERROR_SUCCESS; CHAR szTempPath[MAXIMUM_SUB_KEY_LENGTH] = "\0"; LPCSTR lpszHKLMPath = szTempPath; LPCSTR lpszHKCUPath = szTempPath; ASSERT(ppUSKey); // The following are invalid parameters... // 1. ppUSKey cannot be NULL // 2. If this is a relative open, pUSKeyRelative needs to be a valid HUSKEY. // 3. The user needs to have specified one of the following: SHREGSET_HKCU, SHREGSET_FORCE_HKCU, SHREGSET_HKLM, SHREGSET_FORCE_HKLM. if ((!ppUSKey) || // 1. (pUSKeyRelative && FALSE == IS_HUSKEY_VALID(pUSKeyRelative)) || // 2. !(dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU | SHREGSET_HKLM | SHREGSET_FORCE_HKLM))) // 3. { return ERROR_INVALID_PARAMETER; } // The temp path will be used when bringing the keys // up todate that was out of date in the Relative key. if (pUSKeyRelative) { lstrcpyA(szTempPath, pUSKeyRelative->szSubPath); PathAddBackslashA(szTempPath); } StrNCatA(szTempPath, pszPath, ARRAYSIZE(szTempPath) - lstrlenA(szTempPath) - 1); ///// 1. Allocate a new USKEY structure. pUSKey = *ppUSKey = (PUSKEY)LocalAlloc(LPTR, sizeof(USKEY)); if (!pUSKey) return ERROR_NOT_ENOUGH_MEMORY; ///// 2. Initialize the structure. if (!pUSKeyRelative) { // Init a new (non-relative) open. pUSKey->hkeyLocalMachineRelative = HKEY_LOCAL_MACHINE; pUSKey->hkeyCurrentUserRelative = HKEY_CURRENT_USER; } else { // Init a new (relative) open. *pUSKey = *pUSKeyRelative; if (pUSKey->hkeyLocalMachine) { pUSKey->hkeyLocalMachineRelative = pUSKey->hkeyLocalMachine; pUSKey->hkeyLocalMachine = NULL; lpszHKLMPath = pszPath; // This key is up to date in the Relative Key. If the // user doesn't want it to be up todate in the new key, // we don't need the path from the Relative key. if (!(dwFlags & (SHREGSET_HKLM | SHREGSET_FORCE_HKLM))) *(pUSKey->szSubPath) = '\0'; } // We need to copy the key if: // 1. It will not be created in this call, and // 2. The relative key is not HKEY_LOCAL_MACHINE. if (!(dwFlags & (SHREGSET_HKLM | SHREGSET_FORCE_HKLM)) && (pUSKey->hkeyLocalMachineRelative != HKEY_LOCAL_MACHINE)) { // Make a duplicate of this key. lRet = RegOpenKeyExA(pUSKey->hkeyLocalMachineRelative, NULL, 0, pUSKey->samDesired, &(pUSKey->hkeyLocalMachineRelative)); } if (pUSKey->hkeyCurrentUser) { pUSKey->hkeyCurrentUserRelative = pUSKey->hkeyCurrentUser; pUSKey->hkeyCurrentUser = NULL; lpszHKCUPath = pszPath; // This key is up to date in the Relative Key. If the // user doesn't want it to be up todate in the new key, // we don't need the path from the Relative key. if (!(dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU))) *(pUSKey->szSubPath) = '\0'; } // We need to copy the key if: // 1. It will not be created in this call, and // 2. The relative key is not HKEY_CURRENT_USER. if (!(dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU)) && (pUSKey->hkeyCurrentUserRelative != HKEY_CURRENT_USER)) { // Make a duplicate of this key. lRet = RegOpenKeyExA(pUSKey->hkeyCurrentUserRelative, NULL, 0, pUSKey->samDesired, &(pUSKey->hkeyCurrentUserRelative)); } } pUSKey->samDesired = samDesired; ///// 3. Create/Open HKLM if that flag is set or fill in the structure as appropriate. if ((ERROR_SUCCESS == lRet) && (dwFlags & (SHREGSET_HKLM | SHREGSET_FORCE_HKLM))) lRet = PrivCreateKey(&(pUSKey->hkeyLocalMachine), &(pUSKey->hkeyLocalMachineRelative), lpszHKLMPath, pUSKey->samDesired); ///// 4. Create/Open HKCU if that flag is set or fill in the structure as appropriate. if ((ERROR_SUCCESS == lRet) && (dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU))) lRet = PrivCreateKey(&(pUSKey->hkeyCurrentUser), &(pUSKey->hkeyCurrentUserRelative), lpszHKCUPath, pUSKey->samDesired); if ((dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU)) && (dwFlags & (SHREGSET_HKLM | SHREGSET_FORCE_HKLM))) { // The caller wanted both to be opened. *(pUSKey->szSubPath) = '\0'; // Both paths are open so Delta Path is empty. } else { // One of the paths is not open so set the Delta Path in case it needs to be opened later. if (*(pUSKey->szSubPath)) PathAddBackslashA(pUSKey->szSubPath); StrNCatA(pUSKey->szSubPath, pszPath, ARRAYSIZE(pUSKey->szSubPath) - lstrlenA(pUSKey->szSubPath) - 1); } // Free the memory if we are not successful. if (ERROR_SUCCESS != lRet) { if (pUSKey->hkeyCurrentUser) RegCloseKey(pUSKey->hkeyCurrentUser); if (pUSKey->hkeyCurrentUserRelative && pUSKey->hkeyCurrentUserRelative != HKEY_CURRENT_USER) RegCloseKey(pUSKey->hkeyCurrentUserRelative); if (pUSKey->hkeyLocalMachine) RegCloseKey(pUSKey->hkeyLocalMachine); if (pUSKey->hkeyLocalMachineRelative && pUSKey->hkeyLocalMachineRelative != HKEY_LOCAL_MACHINE) RegCloseKey(pUSKey->hkeyLocalMachineRelative); LocalFree((HLOCAL)pUSKey); *ppUSKey = NULL; } return lRet; } /*---------------------------------------------------------- Purpose: Create or open a user specifc registry key (HUSKEY). Description: This function will: 1. Allocate a new USKEY structure. 2. Initialize the structure. 3. Create/Open HKLM if that flag is set. 4. Create/Open HKCU if that flag is set. Note that there is no difference between FORCE and don't force in the dwFlags parameter. The hUSKeyRelative parameter should have also been opened by a call to SHRegCreateUSKey. If SHRegOpenUSKey was called, it could have returned ERROR_SUCCESS but still be invalid for calling this function. This will occur if: 1) the parameter fIgnoreHKCU was FALSE, 2) it was a relative open, 3) the HKCU branch could not be opened because it didn't exist, and 4) HKLM opened successfully. This situation renders the HUSKEY valid for reading but not writing. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegCreateUSKeyW( IN LPCWSTR pwzPath, IN REGSAM samDesired, // security access mask IN HUSKEY hUSKeyRelative, OPTIONAL OUT PHUSKEY phUSKey, IN DWORD dwFlags) // Indicates whether to create/open HKCU, HKLM, or both { CHAR szNewPath[MAXIMUM_SUB_KEY_LENGTH]; // Thunk Path to Wide chars. if (FALSE == WideCharToMultiByte(CP_ACP, 0, pwzPath, -1, szNewPath, sizeof(szNewPath), NULL, 0)) return GetLastError(); return SHRegCreateUSKeyA(szNewPath, samDesired, hUSKeyRelative, phUSKey, dwFlags); } /*---------------------------------------------------------- Purpose: Open a user specifc registry key (HUSKEY). Description: This function will: 1. Allocate a new USKEY structure. 2. Initialize the structure. 3. Determine which key (HKLM or HKCU) will be the one brought up to date. 4. Open the key that is going to be brought up to date. If #4 Succeeded: 5a. Copy the handle of the out of date key, so it can be opened later if needed. If #4 Failed: 5b. The other key will now be the one brought up to date, as long as it is HKLM. 6b. Tag the out of date as INVALID. (Key == NULL; RelKey == NULL) Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegOpenUSKeyA( IN LPCSTR pszPath, IN REGSAM samDesired, // security access mask IN HUSKEY hUSKeyRelative, OPTIONAL OUT PHUSKEY phUSKey, IN BOOL fIgnoreHKCU) { PUSKEY pUSKeyRelative = (PUSKEY) hUSKeyRelative; PPUSKEY ppUSKey = (PPUSKEY) phUSKey; PUSKEY pUSKey; LONG lRet = ERROR_SUCCESS; HKEY * phkeyMaster; HKEY * phkeyRelMaster; HKEY * phkeyOld; HKEY * phkeyRelOld; ASSERT(ppUSKey); // The following are invalid parameters... // 1. ppUSKey cannot be NULL // 2. If this is a relative open, pUSKeyRelative needs to be a valid HUSKEY. if ((!ppUSKey) || // 1. (pUSKeyRelative && FALSE == IS_HUSKEY_VALID(pUSKeyRelative))) // 2. { return ERROR_INVALID_PARAMETER; } ///// 1. Allocate a new USKEY structure. pUSKey = *ppUSKey = (PUSKEY)LocalAlloc(LPTR, sizeof(USKEY)); if (!pUSKey) return ERROR_NOT_ENOUGH_MEMORY; ///// 2. Initialize the structure. if (!pUSKeyRelative) { // Init a new (non-relative) open. pUSKey->hkeyLocalMachineRelative = HKEY_LOCAL_MACHINE; pUSKey->hkeyCurrentUserRelative = HKEY_CURRENT_USER; } else { // Init a new (relative) open. *pUSKey = *pUSKeyRelative; } pUSKey->samDesired = samDesired; ///// 3. Determine which key (HKLM or HKCU) will be the one brought up to date. // The HUSKY struct will contain 4 HKEYs. HKCU, HKCU Relative, HKLM, and HKLM Relative. // For efficiency, only one key will be up to date (HKCU or HKLM). The one that // is out of date will be NULL to indicate out of date. The relative key for the // out of date key, will be the last opened key. The string will be the delta between // the last open key and the current open level. // We will determine which key will be the new valid key (Master). if (FALSE == fIgnoreHKCU) { phkeyMaster = &(pUSKey->hkeyCurrentUser); phkeyRelMaster = &(pUSKey->hkeyCurrentUserRelative); phkeyOld = &(pUSKey->hkeyLocalMachine); phkeyRelOld = &(pUSKey->hkeyLocalMachineRelative); } else { phkeyMaster = &(pUSKey->hkeyLocalMachine); phkeyRelMaster = &(pUSKey->hkeyLocalMachineRelative); phkeyOld = &(pUSKey->hkeyCurrentUser); phkeyRelOld = &(pUSKey->hkeyCurrentUserRelative); } // Add the new Path to the Total path. if ('\0' != *(pUSKey->szSubPath)) PathAddBackslashA(pUSKey->szSubPath); // Add separator \ if reqd. StrNCatA(pUSKey->szSubPath, pszPath, ARRAYSIZE(pUSKey->szSubPath) - lstrlenA(pUSKey->szSubPath) - 1); ///// 4. Open the key that is going to be brought up to date. if (*phkeyMaster) { // Masterkey is already up to date, so just do the relative open and add the string to szSubPath // It's safe to write write (*phkeyMaster) because it will be freed by the HUSKEY used for the // relative open. lRet = RegOpenKeyExA(*phkeyMaster, pszPath, 0, pUSKey->samDesired, phkeyMaster); } else { // Open Masterkey with the full path (pUSKey->szSubPath + pszPath) if (*phkeyRelMaster) lRet = RegOpenKeyExA(*phkeyRelMaster, pUSKey->szSubPath, 0, pUSKey->samDesired, phkeyMaster); else lRet = ERROR_FILE_NOT_FOUND; lstrcpyA(pUSKey->szSubPath, pszPath); *phkeyRelMaster = NULL; } ///// Did #4 Succeeded? if (ERROR_FILE_NOT_FOUND == lRet) { ///// #4 Failed, Now we can try to open HKLM if the previous attempt was to open HKCU. if (!fIgnoreHKCU) { if (*phkeyRelOld) // Can HKLM be opened? { ASSERT(*phkeyOld == NULL); // *phkeyOld should never have a value if *phkeyRelOld does. ///// 5b. The other key will now be the one brought up to date, as long as it is HKLM. lRet = RegOpenKeyExA(*phkeyRelOld, pUSKey->szSubPath, 0, pUSKey->samDesired, phkeyOld); *phkeyRelOld = NULL; } else if (*phkeyOld) // Can HKLM be opened? { ///// 5b. Attempt to bring the other key up to date. lRet = RegOpenKeyExA(*phkeyOld, pUSKey->szSubPath, 0, pUSKey->samDesired, phkeyOld); } } else { *phkeyOld = NULL; // Tag this as INVALID *phkeyRelOld = NULL; // Tag this as INVALID } ///// 6b. Tag the out of date as INVALID. (Key == NULL; RelKey == NULL) *phkeyMaster = NULL; // Tag this as INVALID *phkeyRelMaster = NULL; // Tag this as INVALID } else { ///// #4 Succeeded: ///// 5a. Does the out of date key need to be copied? if (*phkeyOld) { // Copy the handle of the out of date key, so it can be opened later if needed. // We can be assured that any NON-Relative HKEY will not be HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER ASSERT(*phkeyOld != HKEY_LOCAL_MACHINE && *phkeyOld != HKEY_CURRENT_USER); // But let's assert anyway. RegOpenKeyExA(*phkeyOld, NULL, 0, pUSKey->samDesired, phkeyOld); } else { if ((*phkeyRelOld) && (*phkeyRelOld != HKEY_LOCAL_MACHINE) && (*phkeyRelOld != HKEY_CURRENT_USER)) { // Copy the handle of the out of date key, so it can be opened later if needed. lRet = RegOpenKeyExA(*phkeyRelOld, NULL, 0, pUSKey->samDesired, phkeyRelOld); } } if (*phkeyOld) { *phkeyRelOld = *phkeyOld; *phkeyOld = NULL; // Mark this key as being out of date. } } // Free the memory if we are not successful. if (ERROR_SUCCESS != lRet) { pUSKey->hkeyCurrentUser = NULL; // Mark invalid. pUSKey->hkeyLocalMachine = NULL; LocalFree((HLOCAL)pUSKey); *ppUSKey = NULL; } return lRet; } /*---------------------------------------------------------- Purpose: Open a user specifc registry key (HUSKEY). Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegOpenUSKeyW( IN LPCWSTR pwzPath, IN REGSAM samDesired, // security access mask IN HUSKEY hUSKeyRelative, OPTIONAL OUT PHUSKEY phUSKey, IN BOOL fIgnoreHKCU) { CHAR szNewPath[MAXIMUM_SUB_KEY_LENGTH]; // Thunk Path to Wide chars. if (FALSE == WideCharToMultiByte(CP_ACP, 0, pwzPath, -1, szNewPath, sizeof(szNewPath), NULL, 0)) return GetLastError(); return SHRegOpenUSKeyA(szNewPath, samDesired, hUSKeyRelative, phUSKey, fIgnoreHKCU); } /*---------------------------------------------------------- Purpose: Query a user specific registry entry for it's value. This will NOT open and close the keys in which the value resides. The caller needs to do this and it should be done when several keys will be queried for a perf increase. Callers that only call this once, will probably want to call SHGetUSValue(). Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegQueryUSValueA( IN HUSKEY hUSKey, IN LPCSTR pszValue, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData, OPTIONAL IN BOOL fIgnoreHKCU, IN LPVOID pvDefaultData, OPTIONAL IN DWORD dwDefaultDataSize) OPTIONAL { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_SUCCESS; DWORD dwSize = (pcbData ? *pcbData : 0); DWORD dwType = (pdwType ? *pdwType : 0); // callers responsibility to set pdwType to the type of pvDefaultData (if they care) if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (!fIgnoreHKCU) { lRet = PrivRegQueryValue(pUSKey, &(pUSKey->hkeyCurrentUser), (LPWSTR)pszValue, FALSE, pdwType, pvData, pcbData); } if (fIgnoreHKCU || ERROR_SUCCESS != lRet) { if (pcbData) *pcbData = dwSize; // We may need to reset if previous open failed. lRet = PrivRegQueryValue(pUSKey, &(pUSKey->hkeyLocalMachine), (LPWSTR)pszValue, FALSE, pdwType, pvData, pcbData); } // if fail, use default value. if ((ERROR_SUCCESS != lRet) && (pvDefaultData) && (dwDefaultDataSize) && (pvData) && (dwSize >= dwDefaultDataSize)) { MoveMemory(pvData, pvDefaultData, dwDefaultDataSize); if (pcbData) { *pcbData = dwDefaultDataSize; } if (pdwType) { *pdwType = dwType; } lRet = ERROR_SUCCESS; // Call will now use a default value. } return lRet; } /*---------------------------------------------------------- Purpose: Query a user specific registry entry for it's value. This will NOT open and close the keys in which the value resides. The caller needs to do this and it should be done when several keys will be queried for a perf increase. Callers that only call this once, will probably want to call SHGetUSValue(). Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegQueryUSValueW( IN HUSKEY hUSKey, IN LPCWSTR pwzValue, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData, OPTIONAL IN BOOL fIgnoreHKCU, IN LPVOID pvDefaultData, OPTIONAL IN DWORD dwDefaultDataSize) OPTIONAL { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet; DWORD dwSize = (pcbData ? *pcbData : 0); DWORD dwType = (pdwType ? *pdwType : 0); // callers responsibility to set pdwType to the type of pvDefaultData (if they care) if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (!fIgnoreHKCU) { lRet = PrivRegQueryValue(pUSKey, &(pUSKey->hkeyCurrentUser), pwzValue, TRUE, pdwType, pvData, pcbData); } if (fIgnoreHKCU || ERROR_SUCCESS != lRet) { if (pcbData) *pcbData = dwSize; // We may need to reset if previous open failed. lRet = PrivRegQueryValue(pUSKey, &(pUSKey->hkeyLocalMachine), pwzValue, TRUE, pdwType, pvData, pcbData); } // if fail, use default value. if ((ERROR_SUCCESS != lRet) && (pvDefaultData) && (dwDefaultDataSize) && (pvData) && (dwSize >= dwDefaultDataSize)) { MoveMemory(pvData, pvDefaultData, dwDefaultDataSize); if (pcbData) { *pcbData = dwDefaultDataSize; } if (pdwType) { *pdwType = dwType; } lRet = ERROR_SUCCESS; // Call will now use a default value. } return lRet; } /*---------------------------------------------------------- Purpose: Write a user specific registry entry. Parameters: hUSKey - Needs to have been open with KEY_SET_VALUE permissions. KEY_QUERY_VALUE also needs to have been used if this is not a force write. pszValue - Registry Key value to write to. dwType - Type for the new registry key. pvData - Pointer to data to store cbData - Size of data to store. dwFlags - Flags to determine if the registry entry should be written to HKLM, HKCU, or both. Also determines if these are force or non-force writes. (non-force means it will only write the value if it's empty) Using FORCE is faster than non-force. Decription: This function will write the value to the registry in either the HKLM or HKCU branches depending on the flags set in the dwFlags parameter. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegWriteUSValueA( IN HUSKEY hUSKey, IN LPCSTR pszValue, IN DWORD dwType, IN LPCVOID pvData, IN DWORD cbData, IN DWORD dwFlags) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_SUCCESS; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; // Assert if: 1) This is not a force open, and 2) they key was not // opened with KEY_QUERY_VALUE permissions. if (!(dwFlags & (SHREGSET_FORCE_HKCU|SHREGSET_FORCE_HKLM)) && !(pUSKey->samDesired & KEY_QUERY_VALUE)) { ASSERT(NULL); // ERROR_INVALID_PARAMETER return(ERROR_INVALID_PARAMETER); } if (dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU)) { lRet = PrivRegWriteValue(pUSKey, &(pUSKey->hkeyCurrentUser), (LPWSTR)pszValue, FALSE, dwFlags & SHREGSET_FORCE_HKCU, dwType, pvData, cbData); } if ((dwFlags & (SHREGSET_HKLM | SHREGSET_FORCE_HKLM)) && (ERROR_SUCCESS == lRet)) { lRet = PrivRegWriteValue(pUSKey, &(pUSKey->hkeyLocalMachine), (LPWSTR)pszValue, FALSE, dwFlags & SHREGSET_FORCE_HKLM, dwType, pvData, cbData); } return lRet; } /*---------------------------------------------------------- Purpose: Write a user specific registry entry. Parameters: hUSKey - Needs to have been open with KEY_SET_VALUE permissions. KEY_QUERY_VALUE also needs to have been used if this is not a force write. pszValue - Registry Key value to write to. dwType - Type for the new registry key. pvData - Pointer to data to store cbData - Size of data to store. dwFlags - Flags to determine if the registry entry should be written to HKLM, HKCU, or both. Also determines if these are force or non-force writes. (non-force means it will only write the value if it's empty) Using FORCE is faster than non-force. Decription: This function will write the value to the registry in either the HKLM or HKCU branches depending on the flags set in the dwFlags parameter. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegWriteUSValueW( IN HUSKEY hUSKey, IN LPCWSTR pwzValue, IN DWORD dwType, IN LPCVOID pvData, IN DWORD cbData, IN DWORD dwFlags) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_SUCCESS; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; // Assert if: 1) This is not a force open, and 2) they key was not // opened with access permissions. if (!(dwFlags & (SHREGSET_FORCE_HKCU|SHREGSET_FORCE_HKLM)) && !(pUSKey->samDesired & KEY_QUERY_VALUE)) { ASSERT(NULL); // ERROR_INVALID_PARAMETER return(ERROR_INVALID_PARAMETER); } if (dwFlags & (SHREGSET_HKCU | SHREGSET_FORCE_HKCU)) { lRet = PrivRegWriteValue(pUSKey, &(pUSKey->hkeyCurrentUser), (LPWSTR)pwzValue, TRUE, dwFlags & SHREGSET_FORCE_HKCU, dwType, pvData, cbData); } if (dwFlags & (SHREGSET_HKLM | SHREGSET_FORCE_HKLM)) { lRet = PrivRegWriteValue(pUSKey, &(pUSKey->hkeyLocalMachine), (LPWSTR)pwzValue, TRUE, dwFlags & SHREGSET_FORCE_HKLM, dwType, pvData, cbData); } return lRet; } /*---------------------------------------------------------- Purpose: Deletes a registry value. This will delete HKLM, HKCU, or both depending on the hkey parameter. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegDeleteUSValueA( IN HUSKEY hUSKey, IN LPCSTR pszValue, IN SHREGDEL_FLAGS delRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGDEL_DEFAULT == delRegFlags) // Delete whatever keys are open { if (!pUSKey->hkeyCurrentUser) // Attempt to open HKCU if not currently open lRet = PrivFullOpen(pUSKey); if (pUSKey->hkeyCurrentUser) delRegFlags = SHREGDEL_HKCU; else { // We prefer to delete HKCU, but we got here, so we will delete HKLM // if it is open. if (pUSKey->hkeyLocalMachine) delRegFlags = SHREGDEL_HKLM; } } if (IsFlagSet(delRegFlags, SHREGDEL_HKCU)) // Check if the call wants to delete the HKLM value. { if (!pUSKey->hkeyCurrentUser) PrivFullOpen(pUSKey); if (pUSKey->hkeyCurrentUser) { lRet = RegDeleteValueA(pUSKey->hkeyCurrentUser, pszValue); if (ERROR_FILE_NOT_FOUND == lRet) delRegFlags = SHREGDEL_HKLM; // Delete the HKLM value if the HKCU value wasn't found. } } if (IsFlagSet(delRegFlags, SHREGDEL_HKLM)) // Check if the call wants to delete the HKLM value. { if (!pUSKey->hkeyLocalMachine) PrivFullOpen(pUSKey); if (pUSKey->hkeyLocalMachine) lRet = RegDeleteValueA(pUSKey->hkeyLocalMachine, pszValue); } return lRet; } /*---------------------------------------------------------- Purpose: Deletes a registry value. This will delete HKLM, HKCU, or both depending on the hkey parameter. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegDeleteUSValueW( IN HUSKEY hUSKey, IN LPCWSTR pwzValue, IN SHREGDEL_FLAGS delRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { CHAR szNewPath[MAXIMUM_VALUE_NAME_LENGTH]; // Thunk Path to Wide chars. if (FALSE == WideCharToMultiByte(CP_ACP, 0, pwzValue, -1, szNewPath, sizeof(szNewPath), NULL, 0)) return GetLastError(); return SHRegDeleteUSValueA(hUSKey, szNewPath, delRegFlags); } /*---------------------------------------------------------- Purpose: Deletes a registry sub-key if empty. This will delete HKLM, HKCU, or both depending on the delRegFlags parameter. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegDeleteEmptyUSKeyA( IN HUSKEY hUSKey, IN LPCSTR pszSubKey, IN SHREGDEL_FLAGS delRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGDEL_DEFAULT == delRegFlags) // Delete whatever keys are open { if (!pUSKey->hkeyCurrentUser) // Attempt to open HKCU if not currently open lRet = PrivFullOpen(pUSKey); if (pUSKey->hkeyCurrentUser) delRegFlags = SHREGDEL_HKCU; else { // We prefer to delete HKCU, but we got here, so we will delete HKLM // if it is open. if (pUSKey->hkeyLocalMachine) delRegFlags = SHREGDEL_HKLM; } } if (IsFlagSet(delRegFlags, SHREGDEL_HKCU)) // Check if the call wants to delete the HKLM key. { if (!pUSKey->hkeyCurrentUser) PrivFullOpen(pUSKey); if (pUSKey->hkeyCurrentUser) { lRet = SHDeleteEmptyKeyA(pUSKey->hkeyCurrentUser, pszSubKey); if (ERROR_FILE_NOT_FOUND == lRet) delRegFlags = SHREGDEL_HKLM; // Delete the HKLM key if the HKCU key wasn't found. } } if (IsFlagSet(delRegFlags, SHREGDEL_HKLM)) // Check if the call wants to delete the HKLM key. { if (!pUSKey->hkeyLocalMachine) PrivFullOpen(pUSKey); if (pUSKey->hkeyLocalMachine) lRet = SHDeleteEmptyKeyA(pUSKey->hkeyLocalMachine, pszSubKey); } return lRet; } /*---------------------------------------------------------- Purpose: Deletes a registry key if empty. This will delete HKLM, HKCU, or both depending on the delRegFlags parameter. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegDeleteEmptyUSKeyW( IN HUSKEY hUSKey, IN LPCWSTR pwzSubKey, IN SHREGDEL_FLAGS delRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { CHAR szNewPath[MAXIMUM_SUB_KEY_LENGTH]; // Thunk Path to Wide chars. if (FALSE == WideCharToMultiByte(CP_ACP, 0, pwzSubKey, -1, szNewPath, sizeof(szNewPath), NULL, 0)) return GetLastError(); return SHRegDeleteEmptyUSKeyA(hUSKey, szNewPath, delRegFlags); } /*---------------------------------------------------------- Purpose: Enumerates sub-keys under a given HUSKEY. SHREGENUM_FLAGS specifies how to do the enumeration. SHREGENUM_DEFAULT - Will look in HKCU followed by HKLM if not found. SHREGENUM_HKCU - Enumerates HKCU only. SHREGENUM_HKLM = Enumerates HKLM only. SHREGENUM_BOTH - This is supposed to do a union of the HKLM and HKCU subkeys. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegEnumUSKeyA( IN HUSKEY hUSKey, IN DWORD dwIndex, OUT LPSTR pszName, IN LPDWORD pcchName, IN SHREGENUM_FLAGS enumRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGENUM_BOTH == enumRegFlags) { // This is not supported yet. ASSERT(FALSE); return ERROR_CALL_NOT_IMPLEMENTED; } if (SHREGENUM_HKCU != enumRegFlags && SHREGENUM_HKLM != enumRegFlags && SHREGENUM_DEFAULT != enumRegFlags) { // check your arguments. ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } // Default is to try HKCU first. if (SHREGENUM_HKCU == enumRegFlags || SHREGENUM_DEFAULT == enumRegFlags) { lRet = PrivRegEnumKey(pUSKey, &(pUSKey->hkeyCurrentUser), dwIndex, (LPWSTR)pszName, FALSE, pcchName); } if ((SHREGENUM_HKLM == enumRegFlags) || ((SHREGENUM_DEFAULT == enumRegFlags) && ((ERROR_SUCCESS != lRet) && (ERROR_MORE_DATA != lRet) && (ERROR_NO_MORE_ITEMS != lRet)))) { lRet = PrivRegEnumKey(pUSKey, &(pUSKey->hkeyLocalMachine), dwIndex, (LPWSTR)pszName, FALSE, pcchName); } return lRet; } /*---------------------------------------------------------- Purpose: Enumerates sub-keys under a given HUSKEY. SHREGENUM_FLAGS specifies how to do the enumeration. SHREGENUM_DEFAULT - Will look in HKCU followed by HKLM if not found. SHREGENUM_HKCU - Enumerates HKCU only. SHREGENUM_HKLM = Enumerates HKLM only. SHREGENUM_BOTH - This is supposed to do a union of the HKLM and HKCU subkeys. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegEnumUSKeyW( IN HUSKEY hUSKey, IN DWORD dwIndex, OUT LPWSTR pszName, IN LPDWORD pcchName, IN SHREGENUM_FLAGS enumRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGENUM_BOTH == enumRegFlags) { // This is not supported yet. ASSERT(FALSE); return ERROR_CALL_NOT_IMPLEMENTED; } if (SHREGENUM_HKCU != enumRegFlags && SHREGENUM_HKLM != enumRegFlags && SHREGENUM_DEFAULT != enumRegFlags) { // check your arguments. ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } // Default is to try HKCU first. if (SHREGENUM_HKCU == enumRegFlags || SHREGENUM_DEFAULT == enumRegFlags) { lRet = PrivRegEnumKey(pUSKey, &(pUSKey->hkeyCurrentUser), dwIndex, pszName, TRUE, pcchName); } if ((SHREGENUM_HKLM == enumRegFlags) || ((SHREGENUM_DEFAULT == enumRegFlags) && ((ERROR_SUCCESS != lRet) && (ERROR_MORE_DATA != lRet) && (ERROR_NO_MORE_ITEMS != lRet)))) { lRet = PrivRegEnumKey(pUSKey, &(pUSKey->hkeyLocalMachine), dwIndex, pszName, TRUE, pcchName); } return lRet; } /*---------------------------------------------------------- Purpose: Enumerates Values under a given HUSKEY. SHREGENUM_FLAGS specifies how to do the enumeration. SHREGENUM_DEFAULT - Will look in HKCU followed by HKLM if not found. SHREGENUM_HKCU - Enumerates HKCU only. SHREGENUM_HKLM = Enumerates HKLM only. SHREGENUM_BOTH - This is supposed to do a union of the HKLM and HKCU subkeys. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegEnumUSValueA( IN HUSKEY hUSKey, IN DWORD dwIndex, OUT LPSTR pszValueName, IN LPDWORD pcchValueNameLen, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData, OPTIONAL IN SHREGENUM_FLAGS enumRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGENUM_BOTH == enumRegFlags) { // This is not supported yet. ASSERT(FALSE); return ERROR_CALL_NOT_IMPLEMENTED; } if (SHREGENUM_HKCU != enumRegFlags && SHREGENUM_HKLM != enumRegFlags && SHREGENUM_DEFAULT != enumRegFlags) { // check your arguments. ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } // Default is to try HKCU first. if (SHREGENUM_HKCU == enumRegFlags || SHREGENUM_DEFAULT == enumRegFlags) { lRet = PrivRegEnumValue(pUSKey, &(pUSKey->hkeyCurrentUser), dwIndex, (LPWSTR)pszValueName, FALSE, pcchValueNameLen, pdwType, pvData, pcbData); } if ((SHREGENUM_HKLM == enumRegFlags) || ((SHREGENUM_DEFAULT == enumRegFlags) && ((ERROR_SUCCESS != lRet) && (ERROR_MORE_DATA != lRet) && (ERROR_NO_MORE_ITEMS != lRet)))) { lRet = PrivRegEnumValue(pUSKey, &(pUSKey->hkeyLocalMachine), dwIndex, (LPWSTR)pszValueName, FALSE, pcchValueNameLen, pdwType, pvData, pcbData); } return lRet; } /*---------------------------------------------------------- Purpose: Enumerates Values under a given HUSKEY. SHREGENUM_FLAGS specifies how to do the enumeration. SHREGENUM_DEFAULT - Will look in HKCU followed by HKLM if not found. SHREGENUM_HKCU - Enumerates HKCU only. SHREGENUM_HKLM = Enumerates HKLM only. SHREGENUM_BOTH - This is supposed to do a union of the HKLM and HKCU subkeys. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegEnumUSValueW( IN HUSKEY hUSKey, IN DWORD dwIndex, OUT LPWSTR pszValueName, IN LPDWORD pcchValueNameLen, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData, OPTIONAL IN SHREGENUM_FLAGS enumRegFlags) // (HKLM, HKCU, or (HKLM | HKCU)) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGENUM_BOTH == enumRegFlags) { // This is not supported yet. ASSERT(FALSE); return ERROR_CALL_NOT_IMPLEMENTED; } if (SHREGENUM_HKCU != enumRegFlags && SHREGENUM_HKLM != enumRegFlags && SHREGENUM_DEFAULT != enumRegFlags) { // check your arguments. ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } // Default is to try HKCU first. if (SHREGENUM_HKCU == enumRegFlags || SHREGENUM_DEFAULT == enumRegFlags) { lRet = PrivRegEnumValue(pUSKey, &(pUSKey->hkeyCurrentUser), dwIndex, pszValueName, TRUE, pcchValueNameLen, pdwType, pvData, pcbData); } if ((SHREGENUM_HKLM == enumRegFlags) || ((SHREGENUM_DEFAULT == enumRegFlags) && ((ERROR_SUCCESS != lRet) && (ERROR_MORE_DATA != lRet) && (ERROR_NO_MORE_ITEMS != lRet)))) { lRet = PrivRegEnumValue(pUSKey, &(pUSKey->hkeyLocalMachine), dwIndex, pszValueName, TRUE, pcchValueNameLen, pdwType, pvData, pcbData); } return lRet; } /*---------------------------------------------------------- Purpose: Gets Info about a HUSKEY. Re-uses same flags as enumeration functions. Look at SHRegEnumKeyExA for an explanation of the flags. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegQueryInfoUSKeyA ( IN HUSKEY hUSKey, OUT LPDWORD pcSubKeys, OPTIONAL OUT LPDWORD pcchMaxSubKeyLen, OPTIONAL OUT LPDWORD pcValues, OPTIONAL OUT LPDWORD pcchMaxValueNameLen, OPTIONAL IN SHREGENUM_FLAGS enumRegFlags ) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGENUM_BOTH == enumRegFlags) { // This is not supported yet. ASSERT(FALSE); return ERROR_CALL_NOT_IMPLEMENTED; } if (SHREGENUM_HKCU != enumRegFlags && SHREGENUM_HKLM != enumRegFlags && SHREGENUM_DEFAULT != enumRegFlags) { // check your arguments. ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } // Default is to try HKCU first. if (SHREGENUM_HKCU == enumRegFlags || SHREGENUM_DEFAULT == enumRegFlags) { lRet = PrivRegQueryInfoKey(pUSKey, &(pUSKey->hkeyCurrentUser), FALSE, pcSubKeys, pcchMaxSubKeyLen, pcValues, pcchMaxValueNameLen); } if ((SHREGENUM_HKLM == enumRegFlags) || ((SHREGENUM_DEFAULT == enumRegFlags) && ((ERROR_SUCCESS != lRet) && (ERROR_MORE_DATA != lRet)))) { lRet = PrivRegQueryInfoKey(pUSKey, &(pUSKey->hkeyLocalMachine), FALSE, pcSubKeys, pcchMaxSubKeyLen, pcValues, pcchMaxValueNameLen); } return lRet; } /*---------------------------------------------------------- Purpose: Gets Info about a HUSKEY. Re-uses same flags as enumeration functions. Look at SHRegEnumKeyExA for an explanation of the flags. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegQueryInfoUSKeyW ( IN HUSKEY hUSKey, OUT LPDWORD pcSubKeys, OPTIONAL OUT LPDWORD pcchMaxSubKeyLen, OPTIONAL OUT LPDWORD pcValues, OPTIONAL OUT LPDWORD pcchMaxValueNameLen, OPTIONAL IN SHREGENUM_FLAGS enumRegFlags ) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_INVALID_PARAMETER; if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (SHREGENUM_BOTH == enumRegFlags) { // This is not supported yet. ASSERT(FALSE); return ERROR_CALL_NOT_IMPLEMENTED; } if (SHREGENUM_HKCU != enumRegFlags && SHREGENUM_HKLM != enumRegFlags && SHREGENUM_DEFAULT != enumRegFlags) { // check your arguments. ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } // Default is to try HKCU first. if (SHREGENUM_HKCU == enumRegFlags || SHREGENUM_DEFAULT == enumRegFlags) { lRet = PrivRegQueryInfoKey(pUSKey, &(pUSKey->hkeyCurrentUser), TRUE, pcSubKeys, pcchMaxSubKeyLen, pcValues, pcchMaxValueNameLen); } if ((SHREGENUM_HKLM == enumRegFlags) || ((SHREGENUM_DEFAULT == enumRegFlags) && ((ERROR_SUCCESS != lRet) && (ERROR_MORE_DATA != lRet)))) { lRet = PrivRegQueryInfoKey(pUSKey, &(pUSKey->hkeyLocalMachine), TRUE, pcSubKeys, pcchMaxSubKeyLen, pcValues, pcchMaxValueNameLen); } return lRet; } /*---------------------------------------------------------- Purpose: Closes a HUSKEY (Handle to a User Specifc registry key). Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegCloseUSKey( OUT HUSKEY hUSKey) { PUSKEY pUSKey = (PUSKEY) hUSKey; LONG lRet = ERROR_SUCCESS; ASSERT(pUSKey); if (FALSE == IS_HUSKEY_VALID(pUSKey)) return ERROR_INVALID_PARAMETER; if (pUSKey->hkeyLocalMachine) { lRet = RegCloseKey(pUSKey->hkeyLocalMachine); pUSKey->hkeyLocalMachine = NULL; // Used to indicate that it's invalid. } if (pUSKey->hkeyLocalMachineRelative && HKEY_LOCAL_MACHINE != pUSKey->hkeyLocalMachineRelative) { lRet = RegCloseKey(pUSKey->hkeyLocalMachineRelative); } if (pUSKey->hkeyCurrentUser) { lRet = RegCloseKey(pUSKey->hkeyCurrentUser); pUSKey->hkeyCurrentUser = NULL; // Used to indicate that it's invalid. } if (pUSKey->hkeyCurrentUserRelative && HKEY_CURRENT_USER != pUSKey->hkeyCurrentUserRelative) { lRet = RegCloseKey(pUSKey->hkeyCurrentUserRelative); } LocalFree((HLOCAL)pUSKey); return lRet; } /*---------------------------------------------------------- Purpose: Gets a registry value that is User Specifc. This opens and closes the key in which the value resides. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and then call SHRegQueryUSValue rather than using this function repeatedly. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegGetUSValueA( IN LPCSTR pszSubKey, IN LPCSTR pszValue, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData, OPTIONAL IN BOOL fIgnoreHKCU, IN LPVOID pvDefaultData, OPTIONAL IN DWORD dwDefaultDataSize) { LONG lRet; HUSKEY hUSkeys; DWORD dwInitialSize = (pcbData ? *pcbData : 0); DWORD dwType = (pdwType ? *pdwType : 0); // callers responsibility to set pdwType to the type of pvDefaultData (if they care) lRet = SHRegOpenUSKeyA(pszSubKey, KEY_QUERY_VALUE, NULL, &hUSkeys, fIgnoreHKCU); if (ERROR_SUCCESS == lRet) { lRet = SHRegQueryUSValueA(hUSkeys, pszValue, pdwType, pvData, pcbData, fIgnoreHKCU, pvDefaultData, dwDefaultDataSize); SHRegCloseUSKey(hUSkeys); } if (ERROR_SUCCESS != lRet) { // if fail on open OR on query, use default value as long as dwDefaultDataSize isn't 0. (So we return the error) if ((pvDefaultData) && (dwDefaultDataSize) && (pvData) && (dwInitialSize >= dwDefaultDataSize)) { MoveMemory(pvData, pvDefaultData, dwDefaultDataSize); if (pcbData) { *pcbData = dwDefaultDataSize; } if (pdwType) { *pdwType = dwType; } lRet = ERROR_SUCCESS; // Call will now use a default value. } } return lRet; } /*---------------------------------------------------------- Purpose: Gets a registry value that is User Specifc. This opens and closes the key in which the value resides. Perf: if your code involves setting/getting a series of values in the same key, it is better to open the key once and then call SHRegQueryUSValue rather than using this function repeatedly. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegGetUSValueW( IN LPCWSTR pwzSubKey, IN LPCWSTR pwzValue, OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData, OPTIONAL IN BOOL fIgnoreHKCU, IN LPVOID pvDefaultData, OPTIONAL IN DWORD dwDefaultDataSize) { LONG lRet; HUSKEY hUSkeys; DWORD dwInitialSize = (pcbData ? *pcbData : 0); DWORD dwType = (pdwType ? *pdwType : 0); // callers responsibility to set pdwType to the type of pvDefaultData (if they care) lRet = SHRegOpenUSKeyW(pwzSubKey, KEY_QUERY_VALUE, NULL, &hUSkeys, fIgnoreHKCU); if (ERROR_SUCCESS == lRet) { lRet = SHRegQueryUSValueW(hUSkeys, pwzValue, pdwType, pvData, pcbData, fIgnoreHKCU, pvDefaultData, dwDefaultDataSize); SHRegCloseUSKey(hUSkeys); } if (ERROR_SUCCESS != lRet) { // if fail on open OR on query, use default value as long as dwDefaultDataSize isn't 0. (So we return the error) if ((pvDefaultData) && (dwDefaultDataSize) && (pvData) && (dwInitialSize >= dwDefaultDataSize)) { // if fail, use default value. MoveMemory(pvData, pvDefaultData, dwDefaultDataSize); if (pcbData) { *pcbData = dwDefaultDataSize; } if (pdwType) { *pdwType = dwType; } lRet = ERROR_SUCCESS; // Call will now use a default value. } } return lRet; } /*---------------------------------------------------------- Purpose: Sets a registry value that is User Specifc. This opens and closes the key in which the value resides. Perf: if your code involves setting a series of values in the same key, it is better to open the key once and then call SHRegWriteUSValue rather than using this function repeatedly. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegSetUSValueA( IN LPCSTR pszSubKey, IN LPCSTR pszValue, IN DWORD dwType, IN LPCVOID pvData, OPTIONAL IN DWORD cbData, OPTIONAL IN DWORD dwFlags) OPTIONAL { LONG lRet; HUSKEY hUSkeys; lRet = SHRegCreateUSKeyA(pszSubKey, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hUSkeys, dwFlags); if (ERROR_SUCCESS == lRet) { lRet = SHRegWriteUSValueA(hUSkeys, pszValue, dwType, pvData, cbData, dwFlags); SHRegCloseUSKey(hUSkeys); } return lRet; } /*---------------------------------------------------------- Purpose: Sets a registry value that is User Specifc. This opens and closes the key in which the value resides. Perf: if your code involves setting a series of values in the same key, it is better to open the key once and then call SHRegWriteUSValue rather than using this function repeatedly. Returns: LONG containing success or error code. Cond: -- */ STDAPI_(LONG) SHRegSetUSValueW( IN LPCWSTR pwzSubKey, IN LPCWSTR pwzValue, IN DWORD dwType, OPTIONAL IN LPCVOID pvData, OPTIONAL IN DWORD cbData, OPTIONAL IN DWORD dwFlags) OPTIONAL { LONG lRet; HUSKEY hUSkeys; lRet = SHRegCreateUSKeyW(pwzSubKey, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hUSkeys, dwFlags); if (ERROR_SUCCESS == lRet) { lRet = SHRegWriteUSValueW(hUSkeys, pwzValue, dwType, pvData, cbData, dwFlags); SHRegCloseUSKey(hUSkeys); } return lRet; } /*---------------------------------------------------------- Purpose: Gets a BOOL Setting from the registry. The default parameter will be used if it's not found in the registry. Cond: -- */ #define BOOLSETTING_BOOL_TRUE1W L"YES" #define BOOLSETTING_BOOL_TRUE1A "YES" #define BOOLSETTING_BOOL_TRUE2W L"TRUE" #define BOOLSETTING_BOOL_TRUE2A "TRUE" #define BOOLSETTING_BOOL_FALSE1W L"NO" #define BOOLSETTING_BOOL_FALSE1A "NO" #define BOOLSETTING_BOOL_FALSE2W L"FALSE" #define BOOLSETTING_BOOL_FALSE2A "FALSE" #define BOOLSETTING_BOOL_1W L"1" #define BOOLSETTING_BOOL_1A "1" #define BOOLSETTING_BOOL_0W L"0" #define BOOLSETTING_BOOL_0A "0" STDAPI_(BOOL) SHRegGetBoolUSValueW( IN LPCWSTR pwzSubKey, IN LPCWSTR pwzValue, IN BOOL fIgnoreHKCU, IN BOOL fDefault) { LONG lRet; WCHAR szData[MAX_PATH]; DWORD dwType = REG_SZ; //because the default value we pass in is a string. DWORD dwSize = sizeof(szData); LPCWSTR pszDefault = fDefault ? BOOLSETTING_BOOL_TRUE1W : BOOLSETTING_BOOL_FALSE1W; DWORD dwDefaultSize = fDefault ? sizeof(BOOLSETTING_BOOL_TRUE1W) : sizeof(BOOLSETTING_BOOL_FALSE1W); // sizeof() includes terminating NULL lRet = SHRegGetUSValueW(pwzSubKey, pwzValue, &dwType, (LPVOID) szData, &dwSize, fIgnoreHKCU, (LPVOID) pszDefault, dwDefaultSize); if (ERROR_SUCCESS == lRet) { if (dwType == REG_BINARY || dwType == REG_DWORD) { fDefault = (*((DWORD*)szData) != 0); } else { if ((0 == lstrcmpiW(BOOLSETTING_BOOL_TRUE1W, szData)) || (0 == lstrcmpiW(BOOLSETTING_BOOL_TRUE2W, szData)) || (0 == lstrcmpiW(BOOLSETTING_BOOL_1W, szData))) { fDefault = TRUE; // We read TRUE from the registry. } else if ((0 == lstrcmpiW(BOOLSETTING_BOOL_FALSE1W, szData)) || (0 == lstrcmpiW(BOOLSETTING_BOOL_FALSE2W, szData)) || (0 == lstrcmpiW(BOOLSETTING_BOOL_0W, szData))) { fDefault = FALSE; // We read TRUE from the registry. } } } return fDefault; } /*---------------------------------------------------------- Purpose: Gets a BOOL Setting from the registry. The default parameter will be used if it's not found in the registry. Cond: -- */ STDAPI_(BOOL) SHRegGetBoolUSValueA( IN LPCSTR pszSubKey, IN LPCSTR pszValue, IN BOOL fIgnoreHKCU, IN BOOL fDefault) { LONG lRet; CHAR szData[MAX_PATH]; DWORD dwType = REG_SZ; //because the default value we pass in is a string. DWORD dwSize = sizeof(szData); LPCSTR pszDefault = fDefault ? BOOLSETTING_BOOL_TRUE1A : BOOLSETTING_BOOL_FALSE1A; DWORD dwDefaultSize = (fDefault ? sizeof(BOOLSETTING_BOOL_TRUE1A) : sizeof(BOOLSETTING_BOOL_FALSE1A)) + sizeof(CHAR); lRet = SHRegGetUSValueA(pszSubKey, pszValue, &dwType, (LPVOID) szData, &dwSize, fIgnoreHKCU, (LPVOID) pszDefault, dwDefaultSize); if (ERROR_SUCCESS == lRet) { if (dwType == REG_BINARY || dwType == REG_DWORD) { fDefault = (*((DWORD*)szData) != 0); } else { if ((0 == lstrcmpiA(BOOLSETTING_BOOL_TRUE1A, szData)) || (0 == lstrcmpiA(BOOLSETTING_BOOL_TRUE2A, szData)) || (0 == lstrcmpiA(BOOLSETTING_BOOL_1A, szData))) { fDefault = TRUE; // We read TRUE from the registry. } else if ((0 == lstrcmpiA(BOOLSETTING_BOOL_FALSE1A, szData)) || (0 == lstrcmpiA(BOOLSETTING_BOOL_FALSE2A, szData)) || (0 == lstrcmpiA(BOOLSETTING_BOOL_0A, szData)) ) { fDefault = FALSE; // We read TRUE from the registry. } } } return fDefault; } STDAPI_(DWORD) SHGetValueGoodBootA(HKEY hkeyParent, LPCSTR pcszSubKey, LPCSTR pcszValue, PDWORD pdwValueType, PBYTE pbyteBuf, PDWORD pdwcbBufLen) { if (GetSystemMetrics(SM_CLEANBOOT)) return ERROR_GEN_FAILURE; return SHGetValueA(hkeyParent, pcszSubKey, pcszValue, pdwValueType, pbyteBuf, pdwcbBufLen); } STDAPI_(DWORD) SHGetValueGoodBootW(HKEY hkeyParent, LPCWSTR pcwzSubKey, LPCWSTR pcwzValue, PDWORD pdwValueType, PBYTE pbyteBuf, PDWORD pdwcbBufLen) { if (GetSystemMetrics(SM_CLEANBOOT)) return ERROR_GEN_FAILURE; return SHGetValueW(hkeyParent, pcwzSubKey, pcwzValue, pdwValueType, pbyteBuf, pdwcbBufLen); } /*---------------------------------------------------------- Purpose: Given a CLSID open and return that key from HKCR, or the user local version. Cond: -- */ LWSTDAPI SHRegGetCLSIDKeyW(UNALIGNED REFGUID rguid, LPCWSTR pszSubKey, BOOL fUserSpecific, BOOL fCreate, HKEY *phkey) { HKEY hkeyRef; WCHAR szThisCLSID[GUIDSTR_MAX]; WCHAR szPath[GUIDSTR_MAX+MAX_PATH+1]; // room for clsid + extra ULONG err; SHStringFromGUIDW(rguid, szThisCLSID, ARRAYSIZE(szThisCLSID)); if (fUserSpecific) { hkeyRef = HKEY_CURRENT_USER; if (g_bRunningOnNT) { StrCpyW(szPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\"); } else { // // On 9x we have to keep using the old location because Plus // relies on that location. (Plus changes the DefaultIcon // subkey to get custom regitem icons.) // StrCpyW(szPath, L"Software\\Classes\\CLSID\\"); } } else { hkeyRef = HKEY_CLASSES_ROOT; StrCpyW(szPath, L"CLSID"); StrCatW(szPath, L"\\"); } StrCatW(szPath, szThisCLSID); if (pszSubKey) { StrCatW(szPath, L"\\"); StrCatBuffW(szPath, pszSubKey, ARRAYSIZE(szPath)); } if (fCreate) { err = RegCreateKeyW(hkeyRef, szPath, phkey); } else { err = RegOpenKeyExW(hkeyRef, szPath, 0, MAXIMUM_ALLOWED, phkey); } return HRESULT_FROM_WIN32(err); } LWSTDAPI SHRegGetCLSIDKeyA(UNALIGNED REFGUID rguid, LPCSTR pszSubKey, BOOL fUserSpecific, BOOL fCreate, HKEY *phkey) { HKEY hkeyRef; CHAR szThisCLSID[GUIDSTR_MAX]; CHAR szPath[GUIDSTR_MAX+MAX_PATH+1]; // room for clsid + extra ULONG err; SHStringFromGUIDA(rguid, szThisCLSID, ARRAYSIZE(szThisCLSID)); if (fUserSpecific) { hkeyRef = HKEY_CURRENT_USER; if (g_bRunningOnNT) { StrCpyA(szPath, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\"); } else { // // On 9x we have to keep using the old location because Plus // relies on that location. (Plus changes the DefaultIcon // subkey to get custom regitem icons.) // StrCpyA(szPath, "Software\\Classes\\CLSID\\"); } } else { hkeyRef = HKEY_CLASSES_ROOT; StrCpyA(szPath, "CLSID\\"); } StrCatA(szPath, szThisCLSID); if (pszSubKey) { StrCatA(szPath, "\\"); StrCatBuffA(szPath, pszSubKey, ARRAYSIZE(szPath)); } if (fCreate) err = RegCreateKeyA(hkeyRef, szPath, phkey); else err = RegOpenKeyExA(hkeyRef, szPath, 0, MAXIMUM_ALLOWED, phkey); return HRESULT_FROM_WIN32(err); } /*---------------------------------------------------------- Purpose: Duplicate an hkey if an object wants to keep one open //*** Reg_DupKey -- duplicate registry key (upping refcnt) // NOTES // REARCHITECT gotta fix this logic (now that i understand how bogus it is). // // what we're trying to do is dup the handle. sounds easy. it isn't. // here's the deal. // 1- RegOpenKeyEx(hkey, NULL, ..., &hkey2) is spec'ed as giving back the // same handle. on win95 it ups the refcnt (good!). // 2- but on winNT there is no refcnt associated w/ it. so it gives back // the same handle but now *any* close will make *all* of the 'pseudo-dup'ed // handles invalid. // 3- (on winNT) if we add MAXIMUM_ALLOWED, we're asking for a new SAM. // but the SAM is associated w/ the handle, so the only (or rather, closest) // way to do that is to give a new handle. (presumably this only works // if we're not dup'ing a handle that's already MAXIMUM_ALLOWED). // 4- (on winNT) but wait! if we open HKEY_CURRENT_USER, we *always* get // back 0x80000001 (or somesuch). but closes on that are ignored, so all // works. // // so what we probably should do is: // - win95: just do #1, w/ default security. win95 will give us the same // handle w/ an *upped* refcnt and we'll be fine. // - winNT: do a DuplicateHandle. this will correctly give us a *new* // handle and we'll be fine. // */ HKEY SHRegDuplicateHKey(HKEY hkey) { HKEY hkeyDup = NULL; // in case incoming hkey is invalid // NULL returns key to same place and ups refcnt RegOpenKeyExA(hkey, NULL, 0, MAXIMUM_ALLOWED, &hkeyDup); // This is a bogus assert on Win95, since they have reference counting on these hkeys... if (g_bRunningOnNT) { ASSERT(hkeyDup != hkey || hkey == HKEY_CURRENT_USER || hkey == HKEY_CLASSES_ROOT || hkey == HKEY_LOCAL_MACHINE); } return hkeyDup; } /*---------------------------------------------------------- Purpose: Read a string value from the registry and convert it to an integer. */ LWSTDAPI_(int) SHRegGetIntW(HKEY hk, LPCWSTR szKey, int nDefault) { DWORD cb; WCHAR ach[20]; if (hk == NULL) return nDefault; ach[0] = 0; cb = sizeof(ach); SHQueryValueExW(hk, szKey, NULL, NULL, (LPBYTE)ach, &cb); if (ach[0] >= L'0' && ach[0] <= L'9') return StrToIntW(ach); else return nDefault; } // Stores a file path in the registry but looks for a match with // certain environment variables first. This is a FIXED list. // Parameters: // hKey - an open HKEY or registry root key // pszSubKey - subkey in registry or NULL/zero length string // pszValue - value name in registry // pszPath - Win32 file path to write // dwFlags - unused / future expansion // Return value: // Returns Win32 error code from ADVAPI32.DLL function calls. // // Match %USERPROFILE% - x:\WINNT\Profiles\ // - x:\Documents And Settings\ // %ALLUSERSPROFILES% - x:\WINNT\Profiles\ // - x:\Documents And Settings\ // %ProgramFiles% - x:\Program Files // %SystemRoot% - x:\WINNT // // %ALLUSERSPROFILE% and %ProgramFiles% are dubious and can be // removed. // // WARNING: DO NOT CHANGE THE MATCH ORDER OF %USERPROFILE% AND // %SystemRoot% // // If %SystemRoot% is matched first then %USERPROFILE% will // NEVER be matched if inside x:\WINNT\ // DWORD SHRegSetPathW (HKEY hKey, LPCWSTR pszSubKey, LPCWSTR pszValue, LPCWSTR pszPath, DWORD dwFlags) { DWORD dwType; PCWSTR pszData; WCHAR szTemp[MAX_PATH]; if (PathUnExpandEnvStringsW(pszPath, szTemp, ARRAYSIZE(szTemp))) { dwType = REG_EXPAND_SZ; pszData = szTemp; } else { dwType = REG_SZ; pszData = pszPath; } return SHSetValueW(hKey, pszSubKey, pszValue, dwType, pszData, (lstrlenW(pszData) + 1) * sizeof(pszData[0])); } DWORD SHRegSetPathA(HKEY hKey, LPCSTR pszSubKey, LPCSTR pszValue, LPCSTR pszPath, DWORD dwFlags) { DWORD dwType; PCSTR pszData; CHAR szTemp[MAX_PATH]; if (PathUnExpandEnvStringsA(pszPath, szTemp, ARRAYSIZE(szTemp))) { dwType = REG_EXPAND_SZ; pszData = szTemp; } else { dwType = REG_SZ; pszData = pszPath; } return SHSetValueA(hKey, pszSubKey, pszValue, dwType, pszData, (lstrlenA(pszData) + 1) * sizeof(pszData[0])); } // RegGetPath: Unicode implementation of function. // Returns an expanded file path from the registry. // Parameters: // hKey - an open HKEY or registry root key // pszSubKey - subkey in registry or NULL/zero length string // pszValue - value name in registry // pwszPath - string to place path in (assumed size of MAX_PATH chars) // dwFlags - unused / future expansion // Return value: // Returns Win32 error code from ADVAPI32.DLL function calls. DWORD SHRegGetPathA (HKEY hKey, LPCSTR pszSubKey, LPCSTR pszValue, LPSTR pszPath, DWORD dwFlags) { DWORD cb = MAX_PATH * sizeof(pszPath[0]); return SHGetValueA(hKey, pszSubKey, pszValue, NULL, pszPath, &cb); } DWORD SHRegGetPathW (HKEY hKey, LPCWSTR pszSubKey, LPCWSTR pszValue, LPWSTR pszPath, DWORD dwFlags) { DWORD cb = MAX_PATH * sizeof(pszPath[0]); return SHGetValueW(hKey, pszSubKey, pszValue, NULL, pszPath, &cb); } BOOL Reg_GetCommand(HKEY hkey, LPCWSTR pszKey, LPCWSTR pszValue, LPWSTR pszCommand) { WCHAR szKey[1024]; LONG cbSize = sizeof(szKey); int iLen; ASSERT(pszKey); StrCpyNW(szKey, pszKey, ARRAYSIZE(szKey)); iLen = lstrlenW(szKey); pszCommand[0] = 0; // a trailing backslash means no value key if (szKey[iLen-1] == L'\\' || (pszValue && !pszValue[0])) { if (!pszValue) szKey[iLen-1] = 0; RegQueryValueW(hkey, szKey, pszCommand, &cbSize); } else { if (!pszValue) pszValue = PathFindFileNameW(szKey); ASSERT(pszValue); if (!pszValue) return FALSE; PathRemoveFileSpecW(szKey); SHGetValueGoodBootW(hkey, szKey, pszValue, NULL, (LPBYTE)pszCommand, (DWORD*)&cbSize); } if (pszCommand[0]) { LPWSTR pszNextKey; // see if it's a registry spec if (!StrCmpNIW(pszCommand, L"HKCU:", 5)) { hkey = HKEY_CURRENT_USER; pszNextKey = pszCommand + 5; } else if (!StrCmpNIW(pszCommand, L"HKLM:", 5)) { hkey = HKEY_LOCAL_MACHINE; pszNextKey = pszCommand + 5; } else if (!StrCmpNIW(pszCommand, L"HKCR:", 5)) { hkey = HKEY_CLASSES_ROOT; pszNextKey = pszCommand + 5; } else { return (BOOL)pszCommand[0]; } StrCpyNW(szKey, pszNextKey, ARRAYSIZE(szKey)); return (Reg_GetCommand(hkey, szKey, NULL, pszCommand)); } return (BOOL)pszCommand[0]; } #define FillExecInfo(_info, _hwnd, _verb, _file, _params, _dir, _show) \ (_info).hwnd = _hwnd; \ (_info).lpVerb = _verb; \ (_info).lpFile = _file; \ (_info).lpParameters = _params; \ (_info).lpDirectory = _dir; \ (_info).nShow = _show; \ (_info).fMask = 0; \ (_info).cbSize = sizeof(SHELLEXECUTEINFOW); HRESULT RunRegCommand(HWND hwnd, HKEY hkey, LPCWSTR pszKey) { HRESULT hr = E_FAIL; WCHAR szCommand[1024]; if (Reg_GetCommand(hkey, pszKey, L"", szCommand)) { LPWSTR pszArgs; SHELLEXECUTEINFOW ExecInfo; WCHAR szExpCommand[1024]; SHExpandEnvironmentStringsW(szCommand, szExpCommand, ARRAYSIZE(szExpCommand)); // Long filenames _should_ be surrounded by quote marks. However, some aren't. // This causes problems because the registry entry might be of the form // (c:\program files\Windows Messaging\[...]) instead of // ("c:\program files\Windows Messaging\[...]"). Compare this with // a reg value with (rundll32 C:\progra~1\etc) // We end up parsing attempting to run C:\program, which of course doesn't exist. // This is a hack for the benefit OSR2, which turns szExpCommand // into a null string, rather than letting it be, if it can't be shortened. GetShortPathNameW(szExpCommand, szExpCommand, ARRAYSIZE(szExpCommand)); if ((*szExpCommand==L'\0') && (*szCommand!=L'\0')) { SHExpandEnvironmentStringsW(szCommand, szExpCommand, ARRAYSIZE(szExpCommand)); } pszArgs = PathGetArgsW(szExpCommand); PathRemoveArgsW(szExpCommand); PathUnquoteSpacesW(szExpCommand); FillExecInfo(ExecInfo, hwnd, NULL, szExpCommand, pszArgs, NULL, SW_SHOWNORMAL); if (IsOS(OS_WHISTLERORGREATER)) { ExecInfo.fMask |= SEE_MASK_FLAG_LOG_USAGE; } hr = ShellExecuteExWrapW(&ExecInfo) ? S_OK : E_FAIL; } return hr; } // NOTE! RunIndirectRegCommand logs the action as user-initiated! HRESULT RunIndirectRegCommand(HWND hwnd, HKEY hkey, LPCWSTR pszKey, LPCWSTR pszVerb) { HRESULT hr = E_FAIL; WCHAR szDefApp[80]; LONG cbSize = sizeof(szDefApp); if (RegQueryValueW(hkey, pszKey, szDefApp, &cbSize) == ERROR_SUCCESS) { WCHAR szFullKey[256]; // tack on shell\%verb%\command wnsprintfW(szFullKey, ARRAYSIZE(szFullKey), L"%s\\%s\\shell\\%s\\command", pszKey, szDefApp, pszVerb); hr = RunRegCommand(hwnd, hkey, szFullKey); } return hr; } HRESULT SHRunIndirectRegClientCommand(HWND hwnd, LPCWSTR pszClient) { WCHAR szKey[80]; wnsprintfW(szKey, ARRAYSIZE(szKey), L"Software\\Clients\\%s", pszClient); return RunIndirectRegCommand(hwnd, HKEY_LOCAL_MACHINE, szKey, L"Open"); }