Skip to content
Chris Guzak edited this page Dec 16, 2023 · 11 revisions

WIL registry helpers are basic "free function" helpers for reading, writing, and manipulating the registry. They also include a basic registry watcher, for watching registry changes.

// read a value from an app configuration stored in the registry
const auto typeOverlay = wil::reg::get_value_dword(HKEY_CURRENT_USER_LOCAL_SETTINGS, LR"(Windows\FileExplorer)", L"ShowFileExtensions");

// write a value to an app configuration stored in the registry
wil::reg::set_value_dword(HKEY_CURRENT_USER_LOCAL_SETTINGS, LR"(Vendor\App)", 
    L"Setting", 1);

Usage

To use WIL error handling helpers, add wil/registry.h to your C++ source file:

#include <wil/registry.h>

Requirements

Some of the free functions have specific requirements:

Requires
Getters & setters that throw Requires exceptions
try_get_* Requires exceptions & <optional>
std::wstring get/set Requires exceptions & <string>

Opening & creating keys

// "Open" guaranteed-existing keys or "create" to potentially create if non-existent
const auto r_unique_key = wil::reg::open_unique_key(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer");
const auto rw_shared_key = wil::reg::create_shared_key(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", wil::reg::key_access::readwrite);

// nothrow version, if you don't have exceptions
wil::unique_hkey nothrow_key;
THROW_IF_FAILED(wil::reg::open_unique_key_nothrow(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", nothrow_key, wil::reg::key_access::readwrite));
Function name Full Signature
Open if exists Unique pointer wil::reg::open_unique_key wil::unique_hkey wil::reg::open_unique_key(HKEY key, PCWSTR path, wil::reg::key_access access = wil::reg::key_access::read)
wil::reg::open_unique_key_nothrow HRESULT wil::reg::open_unique_key_nothrow(HKEY key, PCWSTR path, _Out_ ::wil::unique_hkey& hkey, wil::reg::key_access access = wil::reg::key_access::read) nothrow
Shared pointer wil::reg::open_shared_key wil::shared_hkey wil::reg::open_shared_key(HKEY key, PCWSTR path, wil::reg::key_access access = wil::reg::key_access::read)
wil::reg::open_shared_key_nothrow HRESULT wil::reg::open_shared_key_nothrow(HKEY key, PCWSTR path, _Out_ ::wil::shared_hkey& hkey, wil::reg::key_access access = wil::reg::key_access::read) nothrow
Create if missing, open if exists Unique pointer wil::reg::create_unique_key wil::unique_hkey wil::reg::create_unique_key(HKEY key, PCWSTR path, wil::reg::key_access access = wil::reg::key_access::read)
wil::reg::create_unique_key_nothrow HRESULT wil::reg::create_unique_key_nothrow(HKEY key, PCWSTR path, _Out_ wil::unique_hkey& hkey, wil::reg::key_access access = wil::reg::key_access::read) nothrow
Shared pointer wil::reg::create_shared_key wil::shared_hkey wil::reg::create_shared_key(HKEY key, PCWSTR path, wil::reg::key_access access = wil::reg::key_access::read)
wil::reg::create_shared_key_nothrow HRESULT wil::reg::create_shared_key_nothrow(HKEY key, PCWSTR path, _Out_ wil::shared_hkey& hkey, wil::reg::key_access access = wil::reg::key_access::read) nothrow

Parameters

  • HKEY key - a handle to an open or well-known registry key.
  • PCWSTR path - the path of a registry key relative to the key specified by the key parameter
  • wil::reg::key_access access - how to open the key (read/readwrite). See Registry Key Security and Access Rights.
    • Possible values:
      • wil::reg::key_access::read (default) - read only
      • wil::reg::key_access::readwrite - read and write allowed

See documentation for RegCreateKeyEx.

Returns

Returns:

  • wil::unique_hkey for *_unique_key functions.
  • wil::shared_hkey for *_shared_key variants.
  • HRESULT for *_key_nothrow variants. Uses _Out_ pointers to return wil::unique_hkey or wil::shared_hkey.

Nothrow variants will never throw; any errors are propagated to the HRESULT return value. All other functions will throw for an error. For open_*, that includes when the key does not yet exist.

Reading values

// Get values (or try_get if the value might not exist)
const DWORD dword = wil::reg::get_value_dword(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme");
const std::optional<std::wstring> stringOptional = wil::reg::try_get_value_string(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes", L"CurrentTheme");

// Known HKEY
const auto key = wil::reg::open_unique_key(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
const DWORD otherDword = wil::reg::get_value_dword(key.get(), L"AppsUseLightTheme");

// nothrow version, if you don't have exceptions
wil::unique_bstr bstr;
THROW_IF_FAILED(wil::reg::get_value_string_nothrow(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes", L"CurrentTheme", bstr));

// Templated version
const auto value = wil::reg::get_value<std::wstring>(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes", L"CurrentTheme");

Function prefaces for read and write

Each function has 2 overloads. One for accessing values of specific, already-opened keys (e.g., you called wil::reg::open_key before) and one for accessing subkeys of open keys:

  • Access known key (just an HKEY)
    • (HKEY key, _In_opt_ PCWSTR value_name, /* ... */)
    • Example: wil::reg::get_value_dword(myKeyFromElsewhere, L"Foo");
  • Access arbitrary subkeys (HKEY + subkey string)
    • (HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, /* ... */)
    • Example: wil::reg::get_value_dword(HKEY_CURRENT_USER, L"Microsoft\\Windows\\Advanced", L"Foo");

Full API

Here are all the getters, by type (noting that each function has 2 overloads, see Function prefaces for read and write above).

type note get try_get get (nothrow)
(requires exceptions)
returns the expected type
throws on any error
(requires exceptions + <optional>)
returns an optional of the expected type, std::nullopt on ERROR_FILE_NOT_FOUND & ERROR_PATH_NOT_FOUND
throws on any other error
returns HRESULTs
does not throw
DWORD uint32_t wil::reg::get_value_dword(/*...*/) std::optional<uint32_t> wil::reg::try_get_value_dword(/*...*/) HRESULT wil::reg::get_value_dword_nothrow(/*...*/, _Out_ uint32_t*) noexcept
QWORD (aka DWORD64) uint64_t wil::reg::get_value_qword(/*...*/) std::optional<uint64_t> wil::reg::try_get_value_qword(/*...*/) HRESULT wil::reg::get_value_qword_nothrow(/*...*/, _Out_ uint64_t*) noexcept
String (std::wstring) std::wstring wil::reg::get_value_string(/*...*/) std::optional<std::wstring> wil::reg::try_get_value_string(/*...*/) (see next)
String (wchar_t[N]) - - HRESULT get_value_string_nothrow(/*...*/, _Out_ wchar_t[N]) noexcept
String (any supported string type StringT; see Strings notes below) StringT wil::reg::get_value_string<StringT>(/*...*/) std::optional<StringT> wil::reg::try_get_value_string<StringT>(/*...*/) HRESULT wil::reg::get_value_string_nothrow<StringT>(/*...*/, _Inout_ StringT&) noexcept
Expanded string (std::wstring) std::wstring wil::reg::get_value_expanded_string(/*...*/) std::optional<std::wstring> wil::reg::try_get_value_expanded_string(/*...*/) (see next)
Expanded string (wchar_t[N]) - - HRESULT wil::reg::get_value_expanded_string_nothrow(/*...*/, _Out_ wchar_t[N]) noexcept
Expanded string (any supported string type StringT) StringT wil::reg::get_value_expanded_string<StringT>(/*...*/) std::optional<StringT> wil::reg::try_get_value_expanded_string(/*...*/) (see Strings notes below) HRESULT wil::reg::get_value_expanded_string_nothrow<StringT>(/*...*/, _Inout_ StringT&) noexcept
Multistring std::vector<std::wstring> wil::reg::get_value_multistring(/*...*/) std::optional<std::vector<std::wstring>> wil::reg::try_get_value_multistring(/*...*/) HRESULT wil::reg::get_value_multistring_nothrow(/*...*/, wil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_string>&) noexcept
Binary (requires extra type parameter like REG_BINARY or REG_DWORD) std::vector<uint8_t> wil::reg::get_value_binary(/*...*/, uint32_t type) std::optional<std::vector<uint8_t>> wil::reg::get_value_binary(/*...*/, uint32_t type) HRESULT wil::reg::get_value_binary_nothrow(/*...*/, uint32_t type, _Inout_ wil::unique_cotaskmem_array_ptr<uint8_t>&) noexcept
Any (templated function for any of the above types, except binary) T wil::reg::get_value<T>(/*...*/) std::optional<T> wil::reg::try_get_value<T>(/*...*/) HRESULT wil::reg::get_value_nothrow(/*...*/, _Out_ T*) noexcept
(note: some types are _Inout_ T&, not pointers)

Parameters

  • HKEY key - a handle to an open or well-known registry key.
  • _In_opt_ PCWSTR subkey - the path of a registry key relative to the key specified by the key parameter.
    • May be nullptr or the empty string (L"") to read the value specified by key itself.
    • Note: each getter has a variant where this parameter can be left out entirely (see Function prefaces for read and write above).
  • _In_opt_ PCWSTR value_name - the name of the registry value.
    • May be nullptr or the empty string (L"") to read from the key's unnamed and default value, if any.

See documentation for RegGetValue.

Returns

  • Specified type T (see table) for get_value_* functions.
  • std::optional<T> of specified type T (see table) for try_get_value_* functions.
  • HRESULT for *_nothrow functions. Uses _Out_ pointers to return result.

Errors are propagated as follows:

  • get_value_* functions throw on all errors.
  • try_get_value_* functions return std::nullopt if the value does not exist and throw on all other errors.
  • *_nothrow functions return all errors as HRESULTs.

Strings: supported types and caveats

You can use any of the following types as StringT for the *_string and *_expanded_string functions:

  • std::wstring
  • wil::unique_cotaskmem_string
  • wil::shared_cotaskmem_string
  • wil::unique_bstr
  • wil::shared_bstr

For the try_get_ functions, only these types are available (the unique_ types are disabled):

  • std::wstring
  • wil::shared_cotaskmem_string
  • wil::shared_bstr

This is because it is very difficult to access these unique values from a std::optional.

Writing values

// Set values
wil::reg::set_value_dword(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", L"DwordValue", 18);
wil::reg::set_value_string(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", L"StringValue", L"Wowee zowee");

// Generic versions, if you don't want to specify type.
wil::reg::set_value(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", L"DwordValue2", 1);
wil::reg::set_value(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", L"StringValue2", L"Besto wuz here");

// Known HKEY
const auto key = wil::reg::create_unique_key(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", wil::reg::key_access::readwrite);
wil::reg::set_value_dword(key.get(), L"DwordValue3", 42);

// nothrow version, if you don't have exceptions
THROW_IF_FAILED(wil::reg::set_value_string_nothrow(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", L"StringValue3", L"Hi, Mom!"));

Here are all the getters, by type (noting that each function has 2 overloads, see Function prefaces for read and write above).

type note set set (nothrow)
(requires exceptions)
throws on any error
returns HRESULTs, does not throw
DWORD void wil::reg::set_value_dword(/*...*/, uint32_t) HRESULT wil::reg::set_value_dword_nothrow(/*...*/, uint32_t) noexcept
QWORD (aka DWORD64) void wil::reg::set_value_qword(/*...*/, uint64_t) HRESULT wil::reg::set_value_qword_nothrow(/*...*/, uint64_t) noexcept
String void wil::reg::set_value_string(/*...*/, PCWSTR) HRESULT wil::reg::set_value_string_nothrow(/*...*/, PCWSTR) noexcept
Expanded string void wil::reg::set_value_expanded_string(/*...*/, PCWSTR) HRESULT wil::reg::set_value_expanded_string_nothrow(/*...*/, PCWSTR) noexcept
Multistring void wil::reg::set_value_multistring(/*...*/, const std::vector<std::wstring>&) HRESULT wil::reg::set_value_multistring_nothrow(/*...*/, const std::vector<std::wstring>&) noexcept
Byte vector/binary blob void wil::reg::set_value_binary(/*...*/, uint32_t type, const std::vector<uint8_t>&) void wil::reg::set_value_binary_nothrow(/*...*/, uint32_t type, const wil::unique_cotaskmem_array_ptr<uint8_t>&) noexcept
Any (templated function for any of the above types, except binary) void wil::reg::set_value<T>(/*...*/, const T&) HRESULT wil::reg::set_value_nothrow<T>(/*...*/, const T&) noexcept

Parameters

  • HKEY key - a handle to an open or well-known registry key. If using a manually-opened key, the key must have been opened with the KEY_SET_VALUE access right (wil::reg::key_access::readwrite if using the WIL helpers).
  • _In_opt_ PCWSTR subkey - the path of a registry key relative to the key specified by the key parameter.
    • May be nullptr or the empty string (L"") to read the value specified by key itself.
    • Created if not previously existent.
    • Note: each getter has a variant where this parameter can be left out entirely (see Function prefaces for read and write above).
  • _In_opt_ PCWSTR value_name - the name of the registry value.
    • May be nullptr or the empty string (L"") to write to the key's unnamed and default value.
    • Created if not previously existent.
  • T data - the data to write (where T is the type indicated by the API in the table above).

See the documentation for RegSetKeyValue.

Returns

  • void for all throwing functions. Throws on any error.
  • HRESULT for all *_nothrow variants. Never throws; all errors propagated as HRESULTs.

Helper functions

const auto key = wil::reg::create_unique_key(HKEY_CURRENT_USER, L"Software\\Microsoft\\BasicRegistryTest", wil::reg::key_access::readwrite);

// Get count of child keys and values.
const uint32_t childValCount = wil::reg::get_child_value_count(key.get());
const uint32_t childKeyCount = wil::reg::get_child_key_count(key.get());

// Get last modified date
const FILETIME lastModified = wil::reg::get_last_write_filetime(key.get());

// Simple helpers for analyzing returned HRESULTs
const bool a = wil::reg::is_registry_buffer_too_small(HRESULT_FROM_WIN32(ERROR_MORE_DATA)); // => true
const bool b = wil::reg::is_registry_not_found(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); // => true
const bool c = wil::reg::is_registry_not_found(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); // => true
  • uint32_t wil::reg::get_child_key_count(HKEY key) - get the number of child keys of a given open key or well-known key.
  • uint32_t wil::reg::get_child_value_count(HKEY key) - get the number of child values of a given open key or well-known key.
  • FILETIME wil::reg::get_last_write_filetime(HKEY key) - get the last-modified date of a given open key or well-known key.

See documentation for RegQueryInfoKey. Each of these functions has a _nothrow variant.

  • bool wil::reg::is_hresult_buffer_too_small(HRESULT hr) - returns true iff hr signals the destination buffer is too small.
  • bool wil::reg::is_hresult_not_found(HRESULT hr) - returns true iff hr signals that the desired key/value is not found.

Useful for handling get_value* HRESULTs manually. See documentation for RegGetValue's return value.

Registry watcher

These functions enable observing changes to registry keys

#include <wil/registry.h>

auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, LR"(Software\Microsoft\Settings)", true, [](wil::RegistryChangeKind changeType)
{
    InvalidateRegistryCache()
});

Enumeration

wil::reg::key_iterator and wil::reg::value_iterator enable enumerating the contents of a registry key.

// Inspect the sub-keys
for (const auto& key_data : wil::make_range(wil::reg::key_iterator{HKEY_CURRENT_USER_LOCAL_SETTINGS}, wil::reg::key_iterator{}))
{
    key_data.name;
}
// Inspect the values under a key and their type
for (const auto& key_data : wil::make_range(wil::reg::value_iterator{HKEY_CURRENT_USER_LOCAL_SETTINGS}, wil::reg::value_iterator{}))
{
    key_data.type;
    key_data.name;
}
Clone this wiki locally