#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_SUPPORT_SOURCE 1 #include "Nuclex/Support/Settings/RegistrySettingsStore.h" #if defined(NUCLEX_SUPPORT_WINDOWS) #include "Nuclex/Support/ScopeGuard.h" #include "Nuclex/Support/Text/StringMatcher.h" #include "Nuclex/Support/Text/StringConverter.h" #include "Nuclex/Support/Text/LexicalCast.h" #include "../Platform/WindowsRegistryApi.h" #include // for assert() #include // for std::logic_error #include // for std::unique_ptr // TODO: This file has become too long. // Split the registry read/write methods into an API wrapper namespace { // ------------------------------------------------------------------------------------------- // /// Looks for the next forward or backward slash in a string /// Path in which the next forward or backward slash is searched /// Index at which the search will start /// /// The index of the next forward or backward slash. If no slashes were found, /// std::string::npos is returned. /// std::string::size_type findNextSlash( const std::string &path, std::string::size_type startIndex = 0 ) { std::string::size_type length = path.length(); for(std::string::size_type index = startIndex; index < length; ++index) { char current = path[index]; if((current == '\\') || (current == '/')) { return index; } } return std::string::npos; } // ------------------------------------------------------------------------------------------- // /// Changes all slashes in a UTF-8 string to backward slashes /// String in which the slashes will be changed void makeAllSlashesBackward(std::string &stringToChange) { std::string::size_type length = stringToChange.length(); for(std::string::size_type index = 0; index < length; ++index) { if(stringToChange[index] == '/') { stringToChange[index] = '\\'; } } } // ------------------------------------------------------------------------------------------- // /// Interprets a registry value as the type requested by the caller /// Type as which the value will be interpreted /// Buffer that contains the value /// Size of the value in bytes /// Type of the value /// The value interpreted as the requested type template std::optional interpretValue( const BYTE *valueBytes, std::size_t valueSize, ::DWORD valueType ) { // Due to the specializations on the other types, we know TValue is either int32 or int64 switch(valueType) { case REG_DWORD: { if constexpr(std::is_signed::value) { return static_cast(*reinterpret_cast(valueBytes)); } else { return static_cast(*reinterpret_cast(valueBytes)); } } case REG_QWORD: { if constexpr(std::is_signed::value) { return static_cast(*reinterpret_cast(valueBytes)); } else { return static_cast(*reinterpret_cast(valueBytes)); } } case REG_SZ: { return Nuclex::Support::Text::lexical_cast( std::string(reinterpret_cast(valueBytes), valueSize) ); } default: { throw std::runtime_error(u8"Read registry value had a type we don't support"); } } } // ------------------------------------------------------------------------------------------- // /// Interprets a registry value as a boolean /// Buffer that contains the value /// Size of the value in bytes /// Type of the value /// The value interpreted as a boolean template<> std::optional interpretValue( const BYTE *valueBytes, std::size_t valueSize, ::DWORD valueType ) { switch(valueType) { case REG_DWORD: { return (*reinterpret_cast(valueBytes) != 0); } case REG_QWORD: { return (*reinterpret_cast(valueBytes) != 0); } case REG_SZ: { return Nuclex::Support::Text::lexical_cast( std::string(reinterpret_cast(valueBytes), valueSize) ); } default: { throw std::runtime_error(u8"Read registry value had a type we don't support"); } } } // ------------------------------------------------------------------------------------------- // /// Interprets a registry value as a string /// Buffer that contains the value /// Size of the value in bytes /// Type of the value /// The value interpreted as a string template<> std::optional interpretValue( const BYTE *valueBytes, std::size_t valueSize, ::DWORD valueType ) { switch(valueType) { case REG_DWORD: { return Nuclex::Support::Text::lexical_cast( *reinterpret_cast(valueBytes) ); } case REG_QWORD: { return Nuclex::Support::Text::lexical_cast( *reinterpret_cast(valueBytes) ); } case REG_SZ: { std::wstring valueUtf16( reinterpret_cast(valueBytes), (valueSize - 1) / sizeof(std::wstring::value_type) ); return Nuclex::Support::Text::StringConverter::Utf8FromWide(valueUtf16); } default: { throw std::runtime_error(u8"Read registry value had a type we don't support"); } } } // ------------------------------------------------------------------------------------------- // /// Retrieves a value stored under a registry key /// Type of value that will be read /// Handle of the registry key in which the value is stored /// Name of the value in the registry /// Expected size of the value, useful for strings /// The registry value, if it was present template std::optional queryValue( ::HKEY keyHandle, const std::string &valueName, std::size_t sizeHint = 16 ) { std::wstring utf16 = Nuclex::Support::Text::StringConverter::WideFromUtf8(valueName); ::DWORD valueType; ::DWORD valueSize = static_cast<::DWORD>(sizeHint); // First, try to use a stack-allocated memory buffer if(sizeHint < 17) { ::BYTE stackValue[16]; ::LSTATUS result = ::RegQueryValueExW( keyHandle, utf16.c_str(), // UTF-16 value name nullptr, &valueType, stackValue, &valueSize ); if(result == ERROR_FILE_NOT_FOUND) { return std::optional(); } else if(result == ERROR_SUCCESS) { return interpretValue(stackValue, valueSize, valueType); } else if(result != ERROR_MORE_DATA) { Nuclex::Support::Platform::WindowsApi::ThrowExceptionForSystemError( u8"Could not query value stored in registry key", result ); } } // Either the caller hinted at a larger size or the value turned out to be larger // Try a heap-allocated buffer with the hinted or known size. for(;;) { std::unique_ptr heapValue(new std::uint8_t[valueSize]); ::LSTATUS result = ::RegQueryValueExW( keyHandle, utf16.c_str(), // UTF-16 value name nullptr, &valueType, heapValue.get(), &valueSize ); if(result == ERROR_FILE_NOT_FOUND) { return std::optional(); } else if(result == ERROR_SUCCESS) { return interpretValue(heapValue.get(), valueSize, valueType); } // If this was our first attempt, retry with the now known size. // Otherwise, we treat even ERROR_MORE_DATA as an error if(sizeHint >= 17) { if(result == ERROR_MORE_DATA) { sizeHint = 0; continue; } } // This point is reached if the ::RegQueryValueExW() method call called two times // already, the second time with a bfufer using its self-provided value size. Nuclex::Support::Platform::WindowsApi::ThrowExceptionForSystemError( u8"Could not query value stored in registry key", result ); } } // ------------------------------------------------------------------------------------------- // /// Retrieves a value stored under a registry key /// Type of value that will be read /// Handle of the settings root key /// Name of the category in which the value is stored /// Name of the value in the registry /// The registry value, if it was present template std::optional retrieveValue( ::HKEY settingsKeyHandle, const std::string &categoryName, const std::string &propertyName ) { if(categoryName.empty()) { return queryValue(settingsKeyHandle, propertyName); } else { ::HKEY subKeyHandle = Nuclex::Support::Platform::WindowsRegistryApi::OpenExistingSubKey( settingsKeyHandle, categoryName ); if(subKeyHandle == ::HKEY(nullptr)) { return std::optional(); } else { ON_SCOPE_EXIT{ ::LRESULT result = ::RegCloseKey(subKeyHandle); NUCLEX_SUPPORT_NDEBUG_UNUSED(result); assert((result == ERROR_SUCCESS) && u8"Registry subkey is closed successfully"); }; return queryValue(subKeyHandle, propertyName); } } } // ------------------------------------------------------------------------------------------- // /// Sets a registry value under the specified parent key /// Type of value that will be stored in the registry /// Key under which the value will be stored /// UTF-16 encoded name of the value /// Value that will be stored /// The result returned from the method template ::LSTATUS setValue( ::HKEY parentKeyHandle, const std::wstring &propertyNameUtf16, const TValue &propertyValue ) { ::DWORD valueSize; ::DWORD valueType; if constexpr(sizeof(TValue) == sizeof(std::uint64_t)) { valueSize = 8; valueType = REG_QWORD; } else { valueSize = 4; valueType = REG_DWORD; } return ::RegSetValueExW( parentKeyHandle, propertyNameUtf16.c_str(), 0, // reserved valueType, reinterpret_cast(&propertyValue), valueSize ); } // ------------------------------------------------------------------------------------------- // /// Sets a registry value under the specified parent key to a boolean /// Type of value that will be stored in the registry /// Key under which the value will be stored /// UTF-16 encoded name of the value /// Boolean that will be stored /// The result returned from the method template<> ::LSTATUS setValue( ::HKEY parentKeyHandle, const std::wstring &propertyNameUtf16, const bool &propertyValue ) { ::DWORD value = (propertyValue ? 1 : 0); return ::RegSetValueExW( parentKeyHandle, propertyNameUtf16.c_str(), 0, // reserved REG_DWORD, reinterpret_cast(&value), 4 ); } // ------------------------------------------------------------------------------------------- // /// Sets a registry value under the specified parent key to a string /// Type of value that will be stored in the registry /// Key under which the value will be stored /// UTF-16 encoded name of the value /// String that will be stored /// The result returned from the method template<> ::LSTATUS setValue( ::HKEY parentKeyHandle, const std::wstring &propertyNameUtf16, const std::string &propertyValue ) { std::wstring propertyValueUtf16 = ( Nuclex::Support::Text::StringConverter::WideFromUtf8(propertyValue) ); ::DWORD valueSize = static_cast<::DWORD>( (propertyValueUtf16.length() + 1) * sizeof(std::wstring::value_type) ); return ::RegSetValueExW( parentKeyHandle, propertyNameUtf16.c_str(), 0, // reserved REG_SZ, reinterpret_cast(propertyValueUtf16.c_str()), valueSize ); } // ------------------------------------------------------------------------------------------- // /// Stores a value stored in a registry key /// Type of value that will be stores /// Handle of the settings root key /// Name of the category in which the value will be stored /// Name of the value in the registry /// Value that will be stored in the registry /// The registry value, if it was present template void storeValue( ::HKEY settingsKeyHandle, const std::string &categoryName, const std::string &propertyName, const TValue &propertyValue ) { ::LSTATUS result; { std::wstring propertyNameUtf16 = Nuclex::Support::Text::StringConverter::WideFromUtf8( propertyName ); if(categoryName.empty()) { result = setValue(settingsKeyHandle, propertyNameUtf16, propertyValue); } else { ::HKEY subKeyHandle = Nuclex::Support::Platform::WindowsRegistryApi::OpenOrCreateSubKey( settingsKeyHandle, categoryName ); ON_SCOPE_EXIT{ ::LRESULT result = ::RegCloseKey(subKeyHandle); NUCLEX_SUPPORT_NDEBUG_UNUSED(result); assert((result == ERROR_SUCCESS) && u8"Registry subkey is closed successfully"); }; result = setValue(subKeyHandle, propertyNameUtf16, propertyValue); } } // propertyName scope if(unlikely(result != ERROR_SUCCESS)) { std::string message(u8"Could not store setting '", 25); message.append(propertyName); message.append(u8"' in registry", 13); Nuclex::Support::Platform::WindowsApi::ThrowExceptionForSystemError(message, result); } } // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Support { namespace Settings { // ------------------------------------------------------------------------------------------- // bool RegistrySettingsStore::DeleteKey(const std::string ®istryPath) { std::string::size_type firstSlashIndex = findNextSlash(registryPath); if(firstSlashIndex == std::string::npos) { std::string message(u8"Refusing to delete '", 20); message.append(registryPath); message.append(u8"' because it does not contain a path to a subkey", 48); throw std::invalid_argument(message); } else { // Slashes present, separate the registry hive from the rest ::HKEY hiveKeyHandle = Platform::WindowsRegistryApi::GetHiveFromString( registryPath, firstSlashIndex ); // We need a string containing the name of the subkey to open, also using only // backward slashes (unlike Windows' file system API, the registry API isn't lax) std::wstring subKeyNameUtf16; { std::string subkeyName = registryPath.substr(firstSlashIndex + 1); makeAllSlashesBackward(subkeyName); subKeyNameUtf16 = Text::StringConverter::WideFromUtf8(subkeyName); } ::LSTATUS result = ::RegDeleteTreeW(hiveKeyHandle, subKeyNameUtf16.c_str()); if(result == ERROR_FILE_NOT_FOUND) { return false; } else if(unlikely(result != ERROR_SUCCESS)) { std::string message(u8"Could not delete registry tree at '", 35); message.append(registryPath); message.append(u8"'", 1); Platform::WindowsApi::ThrowExceptionForSystemError(message, result); } } return true; } // ------------------------------------------------------------------------------------------- // RegistrySettingsStore::RegistrySettingsStore( const std::string ®istryPath, bool writable /* = true */ ) : settingsKeyHandle(0) { // If no slashes are in the path, it may still be a valid registry hive... std::string::size_type firstSlashIndex = findNextSlash(registryPath); if(firstSlashIndex == std::string::npos) { ::HKEY hiveKeyHandle = Platform::WindowsRegistryApi::GetHiveFromString( registryPath, registryPath.length() ); *reinterpret_cast<::HKEY *>(&this->settingsKeyHandle) = ( Platform::WindowsRegistryApi::OpenExistingSubKey( hiveKeyHandle, std::string(), writable ) ); } else { // Slashes present, separate the registry hive from the rest ::HKEY hiveKeyHandle = Platform::WindowsRegistryApi::GetHiveFromString( registryPath, firstSlashIndex ); // We need a string containing the name of the subkey to open, also using only // backward slashes (unlike Windows' file system API, the registry API isn't lax) std::string subkeyName = registryPath.substr(firstSlashIndex + 1); makeAllSlashesBackward(subkeyName); // If the key doesn't exist, we do one of two things: // // - in read-only mode, we act as if an empty key existed. This is consistent with // the behavior of the Retrieve() method and allows applications to start without // their registry keys present, using default settings. // // - in writable mode, we try to create a new key. This is the correct behavior here // because the caller expects us to hold onto any settings written to the registry // and we can't very well do that if we don't have a registry to write to. // if(writable) { *reinterpret_cast<::HKEY *>(&this->settingsKeyHandle) = ( Platform::WindowsRegistryApi::OpenOrCreateSubKey(hiveKeyHandle, subkeyName) ); } else { *reinterpret_cast<::HKEY *>(&this->settingsKeyHandle) = ( Platform::WindowsRegistryApi::OpenExistingSubKey(hiveKeyHandle, subkeyName, false) ); } // if open writable } // if slashes present / not present } // ------------------------------------------------------------------------------------------- // RegistrySettingsStore::~RegistrySettingsStore() { ::HKEY thisSettingsKeyHandle = *reinterpret_cast<::HKEY *>(&this->settingsKeyHandle); if(thisSettingsKeyHandle != nullptr) { ::LSTATUS result = ::RegCloseKey(thisSettingsKeyHandle); NUCLEX_SUPPORT_NDEBUG_UNUSED(result); assert((result == ERROR_SUCCESS) && u8"Accessed registry key was closed successfully"); } } // ------------------------------------------------------------------------------------------- // std::vector RegistrySettingsStore::GetAllCategories() const { if(this->settingsKeyHandle == 0) { return std::vector(); // Non-existent key accessed in read-only mode } return Platform::WindowsRegistryApi::GetAllSubKeyNames( *reinterpret_cast(&this->settingsKeyHandle) ); } // ------------------------------------------------------------------------------------------- // std::vector RegistrySettingsStore::GetAllProperties( const std::string &categoryName /* = std::string() */ ) const { if(this->settingsKeyHandle == 0) { return std::vector(); // Non-existent key accessed in read-only mode } if(categoryName.empty()) { return Platform::WindowsRegistryApi::GetAllValueNames( *reinterpret_cast(&this->settingsKeyHandle) ); } else { ::HKEY subKeyHandle = Nuclex::Support::Platform::WindowsRegistryApi::OpenExistingSubKey( *reinterpret_cast(&this->settingsKeyHandle), categoryName ); if(subKeyHandle == ::HKEY(nullptr)) { return std::vector(); // Non-existent key accessed in read-only mode } else { ON_SCOPE_EXIT{ ::LRESULT result = ::RegCloseKey(subKeyHandle); NUCLEX_SUPPORT_NDEBUG_UNUSED(result); assert((result == ERROR_SUCCESS) && u8"Registry subkey is closed successfully"); }; return Platform::WindowsRegistryApi::GetAllValueNames(subKeyHandle); } } } // ------------------------------------------------------------------------------------------- // bool RegistrySettingsStore::DeleteCategory(const std::string &categoryName) { if(this->settingsKeyHandle == 0) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } if(categoryName.empty()) { std::vector valueNames = Platform::WindowsRegistryApi::GetAllValueNames( *reinterpret_cast(&this->settingsKeyHandle) ); if(valueNames.empty()) { return false; } for(std::size_t index = 0; index < valueNames.size(); ++index) { std::wstring valueNameUtf16 = Text::StringConverter::WideFromUtf8(valueNames[index]); ::LSTATUS result = ::RegDeleteValueW( *reinterpret_cast(&this->settingsKeyHandle), valueNameUtf16.c_str() ); if(unlikely(result != ERROR_SUCCESS)) { std::string message(u8"Could not delete value '", 24); message.append(categoryName); message.append(u8"' from settings key in registry", 31); Platform::WindowsApi::ThrowExceptionForSystemError(message, result); } } } else { std::wstring subKeyNameUtf16 = Text::StringConverter::WideFromUtf8(categoryName); ::LSTATUS result = ::RegDeleteTreeW( *reinterpret_cast(&this->settingsKeyHandle), subKeyNameUtf16.c_str() ); if(result == ERROR_FILE_NOT_FOUND) { return false; } else if(unlikely(result != ERROR_SUCCESS)) { std::string message(u8"Could not delete subtree '", 26); message.append(categoryName); message.append(u8"' from settings key in registry", 31); Platform::WindowsApi::ThrowExceptionForSystemError(message, result); } } return true; } // ------------------------------------------------------------------------------------------- // bool RegistrySettingsStore::DeleteProperty( const std::string &categoryName, const std::string &propertyName ) { if(this->settingsKeyHandle == 0) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } ::LSTATUS result; { std::wstring propertyNameUtf16 = Text::StringConverter::WideFromUtf8(propertyName); if(categoryName.empty()) { result = ::RegDeleteValueW( *reinterpret_cast(&this->settingsKeyHandle), propertyNameUtf16.c_str() ); } else { ::HKEY subKeyHandle = Nuclex::Support::Platform::WindowsRegistryApi::OpenExistingSubKey( *reinterpret_cast(&this->settingsKeyHandle), categoryName ); if(subKeyHandle == ::HKEY(nullptr)) { return false; } else { ON_SCOPE_EXIT{ ::LRESULT result = ::RegCloseKey(subKeyHandle); NUCLEX_SUPPORT_NDEBUG_UNUSED(result); assert((result == ERROR_SUCCESS) && u8"Registry subkey is closed successfully"); }; result = ::RegDeleteValueW(subKeyHandle, propertyNameUtf16.c_str()); } } } // propertyName scope if(result == ERROR_FILE_NOT_FOUND) { return false; } else if(unlikely(result != ERROR_SUCCESS)) { std::string message(u8"Could not delete settings value '", 33); message.append(propertyName); message.append(u8"' from registry", 15); Platform::WindowsApi::ThrowExceptionForSystemError(message, result); } else { return true; } } // ------------------------------------------------------------------------------------------- // std::optional RegistrySettingsStore::RetrieveBooleanProperty( const std::string &categoryName, const std::string &propertyName ) const { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { return std::optional(); } return retrieveValue(thisSettingsKeyHandle, categoryName, propertyName); } // ------------------------------------------------------------------------------------------- // std::optional RegistrySettingsStore::RetrieveUInt32Property( const std::string &categoryName, const std::string &propertyName ) const { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { return std::optional(); } return retrieveValue(thisSettingsKeyHandle, categoryName, propertyName); } // ------------------------------------------------------------------------------------------- // std::optional RegistrySettingsStore::RetrieveInt32Property( const std::string &categoryName, const std::string &propertyName ) const { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { return std::optional(); } return retrieveValue(thisSettingsKeyHandle, categoryName, propertyName); } // ------------------------------------------------------------------------------------------- // std::optional RegistrySettingsStore::RetrieveUInt64Property( const std::string &categoryName, const std::string &propertyName ) const { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { return std::optional(); } return retrieveValue(thisSettingsKeyHandle, categoryName, propertyName); } // ------------------------------------------------------------------------------------------- // std::optional RegistrySettingsStore::RetrieveInt64Property( const std::string &categoryName, const std::string &propertyName ) const { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { return std::optional(); } return retrieveValue(thisSettingsKeyHandle, categoryName, propertyName); } // ------------------------------------------------------------------------------------------- // std::optional RegistrySettingsStore::RetrieveStringProperty( const std::string &categoryName, const std::string &propertyName ) const { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { return std::optional(); } return retrieveValue(thisSettingsKeyHandle, categoryName, propertyName); } // ------------------------------------------------------------------------------------------- // void RegistrySettingsStore::StoreBooleanProperty( const std::string &categoryName, const std::string &propertyName, bool value ) { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } storeValue(thisSettingsKeyHandle, categoryName, propertyName, value); } // ------------------------------------------------------------------------------------------- // void RegistrySettingsStore::StoreUInt32Property( const std::string &categoryName, const std::string &propertyName, std::uint32_t value ) { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } storeValue(thisSettingsKeyHandle, categoryName, propertyName, value); } // ------------------------------------------------------------------------------------------- // void RegistrySettingsStore::StoreInt32Property( const std::string &categoryName, const std::string &propertyName, std::int32_t value ) { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } storeValue(thisSettingsKeyHandle, categoryName, propertyName, value); } // ------------------------------------------------------------------------------------------- // void RegistrySettingsStore::StoreUInt64Property( const std::string &categoryName, const std::string &propertyName, std::uint64_t value ) { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } storeValue(thisSettingsKeyHandle, categoryName, propertyName, value); } // ------------------------------------------------------------------------------------------- // void RegistrySettingsStore::StoreInt64Property( const std::string &categoryName, const std::string &propertyName, std::int64_t value ) { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } storeValue(thisSettingsKeyHandle, categoryName, propertyName, value); } // ------------------------------------------------------------------------------------------- // void RegistrySettingsStore::StoreStringProperty( const std::string &categoryName, const std::string &propertyName, const std::string &value ) { ::HKEY thisSettingsKeyHandle = *reinterpret_cast(&this->settingsKeyHandle); if(thisSettingsKeyHandle == nullptr) { throw std::logic_error(u8"Registry settings store was not opened as writable"); } storeValue(thisSettingsKeyHandle, categoryName, propertyName, value); } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Support::Settings #endif // defined(NUCLEX_SUPPORT_WINDOWS)