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
16 changes: 8 additions & 8 deletions include/wil/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace reg
* @return A wil::unique_hkey containing the resulting opened HKEY
* @exception std::exception (including wil::ResultException) will be thrown on all failures
*/
inline ::wil::unique_hkey open_unique_key(HKEY key, _In_opt_ PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read)
inline ::wil::unique_hkey open_unique_key(HKEY key, _In_opt_ PCWSTR subKey, ::wil::reg::access_mask access = ::wil::reg::key_access::read)
{
const reg_view_details::reg_view regview{key};
::wil::unique_hkey return_value;
Expand All @@ -58,7 +58,7 @@ namespace reg
* @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::access_mask access = ::wil::reg::key_access::read)
{
const reg_view_details::reg_view regview{key};
::wil::unique_hkey return_value;
Expand All @@ -76,7 +76,7 @@ namespace reg
* @return A wil::shared_hkey containing the resulting opened HKEY
* @exception std::exception (including wil::ResultException) will be thrown on all failures
*/
inline ::wil::shared_hkey open_shared_key(HKEY key, _In_opt_ PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read)
inline ::wil::shared_hkey open_shared_key(HKEY key, _In_opt_ PCWSTR subKey, ::wil::reg::access_mask access = ::wil::reg::key_access::read)
{
const reg_view_details::reg_view regview{key};
::wil::shared_hkey return_value;
Expand All @@ -93,7 +93,7 @@ namespace reg
* @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::access_mask access = ::wil::reg::key_access::read)
{
const reg_view_details::reg_view regview{key};
::wil::shared_hkey return_value;
Expand All @@ -113,7 +113,7 @@ namespace reg
* @return HRESULT error code indicating success or failure (does not throw C++ exceptions)
*/
inline HRESULT open_unique_key_nothrow(
HKEY key, _In_opt_ PCWSTR subKey, ::wil::unique_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT
HKEY key, _In_opt_ PCWSTR subKey, ::wil::unique_hkey& hkey, ::wil::reg::access_mask access = ::wil::reg::key_access::read) WI_NOEXCEPT
{
const reg_view_details::reg_view_nothrow regview{key};
return regview.open_key(subKey, hkey.put(), access);
Expand All @@ -129,7 +129,7 @@ namespace reg
* @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::access_mask access = ::wil::reg::key_access::read) WI_NOEXCEPT
{
const reg_view_details::reg_view_nothrow regview{key};
return regview.create_key(subKey, hkey.put(), access);
Expand All @@ -146,7 +146,7 @@ namespace reg
* @return HRESULT error code indicating success or failure (does not throw C++ exceptions)
*/
inline HRESULT open_shared_key_nothrow(
HKEY key, _In_opt_ PCWSTR subKey, ::wil::shared_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT
HKEY key, _In_opt_ PCWSTR subKey, ::wil::shared_hkey& hkey, ::wil::reg::access_mask access = ::wil::reg::key_access::read) WI_NOEXCEPT
{
const reg_view_details::reg_view_nothrow regview{key};
return regview.open_key(subKey, hkey.put(), access);
Expand All @@ -162,7 +162,7 @@ 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::access_mask access = ::wil::reg::key_access::read) WI_NOEXCEPT
{
const reg_view_details::reg_view_nothrow regview{key};
return regview.create_key(subKey, hkey.put(), access);
Expand Down
65 changes: 43 additions & 22 deletions include/wil/registry_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,44 @@ namespace reg
readwrite64,
};

// Accepts either a named wil::reg::key_access value or a raw REGSAM access mask, allowing the registry helpers to be
// called with arbitrary access rights (for example KEY_READ | KEY_WOW64_64KEY) while keeping the convenience of the
// named key_access values. Both conversions are implicit, so callers can pass either form directly.
struct access_mask
{
DWORD value;

constexpr access_mask(::wil::reg::key_access access) WI_NOEXCEPT : value(map_key_access(access))
{
}

constexpr access_mask(REGSAM mask) WI_NOEXCEPT : value(static_cast<DWORD>(mask))
{
}

private:
static constexpr DWORD map_key_access(::wil::reg::key_access access) WI_NOEXCEPT
{
switch (access)
{
case ::wil::reg::key_access::read:
return KEY_READ;
case ::wil::reg::key_access::write:
return KEY_WRITE;
case ::wil::reg::key_access::readwrite:
return KEY_ALL_ACCESS;
case ::wil::reg::key_access::read64:
return KEY_READ | KEY_WOW64_64KEY;
case ::wil::reg::key_access::write64:
return KEY_WRITE | KEY_WOW64_64KEY;
case ::wil::reg::key_access::readwrite64:
return KEY_ALL_ACCESS | KEY_WOW64_64KEY;
}
FAIL_FAST();
RESULT_NORETURN_RESULT(0);
}
};

/// @cond
namespace reg_view_details
{
Expand Down Expand Up @@ -111,23 +149,7 @@ namespace reg

constexpr DWORD get_access_flags(key_access access) WI_NOEXCEPT
{
switch (access)
{
case key_access::read:
return KEY_READ;
case key_access::write:
return KEY_WRITE;
case key_access::readwrite:
return KEY_ALL_ACCESS;
case key_access::read64:
return KEY_READ | KEY_WOW64_64KEY;
case key_access::write64:
return KEY_WRITE | KEY_WOW64_64KEY;
case key_access::readwrite64:
return KEY_ALL_ACCESS | KEY_WOW64_64KEY;
}
FAIL_FAST();
RESULT_NORETURN_RESULT(0);
return ::wil::reg::access_mask{access}.value;
}

/**
Expand Down Expand Up @@ -1105,14 +1127,13 @@ namespace reg
reg_view_t& operator=(reg_view_t&&) = delete;

typename err_policy::result open_key(
_In_opt_ PCWSTR subKey, _Out_ HKEY* hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) const
_In_opt_ PCWSTR subKey, _Out_ HKEY* hkey, ::wil::reg::access_mask access = ::wil::reg::key_access::read) const
{
constexpr DWORD zero_options{0};
return err_policy::HResult(
HRESULT_FROM_WIN32(::RegOpenKeyExW(m_key, subKey, zero_options, get_access_flags(access), hkey)));
return err_policy::HResult(HRESULT_FROM_WIN32(::RegOpenKeyExW(m_key, subKey, zero_options, access.value, 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::access_mask access = ::wil::reg::key_access::read) const
{
*hkey = nullptr;

Expand All @@ -1122,7 +1143,7 @@ namespace reg
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)));
m_key, subKey, zero_reserved, null_class, zero_options, access.value, null_security_attributes, hkey, &disposition)));
}

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

TEST_CASE("BasicRegistryTests::raw_regsam_access", "[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/open with a raw REGSAM mask")
{
// create with an explicit REGSAM (not expressible via key_access)
wil::unique_hkey hkey;
REQUIRE_SUCCEEDED(wil::reg::create_unique_key_nothrow(
HKEY_CURRENT_USER, testSubkey, hkey, static_cast<REGSAM>(KEY_QUERY_VALUE | KEY_SET_VALUE)));
REQUIRE_SUCCEEDED(wil::reg::set_value_dword_nothrow(hkey.get(), dwordValueName, test_dword_two));

// open with KEY_QUERY_VALUE only - reading works, writing is denied
wil::unique_hkey readonly;
REQUIRE_SUCCEEDED(
wil::reg::open_unique_key_nothrow(HKEY_CURRENT_USER, testSubkey, readonly, static_cast<REGSAM>(KEY_QUERY_VALUE)));
DWORD result{};
REQUIRE_SUCCEEDED(wil::reg::get_value_dword_nothrow(readonly.get(), dwordValueName, &result));
REQUIRE(result == test_dword_two);
REQUIRE(wil::reg::set_value_dword_nothrow(readonly.get(), dwordValueName, test_dword_three) == E_ACCESSDENIED);
}

SECTION("named key_access still works alongside REGSAM overload")
{
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("throwing open with a raw REGSAM mask")
{
wil::reg::set_value_dword(HKEY_CURRENT_USER, testSubkey, dwordValueName, test_dword_two);
const wil::unique_hkey readonly{wil::reg::open_unique_key(HKEY_CURRENT_USER, testSubkey, static_cast<REGSAM>(KEY_QUERY_VALUE))};
REQUIRE(wil::reg::get_value_dword(readonly.get(), dwordValueName) == test_dword_two);
}
#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