summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-04-14 13:26:12 (GMT)
committerKitware Robot <kwrobot@kitware.com>2022-04-14 13:26:20 (GMT)
commit0cfd55788326b98eedb77be9d9f8f821effae9b4 (patch)
tree1c4747a978842adb7cdc46ff95dd8f592a2c443f
parent3ca187ad3d16efcd2ee6ffdcbfe3fc05feebc34b (diff)
parent17ff86547e759fea54b74eecce5fa62f33bba7f6 (diff)
downloadCMake-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
-rw-r--r--Help/command/cmake_host_system_information.rst152
-rw-r--r--Help/release/dev/chsi-query-windows-registry.rst5
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmCMakeHostSystemInformationCommand.cxx108
-rw-r--r--Source/cmWindowsRegistry.cxx442
-rw-r--r--Source/cmWindowsRegistry.h55
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadKey1.cmake4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadKey2.cmake4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1.cmake1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt5
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2.cmake1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView1-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt5
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView1.cmake1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView2-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView2-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView2.cmake1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView3-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView3-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView3.cmake1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-result.txt1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_NoArgs.cmake1
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_Query.cmake232
-rw-r--r--Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake24
-rw-r--r--Tests/RunCMake/cmake_host_system_information/registry_data.regbin0 -> 2322 bytes
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
new file mode 100644
index 0000000..8596648
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/registry_data.reg
Binary files differ