Code Monkey home page Code Monkey logo

winreg's Introduction

WinReg v6.2.0

High-level C++ Wrapper Around the Low-level Windows Registry C-interface API

by Giovanni Dicanio

The Windows Registry C-interface API is very low-level and hard to use.

I developed some C++ wrappers around this low-level Win32 API, to raise the semantic level, using C++ classes like std::wstring, std::vector, etc. instead of raw C-style buffers and low-level mechanisms.

For example, the REG_MULTI_SZ registry type associated to double-NUL-terminated C-style strings is handled using a much easier higher-level vector<wstring>. My C++ code does the translation between high-level C++ STL-based stuff and the low-level Win32 C-interface API.

Moreover, Win32 error codes are translated to C++ exceptions. However, note that if you prefer checking return codes, there are also methods that follow this pattern (e.g. TryXxxx() methods like TryOpen(), TryGetDwordValue(), TryGetStringValue(), etc.).

The Win32 registry value types are mapped to C++ higher-level types according the following table:

Win32 Registry Type C++ Type
REG_DWORD DWORD
REG_QWORD ULONGLONG
REG_SZ std::wstring
REG_EXPAND_SZ std::wstring
REG_MULTI_SZ std::vector<std::wstring>
REG_BINARY std::vector<BYTE>

This code is currently developed using Visual Studio 2019 with C++17 features enabled (/std:c++17). I have no longer tested the code with previous compilers. The code compiles cleanly at warning level 4 (/W4) in both 32-bit and 64-bit builds.

This is a header-only library, implemented in the WinReg.hpp header file.

WinRegTest.cpp contains some demo/test code for the library: check it out for some sample usage.

The library exposes four main classes:

  • RegKey: a tiny efficient wrapper around raw Win32 HKEY handles
  • RegException: an exception class to signal error conditions
  • RegResult: a tiny wrapper around Windows Registry API LSTATUS error codes, returned by some Try methods (like RegKey::TryOpen)
  • RegExpected<T>: an object that contains a value of type T (e.g. a DWORD read from the registry) on success, or an instance of a RegResult-wrapped return code on error

There are many member functions inside the RegKey class, that wrap several parts of the native C-interface Windows Registry API, in a convenient higher-level C++ way.

For example, you can simply open a registry key and get registry values with C++ code like this:

RegKey  key{ HKEY_CURRENT_USER, L"SOFTWARE\\SomeKey" };

DWORD   dw = key.GetDwordValue (L"SomeDwordValue");
wstring s  = key.GetStringValue(L"SomeStringValue");

You can also open a registry key using a two-step construction process:

RegKey key;
key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\SomeKey");

The above code will throw an exception on error. If you prefer to check return codes, you can do that as well, using a TryXxxx method, e.g.:

RegKey key;
RegResult result = key.TryOpen(HKEY_CURRENT_USER, L"SOFTWARE\\SomeKey");
if (! result)
{
    //
    // Open failed.
    //
    // You can invoke the RegResult::Code and RegResult::ErrorMessage methods
    // for further details.
    //
    ...
}

You can also enumerate all the values under a given key with simple C++ code like this:

auto values = key.EnumValues();

for (const auto & v : values)
{
    //
    // Process current value:
    //
    //   - v.first  (wstring) is the value name
    //   - v.second (DWORD)   is the value type
    //
    ...
}

You can simplify the above iteration code using C++17 structured bindings, as well:

auto values = key.EnumValues();

for (const auto & [valueName, valueType] : values)
{
    //
    // Use valueName and valueType
    //
    ...
}

You can also use the RegKey::TryGet...Value methods, that return RegExpected<T> instead of throwing an exception on error:

//
// RegKey::TryGetDwordValue() returns a RegExpected<DWORD>;
// the returned RegExpected contains a DWORD on success, 
// or a RegResult instance on error.
//
// 'res' is a RegExpected<DWORD> in this case:
//
const auto res = key.TryGetDwordValue(L"SomeDwordValue");
if (res.IsValid())  // or simply:  if (res)
{
    //
    // All right: Process the returned value ...
    //
    // Use res.GetValue() to access the stored DWORD.
    //
}
else
{
    //
    // The method has failed: 
    //
    // The returned RegExpected contains a RegResult with an error code.
    // Use res.GetError() to access the RegResult object.
    //
}

Version Note: WinReg v5.1.1 is the latest version in which the TryGetXxxValue methods return std::optional<T> (discarding the information about the error code). Starting from v6.0.0, the TryGetXxxxValue methods return RegExpected<T> (which keeps the error information on failure).

Note that many methods are available in two forms: one that throws an exception of type RegException on error (e.g. RegKey::Open), and another that returns an error status object of type RegResult (e.g. RegKey::TryOpen) instead of throwing an exception. In addition, as indicated above, some methods like the RegKey::TryGet...Value ones return RegExpected instead of throwing exceptions; in case of errors, the returned RegExpected contains a RegResult storing the error code.

You can take a look at the test code in WinRegTest.cpp for some sample usage.

The library stuff lives under the winreg namespace.

See the WinReg.hpp header for more details and documentation.

Thanks to everyone who contributed to this project with some additional features and constructive comments and suggestions.

winreg's People

Contributors

giovannidicanio avatar jamesmcguirepro avatar loqaritm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

winreg's Issues

Code does not handle keys otherwise mapped to Wow6432Node in 32-bit builds

There are still a lot of 32-bit builds going on (usually due to platform constraints), and the library will not correctly map 32-bit builds to the "correct" location rather than to Wow6432Node, which is just confusing. Why Microsoft thought that particular section was a good idea is anybody's guess, but adding KEY_WOW64_64KEY to the API options calls resolves it.

Need function TryQueryValueType

Thank you for your very nice library.

I think it also need function "TryQueryValueType", because "QueryValueType" failing when value not set (see image):

image

Visual Studio 2013 not supported

'noexcept' and 'constexpr' are not supported by Visual Studio 2013.
It would be nice if the code was updated so it could be used in a Visual Studio 2013 project.

mingw build error

gcc12
On this line [[nodiscard]] must be before inline specifier. Like below.
Actually inline should be completely removed because it does not make any sense for templates.

template <typename T>
[[nodiscard]]
inline RegExpected<T> MakeRegExpectedWithError(const LSTATUS retCode)

Incredibly good library! NuGet Package?

Thanks @GiovanniDicanio for an extremely well-written, light and easy to use library! Seriously great work!

I noticed that it's been "final" for a long time and barely changes, so maybe it's time to add it to Microsoft's NuGet (https://www.nuget.org/) so that it can be added as an auto-installed dependency in Visual Studio? Since it doesn't change often, it would be a very easy way for people to install the library.

What do you think?

TryOpen was removed?

Hi,

Seems like it is.
But how do we open keys now without exception?
The code is

winreg::RegKey kits;
auto r = kits.TryOpen(HKEY_LOCAL_MACHINE, root, access);
if (r.IsOk())
{

Enumeration returns names but not values

Using the test code.

const wstring testSubKey = L"SOFTWARE\\Google";
		RegKey key;// { HKEY_LOCAL_MACHINE, testSubKey };
		key.Create(HKEY_LOCAL_MACHINE, testSubKey,KEY_READ);

        vector<wstring> subKeyNames = key.EnumSubKeys();
        wcout << L"Subkeys:\n";
        for (const auto& s : subKeyNames)
        {
            wcout << L"  [" << s << L"]\n";
        }
        wcout << L'\n';

        vector<pair<wstring, DWORD>> values = key.EnumValues();
        wcout << L"Values:\n";
        for (const auto& v : values)
        {
            wcout << L"  [" << v.first << L"](" << RegKey::RegTypeToString(v.second) << L")\n";
        }
        wcout << L'\n';

        key.Close();

I get a list of sub key names but no values are returned even though I can see the values and several other sub keys through the Regedt32 utility

Default to 64-bit registry for 32-bit applications on 64-bit OS

Situation: 32-bit program needs to read registry keys on 32 and 64-bit computers. In this case I need the subkeys in:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\Packages

The problem is that attempts to open that key on a 64-bit computer resulted in "Access Denied". If I have the program list the subkeys of "Component Based Service" it doesn't list "Packages", but using a "reg query" does show that subkey. It turns out the 32-bit program was being fed the keys stored in the HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows... path.

Solution/workaround: The solution was to add "KEY_WOW64_64KEY" to the desired access request like this (KEY_READ | KEY_WOW64_64KEY). I confirmed that this doesn't affect the results when run on a 32-bit OS. I open a key with:
RegKey key{};
key.Open(HKEY_LOCAL_MACHINE, testSubKey, (REGSAM)(KEY_READ | KEY_WOW64_64KEY));

Proposal: Would it make sense to have the WinReg library default to using the KEY_WOW64_64KEY in access requests, or at least mention in the documentation that the default registry view depends on how you compile the program, especially a 32-bit app on a 64-bit OS?

The Microsoft reference is here:
https://docs.microsoft.com/en-us/windows/win32/winprog64/accessing-an-alternate-registry-view

Thoughts?

noexcept TryGet/TrySet API consistency

Hi.

I would like to suggest an API change for the exceptionless routines.

Currently if we examine the TrySet* and TryGet* routines it is seen that they have different semantics.
TrySet* return RegResult so it is possible to examine the error code.
TryGet* on other hand return std::optinal<>, so either result value in case of success or nothing in case of failure, making it impossible to examine the return code.

I would suggest making so, that TryGet* would also report the RegResult code.

I would think of several ways to do so

  1. Return RegResult, thus making TryGet* routines be consistent on return value with TrySet* routines

e.g.

[[nodiscard]] RegResult TryGetDwordValue(const std::wstring& valueName, DWORD * value) const;
  1. Return std::variant holding either RegResult in case of failure or result value in case of success

e.g.

[[nodiscard]] std::variant<RegResult, DWORD> TryGetDwordValue(const std::wstring& valueName) const;

and with the following usage

RegKey key;
...

auto const res = key.TryGetDwordValue(L"BAMBAM");

if (std::holds_alternative<RegResult>(res))
{
    // failure here
    // print e.d. error code to stdout
    std::cout << std::get<RegResult>(res).code().value();
} else
{
    // Value query success
    const DWORD value = std::get<DWORD>(res);
}
  1. Optionally provide a default-valued argument for RegResult storing if it is needed. This option is redundant from one side but form the other side is maybe more easy/convenient to use.
[[nodiscard]] std::optional<DWORD> TryGetDwordValue(const std::wstring& valueName, RegResult * res = nullptr) const;

What do you think about the suggestion in general and about those options?

Access denied with default RegKey constructor

The default RegKey constructor tries to open the registry key with read and write access.

REGSAM desiredAccess = KEY_READ | KEY_WRITE

This cause problems for a lot of registry keys as they require elevated permissions to be able to be written.
E.g. 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB'

Maybe better to open the keys with read access only?
Or add the permission level as constructor argument?

Errors when compiling with Multi-Byte Character Set

I have a legacy project that uses Multi-Byte character set instead of Unicode. When I try to compile project containing winreg.hpp I get 163 errors with following message:

cannot convert argument 3 from 'const wchar_t *' to 'LPCSTR'

Is there an easy way to fix this?

Allow to build dll

Hi,

Thank you for your work on non-throwing versions of functions!
I'll try it later today.

Next feature I'd like to ask is to add dll support.
Usually I do it by adding MY_LIB_API to class and function headers.
Example:

class WINREG_API RegKey { ... };

WINREG_API
void somefunc();

If you don't want to make big changes to VS SLN, it is possible to simply add empty definition in it WINREG_API=.
I use other build system anyway, but I can use those api specs to turn library into dll.

(I'm able to send a PR, but I don't want to touch any VS files myself.)

Windows XP support

In windows XP there is no RegGetValue function
Maybe should use RegQueryValueEx instead?

or something like (not fully working in some cases)
LONG RegGetValueXP(HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpValue, DWORD dwFlags, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) { return RegQueryValueEx(hkey, lpValue, nullptr, pdwType, (LPBYTE)pvData, pcbData); }

No-exception routines

Hi,

Is it possible to add support for not throwing exceptions?
So, functions will return bool.

Clarify C++ standard

My suggestion is to specify standard in redme.md explicitly (C++17) and notify user about last release which support C++11. As I can elaborate it on my own iteratively - winreg-2.2.3. Anyway, thank you for you effort!

Organize repository files

Source file, build files, resources and examples should be separated for a cleaner and flexible integration

I would suggest in large the following:

|Root folder
|  - |->include
|    - |->WinReg or the "namespace" of the project, could be GiovanniDicanio, or GiDi for short.
|       - |->WinReg.hpp
| - | tests
    |     test1 or rather a descriptive name for the test

| -VS2019 ( sln and vcxproj here)

| - CMakelists.txt - for integrating with other CMake projects
| - License
| - README.md

WinReg.vcxproj.user should be deleted

If minimum required Visual studio version is set to 2019, then I would suggest removing .sln and .vcxproj entirely , and using only the cmake, VS2019 has native support for it.

Add save setters

Hello. It would be good to have save setters like you did with getters (TryGetDwordValue, TryGetQwordValue, etc)

open HKEY_LOCAL_MACHINE\SOFTWARE fail

#include <iostream>
#include <Windows.h>
#include <string>
#include "WinReg.hpp"

int main()
{
	winreg::RegKey  key{ HKEY_LOCAL_MACHINE, L"SOFTWARE" };

	return 0;
}

image
Is there any problem with my code and why is it throwing an exception
image
The registry also exists

Open Source and Commercial Users of WinReg

I'd like to compile a list of open source and commercial users of this C++ WinReg library.

If anyone would like to contribute and is willing to share this information, please add that below.

Thank you.

TryGetMultiStringValue only returns the first value

Hello,

When I use TryGetMultiStringValue to obtain a REG_MULTI_SZ value, it only returns the first value.

I am reading a key that's populated in some cases when applications request a reboot: SYSTEM\CurrentControlSet\Control\Session Manager, property PendingFileRenameOperations

For instance, how the value in my registry is:
??\C:\Windows\System32\Intel\HwKws\SET2048.tmp

??\C:\Windows\System32\cAVS\Communication DLLs\SET257D.tmp

??\C:\Windows\system32\drivers\SET49C5.tmp

However, TryGetMultiStringValue only returns the first value. I checked in the debugger that we only enter this loop once in function TryGetMultiStringValue:

while (*currStringPtr != L'\0')
{
// Current string is NUL-terminated, so get its length calling wcslen
const size_t currStringLength = wcslen(currStringPtr);

    // Add current string to the result vector
    result.emplace_back(currStringPtr, currStringLength);

    // Move to the next string
    currStringPtr += currStringLength + 1;
}

Add validation for zero-length binary values

Hello. I have a zero value register and when I try to read them, I got error from vector.
For example, TryGetBinaryValue(). We check if this value exists and retCode = ERROR_SUCCESS but dataSize = 0. There is a problem on the next read.

_NODISCARD _CONSTEXPR20 _Ty& operator[](const size_type _Pos) noexcept /* strengthened */ {
        auto& _My_data = _Mypair._Myval2;
#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(
            _Pos < static_cast<size_type>(_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0

        return _My_data._Myfirst[_Pos];
    }

image

key.TryOpen results in access denied unless FullControl is granted to user. Even if read is enabled for the user.

So my user has Read Access to a key. I try to access this via key.TryOpen and get Access Denied When I grant full control for that user to the specific registry key, then this works. My guess is that key.TryOpen tries to open it for write access to which would fail for accounts simply trying to read the key.

Is there functionality to open only as read permissions? Cause right now I'm having to give the user account full control which creates a security risk that I dont want. If not, could we implement a method to only open the key in read mode?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.