|
@@ -0,0 +1,442 @@
|
|
|
|
|
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
|
+ file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
|
+
|
|
|
|
|
+#include "cmWindowsRegistry.h"
|
|
|
|
|
+
|
|
|
|
|
+#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
|
|
|
+# include <algorithm>
|
|
|
|
|
+# include <cstdint>
|
|
|
|
|
+# include <exception>
|
|
|
|
|
+# include <iterator>
|
|
|
|
|
+# include <utility>
|
|
|
|
|
+# include <vector>
|
|
|
|
|
+
|
|
|
|
|
+# include <cm/memory>
|
|
|
|
|
+# include <cmext/string_view>
|
|
|
|
|
+
|
|
|
|
|
+# include <windows.h>
|
|
|
|
|
+
|
|
|
|
|
+# include "cmsys/Encoding.hxx"
|
|
|
|
|
+# include "cmsys/SystemTools.hxx"
|
|
|
|
|
+
|
|
|
|
|
+# include "cmMakefile.h"
|
|
|
|
|
+# include "cmStringAlgorithms.h"
|
|
|
|
|
+# include "cmValue.h"
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
|
|
|
+namespace {
|
|
|
|
|
+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
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 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;
|
|
|
|
|
+
|
|
|
|
|
+ KeyHandler(HKEY hkey)
|
|
|
|
|
+ : Handler(hkey)
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+ ~KeyHandler() { RegCloseKey(this->Handler); }
|
|
|
|
|
+
|
|
|
|
|
+ static KeyHandler OpenKey(cm::string_view key, View view);
|
|
|
|
|
+
|
|
|
|
|
+ std::string ReadValue(cm::string_view name, cm::string_view separator);
|
|
|
|
|
+
|
|
|
|
|
+ std::vector<std::string> GetValueNames();
|
|
|
|
|
+ std::vector<std::string> GetSubKeys();
|
|
|
|
|
+
|
|
|
|
|
+private:
|
|
|
|
|
+ static std::string FormatSystemError(LSTATUS status);
|
|
|
|
|
+
|
|
|
|
|
+ HKEY Handler;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
|
|
|
|
|
+{
|
|
|
|
|
+ if (view == View::Reg64 && !Is64BitWindows()) {
|
|
|
|
|
+ throw registry_error("No 64bit registry on Windows32.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ auto start = key.find_first_of("\\/"_s);
|
|
|
|
|
+ auto rootKey = key.substr(0, start);
|
|
|
|
|
+ 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."));
|
|
|
|
|
+ }
|
|
|
|
|
+ std::wstring subKey;
|
|
|
|
|
+ if (start != cm::string_view::npos) {
|
|
|
|
|
+ subKey = cmsys::Encoding::ToWide(key.substr(start + 1).data());
|
|
|
|
|
+ }
|
|
|
|
|
+ // Update path format
|
|
|
|
|
+ std::replace(subKey.begin(), subKey.end(), L'/', L'\\');
|
|
|
|
|
+
|
|
|
|
|
+ REGSAM options = KEY_READ;
|
|
|
|
|
+ if (Is64BitWindows()) {
|
|
|
|
|
+ options |= view == View::Reg64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ HKEY hKey;
|
|
|
|
|
+ if (LSTATUS status = RegOpenKeyExW(hRootKey, subKey.c_str(), 0, options,
|
|
|
|
|
+ &hKey) != ERROR_SUCCESS) {
|
|
|
|
|
+ throw registry_error(FormatSystemError(status));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return KeyHandler(hKey);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+std::string KeyHandler::FormatSystemError(LSTATUS status)
|
|
|
|
|
+{
|
|
|
|
|
+ std::string formattedMessage;
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ formattedMessage = "Windows Registry: unexpected error.";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ formattedMessage = cmTrimWhitespace(cmsys::Encoding::ToNarrow(message));
|
|
|
|
|
+ }
|
|
|
|
|
+ LocalFree(message);
|
|
|
|
|
+
|
|
|
|
|
+ return formattedMessage;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+std::string KeyHandler::ReadValue(cm::string_view name,
|
|
|
|
|
+ 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 = cmsys::Encoding::ToWide(name.data());
|
|
|
|
|
+ if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr,
|
|
|
|
|
+ &type, data.get(), &size)) != ERROR_SUCCESS) {
|
|
|
|
|
+ throw registry_error(this->FormatSystemError(status));
|
|
|
|
|
+ }
|
|
|
|
|
+ switch (type) {
|
|
|
|
|
+ case REG_SZ:
|
|
|
|
|
+ return cmsys::Encoding::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 cmsys::Encoding::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 = cmsys::Encoding::ToWide(separator.data())[0];
|
|
|
|
|
+ std::replace(reinterpret_cast<wchar_t*>(data.get()),
|
|
|
|
|
+ reinterpret_cast<wchar_t*>(data.get()) +
|
|
|
|
|
+ (size / sizeof(wchar_t)) - 1,
|
|
|
|
|
+ sep, L';');
|
|
|
|
|
+ return cmsys::Encoding::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 = cmsys::Encoding::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(cmsys::Encoding::ToNarrow(data.get()));
|
|
|
|
|
+ ++index;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (status != ERROR_NO_MORE_ITEMS) {
|
|
|
|
|
+ throw registry_error(this->FormatSystemError(status));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return subKeys;
|
|
|
|
|
+}
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+// class cmWindowsRegistry
|
|
|
|
|
+cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
|
|
|
|
|
+#if !defined(_WIN32) || defined(__CYGWIN__)
|
|
|
|
|
+ : 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;
|
|
|
|
|
+#endif
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+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 (cmsys::SystemTools::Strucmp(name.data(), "(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, 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;
|
|
|
|
|
+}
|