From ab2223aadb106c5c3c01db0139658e88289003b6 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 16 Feb 2024 17:39:48 +0100 Subject: [PATCH] small fixes --- Example/main.cpp | 4 +- Example/pch.h | 2 +- RegistryHelper.h | 406 ++++++++++++++++++ RegistryHelper.sln | 10 +- RegistryInterface/RegistryInterface.h | 2 - RegistryInterface/RegistryInterface.vcxproj | 1 - .../RegistryInterface.vcxproj.filters | 3 - RegistryInterface/framework.h | 9 +- 8 files changed, 420 insertions(+), 17 deletions(-) create mode 100644 RegistryHelper.h diff --git a/Example/main.cpp b/Example/main.cpp index 64a816d..f8320a6 100644 --- a/Example/main.cpp +++ b/Example/main.cpp @@ -1,5 +1,7 @@ #include "pch.h" +#include "../RegistryHelper.h" + int main() { @@ -62,7 +64,7 @@ main() } } catch (const RegistryError& ex) { std::cerr << "Registry Error: " << ex.what() - << " (Error Code: " << ex.ErrorCode() << ")" << std::endl; + << " (Error Code: " << ex.GetErrorCode() << ")" << std::endl; } catch (const std::exception& ex) { std::cerr << "Error: " << ex.what() << std::endl; } diff --git a/Example/pch.h b/Example/pch.h index 87e16f2..172cd69 100644 --- a/Example/pch.h +++ b/Example/pch.h @@ -2,7 +2,7 @@ #define PCH_H // add headers that you want to pre-compile here -#include "../RegistryInterface/RegistryHelper.h" + #include #endif // PCH_H diff --git a/RegistryHelper.h b/RegistryHelper.h new file mode 100644 index 0000000..e83c4fa --- /dev/null +++ b/RegistryHelper.h @@ -0,0 +1,406 @@ +#pragma once + +#include +#include +#include +#include +#include + +class RegistryError : public std::runtime_error +{ +public: + RegistryError(const std::string& message, DWORD errorCode) + : std::runtime_error(message + " Error Code: " + std::to_string(errorCode)) + , m_errorCode(errorCode) + { + } + + DWORD GetErrorCode() const { return m_errorCode; } + +private: + DWORD m_errorCode; +}; + +class RegistryHelper +{ +public: + RegistryHelper() + : m_errorCode(ERROR_SUCCESS) + { + } + + DWORD RegGetDword(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value); + + std::wstring RegGetString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value); + + std::vector RegGetMultiString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value); + + std::vector> RegEnumSubKeys( + HKEY hKey, + const std::wstring& subKey); + + std::vector> RegEnumValues( + HKEY hKey, + const std::wstring& subKey); + + void RegSetDword(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value, + DWORD data); + + void RegSetString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value, + const std::wstring& data); + + void RegSetMultiString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value, + const std::vector& data); + +private: + DWORD m_errorCode; +}; + +// Read a DWORD value from the registry +inline DWORD +RegistryHelper::RegGetDword(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) +{ + DWORD data{}; + DWORD dataSize = sizeof(data); + + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_DWORD, + nullptr, + &data, + &dataSize); + + if (m_errorCode != ERROR_SUCCESS) { + // Throw a RegistryError exception if an error occurs + throw RegistryError("Cannot read DWORD from registry.", m_errorCode); + } + + return data; +} + +// Read a string value from the registry +inline std::wstring +RegistryHelper::RegGetString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) +{ + // Retrieve the size of the string value data + DWORD dataSize{}; + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_SZ, + nullptr, + nullptr, + &dataSize); + + // Check if the value information retrieval was successful + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot read string from registry", m_errorCode); + } + + // Use smart pointers for automatic memory management + std::vector data(dataSize / sizeof(wchar_t)); + + // Retrieve the actual string value data + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_SZ, + nullptr, + data.data(), + &dataSize); + + // Check if the value data retrieval was successful + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot read string from registry", m_errorCode); + } + + // Resize the vector based on the actual size of the string + data.resize(dataSize / sizeof(wchar_t) - 1); + + // Convert the wchar_t vector to a wstring and return + return std::wstring(data.begin(), data.end()); +} + +// Read a multi-string value from the registry +inline std::vector +RegistryHelper::RegGetMultiString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) +{ + // Retrieve the size of the multi-string value data + DWORD dataSize{}; + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_MULTI_SZ, + nullptr, + nullptr, + &dataSize); + + // Check if the value information retrieval was successful + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot read multi-string from registry", m_errorCode); + } + + // Use smart pointers for automatic memory management + std::vector data(dataSize / sizeof(wchar_t)); + + // Retrieve the actual multi-string value data + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_MULTI_SZ, + nullptr, + data.data(), + &dataSize); + + // Check if the value data retrieval was successful + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot read multi-string from registry", m_errorCode); + } + + // Resize the vector based on the actual size of the multi-string + data.resize(dataSize / sizeof(wchar_t)); + + // Parse the double-NUL-terminated string into a vector of wstrings + std::vector result; + const wchar_t* currStringPtr = data.data(); + while (*currStringPtr != L'\0') { + const size_t currStringLength = wcslen(currStringPtr); + result.push_back(std::wstring{ currStringPtr, currStringLength }); + currStringPtr += currStringLength + 1; + } + + // Return the vector containing individual strings from the multi-string + return result; +} + +inline std::vector> +RegistryHelper::RegEnumSubKeys(HKEY hKey, const std::wstring& subKey) +{ + HKEY keyHandle; + + // Open the specified key (root key or subkey) + DWORD openResult = + RegOpenKeyEx(hKey, subKey.c_str(), 0, KEY_READ, &keyHandle); + + if (openResult != ERROR_SUCCESS) { + return {}; // Return an empty vector + } + + // Retrieve information about the specified registry key + DWORD subKeyCount{}; // Number of subkeys under the key + DWORD maxSubKeyNameLen{}; // Maximum length of a subkey name + DWORD retCode = ::RegQueryInfoKey(keyHandle, + nullptr, // No user-defined class + nullptr, // No user-defined class size + nullptr, // Reserved + &subKeyCount, + &maxSubKeyNameLen, + nullptr, // No subkey class length + nullptr, // No value count + nullptr, // No value max length + nullptr, // No security descriptor + nullptr, // No last write time + nullptr); + + // Check if the key information retrieval was successful + if (retCode != ERROR_SUCCESS) { + // Handle error, close the key handle and return + RegCloseKey(keyHandle); + throw RegistryError{ + "RegQueryInfoKey failed while preparing for value enumeration.", retCode + }; + } + + // Allocate a buffer for storing subkey names + maxSubKeyNameLen++; + + auto nameBuffer = std::make_unique(maxSubKeyNameLen); + + // Vector to store pairs of subkey names and types + std::vector> subKeys; + + // Enumerate subkeys under the registry key + for (DWORD index = 0; index < subKeyCount; index++) { + DWORD subKeyNameLen = maxSubKeyNameLen; + + // Retrieve information about the specified subkey + retCode = ::RegEnumKeyEx(keyHandle, + index, + nameBuffer.get(), + &subKeyNameLen, + nullptr, // Reserved + nullptr, // No class information + nullptr, // No class size + nullptr // No last write time + ); + + // Check if the subkey information retrieval was successful + if (retCode != ERROR_SUCCESS) { + // Close the key handle and handle the error + RegCloseKey(keyHandle); + throw RegistryError{ "Cannot get subkey info from the registry", + retCode }; + } + + // Add the subkey name and type to the vector + subKeys.push_back( + std::make_pair(std::wstring{ nameBuffer.get(), subKeyNameLen }, 0)); + } + + // Close the key handle + RegCloseKey(keyHandle); + + // Return the vector containing subkey names and types + return subKeys; +} + +inline std::vector> +RegistryHelper::RegEnumValues(HKEY hKey, const std::wstring& subKey) +{ + HKEY keyHandle; + + // Open the specified key (root key or subkey) + DWORD openResult = + RegOpenKeyEx(hKey, subKey.c_str(), 0, KEY_READ, &keyHandle); + + if (openResult != ERROR_SUCCESS) { + // Handle error, you might want to throw an exception or return an empty + // vector + return {}; + } + + DWORD valueCount{}; + DWORD maxValueNameLen{}; + DWORD retCode = ::RegQueryInfoKey(keyHandle, + nullptr, // no user-defined class + nullptr, // no user-defined class size + nullptr, // reserved + nullptr, // no subkey count + nullptr, // no subkey max length + nullptr, // no subkey class length + &valueCount, + &maxValueNameLen, + nullptr, // no max value length + nullptr, // no security descriptor + nullptr // no last write time + ); + if (retCode != ERROR_SUCCESS) { + // Handle error, close the key handle and return + RegCloseKey(keyHandle); + throw RegistryError{ + "RegQueryInfoKey failed while preparing for value enumeration.", retCode + }; + } + + maxValueNameLen++; + auto nameBuffer = std::make_unique(maxValueNameLen); + std::vector> valueInfo; + + // Enumerate the values + for (DWORD index = 0; index < valueCount; index++) { + // Get the name and type + DWORD valueNameLen = maxValueNameLen; + DWORD valueType{}; + retCode = ::RegEnumValue(keyHandle, + index, + nameBuffer.get(), + &valueNameLen, + nullptr, // reserved + &valueType, + nullptr, // no data + nullptr // no data size + ); + if (retCode != ERROR_SUCCESS) { + // Handle error, close the key handle and throw an exception + RegCloseKey(keyHandle); + throw RegistryError{ "Cannot enumerate values: RegEnumValue failed.", + retCode }; + } + + valueInfo.push_back(std::make_pair( + std::wstring{ nameBuffer.get(), valueNameLen }, valueType)); + } + + // Close the key handle + RegCloseKey(keyHandle); + + return valueInfo; +} + +inline void +RegistryHelper::RegSetDword(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value, + DWORD data) +{ + m_errorCode = ::RegSetKeyValue( + hKey, subKey.c_str(), value.c_str(), REG_DWORD, &data, sizeof(data)); + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot set DWORD value in registry.", m_errorCode); + } +} + +inline void +RegistryHelper::RegSetString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value, + const std::wstring& data) +{ + m_errorCode = + ::RegSetKeyValue(hKey, + subKey.c_str(), + value.c_str(), + REG_SZ, + data.c_str(), + static_cast((data.length() + 1) * sizeof(wchar_t))); + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot set string value in registry.", m_errorCode); + } +} + +inline void +RegistryHelper::RegSetMultiString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value, + const std::vector& data) +{ + // Concatenate the strings and add an extra null character at the end + std::wstring multiString; + for (const auto& str : data) { + multiString += str; + multiString.push_back(L'\0'); + } + multiString.push_back(L'\0'); // Extra null character at the end + + m_errorCode = ::RegSetKeyValue( + + hKey, + subKey.c_str(), + value.c_str(), + REG_MULTI_SZ, + multiString.c_str(), + static_cast(multiString.length() * sizeof(wchar_t))); + if (m_errorCode != ERROR_SUCCESS) { + throw RegistryError("Cannot set multi-string value in registry.", + m_errorCode); + } +} diff --git a/RegistryHelper.sln b/RegistryHelper.sln index e4e2986..fc199ce 100644 --- a/RegistryHelper.sln +++ b/RegistryHelper.sln @@ -7,8 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .clang-format = .clang-format .gitignore = .gitignore - LICENSE.md = LICENSE.md - README.md = README.md + RegistryHelper.h = RegistryHelper.h EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example", "Example\Example.vcxproj", "{FD807556-B9F5-4D19-814A-FE0F51F514E1}" @@ -21,6 +20,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{ .github\workflows\msbuild.yml = .github\workflows\msbuild.yml EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repository", "Repository", "{1FF7BFD7-87F0-4612-8468-FB56B6A0DAF0}" + ProjectSection(SolutionItems) = preProject + LICENSE.md = LICENSE.md + README.md = README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -51,6 +56,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {B8AB6EE7-E785-4236-AB6D-F0F24EF1DE76} = {3587BC45-E1D1-4FCE-9F36-D4BD660FB262} + {1FF7BFD7-87F0-4612-8468-FB56B6A0DAF0} = {3587BC45-E1D1-4FCE-9F36-D4BD660FB262} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B012E4D-3A77-4E50-83D9-5040920D82DE} diff --git a/RegistryInterface/RegistryInterface.h b/RegistryInterface/RegistryInterface.h index 4a60754..086d446 100644 --- a/RegistryInterface/RegistryInterface.h +++ b/RegistryInterface/RegistryInterface.h @@ -1,7 +1,5 @@ #pragma once -#include "pch.h" - struct RecoverySettings { std::wstring recoveryOption; diff --git a/RegistryInterface/RegistryInterface.vcxproj b/RegistryInterface/RegistryInterface.vcxproj index 0a5c49e..3060c33 100644 --- a/RegistryInterface/RegistryInterface.vcxproj +++ b/RegistryInterface/RegistryInterface.vcxproj @@ -150,7 +150,6 @@ Create Create - diff --git a/RegistryInterface/RegistryInterface.vcxproj.filters b/RegistryInterface/RegistryInterface.vcxproj.filters index 533d8fb..9277a76 100644 --- a/RegistryInterface/RegistryInterface.vcxproj.filters +++ b/RegistryInterface/RegistryInterface.vcxproj.filters @@ -32,8 +32,5 @@ Source Files - - Header Files - \ No newline at end of file diff --git a/RegistryInterface/framework.h b/RegistryInterface/framework.h index 2c88a72..3249e2c 100644 --- a/RegistryInterface/framework.h +++ b/RegistryInterface/framework.h @@ -2,12 +2,7 @@ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#include +#include "../RegistryHelper.h" + #include -#include -#include -#include #include -#include - -#include "RegistryHelper.h"