mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-07 06:40:16 -05:00
Merge topic 'find_item-query-windows-registry'
8d7e80cf3dfind_* commands: add control over Windows registry views08941a9a40cmWindowsRegistry: Add helper for conversion between string and enum View769f25aa3ccmWindowsRegistry: enhance unicode conversions Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !7211
This commit is contained in:
@@ -734,7 +734,7 @@ set(SRCS
|
||||
bindexplib.cxx
|
||||
)
|
||||
|
||||
SET_PROPERTY(SOURCE cmProcessOutput.cxx APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
SET_PROPERTY(SOURCE cmProcessOutput.cxx cmWindowsRegistry.cxx APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE})
|
||||
|
||||
# Xcode only works on Apple
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/optional>
|
||||
@@ -469,14 +468,6 @@ 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;
|
||||
@@ -522,8 +513,8 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
|
||||
"\"VALUE_NAMES\" or \"SUBKEYS\".");
|
||||
return false;
|
||||
}
|
||||
if (!arguments.View.empty() &&
|
||||
ViewDefinitions.find(arguments.View) == ViewDefinitions.end()) {
|
||||
|
||||
if (!arguments.View.empty() && !cmWindowsRegistry::ToView(arguments.View)) {
|
||||
status.SetError(
|
||||
cmStrCat("given invalid value for \"VIEW\": ", arguments.View, '.'));
|
||||
return false;
|
||||
@@ -533,8 +524,9 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
|
||||
|
||||
makefile.AddDefinition(variable, ""_s);
|
||||
|
||||
auto view =
|
||||
arguments.View.empty() ? View::Both : ViewDefinitions[arguments.View];
|
||||
auto view = arguments.View.empty()
|
||||
? View::Both
|
||||
: *cmWindowsRegistry::ToView(arguments.View);
|
||||
cmWindowsRegistry registry(makefile);
|
||||
if (arguments.ValueNames) {
|
||||
auto result = registry.GetValueNames(key, view);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/optional>
|
||||
#include <cmext/algorithm>
|
||||
|
||||
#include "cmCMakePath.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmWindowsRegistry.h"
|
||||
#include "cmake.h"
|
||||
|
||||
class cmExecutionStatus;
|
||||
@@ -123,6 +125,19 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
|
||||
doing = DoingNone;
|
||||
this->Required = true;
|
||||
newStyle = true;
|
||||
} else if (args[j] == "REGISTRY_VIEW") {
|
||||
if (++j == args.size()) {
|
||||
this->SetError("missing required argument for \"REGISTRY_VIEW\"");
|
||||
return false;
|
||||
}
|
||||
auto view = cmWindowsRegistry::ToView(args[j]);
|
||||
if (view) {
|
||||
this->RegistryView = *view;
|
||||
} else {
|
||||
this->SetError(
|
||||
cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j]));
|
||||
return false;
|
||||
}
|
||||
} else if (this->CheckCommonArgument(args[j])) {
|
||||
doing = DoingNone;
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "cmExecutionStatus.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmMessageType.h"
|
||||
#include "cmPolicies.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmValue.h"
|
||||
@@ -58,6 +59,17 @@ cmFindCommon::cmFindCommon(cmExecutionStatus& status)
|
||||
this->InitializeSearchPathGroups();
|
||||
|
||||
this->DebugMode = false;
|
||||
|
||||
// Windows Registry views
|
||||
// When policy CMP0134 is not NEW, rely on previous behavior:
|
||||
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0134) !=
|
||||
cmPolicies::NEW) {
|
||||
if (this->Makefile->GetDefinition("CMAKE_SIZEOF_VOID_P") == "8") {
|
||||
this->RegistryView = cmWindowsRegistry::View::Reg64;
|
||||
} else {
|
||||
this->RegistryView = cmWindowsRegistry::View::Reg32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmFindCommon::SetError(std::string const& e)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "cmPathLabel.h"
|
||||
#include "cmSearchPath.h"
|
||||
#include "cmWindowsRegistry.h"
|
||||
|
||||
class cmExecutionStatus;
|
||||
class cmMakefile;
|
||||
@@ -131,6 +132,7 @@ protected:
|
||||
bool NoSystemEnvironmentPath;
|
||||
bool NoCMakeSystemPath;
|
||||
bool NoCMakeInstallPath;
|
||||
cmWindowsRegistry::View RegistryView = cmWindowsRegistry::View::Target;
|
||||
|
||||
std::vector<std::string> SearchPathSuffixes;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <cm/memory>
|
||||
#include <cm/optional>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmsys/Directory.hxx"
|
||||
@@ -33,6 +34,7 @@
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmVersion.h"
|
||||
#include "cmWindowsRegistry.h"
|
||||
|
||||
#if defined(__HAIKU__)
|
||||
# include <FindDirectory.h>
|
||||
@@ -317,6 +319,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
|
||||
// Ignore legacy option.
|
||||
configArgs.insert(i);
|
||||
doing = DoingNone;
|
||||
} else if (args[i] == "REGISTRY_VIEW") {
|
||||
if (++i == args.size()) {
|
||||
this->SetError("missing required argument for \"REGISTRY_VIEW\"");
|
||||
return false;
|
||||
}
|
||||
auto view = cmWindowsRegistry::ToView(args[i]);
|
||||
if (view) {
|
||||
this->RegistryView = *view;
|
||||
this->RegistryViewDefined = true;
|
||||
} else {
|
||||
this->SetError(
|
||||
cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[i]));
|
||||
return false;
|
||||
}
|
||||
} else if (this->CheckCommonArgument(args[i])) {
|
||||
configArgs.insert(i);
|
||||
doing = DoingNone;
|
||||
@@ -767,6 +783,11 @@ void cmFindPackageCommand::SetModuleVariables(const std::string& components)
|
||||
id = cmStrCat(this->Name, "_FIND_VERSION_RANGE_MAX");
|
||||
this->AddFindDefinition(id, this->VersionRangeMax);
|
||||
}
|
||||
|
||||
if (this->RegistryViewDefined) {
|
||||
this->AddFindDefinition(cmStrCat(this->Name, "_FIND_REGISTRY_VIEW"),
|
||||
cmWindowsRegistry::FromView(this->RegistryView));
|
||||
}
|
||||
}
|
||||
|
||||
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
|
||||
|
||||
@@ -200,6 +200,7 @@ private:
|
||||
bool UseRealPath = false;
|
||||
bool PolicyScope = true;
|
||||
bool GlobalScope = false;
|
||||
bool RegistryViewDefined = false;
|
||||
std::string LibraryArchitecture;
|
||||
std::vector<std::string> Names;
|
||||
std::vector<std::string> Configs;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "cmStateTypes.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmWindowsRegistry.h"
|
||||
|
||||
class cmExecutionStatus;
|
||||
|
||||
@@ -172,6 +174,18 @@ cmFindProgramCommand::cmFindProgramCommand(cmExecutionStatus& status)
|
||||
this->NamesPerDirAllowed = true;
|
||||
this->VariableDocumentation = "Path to a program.";
|
||||
this->VariableType = cmStateEnums::FILEPATH;
|
||||
// Windows Registry views
|
||||
// When policy CMP0134 is not NEW, rely on previous behavior:
|
||||
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0134) !=
|
||||
cmPolicies::NEW) {
|
||||
if (this->Makefile->GetDefinition("CMAKE_SIZEOF_VOID_P") == "8") {
|
||||
this->RegistryView = cmWindowsRegistry::View::Reg64_32;
|
||||
} else {
|
||||
this->RegistryView = cmWindowsRegistry::View::Reg32_64;
|
||||
}
|
||||
} else {
|
||||
this->RegistryView = cmWindowsRegistry::View::Both;
|
||||
}
|
||||
}
|
||||
|
||||
// cmFindProgramCommand
|
||||
|
||||
@@ -400,6 +400,10 @@ class cmMakefile;
|
||||
SELECT(POLICY, CMP0133, \
|
||||
"The CPack module disables SLA by default in the CPack DragNDrop " \
|
||||
"Generator.", \
|
||||
3, 24, 0, cmPolicies::WARN) \
|
||||
SELECT(POLICY, CMP0134, \
|
||||
"Fallback to \"HOST\" Windows registry view when \"TARGET\" view " \
|
||||
"is not usable.", \
|
||||
3, 24, 0, cmPolicies::WARN)
|
||||
|
||||
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
|
||||
|
||||
+10
-20
@@ -6,11 +6,14 @@
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/optional>
|
||||
|
||||
#include "cmFindCommon.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmWindowsRegistry.h"
|
||||
|
||||
cmSearchPath::cmSearchPath(cmFindCommon* findCmd)
|
||||
: FC(findCmd)
|
||||
@@ -46,26 +49,13 @@ void cmSearchPath::AddUserPath(const std::string& path)
|
||||
|
||||
std::vector<std::string> outPaths;
|
||||
|
||||
// We should view the registry as the target application would view
|
||||
// it.
|
||||
cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
|
||||
cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
|
||||
if (this->FC->Makefile->PlatformIs64Bit()) {
|
||||
view = cmSystemTools::KeyWOW64_64;
|
||||
other_view = cmSystemTools::KeyWOW64_32;
|
||||
}
|
||||
|
||||
// Expand using the view of the target application.
|
||||
std::string expanded = path;
|
||||
cmSystemTools::ExpandRegistryValues(expanded, view);
|
||||
cmSystemTools::GlobDirs(expanded, outPaths);
|
||||
|
||||
// Executables can be either 32-bit or 64-bit, so expand using the
|
||||
// alternative view.
|
||||
if (expanded != path && this->FC->CMakePathName == "PROGRAM") {
|
||||
expanded = path;
|
||||
cmSystemTools::ExpandRegistryValues(expanded, other_view);
|
||||
cmSystemTools::GlobDirs(expanded, outPaths);
|
||||
cmWindowsRegistry registry(*this->FC->Makefile,
|
||||
cmWindowsRegistry::SimpleTypes);
|
||||
auto expandedPaths = registry.ExpandExpression(path, this->FC->RegistryView);
|
||||
if (expandedPaths) {
|
||||
for (const auto& expandedPath : expandedPaths.value()) {
|
||||
cmSystemTools::GlobDirs(expandedPath, outPaths);
|
||||
}
|
||||
}
|
||||
|
||||
// Process them all from the current directory
|
||||
|
||||
+364
-36
@@ -1,31 +1,61 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cmWindowsRegistry.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
# include <algorithm>
|
||||
# include <cstdint>
|
||||
# include <cstring>
|
||||
# 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 {
|
||||
// Case-independent string comparison
|
||||
int Strucmp(cm::string_view l, cm::string_view r)
|
||||
{
|
||||
if (l.empty() && r.empty()) {
|
||||
return 0;
|
||||
}
|
||||
if (l.empty() || r.empty()) {
|
||||
return static_cast<int>(l.size() - r.size());
|
||||
}
|
||||
|
||||
int lc;
|
||||
int rc;
|
||||
cm::string_view::size_type li = 0;
|
||||
cm::string_view::size_type ri = 0;
|
||||
|
||||
do {
|
||||
lc = std::tolower(l[li++]);
|
||||
rc = std::tolower(r[ri++]);
|
||||
} while (lc == rc && li < l.size() && ri < r.size());
|
||||
|
||||
return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
bool Is64BitWindows()
|
||||
{
|
||||
# if defined(_WIN64)
|
||||
@@ -38,6 +68,26 @@ bool Is64BitWindows()
|
||||
# endif
|
||||
}
|
||||
|
||||
// Helper to translate Windows registry value type to enum ValueType
|
||||
cm::optional<cmWindowsRegistry::ValueType> ToValueType(DWORD type)
|
||||
{
|
||||
using ValueType = cmWindowsRegistry::ValueType;
|
||||
|
||||
static std::unordered_map<DWORD, ValueType> ValueTypes{
|
||||
{ REG_SZ, ValueType::Reg_SZ },
|
||||
{ REG_EXPAND_SZ, ValueType::Reg_EXPAND_SZ },
|
||||
{ REG_MULTI_SZ, ValueType::Reg_MULTI_SZ },
|
||||
{ REG_DWORD, ValueType::Reg_DWORD },
|
||||
{ REG_QWORD, ValueType::Reg_QWORD }
|
||||
};
|
||||
|
||||
auto it = ValueTypes.find(type);
|
||||
|
||||
return it == ValueTypes.end()
|
||||
? cm::nullopt
|
||||
: cm::optional<cmWindowsRegistry::ValueType>{ it->second };
|
||||
}
|
||||
|
||||
// class registry_exception
|
||||
class registry_error : public std::exception
|
||||
{
|
||||
@@ -59,6 +109,7 @@ class KeyHandler
|
||||
{
|
||||
public:
|
||||
using View = cmWindowsRegistry::View;
|
||||
using ValueTypeSet = cmWindowsRegistry::ValueTypeSet;
|
||||
|
||||
KeyHandler(HKEY hkey)
|
||||
: Handler(hkey)
|
||||
@@ -66,29 +117,34 @@ public:
|
||||
}
|
||||
~KeyHandler() { RegCloseKey(this->Handler); }
|
||||
|
||||
static KeyHandler OpenKey(cm::string_view rootKey, cm::string_view subKey,
|
||||
View view);
|
||||
static KeyHandler OpenKey(cm::string_view key, View view);
|
||||
|
||||
std::string ReadValue(cm::string_view name, cm::string_view separator);
|
||||
std::string ReadValue(
|
||||
cm::string_view name,
|
||||
ValueTypeSet supportedTypes = cmWindowsRegistry::AllTypes,
|
||||
cm::string_view separator = "\0"_s);
|
||||
|
||||
std::vector<std::string> GetValueNames();
|
||||
std::vector<std::string> GetSubKeys();
|
||||
|
||||
private:
|
||||
static std::string FormatSystemError(LSTATUS status);
|
||||
static std::wstring ToWide(cm::string_view str);
|
||||
static std::string ToNarrow(const wchar_t* str, int size = -1);
|
||||
|
||||
HKEY Handler;
|
||||
};
|
||||
|
||||
KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
|
||||
KeyHandler KeyHandler::OpenKey(cm::string_view rootKey, cm::string_view subKey,
|
||||
View view)
|
||||
{
|
||||
if (view == View::Reg64 && !Is64BitWindows()) {
|
||||
throw registry_error("No 64bit registry on Windows32.");
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -102,12 +158,9 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
|
||||
} 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'\\');
|
||||
auto key = ToWide(subKey);
|
||||
std::replace(key.begin(), key.end(), L'/', L'\\');
|
||||
|
||||
REGSAM options = KEY_READ;
|
||||
if (Is64BitWindows()) {
|
||||
@@ -115,32 +168,107 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
|
||||
}
|
||||
|
||||
HKEY hKey;
|
||||
if (LSTATUS status = RegOpenKeyExW(hRootKey, subKey.c_str(), 0, options,
|
||||
&hKey) != ERROR_SUCCESS) {
|
||||
LSTATUS status;
|
||||
if ((status = RegOpenKeyExW(hRootKey, key.c_str(), 0, options, &hKey)) !=
|
||||
ERROR_SUCCESS) {
|
||||
throw registry_error(FormatSystemError(status));
|
||||
}
|
||||
|
||||
return KeyHandler(hKey);
|
||||
}
|
||||
|
||||
KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
|
||||
{
|
||||
auto start = key.find_first_of("\\/"_s);
|
||||
|
||||
return OpenKey(key.substr(0, start),
|
||||
start == cm::string_view::npos ? cm::string_view{ ""_s }
|
||||
: key.substr(start + 1),
|
||||
view);
|
||||
}
|
||||
|
||||
std::string KeyHandler::FormatSystemError(LSTATUS status)
|
||||
{
|
||||
std::string formattedMessage;
|
||||
std::string formattedMessage{ "Windows Registry: unexpected error." };
|
||||
LPWSTR message = nullptr;
|
||||
DWORD size = 1024;
|
||||
if (FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr,
|
||||
status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) == 0) {
|
||||
formattedMessage = "Windows Registry: unexpected error.";
|
||||
} else {
|
||||
formattedMessage = cmTrimWhitespace(cmsys::Encoding::ToNarrow(message));
|
||||
status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) != 0) {
|
||||
try {
|
||||
formattedMessage = cmTrimWhitespace(ToNarrow(message));
|
||||
} catch (...) {
|
||||
// ignore any exception because this method can be called
|
||||
// as part of the raise of an exception
|
||||
}
|
||||
}
|
||||
LocalFree(message);
|
||||
|
||||
return formattedMessage;
|
||||
}
|
||||
|
||||
std::wstring KeyHandler::ToWide(cm::string_view str)
|
||||
{
|
||||
std::wstring wstr;
|
||||
|
||||
if (str.empty()) {
|
||||
return wstr;
|
||||
}
|
||||
|
||||
const auto wlength =
|
||||
MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
|
||||
int(str.size()), nullptr, 0);
|
||||
if (wlength > 0) {
|
||||
auto wdata = cm::make_unique<wchar_t[]>(wlength);
|
||||
const auto r =
|
||||
MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
|
||||
int(str.size()), wdata.get(), wlength);
|
||||
if (r > 0) {
|
||||
wstr = std::wstring(wdata.get(), wlength);
|
||||
} else {
|
||||
throw registry_error(FormatSystemError(GetLastError()));
|
||||
}
|
||||
} else {
|
||||
throw registry_error(FormatSystemError(GetLastError()));
|
||||
}
|
||||
|
||||
return wstr;
|
||||
}
|
||||
|
||||
std::string KeyHandler::ToNarrow(const wchar_t* wstr, int size)
|
||||
{
|
||||
std::string str;
|
||||
|
||||
if (size == 0 || (size == -1 && wstr[0] == L'\0')) {
|
||||
return str;
|
||||
}
|
||||
|
||||
const auto length =
|
||||
WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
if (length > 0) {
|
||||
auto data = cm::make_unique<char[]>(length);
|
||||
const auto r =
|
||||
WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
|
||||
data.get(), length, nullptr, nullptr);
|
||||
if (r > 0) {
|
||||
if (size == -1) {
|
||||
str = std::string(data.get());
|
||||
} else {
|
||||
str = std::string(data.get(), length);
|
||||
}
|
||||
} else {
|
||||
throw registry_error(FormatSystemError(GetLastError()));
|
||||
}
|
||||
} else {
|
||||
throw registry_error(FormatSystemError(GetLastError()));
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string KeyHandler::ReadValue(cm::string_view name,
|
||||
ValueTypeSet supportedTypes,
|
||||
cm::string_view separator)
|
||||
{
|
||||
LSTATUS status;
|
||||
@@ -153,14 +281,20 @@ std::string KeyHandler::ReadValue(cm::string_view name,
|
||||
}
|
||||
auto data = cm::make_unique<BYTE[]>(size);
|
||||
DWORD type;
|
||||
auto valueName = cmsys::Encoding::ToWide(name.data());
|
||||
auto valueName = this->ToWide(name);
|
||||
if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr,
|
||||
&type, data.get(), &size)) != ERROR_SUCCESS) {
|
||||
throw registry_error(this->FormatSystemError(status));
|
||||
}
|
||||
|
||||
auto valueType = ToValueType(type);
|
||||
if (!valueType || !supportedTypes.contains(*valueType)) {
|
||||
throw registry_error(cmStrCat(type, ": unsupported type."));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case REG_SZ:
|
||||
return cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
|
||||
return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
|
||||
break;
|
||||
case REG_EXPAND_SZ: {
|
||||
auto expandSize = ExpandEnvironmentStringsW(
|
||||
@@ -170,7 +304,7 @@ std::string KeyHandler::ReadValue(cm::string_view name,
|
||||
expandData.get(), expandSize + 1) == 0) {
|
||||
throw registry_error(this->FormatSystemError(GetLastError()));
|
||||
} else {
|
||||
return cmsys::Encoding::ToNarrow(expandData.get());
|
||||
return this->ToNarrow(expandData.get());
|
||||
}
|
||||
} break;
|
||||
case REG_DWORD:
|
||||
@@ -181,12 +315,12 @@ std::string KeyHandler::ReadValue(cm::string_view name,
|
||||
break;
|
||||
case REG_MULTI_SZ: {
|
||||
// replace separator with semicolon
|
||||
auto sep = cmsys::Encoding::ToWide(separator.data())[0];
|
||||
auto sep = this->ToWide(separator)[0];
|
||||
std::replace(reinterpret_cast<wchar_t*>(data.get()),
|
||||
reinterpret_cast<wchar_t*>(data.get()) +
|
||||
(size / sizeof(wchar_t)) - 1,
|
||||
sep, L';');
|
||||
return cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
|
||||
return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
|
||||
} break;
|
||||
default:
|
||||
throw registry_error(cmStrCat(type, ": unsupported type."));
|
||||
@@ -213,7 +347,7 @@ std::vector<std::string> KeyHandler::GetValueNames()
|
||||
while ((status = RegEnumValueW(this->Handler, index, data.get(), &size,
|
||||
nullptr, nullptr, nullptr, nullptr)) ==
|
||||
ERROR_SUCCESS) {
|
||||
auto name = cmsys::Encoding::ToNarrow(data.get());
|
||||
auto name = this->ToNarrow(data.get());
|
||||
valueNames.push_back(name.empty() ? "(default)" : name);
|
||||
size = maxSize;
|
||||
++index;
|
||||
@@ -243,7 +377,7 @@ std::vector<std::string> KeyHandler::GetSubKeys()
|
||||
|
||||
while ((status = RegEnumKeyW(this->Handler, index, data.get(), size)) ==
|
||||
ERROR_SUCCESS) {
|
||||
subKeys.push_back(cmsys::Encoding::ToNarrow(data.get()));
|
||||
subKeys.push_back(this->ToNarrow(data.get()));
|
||||
++index;
|
||||
}
|
||||
if (status != ERROR_NO_MORE_ITEMS) {
|
||||
@@ -252,12 +386,109 @@ std::vector<std::string> KeyHandler::GetSubKeys()
|
||||
|
||||
return subKeys;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ExpressionParser: Helper to parse expression holding multiple
|
||||
// registry specifications
|
||||
class ExpressionParser
|
||||
{
|
||||
public:
|
||||
ExpressionParser(cm::string_view expression)
|
||||
: Expression(expression)
|
||||
, Separator(";"_s)
|
||||
, RegistryFormat{
|
||||
"\\[({.+})?(HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE|HKCR|HKEY_"
|
||||
"CLASSES_"
|
||||
"ROOT|HKCC|HKEY_CURRENT_CONFIG|HKU|HKEY_USERS)[/\\]?([^]]*)\\]"
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
bool Find()
|
||||
{
|
||||
// reset result members
|
||||
this->RootKey = cm::string_view{};
|
||||
this->SubKey = cm::string_view{};
|
||||
this->ValueName = cm::string_view{};
|
||||
|
||||
auto result = this->RegistryFormat.find(this->Expression);
|
||||
|
||||
if (result) {
|
||||
auto separator = cm::string_view{
|
||||
this->Expression.data() + this->RegistryFormat.start(1),
|
||||
this->RegistryFormat.end(1) - this->RegistryFormat.start(1)
|
||||
};
|
||||
if (separator.empty()) {
|
||||
separator = this->Separator;
|
||||
} else {
|
||||
separator = separator.substr(1, separator.length() - 2);
|
||||
}
|
||||
|
||||
this->RootKey = cm::string_view{
|
||||
this->Expression.data() + this->RegistryFormat.start(2),
|
||||
this->RegistryFormat.end(2) - this->RegistryFormat.start(2)
|
||||
};
|
||||
this->SubKey = cm::string_view{
|
||||
this->Expression.data() + this->RegistryFormat.start(3),
|
||||
this->RegistryFormat.end(3) - this->RegistryFormat.start(3)
|
||||
};
|
||||
|
||||
auto pos = this->SubKey.find(separator);
|
||||
if (pos != cm::string_view::npos) {
|
||||
// split in ValueName and SubKey
|
||||
this->ValueName = this->SubKey.substr(pos + separator.size());
|
||||
if (Strucmp(this->ValueName, "(default)") == 0) {
|
||||
// handle magic name for default value
|
||||
this->ValueName = ""_s;
|
||||
}
|
||||
this->SubKey = this->SubKey.substr(0, pos);
|
||||
} else {
|
||||
this->ValueName = ""_s;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
void Replace(const std::string& value)
|
||||
{
|
||||
this->Expression.replace(
|
||||
this->RegistryFormat.start(),
|
||||
this->RegistryFormat.end() - this->RegistryFormat.start(), value);
|
||||
}
|
||||
|
||||
cm::string_view GetRootKey() const { return this->RootKey; }
|
||||
|
||||
cm::string_view GetSubKey() const { return this->SubKey; }
|
||||
cm::string_view GetValueName() const { return this->ValueName; }
|
||||
|
||||
const std::string& GetExpression() const { return this->Expression; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::string Expression;
|
||||
cm::string_view Separator;
|
||||
cmsys::RegularExpression RegistryFormat;
|
||||
cm::string_view RootKey;
|
||||
cm::string_view SubKey;
|
||||
cm::string_view ValueName;
|
||||
};
|
||||
}
|
||||
|
||||
// class cmWindowsRegistry
|
||||
cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
|
||||
#if !defined(_WIN32) || defined(__CYGWIN__)
|
||||
const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::SimpleTypes =
|
||||
cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ,
|
||||
cmWindowsRegistry::ValueType::Reg_EXPAND_SZ,
|
||||
cmWindowsRegistry::ValueType::Reg_DWORD,
|
||||
cmWindowsRegistry::ValueType::Reg_QWORD };
|
||||
const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::AllTypes =
|
||||
cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ;
|
||||
|
||||
cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile,
|
||||
const ValueTypeSet& supportedTypes)
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
: SupportedTypes(supportedTypes)
|
||||
#else
|
||||
: LastError("No Registry on this platform.")
|
||||
#endif
|
||||
{
|
||||
@@ -267,9 +498,56 @@ cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
|
||||
}
|
||||
#else
|
||||
(void)makefile;
|
||||
(void)supportedTypes;
|
||||
#endif
|
||||
}
|
||||
|
||||
cm::optional<cmWindowsRegistry::View> cmWindowsRegistry::ToView(
|
||||
cm::string_view name)
|
||||
{
|
||||
static std::unordered_map<cm::string_view, cmWindowsRegistry::View>
|
||||
ViewDefinitions{
|
||||
{ "BOTH"_s, View::Both }, { "HOST"_s, View::Host },
|
||||
{ "TARGET"_s, View::Target }, { "32"_s, View::Reg32 },
|
||||
{ "64"_s, View::Reg64 }, { "32_64"_s, View::Reg32_64 },
|
||||
{ "64_32"_s, View::Reg64_32 }
|
||||
};
|
||||
|
||||
auto it = ViewDefinitions.find(name);
|
||||
|
||||
return it == ViewDefinitions.end()
|
||||
? cm::nullopt
|
||||
: cm::optional<cmWindowsRegistry::View>{ it->second };
|
||||
}
|
||||
|
||||
// define hash structure required by std::unordered_map
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<cmWindowsRegistry::View>
|
||||
{
|
||||
size_t operator()(cmWindowsRegistry::View const& v) const noexcept
|
||||
{
|
||||
return static_cast<
|
||||
typename underlying_type<cmWindowsRegistry::View>::type>(v);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cm::string_view cmWindowsRegistry::FromView(View view)
|
||||
{
|
||||
static std::unordered_map<cmWindowsRegistry::View, cm::string_view>
|
||||
ViewDefinitions{
|
||||
{ View::Both, "BOTH"_s }, { View::Host, "HOST"_s },
|
||||
{ View::Target, "TARGET"_s }, { View::Reg32, "32"_s },
|
||||
{ View::Reg64, "64"_s }, { View::Reg32_64, "32_64"_s },
|
||||
{ View::Reg64_32, "64_32"_s }
|
||||
};
|
||||
|
||||
auto it = ViewDefinitions.find(view);
|
||||
|
||||
return it == ViewDefinitions.end() ? ""_s : it->second;
|
||||
}
|
||||
|
||||
cm::string_view cmWindowsRegistry::GetLastError() const
|
||||
{
|
||||
return this->LastError;
|
||||
@@ -335,7 +613,7 @@ cm::optional<std::string> cmWindowsRegistry::ReadValue(
|
||||
// compute list of registry views
|
||||
auto views = this->ComputeViews(view);
|
||||
|
||||
if (cmsys::SystemTools::Strucmp(name.data(), "(default)") == 0) {
|
||||
if (Strucmp(name, "(default)") == 0) {
|
||||
// handle magic name for default value
|
||||
name = ""_s;
|
||||
}
|
||||
@@ -347,7 +625,7 @@ cm::optional<std::string> cmWindowsRegistry::ReadValue(
|
||||
try {
|
||||
this->LastError.clear();
|
||||
auto handler = KeyHandler::OpenKey(key, v);
|
||||
return handler.ReadValue(name, separator);
|
||||
return handler.ReadValue(name, this->SupportedTypes, separator);
|
||||
} catch (const registry_error& e) {
|
||||
this->LastError = e.what();
|
||||
continue;
|
||||
@@ -440,3 +718,53 @@ cm::optional<std::vector<std::string>> cmWindowsRegistry::GetSubKeys(
|
||||
#endif
|
||||
return cm::nullopt;
|
||||
}
|
||||
|
||||
cm::optional<std::vector<std::string>> cmWindowsRegistry::ExpandExpression(
|
||||
cm::string_view expression, View view, cm::string_view separator)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
static std::string NOTFOUND{ "/REGISTRY-NOTFOUND" };
|
||||
|
||||
this->LastError.clear();
|
||||
|
||||
// compute list of registry views
|
||||
auto views = this->ComputeViews(view);
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (auto v : views) {
|
||||
ExpressionParser parser(expression);
|
||||
|
||||
while (parser.Find()) {
|
||||
try {
|
||||
auto handler =
|
||||
KeyHandler::OpenKey(parser.GetRootKey(), parser.GetSubKey(), v);
|
||||
auto data = handler.ReadValue(parser.GetValueName(),
|
||||
this->SupportedTypes, separator);
|
||||
parser.Replace(data);
|
||||
} catch (const registry_error& e) {
|
||||
this->LastError = e.what();
|
||||
parser.Replace(NOTFOUND);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result.emplace_back(parser.GetExpression());
|
||||
if (expression == parser.GetExpression()) {
|
||||
// there no substitutions, so can ignore other views
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
(void)view;
|
||||
(void)separator;
|
||||
|
||||
ExpressionParser parser(expression);
|
||||
if (parser.Find()) {
|
||||
// expression holds unsupported registry access
|
||||
// so the expression cannot be used on this platform
|
||||
return cm::nullopt;
|
||||
}
|
||||
return std::vector<std::string>{ std::string{ expression } };
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <cm/optional>
|
||||
#include <cm/string_view>
|
||||
#include <cmext/enum_set>
|
||||
#include <cmext/string_view>
|
||||
|
||||
class cmMakefile;
|
||||
@@ -14,8 +15,6 @@ class cmMakefile;
|
||||
class cmWindowsRegistry
|
||||
{
|
||||
public:
|
||||
cmWindowsRegistry(cmMakefile&);
|
||||
|
||||
enum class View
|
||||
{
|
||||
Both,
|
||||
@@ -27,6 +26,30 @@ public:
|
||||
Reg64
|
||||
};
|
||||
|
||||
// Registry supported types
|
||||
enum class ValueType : std::uint8_t
|
||||
{
|
||||
Reg_SZ,
|
||||
Reg_EXPAND_SZ,
|
||||
Reg_MULTI_SZ,
|
||||
Reg_DWORD,
|
||||
Reg_QWORD
|
||||
};
|
||||
using ValueTypeSet = cm::enum_set<ValueType>;
|
||||
|
||||
// All types as defined by enum ValueType
|
||||
static const ValueTypeSet AllTypes;
|
||||
// same as AllTYpes but without type REG_MULTI_SZ
|
||||
static const ValueTypeSet SimpleTypes;
|
||||
|
||||
cmWindowsRegistry(cmMakefile&,
|
||||
const ValueTypeSet& supportedTypes = AllTypes);
|
||||
|
||||
// Helper routine to convert string to enum value
|
||||
static cm::optional<View> ToView(cm::string_view name);
|
||||
// Helper routine to convert enum to string
|
||||
static cm::string_view FromView(View view);
|
||||
|
||||
cm::optional<std::string> ReadValue(cm::string_view key,
|
||||
View view = View::Both,
|
||||
cm::string_view separator = "\0"_s)
|
||||
@@ -43,6 +66,12 @@ public:
|
||||
cm::optional<std::vector<std::string>> GetSubKeys(cm::string_view key,
|
||||
View view = View::Both);
|
||||
|
||||
// Expand an expression which may contains multiple references
|
||||
// to registry keys.
|
||||
// Depending of the view specified, one or two expansions can be done.
|
||||
cm::optional<std::vector<std::string>> ExpandExpression(
|
||||
cm::string_view expression, View view, cm::string_view separator = "\0"_s);
|
||||
|
||||
cm::string_view GetLastError() const;
|
||||
|
||||
private:
|
||||
@@ -50,6 +79,7 @@ private:
|
||||
std::vector<View> ComputeViews(View view);
|
||||
|
||||
int TargetSize = 0;
|
||||
ValueTypeSet SupportedTypes = AllTypes;
|
||||
#endif
|
||||
std::string LastError;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user