| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmConfigure.h" // IWYU pragma: keep | |
| #include "cmWindowsRegistry.h" | |
| #include <cstddef> | |
| #include <type_traits> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <cmext/string_view> | |
| #include "cmsys/RegularExpression.hxx" | |
| #include "cmsys/String.h" | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| # include <algorithm> | |
| # include <cstring> | |
| # include <exception> | |
| # include <iterator> | |
| # include <vector> | |
| # include <cm/memory> | |
| # include <windows.h> | |
| # include "cmMakefile.h" | |
| # include "cmStringAlgorithms.h" | |
| # include "cmValue.h" | |
| #endif | |
| namespace { | |
| // Case-independent string comparison | |
| int Strucmp(cm::string_view l, cm::string_view r) | |
| { | |
| if (l.empty() && r.empty()) { | |
| return 0; | |
| } | |
| if (l.empty() || r.empty()) { | |
| return static_cast<int>(l.size() - r.size()); | |
| } | |
| int lc; | |
| int rc; | |
| cm::string_view::size_type li = 0; | |
| cm::string_view::size_type ri = 0; | |
| do { | |
| lc = cmsysString_tolower(l[li++]); | |
| rc = cmsysString_tolower(r[ri++]); | |
| } while (lc == rc && li < l.size() && ri < r.size()); | |
| return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc; | |
| } | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| bool Is64BitWindows() | |
| { | |
| # if defined(_WIN64) | |
| // 64-bit programs run only on Win64 | |
| return true; | |
| # else | |
| // 32-bit programs run on both 32-bit and 64-bit Windows, so we must check. | |
| BOOL isWow64 = false; | |
| return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64; | |
| # endif | |
| } | |
| // Helper to translate Windows registry value type to enum ValueType | |
| cm::optional<cmWindowsRegistry::ValueType> ToValueType(DWORD type) | |
| { | |
| using ValueType = cmWindowsRegistry::ValueType; | |
| static std::unordered_map<DWORD, ValueType> ValueTypes{ | |
| { REG_SZ, ValueType::Reg_SZ }, | |
| { REG_EXPAND_SZ, ValueType::Reg_EXPAND_SZ }, | |
| { REG_MULTI_SZ, ValueType::Reg_MULTI_SZ }, | |
| { REG_DWORD, ValueType::Reg_DWORD }, | |
| { REG_QWORD, ValueType::Reg_QWORD } | |
| }; | |
| auto it = ValueTypes.find(type); | |
| return it == ValueTypes.end() | |
| ? cm::nullopt | |
| : cm::optional<cmWindowsRegistry::ValueType>{ it->second }; | |
| } | |
| // class registry_exception | |
| class registry_error : public std::exception | |
| { | |
| public: | |
| registry_error(std::string msg) | |
| : What(std::move(msg)) | |
| { | |
| } | |
| ~registry_error() override = default; | |
| char const* what() const noexcept override { return What.c_str(); } | |
| private: | |
| std::string What; | |
| }; | |
| // Class KeyHandler | |
| class KeyHandler | |
| { | |
| public: | |
| using View = cmWindowsRegistry::View; | |
| using ValueTypeSet = cmWindowsRegistry::ValueTypeSet; | |
| KeyHandler(HKEY hkey) | |
| : Handler(hkey) | |
| { | |
| } | |
| ~KeyHandler() { RegCloseKey(this->Handler); } | |
| static KeyHandler OpenKey(cm::string_view rootKey, cm::string_view subKey, | |
| View view); | |
| static KeyHandler OpenKey(cm::string_view key, View view); | |
| std::string ReadValue( | |
| cm::string_view name, | |
| ValueTypeSet supportedTypes = cmWindowsRegistry::AllTypes, | |
| cm::string_view separator = "\0"_s); | |
| std::vector<std::string> GetValueNames(); | |
| std::vector<std::string> GetSubKeys(); | |
| private: | |
| static std::string FormatSystemError(LSTATUS status); | |
| static std::wstring ToWide(cm::string_view str); | |
| static std::string ToNarrow(wchar_t const* str, int size = -1); | |
| HKEY Handler; | |
| }; | |
| KeyHandler KeyHandler::OpenKey(cm::string_view rootKey, cm::string_view subKey, | |
| View view) | |
| { | |
| if (view == View::Reg64 && !Is64BitWindows()) { | |
| throw registry_error("No 64bit registry on Windows32."); | |
| } | |
| HKEY hRootKey; | |
| if (rootKey == "HKCU"_s || rootKey == "HKEY_CURRENT_USER"_s) { | |
| hRootKey = HKEY_CURRENT_USER; | |
| } else if (rootKey == "HKLM"_s || rootKey == "HKEY_LOCAL_MACHINE"_s) { | |
| hRootKey = HKEY_LOCAL_MACHINE; | |
| } else if (rootKey == "HKCR"_s || rootKey == "HKEY_CLASSES_ROOT"_s) { | |
| hRootKey = HKEY_CLASSES_ROOT; | |
| } else if (rootKey == "HKCC"_s || rootKey == "HKEY_CURRENT_CONFIG"_s) { | |
| hRootKey = HKEY_CURRENT_CONFIG; | |
| } else if (rootKey == "HKU"_s || rootKey == "HKEY_USERS"_s) { | |
| hRootKey = HKEY_USERS; | |
| } else { | |
| throw registry_error(cmStrCat(rootKey, ": invalid root key.")); | |
| } | |
| // Update path format | |
| auto key = ToWide(subKey); | |
| std::replace(key.begin(), key.end(), L'/', L'\\'); | |
| REGSAM options = KEY_READ; | |
| if (Is64BitWindows()) { | |
| options |= view == View::Reg64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY; | |
| } | |
| HKEY hKey; | |
| LSTATUS status; | |
| if ((status = RegOpenKeyExW(hRootKey, key.c_str(), 0, options, &hKey)) != | |
| ERROR_SUCCESS) { | |
| throw registry_error(FormatSystemError(status)); | |
| } | |
| return KeyHandler(hKey); | |
| } | |
| KeyHandler KeyHandler::OpenKey(cm::string_view key, View view) | |
| { | |
| auto start = key.find_first_of("\\/"_s); | |
| return OpenKey(key.substr(0, start), | |
| start == cm::string_view::npos ? cm::string_view{ ""_s } | |
| : key.substr(start + 1), | |
| view); | |
| } | |
| std::string KeyHandler::FormatSystemError(LSTATUS status) | |
| { | |
| std::string formattedMessage{ "Windows Registry: unexpected error." }; | |
| LPWSTR message = nullptr; | |
| DWORD size = 1024; | |
| if (FormatMessageW( | |
| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, | |
| status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) != 0) { | |
| try { | |
| formattedMessage = cmTrimWhitespace(ToNarrow(message)); | |
| } catch (...) { | |
| // ignore any exception because this method can be called | |
| // as part of the raise of an exception | |
| } | |
| } | |
| LocalFree(message); | |
| return formattedMessage; | |
| } | |
| std::wstring KeyHandler::ToWide(cm::string_view str) | |
| { | |
| std::wstring wstr; | |
| if (str.empty()) { | |
| return wstr; | |
| } | |
| auto const wlength = | |
| MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), | |
| int(str.size()), nullptr, 0); | |
| if (wlength > 0) { | |
| auto wdata = cm::make_unique<wchar_t[]>(wlength); | |
| auto const r = | |
| MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), | |
| int(str.size()), wdata.get(), wlength); | |
| if (r > 0) { | |
| wstr = std::wstring(wdata.get(), wlength); | |
| } else { | |
| throw registry_error(FormatSystemError(GetLastError())); | |
| } | |
| } else { | |
| throw registry_error(FormatSystemError(GetLastError())); | |
| } | |
| return wstr; | |
| } | |
| std::string KeyHandler::ToNarrow(wchar_t const* wstr, int size) | |
| { | |
| std::string str; | |
| if (size == 0 || (size == -1 && wstr[0] == L'\0')) { | |
| return str; | |
| } | |
| auto const length = | |
| WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size, | |
| nullptr, 0, nullptr, nullptr); | |
| if (length > 0) { | |
| auto data = cm::make_unique<char[]>(length); | |
| auto const r = | |
| WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size, | |
| data.get(), length, nullptr, nullptr); | |
| if (r > 0) { | |
| if (size == -1) { | |
| str = std::string(data.get()); | |
| } else { | |
| str = std::string(data.get(), length); | |
| } | |
| } else { | |
| throw registry_error(FormatSystemError(GetLastError())); | |
| } | |
| } else { | |
| throw registry_error(FormatSystemError(GetLastError())); | |
| } | |
| return str; | |
| } | |
| std::string KeyHandler::ReadValue(cm::string_view name, | |
| ValueTypeSet supportedTypes, | |
| cm::string_view separator) | |
| { | |
| LSTATUS status; | |
| DWORD size; | |
| // pick-up maximum size for value | |
| if ((status = RegQueryInfoKeyW(this->Handler, nullptr, nullptr, nullptr, | |
| nullptr, nullptr, nullptr, nullptr, nullptr, | |
| &size, nullptr, nullptr)) != ERROR_SUCCESS) { | |
| throw registry_error(this->FormatSystemError(status)); | |
| } | |
| auto data = cm::make_unique<BYTE[]>(size); | |
| DWORD type; | |
| auto valueName = this->ToWide(name); | |
| if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr, | |
| &type, data.get(), &size)) != ERROR_SUCCESS) { | |
| throw registry_error(this->FormatSystemError(status)); | |
| } | |
| auto valueType = ToValueType(type); | |
| if (!valueType || !supportedTypes.contains(*valueType)) { | |
| throw registry_error(cmStrCat(type, ": unsupported type.")); | |
| } | |
| switch (type) { | |
| case REG_SZ: | |
| return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get())); | |
| break; | |
| case REG_EXPAND_SZ: { | |
| auto expandSize = ExpandEnvironmentStringsW( | |
| reinterpret_cast<wchar_t*>(data.get()), nullptr, 0); | |
| auto expandData = cm::make_unique<wchar_t[]>(expandSize + 1); | |
| if (ExpandEnvironmentStringsW(reinterpret_cast<wchar_t*>(data.get()), | |
| expandData.get(), expandSize + 1) == 0) { | |
| throw registry_error(this->FormatSystemError(GetLastError())); | |
| } else { | |
| return this->ToNarrow(expandData.get()); | |
| } | |
| } break; | |
| case REG_DWORD: | |
| return std::to_string(*reinterpret_cast<std::uint32_t*>(data.get())); | |
| break; | |
| case REG_QWORD: | |
| return std::to_string(*reinterpret_cast<std::uint64_t*>(data.get())); | |
| break; | |
| case REG_MULTI_SZ: { | |
| // replace separator with semicolon | |
| auto sep = this->ToWide(separator)[0]; | |
| std::replace(reinterpret_cast<wchar_t*>(data.get()), | |
| reinterpret_cast<wchar_t*>(data.get()) + | |
| (size / sizeof(wchar_t)) - 1, | |
| sep, L';'); | |
| return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get())); | |
| } break; | |
| default: | |
| throw registry_error(cmStrCat(type, ": unsupported type.")); | |
| } | |
| } | |
| std::vector<std::string> KeyHandler::GetValueNames() | |
| { | |
| LSTATUS status; | |
| DWORD maxSize; | |
| // pick-up maximum size for value names | |
| if ((status = RegQueryInfoKeyW( | |
| this->Handler, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |
| nullptr, &maxSize, nullptr, nullptr, nullptr)) != ERROR_SUCCESS) { | |
| throw registry_error(this->FormatSystemError(status)); | |
| } | |
| // increment size for final null | |
| auto data = cm::make_unique<wchar_t[]>(++maxSize); | |
| DWORD index = 0; | |
| DWORD size = maxSize; | |
| std::vector<std::string> valueNames; | |
| while ((status = RegEnumValueW(this->Handler, index, data.get(), &size, | |
| nullptr, nullptr, nullptr, nullptr)) == | |
| ERROR_SUCCESS) { | |
| auto name = this->ToNarrow(data.get()); | |
| valueNames.push_back(name.empty() ? "(default)" : name); | |
| size = maxSize; | |
| ++index; | |
| } | |
| if (status != ERROR_NO_MORE_ITEMS) { | |
| throw registry_error(this->FormatSystemError(status)); | |
| } | |
| return valueNames; | |
| } | |
| std::vector<std::string> KeyHandler::GetSubKeys() | |
| { | |
| LSTATUS status; | |
| DWORD size; | |
| // pick-up maximum size for subkeys | |
| if ((status = RegQueryInfoKeyW( | |
| this->Handler, nullptr, nullptr, nullptr, nullptr, &size, nullptr, | |
| nullptr, nullptr, nullptr, nullptr, nullptr)) != ERROR_SUCCESS) { | |
| throw registry_error(this->FormatSystemError(status)); | |
| } | |
| // increment size for final null | |
| auto data = cm::make_unique<wchar_t[]>(++size); | |
| DWORD index = 0; | |
| std::vector<std::string> subKeys; | |
| while ((status = RegEnumKeyW(this->Handler, index, data.get(), size)) == | |
| ERROR_SUCCESS) { | |
| subKeys.push_back(this->ToNarrow(data.get())); | |
| ++index; | |
| } | |
| if (status != ERROR_NO_MORE_ITEMS) { | |
| throw registry_error(this->FormatSystemError(status)); | |
| } | |
| return subKeys; | |
| } | |
| #endif | |
| // ExpressionParser: Helper to parse expression holding multiple | |
| // registry specifications | |
| class ExpressionParser | |
| { | |
| public: | |
| ExpressionParser(cm::string_view expression) | |
| : Expression(expression) | |
| , Separator(";"_s) | |
| , RegistryFormat{ | |
| "\\[({.+})?(HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE|HKCR|HKEY_" | |
| "CLASSES_" | |
| "ROOT|HKCC|HKEY_CURRENT_CONFIG|HKU|HKEY_USERS)[/\\]?([^]]*)\\]" | |
| } | |
| { | |
| } | |
| bool Find() | |
| { | |
| // reset result members | |
| this->RootKey = cm::string_view{}; | |
| this->SubKey = cm::string_view{}; | |
| this->ValueName = cm::string_view{}; | |
| auto result = this->RegistryFormat.find(this->Expression); | |
| if (result) { | |
| auto separator = cm::string_view{ | |
| this->Expression.data() + this->RegistryFormat.start(1), | |
| this->RegistryFormat.end(1) - this->RegistryFormat.start(1) | |
| }; | |
| if (separator.empty()) { | |
| separator = this->Separator; | |
| } else { | |
| separator = separator.substr(1, separator.length() - 2); | |
| } | |
| this->RootKey = cm::string_view{ | |
| this->Expression.data() + this->RegistryFormat.start(2), | |
| this->RegistryFormat.end(2) - this->RegistryFormat.start(2) | |
| }; | |
| this->SubKey = cm::string_view{ | |
| this->Expression.data() + this->RegistryFormat.start(3), | |
| this->RegistryFormat.end(3) - this->RegistryFormat.start(3) | |
| }; | |
| auto pos = this->SubKey.find(separator); | |
| if (pos != cm::string_view::npos) { | |
| // split in ValueName and SubKey | |
| this->ValueName = this->SubKey.substr(pos + separator.size()); | |
| if (Strucmp(this->ValueName, "(default)") == 0) { | |
| // handle magic name for default value | |
| this->ValueName = ""_s; | |
| } | |
| this->SubKey = this->SubKey.substr(0, pos); | |
| } else { | |
| this->ValueName = ""_s; | |
| } | |
| } | |
| return result; | |
| } | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| void Replace(std::string const& value) | |
| { | |
| this->Expression.replace( | |
| this->RegistryFormat.start(), | |
| this->RegistryFormat.end() - this->RegistryFormat.start(), value); | |
| } | |
| cm::string_view GetRootKey() const { return this->RootKey; } | |
| cm::string_view GetSubKey() const { return this->SubKey; } | |
| cm::string_view GetValueName() const { return this->ValueName; } | |
| std::string const& GetExpression() const { return this->Expression; } | |
| #endif | |
| private: | |
| std::string Expression; | |
| cm::string_view Separator; | |
| cmsys::RegularExpression RegistryFormat; | |
| cm::string_view RootKey; | |
| cm::string_view SubKey; | |
| cm::string_view ValueName; | |
| }; | |
| } | |
| // class cmWindowsRegistry | |
| cmWindowsRegistry::ValueTypeSet const cmWindowsRegistry::SimpleTypes = | |
| cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ, | |
| cmWindowsRegistry::ValueType::Reg_EXPAND_SZ, | |
| cmWindowsRegistry::ValueType::Reg_DWORD, | |
| cmWindowsRegistry::ValueType::Reg_QWORD }; | |
| cmWindowsRegistry::ValueTypeSet const cmWindowsRegistry::AllTypes = | |
| cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ; | |
| cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile, | |
| ValueTypeSet const& supportedTypes) | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| : SupportedTypes(supportedTypes) | |
| #else | |
| : LastError("No Registry on this platform.") | |
| #endif | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| if (cmValue targetSize = makefile.GetDefinition("CMAKE_SIZEOF_VOID_P")) { | |
| this->TargetSize = targetSize == "8" ? 64 : 32; | |
| } | |
| #else | |
| (void)makefile; | |
| (void)supportedTypes; | |
| #endif | |
| } | |
| cm::optional<cmWindowsRegistry::View> cmWindowsRegistry::ToView( | |
| cm::string_view name) | |
| { | |
| static std::unordered_map<cm::string_view, cmWindowsRegistry::View> | |
| ViewDefinitions{ | |
| { "BOTH"_s, View::Both }, { "HOST"_s, View::Host }, | |
| { "TARGET"_s, View::Target }, { "32"_s, View::Reg32 }, | |
| { "64"_s, View::Reg64 }, { "32_64"_s, View::Reg32_64 }, | |
| { "64_32"_s, View::Reg64_32 } | |
| }; | |
| auto it = ViewDefinitions.find(name); | |
| return it == ViewDefinitions.end() | |
| ? cm::nullopt | |
| : cm::optional<cmWindowsRegistry::View>{ it->second }; | |
| } | |
| // define hash structure required by std::unordered_map | |
| namespace std { | |
| template <> | |
| struct hash<cmWindowsRegistry::View> | |
| { | |
| size_t operator()(cmWindowsRegistry::View v) const noexcept | |
| { | |
| return static_cast< | |
| typename underlying_type<cmWindowsRegistry::View>::type>(v); | |
| } | |
| }; | |
| } | |
| cm::string_view cmWindowsRegistry::FromView(View view) | |
| { | |
| static std::unordered_map<cmWindowsRegistry::View, cm::string_view> | |
| ViewDefinitions{ | |
| { View::Both, "BOTH"_s }, { View::Host, "HOST"_s }, | |
| { View::Target, "TARGET"_s }, { View::Reg32, "32"_s }, | |
| { View::Reg64, "64"_s }, { View::Reg32_64, "32_64"_s }, | |
| { View::Reg64_32, "64_32"_s } | |
| }; | |
| auto it = ViewDefinitions.find(view); | |
| return it == ViewDefinitions.end() ? ""_s : it->second; | |
| } | |
| cm::string_view cmWindowsRegistry::GetLastError() const | |
| { | |
| return this->LastError; | |
| } | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| std::vector<cmWindowsRegistry::View> cmWindowsRegistry::ComputeViews(View view) | |
| { | |
| switch (view) { | |
| case View::Both: | |
| switch (this->TargetSize) { | |
| case 64: | |
| return std::vector<View>{ View::Reg64, View::Reg32 }; | |
| break; | |
| case 32: | |
| return Is64BitWindows() | |
| ? std::vector<View>{ View::Reg32, View::Reg64 } | |
| : std::vector<View>{ View::Reg32 }; | |
| break; | |
| default: | |
| // No language specified, fallback to host architecture | |
| return Is64BitWindows() | |
| ? std::vector<View>{ View::Reg64, View::Reg32 } | |
| : std::vector<View>{ View::Reg32 }; | |
| break; | |
| } | |
| break; | |
| case View::Target: | |
| switch (this->TargetSize) { | |
| case 64: | |
| return std::vector<View>{ View::Reg64 }; | |
| break; | |
| case 32: | |
| return std::vector<View>{ View::Reg32 }; | |
| break; | |
| default: | |
| break; | |
| } | |
| CM_FALLTHROUGH; | |
| case View::Host: | |
| return std::vector<View>{ Is64BitWindows() ? View::Reg64 : View::Reg32 }; | |
| break; | |
| case View::Reg64_32: | |
| return Is64BitWindows() ? std::vector<View>{ View::Reg64, View::Reg32 } | |
| : std::vector<View>{ View::Reg32 }; | |
| break; | |
| case View::Reg32_64: | |
| return Is64BitWindows() ? std::vector<View>{ View::Reg32, View::Reg64 } | |
| : std::vector<View>{ View::Reg32 }; | |
| break; | |
| default: | |
| return std::vector<View>{ view }; | |
| break; | |
| } | |
| } | |
| #endif | |
| cm::optional<std::string> cmWindowsRegistry::ReadValue( | |
| cm::string_view key, cm::string_view name, View view, | |
| cm::string_view separator) | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| // compute list of registry views | |
| auto views = this->ComputeViews(view); | |
| if (Strucmp(name, "(default)") == 0) { | |
| // handle magic name for default value | |
| name = ""_s; | |
| } | |
| if (separator.empty()) { | |
| separator = "\0"_s; | |
| } | |
| for (auto v : views) { | |
| try { | |
| this->LastError.clear(); | |
| auto handler = KeyHandler::OpenKey(key, v); | |
| return handler.ReadValue(name, this->SupportedTypes, separator); | |
| } catch (registry_error const& e) { | |
| this->LastError = e.what(); | |
| continue; | |
| } | |
| } | |
| #else | |
| (void)key; | |
| (void)name; | |
| (void)view; | |
| (void)separator; | |
| #endif | |
| return cm::nullopt; | |
| } | |
| cm::optional<std::vector<std::string>> cmWindowsRegistry::GetValueNames( | |
| cm::string_view key, View view) | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| this->LastError.clear(); | |
| // compute list of registry views | |
| auto views = this->ComputeViews(view); | |
| std::vector<std::string> valueNames; | |
| bool querySuccessful = false; | |
| for (auto v : views) { | |
| try { | |
| auto handler = KeyHandler::OpenKey(key, v); | |
| auto list = handler.GetValueNames(); | |
| std::move(list.begin(), list.end(), std::back_inserter(valueNames)); | |
| querySuccessful = true; | |
| } catch (registry_error const& e) { | |
| this->LastError = e.what(); | |
| continue; | |
| } | |
| } | |
| if (!valueNames.empty()) { | |
| // value names must be unique and sorted | |
| std::sort(valueNames.begin(), valueNames.end()); | |
| valueNames.erase(std::unique(valueNames.begin(), valueNames.end()), | |
| valueNames.end()); | |
| } | |
| if (querySuccessful) { | |
| // At least one query was successful, so clean-up any error message | |
| this->LastError.clear(); | |
| return valueNames; | |
| } | |
| #else | |
| (void)key; | |
| (void)view; | |
| #endif | |
| return cm::nullopt; | |
| } | |
| cm::optional<std::vector<std::string>> cmWindowsRegistry::GetSubKeys( | |
| cm::string_view key, View view) | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| this->LastError.clear(); | |
| // compute list of registry views | |
| auto views = this->ComputeViews(view); | |
| std::vector<std::string> subKeys; | |
| bool querySuccessful = false; | |
| for (auto v : views) { | |
| try { | |
| auto handler = KeyHandler::OpenKey(key, v); | |
| auto list = handler.GetSubKeys(); | |
| std::move(list.begin(), list.end(), std::back_inserter(subKeys)); | |
| querySuccessful = true; | |
| } catch (registry_error const& e) { | |
| this->LastError = e.what(); | |
| continue; | |
| } | |
| } | |
| if (!subKeys.empty()) { | |
| // keys must be unique and sorted | |
| std::sort(subKeys.begin(), subKeys.end()); | |
| subKeys.erase(std::unique(subKeys.begin(), subKeys.end()), subKeys.end()); | |
| } | |
| if (querySuccessful) { | |
| // At least one query was successful, so clean-up any error message | |
| this->LastError.clear(); | |
| return subKeys; | |
| } | |
| #else | |
| (void)key; | |
| (void)view; | |
| #endif | |
| return cm::nullopt; | |
| } | |
| cm::optional<std::vector<std::string>> cmWindowsRegistry::ExpandExpression( | |
| cm::string_view expression, View view, cm::string_view separator) | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| static std::string NOTFOUND{ "/REGISTRY-NOTFOUND" }; | |
| this->LastError.clear(); | |
| // compute list of registry views | |
| auto views = this->ComputeViews(view); | |
| std::vector<std::string> result; | |
| for (auto v : views) { | |
| ExpressionParser parser(expression); | |
| while (parser.Find()) { | |
| try { | |
| auto handler = | |
| KeyHandler::OpenKey(parser.GetRootKey(), parser.GetSubKey(), v); | |
| auto data = handler.ReadValue(parser.GetValueName(), | |
| this->SupportedTypes, separator); | |
| parser.Replace(data); | |
| } catch (registry_error const& e) { | |
| this->LastError = e.what(); | |
| parser.Replace(NOTFOUND); | |
| continue; | |
| } | |
| } | |
| result.emplace_back(parser.GetExpression()); | |
| if (expression == parser.GetExpression()) { | |
| // there no substitutions, so can ignore other views | |
| break; | |
| } | |
| } | |
| return result; | |
| #else | |
| (void)view; | |
| (void)separator; | |
| ExpressionParser parser(expression); | |
| if (parser.Find()) { | |
| // expression holds unsupported registry access | |
| // so the expression cannot be used on this platform | |
| return cm::nullopt; | |
| } | |
| return std::vector<std::string>{ std::string{ expression } }; | |
| #endif | |
| } |