diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index cb21491ac01d9..d8405fe3ba39d 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1580,6 +1580,9 @@ FILE: ../../../flutter/shell/platform/windows/platform_handler_win32.h FILE: ../../../flutter/shell/platform/windows/platform_handler_winuwp.cc FILE: ../../../flutter/shell/platform/windows/platform_handler_winuwp.h FILE: ../../../flutter/shell/platform/windows/public/flutter_windows.h +FILE: ../../../flutter/shell/platform/windows/registry.cc +FILE: ../../../flutter/shell/platform/windows/registry.h +FILE: ../../../flutter/shell/platform/windows/registry_unittests.cc FILE: ../../../flutter/shell/platform/windows/string_conversion.cc FILE: ../../../flutter/shell/platform/windows/string_conversion.h FILE: ../../../flutter/shell/platform/windows/string_conversion_unittests.cc diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 9a98e3d814542..16ef73cc17016 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -171,6 +171,18 @@ source_set("flutter_windows_source") { ] } +# Windows registry utilities. +source_set("registry") { + sources = [ + "registry.cc", + "registry.h", + ] + + if (target_os == "winuwp") { + cflags = [ "/EHsc" ] + } +} + # String encoding conversion utilities. source_set("string_conversion") { sources = [ @@ -179,7 +191,6 @@ source_set("string_conversion") { ] if (target_os == "winuwp") { - configs += [ ":cppwinrt_defs" ] cflags = [ "/EHsc" ] } } @@ -222,6 +233,7 @@ executable("flutter_windows_unittests") { sources = [ # "flutter_project_bundle_unittests.cc", //TODO failing due to switches test failing. Blocked on https://github.com/flutter/flutter/issues/74153 # "flutter_windows_engine_unittests.cc", //TODO failing to send / receive platform message get plugins working first. Blocked on https://github.com/flutter/flutter/issues/74155 + "registry_unittests.cc", "string_conversion_unittests.cc", "system_utils_unittests.cc", "testing/engine_modifier.h", @@ -260,6 +272,7 @@ executable("flutter_windows_unittests") { ":flutter_windows_fixtures", ":flutter_windows_headers", ":flutter_windows_source", + ":registry", "//flutter/shell/platform/common:common_cpp", "//flutter/shell/platform/embedder:embedder_as_internal_library", "//flutter/shell/platform/embedder:embedder_test_utils", diff --git a/shell/platform/windows/registry.cc b/shell/platform/windows/registry.cc new file mode 100644 index 0000000000000..e9cae57120420 --- /dev/null +++ b/shell/platform/windows/registry.cc @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/windows/registry.h" + +#include +#include + +namespace flutter { + +RegistryKey::RegistryKey(HKEY key, REGSAM access) + : RegistryKey(key, L"", access) {} + +RegistryKey::RegistryKey(HKEY parent_key, + const std::wstring_view subkey, + REGSAM access) { + LSTATUS result = ::RegOpenKeyEx(parent_key, subkey.data(), 0, access, &key_); + if (result != ERROR_SUCCESS) { + key_ = nullptr; + } +} + +RegistryKey::RegistryKey(const RegistryKey& parent_key, + const std::wstring_view subkey, + REGSAM access) + : RegistryKey(parent_key.key_, subkey, access) {} + +RegistryKey::~RegistryKey() { + Close(); +} + +void RegistryKey::Close() { + if (IsValid()) { + ::RegCloseKey(key_); + key_ = nullptr; + } +} + +std::vector RegistryKey::GetSubKeyNames() const { + if (!IsValid()) { + return {}; + } + + // Get the count of subkeys, and maximum key size in wchar_t. + DWORD max_key_buf_size; + DWORD subkey_count; + LSTATUS result = ::RegQueryInfoKey( + key_, nullptr, nullptr, nullptr, &subkey_count, &max_key_buf_size, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + + // Collect all subkey names. + std::vector subkey_names; + for (int i = 0; i < subkey_count; ++i) { + DWORD key_buf_size = max_key_buf_size; + auto key_buf = std::make_unique(max_key_buf_size); + result = ::RegEnumKeyEx(key_, i, key_buf.get(), &key_buf_size, nullptr, + nullptr, nullptr, nullptr); + if (result == ERROR_SUCCESS) { + subkey_names.emplace_back(key_buf.get()); + } + } + return subkey_names; +} + +LONG RegistryKey::ReadValue(const std::wstring_view name, + std::wstring* out_value) const { + assert(out_value != nullptr); + + // Get the value size, in bytes. + DWORD value_size; + LSTATUS result = ::RegGetValueW(key_, L"", name.data(), RRF_RT_REG_SZ, + nullptr, nullptr, &value_size); + if (result != ERROR_SUCCESS) { + return result; + } + + auto value_buf = std::make_unique(value_size / sizeof(wchar_t)); + result = ::RegGetValueW(key_, L"", name.data(), RRF_RT_REG_SZ, nullptr, + value_buf.get(), &value_size); + if (result == ERROR_SUCCESS) { + *out_value = value_buf.get(); + } + return result; +} + +} // namespace flutter diff --git a/shell/platform/windows/registry.h b/shell/platform/windows/registry.h new file mode 100644 index 0000000000000..1bd71b325a049 --- /dev/null +++ b/shell/platform/windows/registry.h @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_REGISTRY_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_REGISTRY_H_ + +#include +#include + +#include +#include + +namespace flutter { + +// A Windows Registry key. +// +// The Windows registry is structured as a hierarchy of named keys, each of +// which may contain a set of named values of various datatypes. RegistryKey +// objects own the underlying HKEY handle and ensure cleanup at or prior to +// destruction. +class RegistryKey { + public: + // Opens the specified key. + RegistryKey(HKEY key, REGSAM access); + + // Opens a key relative to the specified parent key. + RegistryKey(HKEY parent_key, const std::wstring_view subkey, REGSAM access); + + // Opens a key relative to the specified parent key. + RegistryKey(const RegistryKey& parent_key, + const std::wstring_view subkey, + REGSAM access); + + ~RegistryKey(); + + // Prevent copying. + RegistryKey(const RegistryKey& other) = delete; + RegistryKey& operator=(const RegistryKey& other) = delete; + + // Closes the registry key and releases resources. + void Close(); + + // Returns true if the key is valid. + bool IsValid() const { return key_ != nullptr; } + + // Returns a list of all direct subkey names for this key. + std::vector GetSubKeyNames() const; + + // Reads a string value from the key. + // + // Returns ERROR_SUCCESS on success. + LONG ReadValue(const std::wstring_view name, std::wstring* out_value) const; + + private: + HKEY key_ = nullptr; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_REGISTRY_H_ diff --git a/shell/platform/windows/registry_unittests.cc b/shell/platform/windows/registry_unittests.cc new file mode 100644 index 0000000000000..21a6467dc2753 --- /dev/null +++ b/shell/platform/windows/registry_unittests.cc @@ -0,0 +1,50 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/windows/registry.h" + +#include +#include + +#include + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +// TODO(cbracken): write registry values to be tested, then cleanup. +// https://github.com/flutter/flutter/issues/82095 + +// Verify that a registry key is marked invalid after close. +TEST(RegistryKey, CloseInvalidates) { + RegistryKey key(HKEY_USERS, L".DEFAULT\\Environment", KEY_READ); + ASSERT_TRUE(key.IsValid()); + key.Close(); + ASSERT_FALSE(key.IsValid()); +} + +// Verify that subkeys can be read. +TEST(RegistryKey, GetSubKeyNames) { + RegistryKey key(HKEY_USERS, L".DEFAULT", KEY_READ); + ASSERT_TRUE(key.IsValid()); + + std::vector subkey_names = key.GetSubKeyNames(); + EXPECT_GE(subkey_names.size(), 1); + EXPECT_TRUE(std::find(subkey_names.begin(), subkey_names.end(), + L"Environment") != subkey_names.end()); +} + +// Verify that values can be read. +TEST(RegistryKey, GetValue) { + RegistryKey key(HKEY_USERS, L".DEFAULT\\Environment", KEY_READ); + ASSERT_TRUE(key.IsValid()); + + std::wstring path; + ASSERT_EQ(key.ReadValue(L"Path", &path), ERROR_SUCCESS); + EXPECT_FALSE(path.empty()); +} + +} // namespace testing +} // namespace flutter