Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions include/wil/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,22 @@ namespace reg
* @param subKey The name of a subkey that this function opens or creates.
* Note: this cannot be null (see the above referenced API documentation)
* @param access The requested access desired for the opened key
* @param options Controls whether the created key is volatile - see wil::reg::key_options
* @param[out] disposition Optionally receives whether the key was newly created or an existing key was opened.
* Only written on success - see wil::reg::key_disposition
* @return A wil::unique_hkey or wil::shared_hkey containing the resulting opened HKEY
* @exception std::exception (including wil::ResultException) will be thrown on all failures
*/
inline ::wil::unique_hkey create_unique_key(HKEY key, PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read)
inline ::wil::unique_hkey create_unique_key(
HKEY key,
PCWSTR subKey,
::wil::reg::key_access access = ::wil::reg::key_access::read,
::wil::reg::key_options options = ::wil::reg::key_options::non_volatile,
_Out_opt_ ::wil::reg::key_disposition* disposition = nullptr)
{
const reg_view_details::reg_view regview{key};
::wil::unique_hkey return_value;
regview.create_key(subKey, &return_value, access);
regview.create_key(subKey, &return_value, access, options, disposition);
return return_value;
}

Expand Down Expand Up @@ -90,14 +98,22 @@ namespace reg
* @param subKey The name of a subkey that this function opens or creates.
* Note: this cannot be null (see the above referenced API documentation)
* @param access The requested access desired for the opened key
* @param options Controls whether the created key is volatile - see wil::reg::key_options
* @param[out] disposition Optionally receives whether the key was newly created or an existing key was opened.
* Only written on success - see wil::reg::key_disposition
* @return A wil::shared_hkey or wil::shared_hkey containing the resulting opened HKEY
* @exception std::exception (including wil::ResultException) will be thrown on all failures
*/
inline ::wil::shared_hkey create_shared_key(HKEY key, PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read)
inline ::wil::shared_hkey create_shared_key(
HKEY key,
PCWSTR subKey,
::wil::reg::key_access access = ::wil::reg::key_access::read,
::wil::reg::key_options options = ::wil::reg::key_options::non_volatile,
_Out_opt_ ::wil::reg::key_disposition* disposition = nullptr)
{
const reg_view_details::reg_view regview{key};
::wil::shared_hkey return_value;
regview.create_key(subKey, &return_value, access);
regview.create_key(subKey, &return_value, access, options, disposition);
return return_value;
}
#endif // #if defined(__WIL_WINREG_STL)
Expand Down Expand Up @@ -126,13 +142,21 @@ namespace reg
* Note: this cannot be null (see the above referenced API documentation)
* @param[out] hkey A reference to a wil::unique_hkey to receive the opened HKEY
* @param access The requested access desired for the opened key
* @param options Controls whether the created key is volatile - see wil::reg::key_options
* @param[out] disposition Optionally receives whether the key was newly created or an existing key was opened.
* Only written on success - see wil::reg::key_disposition
* @return HRESULT error code indicating success or failure (does not throw C++ exceptions)
*/
inline HRESULT create_unique_key_nothrow(
HKEY key, PCWSTR subKey, ::wil::unique_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT
HKEY key,
PCWSTR subKey,
::wil::unique_hkey& hkey,
::wil::reg::key_access access = ::wil::reg::key_access::read,
::wil::reg::key_options options = ::wil::reg::key_options::non_volatile,
_Out_opt_ ::wil::reg::key_disposition* disposition = nullptr) WI_NOEXCEPT
{
const reg_view_details::reg_view_nothrow regview{key};
return regview.create_key(subKey, hkey.put(), access);
return regview.create_key(subKey, hkey.put(), access, options, disposition);
}

#if defined(__WIL_WINREG_STL) || defined(WIL_DOXYGEN)
Expand Down Expand Up @@ -162,10 +186,15 @@ namespace reg
* @return HRESULT error code indicating success or failure (does not throw C++ exceptions)
*/
inline HRESULT create_shared_key_nothrow(
HKEY key, PCWSTR subKey, ::wil::shared_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT
HKEY key,
PCWSTR subKey,
::wil::shared_hkey& hkey,
::wil::reg::key_access access = ::wil::reg::key_access::read,
::wil::reg::key_options options = ::wil::reg::key_options::non_volatile,
_Out_opt_ ::wil::reg::key_disposition* disposition = nullptr) WI_NOEXCEPT
{
const reg_view_details::reg_view_nothrow regview{key};
return regview.create_key(subKey, hkey.put(), access);
return regview.create_key(subKey, hkey.put(), access, options, disposition);
}
#endif // #define __WIL_WINREG_STL

Expand Down
54 changes: 49 additions & 5 deletions include/wil/registry_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ namespace reg
readwrite64,
};

// Options controlling how a key is created. See the dwOptions parameter of RegCreateKeyExW.
enum class key_options
{
// The key is preserved when the system is restarted (REG_OPTION_NON_VOLATILE).
non_volatile,

// The key is not preserved when the system is restarted (REG_OPTION_VOLATILE).
is_volatile,
};

// Indicates whether a created key was newly created or an existing key was opened. See the lpdwDisposition
// out-parameter of RegCreateKeyExW.
enum class key_disposition
{
// The key did not exist and was created (REG_CREATED_NEW_KEY).
created_new,

// The key existed and was opened without being changed (REG_OPENED_EXISTING_KEY).
opened_existing,
};

/// @cond
namespace reg_view_details
{
Expand Down Expand Up @@ -130,6 +151,19 @@ namespace reg
RESULT_NORETURN_RESULT(0);
}

constexpr DWORD get_options_flags(key_options options) WI_NOEXCEPT
{
switch (options)
{
case key_options::non_volatile:
return REG_OPTION_NON_VOLATILE;
case key_options::is_volatile:
return REG_OPTION_VOLATILE;
}
FAIL_FAST();
RESULT_NORETURN_RESULT(0);
}

/**
* @brief A utility function that walks a contiguous wchar_t container looking for strings within a multi-string
* @tparam InputIt An iterator type that reference a container that holds wchar_t characters to translate into individual
Expand Down Expand Up @@ -1112,17 +1146,27 @@ namespace reg
HRESULT_FROM_WIN32(::RegOpenKeyExW(m_key, subKey, zero_options, get_access_flags(access), hkey)));
}

typename err_policy::result create_key(PCWSTR subKey, _Out_ HKEY* hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) const
typename err_policy::result create_key(
PCWSTR subKey,
_Out_ HKEY* hkey,
::wil::reg::key_access access = ::wil::reg::key_access::read,
::wil::reg::key_options options = ::wil::reg::key_options::non_volatile,
_Out_opt_ ::wil::reg::key_disposition* disposition = nullptr) const
{
*hkey = nullptr;

constexpr DWORD zero_reserved{0};
constexpr PWSTR null_class{nullptr};
constexpr DWORD zero_options{0};
constexpr SECURITY_ATTRIBUTES* null_security_attributes{nullptr};
DWORD disposition{0};
return err_policy::HResult(HRESULT_FROM_WIN32(::RegCreateKeyExW(
m_key, subKey, zero_reserved, null_class, zero_options, get_access_flags(access), null_security_attributes, hkey, &disposition)));
DWORD local_disposition{0};
const auto hr = HRESULT_FROM_WIN32(::RegCreateKeyExW(
m_key, subKey, zero_reserved, null_class, get_options_flags(options), get_access_flags(access), null_security_attributes, hkey, &local_disposition));
if (disposition && SUCCEEDED(hr))
{
*disposition = (local_disposition == REG_CREATED_NEW_KEY) ? ::wil::reg::key_disposition::created_new
: ::wil::reg::key_disposition::opened_existing;
}
return err_policy::HResult(hr);
}

typename err_policy::result delete_tree(_In_opt_ PCWSTR sub_key) const
Expand Down
59 changes: 59 additions & 0 deletions tests/RegistryTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,65 @@ using ThrowingTypesToTest = std::tuple<DwordFns, GenericDwordFns, QwordFns, Gene
#endif // defined(WIL_ENABLE_EXCEPTIONS)
} // namespace

TEST_CASE("BasicRegistryTests::create_key_options_disposition", "[registry]")
{
const auto deleteHr = HRESULT_FROM_WIN32(::RegDeleteTreeW(HKEY_CURRENT_USER, testSubkey));
if (deleteHr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
REQUIRE_SUCCEEDED(deleteHr);
}

SECTION("create_unique_key_nothrow: disposition reports created vs opened")
{
wil::unique_hkey hkey;
auto disposition = wil::reg::key_disposition::opened_existing;
REQUIRE_SUCCEEDED(wil::reg::create_unique_key_nothrow(
HKEY_CURRENT_USER, testSubkey, hkey, wil::reg::key_access::readwrite, wil::reg::key_options::non_volatile, &disposition));
REQUIRE(disposition == wil::reg::key_disposition::created_new);

wil::unique_hkey hkey2;
disposition = wil::reg::key_disposition::created_new;
REQUIRE_SUCCEEDED(wil::reg::create_unique_key_nothrow(
HKEY_CURRENT_USER, testSubkey, hkey2, wil::reg::key_access::readwrite, wil::reg::key_options::non_volatile, &disposition));
REQUIRE(disposition == wil::reg::key_disposition::opened_existing);
}

SECTION("create_unique_key_nothrow: volatile key is usable")
{
wil::unique_hkey hkey;
auto disposition = wil::reg::key_disposition::opened_existing;
REQUIRE_SUCCEEDED(wil::reg::create_unique_key_nothrow(
HKEY_CURRENT_USER, testSubkey, hkey, wil::reg::key_access::readwrite, wil::reg::key_options::is_volatile, &disposition));
REQUIRE(disposition == wil::reg::key_disposition::created_new);

REQUIRE_SUCCEEDED(wil::reg::set_value_dword_nothrow(hkey.get(), dwordValueName, test_dword_two));
DWORD result{};
REQUIRE_SUCCEEDED(wil::reg::get_value_dword_nothrow(hkey.get(), dwordValueName, &result));
REQUIRE(result == test_dword_two);
}

SECTION("create_unique_key_nothrow: defaults remain backward compatible")
{
wil::unique_hkey hkey;
REQUIRE_SUCCEEDED(wil::reg::create_unique_key_nothrow(HKEY_CURRENT_USER, testSubkey, hkey, wil::reg::key_access::readwrite));
REQUIRE(hkey.get() != nullptr);
}

#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("create_unique_key: throwing variant with disposition")
{
auto disposition = wil::reg::key_disposition::opened_existing;
const wil::unique_hkey hkey{wil::reg::create_unique_key(
HKEY_CURRENT_USER, testSubkey, wil::reg::key_access::readwrite, wil::reg::key_options::non_volatile, &disposition)};
REQUIRE(disposition == wil::reg::key_disposition::created_new);

const wil::unique_hkey hkey2{wil::reg::create_unique_key(
HKEY_CURRENT_USER, testSubkey, wil::reg::key_access::readwrite, wil::reg::key_options::non_volatile, &disposition)};
REQUIRE(disposition == wil::reg::key_disposition::opened_existing);
}
#endif
}

TEMPLATE_LIST_TEST_CASE("BasicRegistryTests::simple types typed nothrow gets/sets", "[registry]", NoThrowTypesToTest)
{
const auto deleteHr = HRESULT_FROM_WIN32(::RegDeleteTreeW(HKEY_CURRENT_USER, testSubkey));
Expand Down
Loading