You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
770 lines
22 KiB
770 lines
22 KiB
2 years ago
|
/* 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 <type_traits>
|
||
|
#include <unordered_map>
|
||
|
#include <utility>
|
||
|
|
||
|
#include <cmext/string_view>
|
||
|
|
||
|
#include "cmsys/RegularExpression.hxx"
|
||
|
|
||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||
|
# include <algorithm>
|
||
|
# include <cstring>
|
||
|
# include <exception>
|
||
|
# include <iterator>
|
||
|
# include <vector>
|
||
|
|
||
|
# include <cm/memory>
|
||
|
|
||
|
# include <windows.h>
|
||
|
|
||
|
# include "cmMakefile.h"
|
||
|
# include "cmStringAlgorithms.h"
|
||
|
# include "cmValue.h"
|
||
|
#endif
|
||
|
|
||
|
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)
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
{
|
||
|
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;
|
||
|
using ValueTypeSet = cmWindowsRegistry::ValueTypeSet;
|
||
|
|
||
|
KeyHandler(HKEY hkey)
|
||
|
: Handler(hkey)
|
||
|
{
|
||
|
}
|
||
|
~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,
|
||
|
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 rootKey, cm::string_view subKey,
|
||
|
View view)
|
||
|
{
|
||
|
if (view == View::Reg64 && !Is64BitWindows()) {
|
||
|
throw registry_error("No 64bit registry on Windows32.");
|
||
|
}
|
||
|
|
||
|
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."));
|
||
|
}
|
||
|
// Update path format
|
||
|
auto key = ToWide(subKey);
|
||
|
std::replace(key.begin(), key.end(), L'/', L'\\');
|
||
|
|
||
|
REGSAM options = KEY_READ;
|
||
|
if (Is64BitWindows()) {
|
||
|
options |= view == View::Reg64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
|
||
|
}
|
||
|
|
||
|
HKEY hKey;
|
||
|
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{ "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) {
|
||
|
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;
|
||
|
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 = 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 this->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 this->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 = 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 this->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 = this->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(this->ToNarrow(data.get()));
|
||
|
++index;
|
||
|
}
|
||
|
if (status != ERROR_NO_MORE_ITEMS) {
|
||
|
throw registry_error(this->FormatSystemError(status));
|
||
|
}
|
||
|
|
||
|
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
|
||
|
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
|
||
|
{
|
||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||
|
if (cmValue targetSize = makefile.GetDefinition("CMAKE_SIZEOF_VOID_P")) {
|
||
|
this->TargetSize = targetSize == "8" ? 64 : 32;
|
||
|
}
|
||
|
#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;
|
||
|
}
|
||
|
|
||
|
#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 (Strucmp(name, "(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, this->SupportedTypes, 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;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|