diff --git a/README.md b/README.md index 930d0a9..2cd05bd 100644 --- a/README.md +++ b/README.md @@ -1 +1,73 @@ -# RegistryHelper +# RegistryHelper - Accessing the Windows Registry with Modern C++ + +This project provides a `RegistryHelper` class for accessing the Windows Registry using Modern C++. The implementation is based on the Microsoft Dev Blog post "[Use Modern C++ to Access the Windows Registry](https://learn.microsoft.com/en-us/archive/msdn-magazine/authors/giovanni_dicanio)" by [Giovanni Dicanio](https://learn.microsoft.com/en-us/archive/msdn-magazine/2017/may/%5Carchive%5Cmsdn-magazine%5Cauthors%5CGiovanni_Dicanio). + +## Overview + +The `RegistryHelper` class allows you to read DWORD, string, and multi-string values from the Windows Registry in a convenient and modern C++ manner. + +## Usage + +To use the `RegistryHelper` class in your project, follow these steps: + +1. Include the "RegistryHelper.h" header file in your project. +2. Add the "RegistryHelper.cpp" source file to your project. + +### Example Usage + +```cpp +#include "RegistryHelper.h" +#include + +int +main() +{ + try { + RegistryHelper registryHelper; + std::wstring subKey = L"SYSTEM\\CurrentControlSet\\Control"; + HKEY hKey = HKEY_LOCAL_MACHINE; + + // Example 1: Reading DWORD value (REG_DWORD) + std::wstring valueNameDword = L"BootDriverFlags"; + + DWORD dwordValue = registryHelper.RegGetDword(hKey, subKey, valueNameDword); + std::wcout << L"DWORD Value: " << dwordValue << std::endl; + + // Example 2: Reading String value (REG_SZ) + std::wstring subKeyString = L"SYSTEM\\CurrentControlSet\\Control"; + std::wstring valueNameString = L"CurrentUser"; + + std::wstring stringValue = + registryHelper.RegGetString(hKey, subKey, valueNameString); + std::wcout << L"String Value: " << stringValue << std::endl; + + // Example 3: Reading Multi-String value (REG_MULTI_SZ) + std::wstring valueNameMultiString = L"PreshutdownOrder"; + + std::vector multiStringValue = + registryHelper.RegGetMultiString(hKey, subKey, valueNameMultiString); + + std::wcout << L"Multi-String Values: " << std::endl; + for (const auto& str : multiStringValue) { + std::wcout << L" " << str << std::endl; + } + } catch (const RegistryError& ex) { + std::cerr << "Registry Error: " << ex.what() + << " (Error Code: " << ex.ErrorCode() << ")" << std::endl; + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; + } + + return 0; +} +``` + +Adjust the registry keys, subkeys, and value names based on your specific use case. + +## Credits + +This project is inspired by the Microsoft Dev Blog post "Use Modern C++ to Access the Windows Registry" by Giovanni Dicanio. The original blog post can be found [here](https://learn.microsoft.com/en-us/archive/msdn-magazine/2017/may/c-use-modern-c-to-access-the-windows-registry). + +## License + +This project is provided under the MIT License. See the LICENSE file for details. diff --git a/RegistryHelper.sln b/RegistryHelper.sln new file mode 100644 index 0000000..cf15f0c --- /dev/null +++ b/RegistryHelper.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34526.213 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RegistryHelper", "RegistryHelper\RegistryHelper.vcxproj", "{7144564F-FE5B-4B24-A6A9-C83F18969BBB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3587BC45-E1D1-4FCE-9F36-D4BD660FB262}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + LICENSE.md = LICENSE.md + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Debug|x64.ActiveCfg = Debug|x64 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Debug|x64.Build.0 = Debug|x64 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Debug|x86.ActiveCfg = Debug|Win32 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Debug|x86.Build.0 = Debug|Win32 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Release|x64.ActiveCfg = Release|x64 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Release|x64.Build.0 = Release|x64 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Release|x86.ActiveCfg = Release|Win32 + {7144564F-FE5B-4B24-A6A9-C83F18969BBB}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6B012E4D-3A77-4E50-83D9-5040920D82DE} + EndGlobalSection +EndGlobal diff --git a/RegistryHelper/RegistryHelper.cpp b/RegistryHelper/RegistryHelper.cpp new file mode 100644 index 0000000..e2f980b --- /dev/null +++ b/RegistryHelper/RegistryHelper.cpp @@ -0,0 +1,124 @@ +#include "RegistryHelper.h" + +// Constructor initializes the error code to ERROR_SUCCESS +RegistryHelper::RegistryHelper() + : m_errorCode(ERROR_SUCCESS) +{ +} + +// Read a DWORD value from the registry. This function is marked as noexcept +// to indicate that it does not throw exceptions during its execution. +DWORD +RegistryHelper::RegGetDword(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) noexcept +{ + DWORD data{}; + DWORD dataSize = sizeof(data); + + // Use specifier to indicate that the function doesn't throw + // exceptions + 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 +std::wstring +RegistryHelper::RegGetString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) noexcept +{ + DWORD dataSize{}; + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_SZ, + nullptr, + nullptr, + &dataSize); + + 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)); + + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_SZ, + nullptr, + data.data(), + &dataSize); + + 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); + + return std::wstring(data.begin(), data.end()); +} + +// Read a multi-string value from the registry +std::vector +RegistryHelper::RegGetMultiString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) noexcept +{ + DWORD dataSize{}; + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_MULTI_SZ, + nullptr, + nullptr, + &dataSize); + + 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)); + + m_errorCode = ::RegGetValue(hKey, + subKey.c_str(), + value.c_str(), + RRF_RT_REG_MULTI_SZ, + nullptr, + data.data(), + &dataSize); + + 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 + 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 result; +} diff --git a/RegistryHelper/RegistryHelper.h b/RegistryHelper/RegistryHelper.h new file mode 100644 index 0000000..26c332d --- /dev/null +++ b/RegistryHelper/RegistryHelper.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +class RegistryError : public std::runtime_error +{ +public: + RegistryError(const char* message, LONG errorCode) + : std::runtime_error{ message } + , m_errorCode{ errorCode } + { + } + + LONG ErrorCode() const noexcept { return m_errorCode; } + +private: + LONG m_errorCode; +}; + +class RegistryHelper +{ +public: + RegistryHelper(); + + DWORD RegGetDword(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) noexcept; + std::wstring RegGetString(HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) noexcept; + std::vector RegGetMultiString( + HKEY hKey, + const std::wstring& subKey, + const std::wstring& value) noexcept; + +private: + LONG m_errorCode; // store the last error code +}; diff --git a/RegistryHelper/RegistryHelper.vcxproj b/RegistryHelper/RegistryHelper.vcxproj new file mode 100644 index 0000000..3f239b3 --- /dev/null +++ b/RegistryHelper/RegistryHelper.vcxproj @@ -0,0 +1,139 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + 17.0 + Win32Proj + {7144564f-fe5b-4b24-a6a9-c83f18969bbb} + RegistryHelper + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + \ No newline at end of file diff --git a/RegistryHelper/RegistryHelper.vcxproj.filters b/RegistryHelper/RegistryHelper.vcxproj.filters new file mode 100644 index 0000000..943fe01 --- /dev/null +++ b/RegistryHelper/RegistryHelper.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/RegistryHelper/main.cpp b/RegistryHelper/main.cpp new file mode 100644 index 0000000..083dfd0 --- /dev/null +++ b/RegistryHelper/main.cpp @@ -0,0 +1,44 @@ +#include "RegistryHelper.h" +#include + +int +main() +{ + try { + RegistryHelper registryHelper; + std::wstring subKey = L"SYSTEM\\CurrentControlSet\\Control"; + HKEY hKey = HKEY_LOCAL_MACHINE; + + // Example 1: Reading DWORD value (REG_DWORD) + std::wstring valueNameDword = L"BootDriverFlags"; + + DWORD dwordValue = registryHelper.RegGetDword(hKey, subKey, valueNameDword); + std::wcout << L"DWORD Value: " << dwordValue << std::endl; + + // Example 2: Reading String value (REG_SZ) + std::wstring subKeyString = L"SYSTEM\\CurrentControlSet\\Control"; + std::wstring valueNameString = L"CurrentUser"; + + std::wstring stringValue = + registryHelper.RegGetString(hKey, subKey, valueNameString); + std::wcout << L"String Value: " << stringValue << std::endl; + + // Example 3: Reading Multi-String value (REG_MULTI_SZ) + std::wstring valueNameMultiString = L"PreshutdownOrder"; + + std::vector multiStringValue = + registryHelper.RegGetMultiString(hKey, subKey, valueNameMultiString); + + std::wcout << L"Multi-String Values: " << std::endl; + for (const auto& str : multiStringValue) { + std::wcout << L" " << str << std::endl; + } + } catch (const RegistryError& ex) { + std::cerr << "Registry Error: " << ex.what() + << " (Error Code: " << ex.ErrorCode() << ")" << std::endl; + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; + } + + return 0; +}