Forráskód Böngészése

cmake_host_system_information: query windows registry

Fixes: #21240, #23367
Marc Chevrier 3 éve
szülő
commit
17ff86547e
33 módosított fájl, 1073 hozzáadás és 3 törlés
  1. 149 3
      Help/command/cmake_host_system_information.rst
  2. 5 0
      Help/release/dev/chsi-query-windows-registry.rst
  3. 2 0
      Source/CMakeLists.txt
  4. 108 0
      Source/cmCMakeHostSystemInformationCommand.cxx
  5. 442 0
      Source/cmWindowsRegistry.cxx
  6. 55 0
      Source/cmWindowsRegistry.h
  7. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-result.txt
  8. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-stderr.txt
  9. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadKey1.cmake
  10. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-result.txt
  11. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-stderr.txt
  12. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadKey2.cmake
  13. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-result.txt
  14. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-stderr.txt
  15. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1.cmake
  16. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-result.txt
  17. 5 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt
  18. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2.cmake
  19. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView1-result.txt
  20. 5 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt
  21. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView1.cmake
  22. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView2-result.txt
  23. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView2-stderr.txt
  24. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView2.cmake
  25. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView3-result.txt
  26. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView3-stderr.txt
  27. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_BadView3.cmake
  28. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-result.txt
  29. 4 0
      Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-stderr.txt
  30. 1 0
      Tests/RunCMake/cmake_host_system_information/Registry_NoArgs.cmake
  31. 232 0
      Tests/RunCMake/cmake_host_system_information/Registry_Query.cmake
  32. 24 0
      Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
  33. BIN
      Tests/RunCMake/cmake_host_system_information/registry_data.reg

+ 149 - 3
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.

+ 5 - 0
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.

+ 2 - 0
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

+ 108 - 0
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) {

+ 442 - 0
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;
+}

+ 55 - 0
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;
+};

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadKey1-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
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\)

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadKey2-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
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\)

+ 4 - 0
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()

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadQuery1-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
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\)

+ 1 - 0
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)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
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\)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2.cmake

@@ -0,0 +1 @@
+cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE VALUE)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadView1-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
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\)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadView1.cmake

@@ -0,0 +1 @@
+cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY HKLM/SOFTWARE VIEW)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadView2-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
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\)

+ 1 - 0
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)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_BadView3-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
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\)

+ 1 - 0
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)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_NoArgs-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
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\)

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Registry_NoArgs.cmake

@@ -0,0 +1 @@
+cmake_host_system_information(RESULT result QUERY WINDOWS_REGISTRY)

+ 232 - 0
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\"")

+ 24 - 0
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()

BIN
Tests/RunCMake/cmake_host_system_information/registry_data.reg