summaryrefslogtreecommitdiffstats
path: root/Source/cmWindowsRegistry.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmWindowsRegistry.cxx')
-rw-r--r--Source/cmWindowsRegistry.cxx770
1 files changed, 770 insertions, 0 deletions
diff --git a/Source/cmWindowsRegistry.cxx b/Source/cmWindowsRegistry.cxx
new file mode 100644
index 0000000..6dba863
--- /dev/null
+++ b/Source/cmWindowsRegistry.cxx
@@ -0,0 +1,770 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmWindowsRegistry.h"
+
+#include <cctype>
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+
+#include <cmext/string_view>
+
+#include "cmsys/RegularExpression.hxx"
+
+#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 = std::tolower(l[li++]);
+ rc = std::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;
+
+ const char* 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(const wchar_t* 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;
+ }
+
+ const auto 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);
+ const auto 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(const wchar_t* wstr, int size)
+{
+ std::string str;
+
+ if (size == 0 || (size == -1 && wstr[0] == L'\0')) {
+ return str;
+ }
+
+ const auto length =
+ WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
+ nullptr, 0, nullptr, nullptr);
+ if (length > 0) {
+ auto data = cm::make_unique<char[]>(length);
+ const auto 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(const std::string& 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; }
+
+ const std::string& 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
+const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::SimpleTypes =
+ cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ,
+ cmWindowsRegistry::ValueType::Reg_EXPAND_SZ,
+ cmWindowsRegistry::ValueType::Reg_DWORD,
+ cmWindowsRegistry::ValueType::Reg_QWORD };
+const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::AllTypes =
+ cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ;
+
+cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile,
+ const ValueTypeSet& 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 const& 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 (const registry_error& 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 (const registry_error& 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 (const registry_error& 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 (const registry_error& 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
+}