diff --git a/include/wil/registry.h b/include/wil/registry.h index 5aa89d01..71bd74cb 100644 --- a/include/wil/registry.h +++ b/include/wil/registry.h @@ -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; @@ -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; @@ -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; @@ -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; @@ -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); @@ -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); @@ -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); @@ -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); diff --git a/include/wil/registry_helpers.h b/include/wil/registry_helpers.h index f796864f..dd655aca 100644 --- a/include/wil/registry_helpers.h +++ b/include/wil/registry_helpers.h @@ -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(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 { @@ -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; } /** @@ -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; @@ -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 diff --git a/tests/RegistryTests.cpp b/tests/RegistryTests.cpp index f774e95c..f7634681 100644 --- a/tests/RegistryTests.cpp +++ b/tests/RegistryTests.cpp @@ -1328,6 +1328,49 @@ using ThrowingTypesToTest = std::tuple(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(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(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));