diff options
author | Brad King <brad.king@kitware.com> | 2022-04-14 13:26:12 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-04-14 13:26:20 (GMT) |
commit | 0cfd55788326b98eedb77be9d9f8f821effae9b4 (patch) | |
tree | 1c4747a978842adb7cdc46ff95dd8f592a2c443f | |
parent | 3ca187ad3d16efcd2ee6ffdcbfe3fc05feebc34b (diff) | |
parent | 17ff86547e759fea54b74eecce5fa62f33bba7f6 (diff) | |
download | CMake-0cfd55788326b98eedb77be9d9f8f821effae9b4.zip CMake-0cfd55788326b98eedb77be9d9f8f821effae9b4.tar.gz CMake-0cfd55788326b98eedb77be9d9f8f821effae9b4.tar.bz2 |
Merge topic 'cmake_host_system_information-query-windows-registry'
17ff86547e cmake_host_system_information: query windows registry
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !7155
33 files changed, 1073 insertions, 3 deletions
diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst index 998e146..f47615a 100644 --- a/Help/command/cmake_host_system_information.rst +++ b/Help/command/cmake_host_system_information.rst @@ -1,9 +1,23 @@ cmake_host_system_information ----------------------------- -Query host system specific information. +Query various host system information. -.. code-block:: cmake +Synopsis +^^^^^^^^ + +.. parsed-literal:: + + `Query host system specific information`_ + cmake_host_system_information(RESULT <variable> QUERY <key> ...) + + `Query Windows registry`_ + cmake_host_system_information(RESULT <variable> QUERY WINDOWS_REGISTRY <key> ...) + +Query host system specific information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: cmake_host_system_information(RESULT <variable> QUERY <key> ...) @@ -180,7 +194,7 @@ distribution-specific files`_ to collect OS identification data and map it into `man 5 os-release`_ variables. Fallback Interface Variables -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +"""""""""""""""""""""""""""" .. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS @@ -246,3 +260,135 @@ Example: .. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/os-release.html .. _various distribution-specific files: http://linuxmafia.com/faq/Admin/release-files.html + +Query Windows registry +^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.24 + +:: + + cmake_host_system_information(RESULT <variable> + QUERY WINDOWS_REGISTRY <key> [VALUE_NAMES|SUBKEYS|VALUE <name>] + [VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)] + [SEPARATOR <separator>] + [ERROR_VARIABLE <result>]) + +Performs query operations on local computer registry subkey. Returns a list of +subkeys or value names that are located under the specified subkey in the +registry or the data of the specified value name. The result of the queried +entity is stored in ``<variable>``. + +.. note:: + + Querying registry for any other platforms than ``Windows``, including + ``CYGWIN``, will always returns an empty string and sets an error message in + the variable specified with sub-option ``ERROR_VARIABLE``. + +``<key>`` specify the full path of a subkey on the local computer. The +``<key>`` must include a valid root key. Valid root keys for the local computer +are: + +* ``HKLM`` or ``HKEY_LOCAL_MACHINE`` +* ``HKCU`` or ``HKEY_CURRENT_USER`` +* ``HKCR`` or ``HKEY_CLASSES_ROOT`` +* ``HKU`` or ``HKEY_USERS`` +* ``HKCC`` or ``HKEY_CURRENT_CONFIG`` + +And, optionally, the path to a subkey under the specified root key. The path +separator can be the slash or the backslash. ``<key>`` is not case sensitive. +For example: + +.. code-block:: cmake + + cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKLM") + cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Kitware") + cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU\\SOFTWARE\\Kitware") + +``VALUE_NAMES`` + Request the list of value names defined under ``<key>``. If a default value + is defined, it will be identified with the special name ``(default)``. + +``SUBKEYS`` + Request the list of subkeys defined under ``<key>``. + +``VALUE <name>`` + Request the data stored in value named ``<name>``. If ``VALUE`` is not + specified or argument is the special name ``(default)``, the content of the + default value, if any, will be returned. + + .. code-block:: cmake + + # query default value for HKLM/SOFTWARE/Kitware key + cmake_host_system_information(RESULT result + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Kitware") + + # query default value for HKLM/SOFTWARE/Kitware key using special value name + cmake_host_system_information(RESULT result + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Kitware" + VALUE "(default)") + + Supported types are: + + * ``REG_SZ``. + * ``REG_EXPAND_SZ``. The returned data is expanded. + * ``REG_MULTI_SZ``. The returned is expressed as a CMake list. See also + ``SEPARATOR`` sub-option. + * ``REG_DWORD``. + * ``REG_QWORD``. + + For all other types, an empty string is returned. + +``VIEW`` + Specify which registry views must be queried. When not specified, ``BOTH`` + view is used. + + ``64`` + Query the 64bit registry. On ``32bit Windows``, returns always an empty + string. + + ``32`` + Query the 32bit registry. + + ``64_32`` + For ``VALUE`` sub-option or default value, query the registry using view + ``64``, and if the request failed, query the registry using view ``32``. + For ``VALUE_NAMES`` and ``SUBKEYS`` sub-options, query both views (``64`` + and ``32``) and merge the results (sorted and duplicates removed). + + ``32_64`` + For ``VALUE`` sub-option or default value, query the registry using view + ``32``, and if the request failed, query the registry using view ``64``. + For ``VALUE_NAMES`` and ``SUBKEYS`` sub-options, query both views (``32`` + and ``64``) and merge the results (sorted and duplicates removed). + + ``HOST`` + Query the registry matching the architecture of the host: ``64`` on ``64bit + Windows`` and ``32`` on ``32bit Windows``. + + ``TARGET`` + Query the registry matching the architecture specified by + :variable:`CMAKE_SIZEOF_VOID_P` variable. If not defined, fallback to + ``HOST`` view. + + ``BOTH`` + Query both views (``32`` and ``64``). The order depends of the following + rules: If :variable:`CMAKE_SIZEOF_VOID_P` variable is defined. Use the + following view depending of the content of this variable: + + * ``8``: ``64_32`` + * ``4``: ``32_64`` + + If :variable:`CMAKE_SIZEOF_VOID_P` variable is not defined, rely on + architecture of the host: + + * ``64bit``: ``64_32`` + * ``32bit``: ``32`` + +``SEPARATOR`` + Specify the separator character for ``REG_MULTI_SZ`` type. When not + specified, the character ``\0`` is used. + +``ERROR_VARIABLE <result>`` + Returns any error raised during query operation. In case of success, the + variable holds an empty string. diff --git a/Help/release/dev/chsi-query-windows-registry.rst b/Help/release/dev/chsi-query-windows-registry.rst new file mode 100644 index 0000000..e75bbd8 --- /dev/null +++ b/Help/release/dev/chsi-query-windows-registry.rst @@ -0,0 +1,5 @@ +chsi-query-windows-registry +--------------------------- + +* :command:`cmake_host_system_information` command gains the capability, on + ``Windows`` platform, to query the registry. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 7661235..c245f68 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -460,6 +460,8 @@ set(SRCS cmVariableWatch.h cmVersion.cxx cmVersion.h + cmWindowsRegistry.cxx + cmWindowsRegistry.h cmWorkerPool.cxx cmWorkerPool.h cmWorkingDirectory.cxx diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 3922c56..0c41c68 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -9,6 +9,7 @@ #include <map> #include <string> #include <type_traits> +#include <unordered_map> #include <utility> #include <cm/optional> @@ -19,10 +20,13 @@ #include "cmsys/Glob.hxx" #include "cmsys/SystemInformation.hxx" +#include "cmArgumentParser.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmWindowsRegistry.h" #ifdef _WIN32 # include "cmAlgorithms.h" @@ -459,6 +463,105 @@ cm::optional<std::string> GetValueChained(GetterFn current, Next... chain) } return GetValueChained(chain...); } + +template <typename Range> +bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, + std::string const& variable) +{ + using View = cmWindowsRegistry::View; + 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 } + }; + + if (args.empty()) { + status.SetError("missing <key> specification."); + return false; + } + std::string const& key = *args.begin(); + + struct Arguments + { + std::string ValueName; + bool ValueNames = false; + bool SubKeys = false; + std::string View; + std::string Separator; + std::string ErrorVariable; + }; + cmArgumentParser<Arguments> parser; + parser.Bind("VALUE"_s, &Arguments::ValueName) + .Bind("VALUE_NAMES"_s, &Arguments::ValueNames) + .Bind("SUBKEYS"_s, &Arguments::SubKeys) + .Bind("VIEW"_s, &Arguments::View) + .Bind("SEPARATOR"_s, &Arguments::Separator) + .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); + std::vector<std::string> invalidArgs; + std::vector<std::string> keywordsMissingValue; + + Arguments const arguments = + parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue); + if (!invalidArgs.empty()) { + status.SetError(cmStrCat("given invalid argument(s) \"", + cmJoin(invalidArgs, ", "_s), "\".")); + return false; + } + if (!keywordsMissingValue.empty()) { + status.SetError(cmStrCat("missing expected value for argument(s) \"", + cmJoin(keywordsMissingValue, ", "_s), "\".")); + return false; + } + if ((!arguments.ValueName.empty() && + (arguments.ValueNames || arguments.SubKeys)) || + (arguments.ValueName.empty() && arguments.ValueNames && + arguments.SubKeys)) { + status.SetError("given mutually exclusive sub-options \"VALUE\", " + "\"VALUE_NAMES\" or \"SUBKEYS\"."); + return false; + } + if (!arguments.View.empty() && + ViewDefinitions.find(arguments.View) == ViewDefinitions.end()) { + status.SetError( + cmStrCat("given invalid value for \"VIEW\": ", arguments.View, '.')); + return false; + } + + auto& makefile = status.GetMakefile(); + + makefile.AddDefinition(variable, ""_s); + + auto view = + arguments.View.empty() ? View::Both : ViewDefinitions[arguments.View]; + cmWindowsRegistry registry(makefile); + if (arguments.ValueNames) { + auto result = registry.GetValueNames(key, view); + if (result) { + makefile.AddDefinition(variable, cmJoin(*result, ";"_s)); + } + } else if (arguments.SubKeys) { + auto result = registry.GetSubKeys(key, view); + if (result) { + makefile.AddDefinition(variable, cmJoin(*result, ";"_s)); + } + } else { + auto result = + registry.ReadValue(key, arguments.ValueName, view, arguments.Separator); + if (result) { + makefile.AddDefinition(variable, *result); + } + } + + // return error message if requested + if (!arguments.ErrorVariable.empty()) { + makefile.AddDefinition(arguments.ErrorVariable, registry.GetLastError()); + } + + return true; +} + // END Private functions } // anonymous namespace @@ -481,6 +584,11 @@ bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args, return false; } + if (args[current_index + 1] == "WINDOWS_REGISTRY"_s) { + return QueryWindowsRegistry(cmMakeRange(args).advance(current_index + 2), + status, variable); + } + static cmsys::SystemInformation info; static auto initialized = false; if (!initialized) { diff --git a/Source/cmWindowsRegistry.cxx b/Source/cmWindowsRegistry.cxx new file mode 100644 index 0000000..c857a3b --- /dev/null +++ b/Source/cmWindowsRegistry.cxx @@ -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; +} diff --git a/Source/cmWindowsRegistry.h b/Source/cmWindowsRegistry.h new file mode 100644 index 0000000..6f10b3a --- /dev/null +++ b/Source/cmWindowsRegistry.h @@ -0,0 +1,55 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> +#include <vector> + +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + +class cmMakefile; + +class cmWindowsRegistry +{ +public: + cmWindowsRegistry(cmMakefile&); + + enum class View + { + Both, + Target, + Host, + Reg64_32, + Reg32_64, + Reg32, + Reg64 + }; + + cm::optional<std::string> ReadValue(cm::string_view key, + View view = View::Both, + cm::string_view separator = "\0"_s) + { + return this->ReadValue(key, ""_s, view, separator); + } + cm::optional<std::string> ReadValue(cm::string_view key, + cm::string_view name, + View view = View::Both, + cm::string_view separator = "\0"_s); + + cm::optional<std::vector<std::string>> GetValueNames(cm::string_view key, + View view = View::Both); + cm::optional<std::vector<std::string>> GetSubKeys(cm::string_view key, + View view = View::Both); + + cm::string_view GetLastError() const; + +private: +#if defined(_WIN32) && !defined(__CYGWIN__) + std::vector<View> ComputeViews(View view); + + int TargetSize = 0; +#endif + std::string LastError; +}; diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-stderr.txt new file mode 100644 index 0000000..1832ada --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at Registry_BadKey1.cmake:[0-9]+ \(message\): + WRONG_ROOT: invalid root key. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1.cmake new file mode 100644 index 0000000..6299f85 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey1.cmake @@ -0,0 +1,4 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY WRONG_ROOT/SUBKEY ERROR_VARIABLE error) +if (NOT error STREQUAL "") + message(FATAL_ERROR "${error}") +endif() diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-stderr.txt new file mode 100644 index 0000000..4be55bf --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at Registry_BadKey2.cmake:[0-9]+ \(message\): + HKLM-SUBKEY: invalid root key. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2.cmake new file mode 100644 index 0000000..1750078 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadKey2.cmake @@ -0,0 +1,4 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM-SUBKEY ERROR_VARIABLE error) +if (NOT error STREQUAL "") + message(FATAL_ERROR "${error}") +endif() diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-stderr.txt new file mode 100644 index 0000000..9510327 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at Registry_BadQuery1.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information given invalid argument\(s\) "BAD_OPTION". +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1.cmake new file mode 100644 index 0000000..a92f35c --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1.cmake @@ -0,0 +1 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE BAD_OPTION) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt new file mode 100644 index 0000000..6a430f1 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at Registry_BadQuery2.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information missing expected value for argument\(s\) + "VALUE". +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2.cmake new file mode 100644 index 0000000..7c751cc --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2.cmake @@ -0,0 +1 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE VALUE) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt new file mode 100644 index 0000000..5eda4ff --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at Registry_BadView1.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information missing expected value for argument\(s\) + "VIEW". +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1.cmake new file mode 100644 index 0000000..6527784 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1.cmake @@ -0,0 +1 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE VIEW) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView2-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView2-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView2-stderr.txt new file mode 100644 index 0000000..201d93a --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView2-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at Registry_BadView2.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information given invalid value for "VIEW": BAD_VIEW. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView2.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadView2.cmake new file mode 100644 index 0000000..d116c76 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView2.cmake @@ -0,0 +1 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE VIEW BAD_VIEW) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView3-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView3-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView3-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView3-stderr.txt new file mode 100644 index 0000000..5b7f7c7 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView3-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at Registry_BadView3.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information given invalid argument\(s\) "64". +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView3.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_BadView3.cmake new file mode 100644 index 0000000..7c5f272 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView3.cmake @@ -0,0 +1 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE VIEW 32 64) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-result.txt b/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-stderr.txt new file mode 100644 index 0000000..ff55fcb --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at Registry_NoArgs.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information missing <key> specification. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs.cmake new file mode 100644 index 0000000..87e253d --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_NoArgs.cmake @@ -0,0 +1 @@ +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY) diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_Query.cmake b/Tests/RunCMake/cmake_host_system_information/Registry_Query.cmake new file mode 100644 index 0000000..9f9fb14 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Registry_Query.cmake @@ -0,0 +1,232 @@ + +# check Windows architecture +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "HKCU" SUBKEYS VIEW 64 ERROR_VARIABLE status) +if (status STREQUAL "") + set(HOST_64BIT TRUE) +else() + set(HOST_64BIT FALSE) +endif() + +# helper function for test validation +function(CHECK key result status expression) + if(status STREQUAL "") + cmake_language(EVAL CODE + "if (NOT (${expression})) + message(SEND_ERROR \"wrong value for key '${key}': '${result}'\") + endif()") + else() + message(SEND_ERROR "query failed for key '${key}': '${status}'") + endif() +endfunction() + + +# HKCU/Software/Classes/CLSID/CMake-Tests/chsi-registry: Query default value +set(KEY "HKCU/Software/Classes/CLSID/CMake-Tests/chsi-registry") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"default 64bit\") + OR (NOT HOST_64BIT AND result STREQUAL \"default 32bit\")") +# query value using special name should be identical to default value +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" VALUE "(default)" ERROR_VARIABLE status) +check("${KEY}{(default)}" "${result2}" "${status}" "result2 STREQUAL result") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VIEW HOST ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"default 64bit\") + OR (NOT HOST_64BIT AND result STREQUAL \"default 32bit\")") +# VIEW TARGET should have same value as VIEW HOST +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result2}" "${status}" "result2 STREQUAL result") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VIEW 64 ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"default 64bit\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VIEW 32 ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"default 32bit\"") + +# reg 64bit is read first +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VIEW 64_32 ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"default 64bit\"") + +# reg 32bit is read first +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VIEW 32_64 ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"default 32bit\"") + + +# HKCU/Software/CMake-Tests/chsi-registry: Query named value +set(KEY "HKCU/Software/Classes/CLSID/CMake-Tests/chsi-registry") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"64bit\") + OR (NOT HOST_64BIT AND result STREQUAL \"32bit\")") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW HOST ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"64bit\") + OR (NOT HOST_64BIT AND result STREQUAL \"32bit\")") +# VIEW TARGET should have same value as VIEW HOST +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result2}" "${status}" "result2 STREQUAL result") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW 64 ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result}" "${status}" "result STREQUAL \"64bit\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW 32 ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result}" "${status}" "result STREQUAL \"32bit\"") + +# reg 64bit is read first +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW 64_32 ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result}" "${status}" "result STREQUAL \"64bit\"") + +# reg 32bit is read first +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW 32_64 ERROR_VARIABLE status) +check("${KEY}{BYTE_SIZE}" "${result}" "${status}" "result STREQUAL \"32bit\"") + + +# HKCU/Software/CMake-Tests/chsi-registry: check retrieval of various types +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE VALUE_SZ ERROR_VARIABLE status) +check("${KEY}{VALUE_SZ}" "${result}" "${status}" "result STREQUAL \"data with space\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE VALUE_EXPAND_SZ ERROR_VARIABLE status) +check("${KEY}{VALUE_EXPAND_SZ}" "${result}" "${status}" + "(NOT result STREQUAL \"PATH=%PATH%\") AND (result MATCHES \"^PATH=\")") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE VALUE_MULTI_SZ ERROR_VARIABLE status) +check("${KEY}{VALUE_MULTI_SZ}" "${result}" "${status}" "result STREQUAL \"data1;data2\"") +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE VALUE2_MULTI_SZ + SEPARATOR "|" ERROR_VARIABLE status) +check("${KEY}{VALUE2_MULTI_SZ}" "${result}" "${status}" "result STREQUAL \"data1;data2\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE VALUE_DWORD ERROR_VARIABLE status) +check("${KEY}{VALUE_DWORD}" "${result}" "${status}" "result EQUAL \"129\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE VALUE_QWORD ERROR_VARIABLE status) +check("${KEY}{VALUE_QWORD}" "${result}" "${status}" "result EQUAL \"513\"") + + +# HKCU/Software/CMake-Tests/chsi-registry: check retrieval of value names +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result}" "${status}" "result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE2_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\"") +# VIEW BOTH should have same result as default view +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW BOTH ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result2}" "${status}" "result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE2_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW HOST ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\") + OR (NOT HOST_64BIT AND result STREQUAL \"(default);BYTE_SIZE;VALUE2_SZ\")") +# VIEW TARGET should have same result as VIEW HOST +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result2}" "${status}" + "(HOST_64BIT AND result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\") + OR (NOT HOST_64BIT AND result STREQUAL \"(default);BYTE_SIZE;VALUE2_SZ\")") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW 64 ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result}" "${status}" + "result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW 32 ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result}" "${status}" "result STREQUAL \"(default);BYTE_SIZE;VALUE2_SZ\"") + +# reg 64bit is read first +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW 64_32 ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result}" "${status}" + "result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE2_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\"") + +# reg 32bit is read first. Result is the same as with view 64_32 +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW 32_64 ERROR_VARIABLE status) +check("${KEY}[VALUE_NAMES]" "${result2}" "${status}" "result2 STREQUAL result") + + +# HKCU/Software/CMake-Tests/chsi-registry: check retrieval of sub keys +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + ERROR_VARIABLE status) +check("${KEY}[SUBKEYS]" "${result}" "${status}" "result STREQUAL \"subkey1;subkey2;subkey3\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW HOST ERROR_VARIABLE status) +check("${KEY}[SUBKEYS]" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"subkey1;subkey2\") + OR (NOT HOST_64BIT AND result STREQUAL \"subkey1;subkey3\")") +# VIEW TARGET should have same result as VIEW HOST +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}[SUBKEYS]" "${result}" "${status}" + "(HOST_64BIT AND result STREQUAL \"subkey1;subkey2\") + OR (NOT HOST_64BIT AND result STREQUAL \"subkey1;subkey3\")") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW 64 ERROR_VARIABLE status) +check("${KEY}[SUBKEYS]" "${result}" "${status}" + "result STREQUAL \"subkey1;subkey2\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW 32 ERROR_VARIABLE status) +check("${KEY}[SUBKEYS]" "${result}" "${status}" + "result STREQUAL \"subkey1;subkey3\"") + +# reg 64bit is read first +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW 64_32 ERROR_VARIABLE status) +check("${KEY}[SUBLEYS]" "${result}" "${status}" "result STREQUAL \"subkey1;subkey2;subkey3\"") + +# reg 32bit is read first. Result is the same as with view 64_32 +cmake_host_system_information(RESULT result2 QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW 32_64 ERROR_VARIABLE status) +check("${KEY}[SUBKEYS]" "${result2}" "${status}" "result2 STREQUAL result") + + +# Check influence of variable CMAKE_SIZEOF_VOID_P +set(CMAKE_SIZEOF_VOID_P 8) + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"default 64bit\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"64bit\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"(default);BYTE_SIZE;VALUE2_MULTI_SZ;VALUE_DWORD;VALUE_EXPAND_SZ;VALUE_MULTI_SZ;VALUE_QWORD;VALUE_SZ\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"subkey1;subkey2\"") + + +set(CMAKE_SIZEOF_VOID_P 4) + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"default 32bit\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE BYTE_SIZE + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"32bit\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" VALUE_NAMES + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"(default);BYTE_SIZE;VALUE2_SZ\"") + +cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY "${KEY}" SUBKEYS + VIEW TARGET ERROR_VARIABLE status) +check("${KEY}" "${result}" "${status}" "result STREQUAL \"subkey1;subkey3\"") diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake index 87b6944..d857bee 100644 --- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake @@ -21,3 +21,27 @@ if(RunCMake_GENERATOR MATCHES "^Visual Studio " AND NOT RunCMake_GENERATOR STREQ else() run_cmake(VsMSBuildMissing) endif() + +# WINDOWS_REGISTRY tests +run_cmake(Registry_NoArgs) +run_cmake(Registry_BadQuery1) +run_cmake(Registry_BadQuery2) +run_cmake(Registry_BadView1) +run_cmake(Registry_BadView2) +run_cmake(Registry_BadView3) +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + run_cmake(Registry_BadKey1) + run_cmake(Registry_BadKey2) + + # Tests using the Windows registry + find_program(REG NAMES "reg.exe" NO_CACHE) + if (REG) + # crete some entries in the registry + cmake_path(CONVERT "${RunCMake_SOURCE_DIR}/registry_data.reg" TO_NATIVE_PATH_LIST registry_data) + execute_process(COMMAND "${REG}" import "${registry_data}" OUTPUT_QUIET ERROR_QUIET) + run_cmake(Registry_Query) + # clean-up registry + execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\CLSID\\CMake-Tests" /f OUTPUT_QUIET ERROR_QUIET) + execute_process(COMMAND "${REG}" delete "HKCU\\SOFTWARE\\Classes\\WOW6432Node\\CLSID\\CMake-Tests" /f OUTPUT_QUIET ERROR_QUIET) + endif() +endif() diff --git a/Tests/RunCMake/cmake_host_system_information/registry_data.reg b/Tests/RunCMake/cmake_host_system_information/registry_data.reg Binary files differnew file mode 100644 index 0000000..8596648 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/registry_data.reg |