diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs index e56be7273d..0e41d098cf 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/PowerPreviewSettings.cs @@ -11,7 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Lib { public const string ModuleName = "File Explorer"; - [JsonPropertyName("Properties")] + [JsonPropertyName("properties")] public PowerPreviewProperties Properties { get; set; } public PowerPreviewSettings() diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs index 0689bc9ca3..fcb50695e9 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Lib/ViewModels/PowerPreviewViewModel.cs @@ -19,11 +19,16 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels private string _settingsConfigFileFolder = string.Empty; - public PowerPreviewViewModel(ISettingsRepository moduleSettingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") + private GeneralSettings GeneralSettingsConfig { get; set; } + + public PowerPreviewViewModel(ISettingsRepository moduleSettingsRepository, ISettingsRepository generalSettingsRepository, Func ipcMSGCallBackFunc, string configFileSubfolder = "") { // Update Settings file folder: _settingsConfigFileFolder = configFileSubfolder; + // To obtain the general Settings configurations of PowerToys + GeneralSettingsConfig = generalSettingsRepository.SettingsConfig; + // To obtain the PowerPreview settings if it exists. // If the file does not exist, to create a new one and return the default settings configurations. Settings = moduleSettingsRepository.SettingsConfig; @@ -99,6 +104,14 @@ namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels return _settingsConfigFileFolder + "\\" + ModuleName; } + public bool IsElevated + { + get + { + return GeneralSettingsConfig.IsElevated; + } + } + private void RaisePropertyChanged([CallerMemberName] string propertyName = null) { // Notify UI of property change diff --git a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs index 85505e6ce9..4175bff6c0 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs @@ -20,10 +20,13 @@ namespace ViewModelTests private Mock mockPowerPreviewSettingsUtils; + private Mock mockGeneralSettingsUtils; + [TestInitialize] public void SetUpStubSettingUtils() { mockPowerPreviewSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); + mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils(); } /// @@ -42,11 +45,17 @@ namespace ViewModelTests PowerPreviewSettings originalSettings = mockSettingsUtils.GetSettings(PowerPreviewSettings.ModuleName); var repository = new BackCompatTestProperties.MockSettingsRepository(mockSettingsUtils); + var mockGeneralIOProvider = BackCompatTestProperties.GetGeneralSettingsIOProvider(version); + var mockGeneralSettingsUtils = new SettingsUtils(mockGeneralIOProvider.Object); + GeneralSettings originalGeneralSettings = mockGeneralSettingsUtils.GetSettings(); + var generalSettingsRepository = new BackCompatTestProperties.MockSettingsRepository(mockGeneralSettingsUtils); + // Initialise View Model with test Config files Func SendMockIPCConfigMSG = msg => { return 0; }; - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(repository, SendMockIPCConfigMSG); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(repository, generalSettingsRepository, SendMockIPCConfigMSG); // Verifiy that the old settings persisted + Assert.AreEqual(originalGeneralSettings.IsElevated, viewModel.IsElevated); Assert.AreEqual(originalSettings.Properties.EnableMdPreview, viewModel.MDRenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableSvgPreview, viewModel.SVGRenderIsEnabled); Assert.AreEqual(originalSettings.Properties.EnableSvgThumbnail, viewModel.SVGThumbnailIsEnabled); @@ -69,7 +78,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SendMockIPCConfigMSG, PowerPreviewSettings.ModuleName); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, PowerPreviewSettings.ModuleName); // act viewModel.SVGRenderIsEnabled = true; @@ -87,7 +96,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SendMockIPCConfigMSG, PowerPreviewSettings.ModuleName); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, PowerPreviewSettings.ModuleName); // act viewModel.SVGThumbnailIsEnabled = true; @@ -105,7 +114,7 @@ namespace ViewModelTests }; // arrange - PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SendMockIPCConfigMSG, PowerPreviewSettings.ModuleName); + PowerPreviewViewModel viewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(mockPowerPreviewSettingsUtils.Object), SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), SendMockIPCConfigMSG, PowerPreviewSettings.ModuleName); ; // act viewModel.MDRenderIsEnabled = true; diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw index 45e1124cef..380e19b3c3 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw +++ b/src/core/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw @@ -759,4 +759,13 @@ Preview Pane - \ No newline at end of file + + You need to run as administrator to modify these settings + + + The settings on this page affect all users on the system + + + A reboot may be required for changes to these settings to take effect + + diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Styles/_Thickness.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Styles/_Thickness.xaml index 05a28978f4..e1f3c28650 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Styles/_Thickness.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Styles/_Thickness.xaml @@ -16,13 +16,14 @@ 12, 0, 0, 0 0, 12, 0, 0 0, 12, 0, 12 + 0, 0, 0, 12 12, 0, 12, 0 0, 0, 12, 0 12, 0, 12, 12 12, 12, 12, 12 -10, 12, 0, 0 - + 8, 0, 0, 0 0, 8, 0, 0 diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml index 7fb8faf05b..8695570958 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/PowerPreviewPage.xaml @@ -4,9 +4,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + + + + @@ -43,25 +48,43 @@ + + + + + Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsElevated, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/> + IsOn="{Binding Mode=TwoWay, Path=SVGRenderIsEnabled}" + IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"/> - + IsOn="{Binding Mode=TwoWay, Path=MDRenderIsEnabled}" + IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"/> + - + Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsElevated, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/> + + + + IsOn="{Binding Mode=TwoWay, Path=SVGThumbnailIsEnabled}" + IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"/> .GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); + ViewModel = new PowerPreviewViewModel(SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; } } diff --git a/src/modules/previewpane/powerpreview/Resources.resx b/src/modules/previewpane/powerpreview/Resources.resx index 070fc17cb7..070462134c 100644 --- a/src/modules/previewpane/powerpreview/Resources.resx +++ b/src/modules/previewpane/powerpreview/Resources.resx @@ -156,4 +156,10 @@ Render SVG images + + Some of the File Explorer modules could not be registered/unregistered as per your settings. Please restart PowerToys as admin for the changes to take place. + + + Failed to modify File Explorer modules + \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/powerpreview.cpp b/src/modules/previewpane/powerpreview/powerpreview.cpp index 61d2a6b072..06c27fb217 100644 --- a/src/modules/previewpane/powerpreview/powerpreview.cpp +++ b/src/modules/previewpane/powerpreview/powerpreview.cpp @@ -5,39 +5,53 @@ #include "trace.h" #include "settings.h" #include "Generated Files/resource.h" +#include + +// Constructor +PowerPreviewModule::PowerPreviewModule() : + m_moduleName(GET_RESOURCE_STRING(IDS_MODULE_NAME)), + m_fileExplorerModules( + { // SVG Preview Handler settings object. + new PreviewHandlerSettings( + true, + L"svg-previewer-toggle-setting", + GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION), + L"{ddee2b8a-6807-48a6-bb20-2338174ff779}", + L"Svg Preview Handler", + new RegistryWrapper()), + + // MarkDown Preview Handler Settings Object. + new PreviewHandlerSettings( + true, + L"md-previewer-toggle-setting", + GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION), + L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}", + L"Markdown Preview Handler", + new RegistryWrapper()), + //SVG Thumbnail Provider settings object. + new ThumbnailProviderSettings( + true, + L"svg-thumbnail-toggle-setting", + GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION), + L"{36B27788-A8BB-4698-A756-DF9F11F64F84}", + L"Svg Thumbnail Provider", + new RegistryWrapper(), + L".svg\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}") }) +{ + // Initialize the toggle states for each module + init_settings(); + + // If the user is on the new settings interface, File Explorer might be disabled if they updated from old to new settings, so initialize the registry state in the constructor as PowerPreviewModule::enable/disable will not be called on startup + if (UseNewSettings()) + { + update_registry_to_match_toggles(); + } +} // Destroy the powertoy and free memory. void PowerPreviewModule::destroy() { Trace::Destroyed(); - for (auto previewHandler : this->m_previewHandlers) - { - if (previewHandler != NULL) - { - // Disable all the active preview handlers. - if (this->m_enabled && previewHandler->GetToggleSettingState()) - { - previewHandler->DisablePreview(); - } - - delete previewHandler; - } - } - - for (auto thumbnailProvider : this->m_thumbnailProviders) - { - if (thumbnailProvider != NULL) - { - // Disable all the active thumbnail providers. - if (this->m_enabled && thumbnailProvider->GetToggleSettingState()) - { - thumbnailProvider->DisablePreview(); - } - - delete thumbnailProvider; - } - } - delete this; } @@ -66,20 +80,12 @@ bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_siz GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC), GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT)); - for (auto previewHandler : this->m_previewHandlers) + for (auto fileExplorerModule : this->m_fileExplorerModules) { settings.add_bool_toggle( - previewHandler->GetToggleSettingName(), - previewHandler->GetToggleSettingDescription(), - previewHandler->GetToggleSettingState()); - } - - for (auto thumbnailProvider : this->m_thumbnailProviders) - { - settings.add_bool_toggle( - thumbnailProvider->GetToggleSettingName(), - thumbnailProvider->GetToggleSettingDescription(), - thumbnailProvider->GetToggleSettingState()); + fileExplorerModule->GetToggleSettingName(), + fileExplorerModule->GetToggleSettingDescription(), + fileExplorerModule->GetToggleSettingState()); } return settings.serialize_to_buffer(buffer, buffer_size); @@ -92,14 +98,17 @@ void PowerPreviewModule::set_config(const wchar_t* config) { PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::from_json_string(config); - for (auto previewHandler : this->m_previewHandlers) + bool updateSuccess = true; + bool isElevated = is_process_elevated(false); + for (auto fileExplorerModule : this->m_fileExplorerModules) { - previewHandler->UpdateState(settings, this->m_enabled); + // If the user is using the new settings interface, as it does not have a toggle to modify enabled consider File Explorer to always be enabled + updateSuccess = updateSuccess && fileExplorerModule->UpdateState(settings, this->m_enabled || UseNewSettings(), isElevated); } - for (auto thumbnailProvider : this->m_thumbnailProviders) + if (!updateSuccess) { - thumbnailProvider->UpdateState(settings, this->m_enabled); + show_update_warning_message(); } settings.save_to_settings_file(); @@ -113,30 +122,10 @@ void PowerPreviewModule::set_config(const wchar_t* config) // Enable preview handlers. void PowerPreviewModule::enable() { - for (auto previewHandler : this->m_previewHandlers) + // Should only be done for old settings as it is already done for new settings in the constructor. + if (!UseNewSettings()) { - if (previewHandler->GetToggleSettingState()) - { - // Enable all the previews with initial state set as true. - previewHandler->EnablePreview(); - } - else - { - previewHandler->DisablePreview(); - } - } - - for (auto thumbnailProvider : this->m_thumbnailProviders) - { - if (thumbnailProvider->GetToggleSettingState()) - { - // Enable all the thumbnail providers with initial state set as true. - thumbnailProvider->EnableThumbnailProvider(); - } - else - { - thumbnailProvider->DisableThumbnailProvider(); - } + update_registry_to_match_toggles(); } if (!this->m_enabled) @@ -150,15 +139,12 @@ void PowerPreviewModule::enable() // Disable active preview handlers. void PowerPreviewModule::disable() { - for (auto previewHandler : this->m_previewHandlers) - { - previewHandler->DisablePreview(); - } - - for (auto thumbnailProvider : this->m_thumbnailProviders) - { - thumbnailProvider->DisableThumbnailProvider(); - } + elevation_check_wrapper([this]() { + for (auto fileExplorerModule : this->m_fileExplorerModules) + { + fileExplorerModule->Disable(); + } + }); if (this->m_enabled) { @@ -184,18 +170,81 @@ void PowerPreviewModule::init_settings() PowerToysSettings::PowerToyValues::load_from_settings_file(PowerPreviewModule::get_name()); // Load settings states. - for (auto previewHandler : this->m_previewHandlers) + for (auto fileExplorerModule : this->m_fileExplorerModules) { - previewHandler->LoadState(settings); - } - - for (auto thumbnailProvider : this->m_thumbnailProviders) - { - thumbnailProvider->LoadState(settings); + fileExplorerModule->LoadState(settings); } } catch (std::exception const& e) { Trace::InitSetErrorLoadingFile(e.what()); } -} \ No newline at end of file +} + +// Function to check if the registry states need to be updated +bool PowerPreviewModule::is_registry_update_required() +{ + for (auto fileExplorerModule : this->m_fileExplorerModules) + { + if (fileExplorerModule->GetToggleSettingState() != fileExplorerModule->CheckRegistryState()) + { + return true; + } + } + + return false; +} + +// Function to warn the user that PowerToys needs to run as administrator for changes to take effect +void PowerPreviewModule::show_update_warning_message() +{ + // Show warning message if update is required + MessageBoxW(NULL, + GET_RESOURCE_STRING(IDS_FILEEXPLORER_ADMIN_RESTART_WARNING_DESCRIPTION).c_str(), + GET_RESOURCE_STRING(IDS_FILEEXPLORER_ADMIN_RESTART_WARNING_TITLE).c_str(), + MB_OK | MB_ICONWARNING); +} + +// Function that checks if a registry method is required and if so checks if the process is elevated and accordingly executes the method or shows a warning +void PowerPreviewModule::registry_and_elevation_check_wrapper(std::function method) +{ + // Check if a registry update is required + if (is_registry_update_required()) + { + elevation_check_wrapper(method); + } +} + +// Function that checks if the process is elevated and accordingly executes the method or shows a warning +void PowerPreviewModule::elevation_check_wrapper(std::function method) +{ + // Check if the process is elevated in order to have permissions to modify HKLM registry + if (is_process_elevated(false)) + { + method(); + } + // Show a warning if it doesn't have permissions + else + { + show_update_warning_message(); + } +} + +// Function that updates the registry state to match the toggle states +void PowerPreviewModule::update_registry_to_match_toggles() +{ + registry_and_elevation_check_wrapper([this]() { + for (auto fileExplorerModule : this->m_fileExplorerModules) + { + if (fileExplorerModule->GetToggleSettingState()) + { + // Enable all the modules with initial state set as true. + fileExplorerModule->Enable(); + } + else + { + fileExplorerModule->Disable(); + } + } + }); +} diff --git a/src/modules/previewpane/powerpreview/powerpreview.h b/src/modules/previewpane/powerpreview/powerpreview.h index e0920712bc..92d7574451 100644 --- a/src/modules/previewpane/powerpreview/powerpreview.h +++ b/src/modules/previewpane/powerpreview/powerpreview.h @@ -3,6 +3,8 @@ #include #include "trace.h" #include "settings.h" +#include "thumbnail_provider.h" +#include "preview_handler.h" #include "registry_wrapper.h" using namespace PowerPreviewSettings; @@ -16,42 +18,25 @@ private: // The PowerToy state. bool m_enabled = false; std::wstring m_moduleName; - std::vector m_previewHandlers; - std::vector m_thumbnailProviders; + std::vector m_fileExplorerModules; + + // Function to check if the registry states need to be updated + bool is_registry_update_required(); + + // Function to warn the user that PowerToys needs to run as administrator for changes to take effect + void show_update_warning_message(); + + // Function that checks if a registry method is required and if so checks if the process is elevated and accordingly executes the method or shows a warning + void registry_and_elevation_check_wrapper(std::function method); + + // Function that checks if the process is elevated and accordingly executes the method or shows a warning + void elevation_check_wrapper(std::function method); + + // Function that updates the registry state to match the toggle states + void update_registry_to_match_toggles(); public: - PowerPreviewModule() : - m_moduleName(GET_RESOURCE_STRING(IDS_MODULE_NAME)), - m_previewHandlers( - { // SVG Preview Handler settings object. - new FileExplorerPreviewSettings( - true, - L"svg-previewer-toggle-setting", - GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION), - L"{ddee2b8a-6807-48a6-bb20-2338174ff779}", - L"SVG Preview Handler", - new RegistryWrapper()), - - // MarkDown Preview Handler Settings Object. - new FileExplorerPreviewSettings( - true, - L"md-previewer-toggle-setting", - GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION), - L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}", - L"Markdown Preview Handler", - new RegistryWrapper()) }), - m_thumbnailProviders( - { // TODO: MOVE THIS SVG Thumbnail Provider settings object. - new FileExplorerPreviewSettings( - true, - L"svg-thumbnail-toggle-setting", - GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION), - L"{36B27788-A8BB-4698-A756-DF9F11F64F84}", - L"SVG Thumbnail Provider", - new RegistryWrapper()) }) - { - init_settings(); - }; + PowerPreviewModule(); virtual void destroy(); virtual const wchar_t* get_name(); diff --git a/src/modules/previewpane/powerpreview/powerpreview.vcxproj b/src/modules/previewpane/powerpreview/powerpreview.vcxproj index ce130a6fb3..a49a3825af 100644 --- a/src/modules/previewpane/powerpreview/powerpreview.vcxproj +++ b/src/modules/previewpane/powerpreview/powerpreview.vcxproj @@ -85,7 +85,7 @@ true true NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - ..\..\..\common\Telemetry;..\..\..\common;..\..\common\inc;..\common\Telemetry;..\;..\..\;..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories) + ..\;..\..\..\common;..\..\..\common\telemetry;..\..\;..\..\..\;..\..\..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories) MultiThreaded stdcpplatest @@ -109,11 +109,13 @@ + + @@ -123,8 +125,10 @@ + + diff --git a/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters b/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters index 584f6254d1..d1bff4109d 100644 --- a/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters +++ b/src/modules/previewpane/powerpreview/powerpreview.vcxproj.filters @@ -19,6 +19,12 @@ Source Files + + Source Files + + + Source Files + @@ -43,6 +49,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/src/modules/previewpane/powerpreview/preview_handler.cpp b/src/modules/previewpane/powerpreview/preview_handler.cpp new file mode 100644 index 0000000000..04f98f0736 --- /dev/null +++ b/src/modules/previewpane/powerpreview/preview_handler.cpp @@ -0,0 +1,53 @@ +#include "pch.h" +#include "preview_handler.h" + +namespace PowerPreviewSettings +{ + const LPCWSTR PreviewHandlerSettings::preview_handlers_subkey = L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers"; + + // Function to enable the preview handler in registry + LONG PreviewHandlerSettings::Enable() + { + // Add registry value to enable preview. + return this->m_registryWrapper->SetRegistryValue(HKEY_LOCAL_MACHINE, preview_handlers_subkey, this->GetCLSID(), REG_SZ, (LPBYTE)this->GetRegistryValueData().c_str(), (DWORD)(this->GetRegistryValueData().length() * sizeof(wchar_t))); + } + + // Function to disable the preview handler in registry + LONG PreviewHandlerSettings::Disable() + { + // Delete the registry key to disable preview. + return this->m_registryWrapper->DeleteRegistryValue(HKEY_LOCAL_MACHINE, preview_handlers_subkey, this->GetCLSID()); + } + + // Function to check if the preview handler is enabled in registry + bool PreviewHandlerSettings::CheckRegistryState() + { + DWORD dataType; + DWORD byteCount = 255; + wchar_t regValue[255] = { 0 }; + + LONG errorCode = this->m_registryWrapper->GetRegistryValue(HKEY_LOCAL_MACHINE, preview_handlers_subkey, this->GetCLSID(), &dataType, regValue, &byteCount); + + // Registry value was found + if (errorCode == ERROR_SUCCESS) + { + // Check if the value type is string + if (dataType == REG_SZ) + { + // Check if the current registry value matches the expected value + if (wcscmp(regValue, this->GetRegistryValueData().c_str()) == 0) + { + return true; + } + } + } + + return false; + } + + // Function to retrieve the registry subkey + LPCWSTR PreviewHandlerSettings::GetSubkey() + { + return preview_handlers_subkey; + } +} diff --git a/src/modules/previewpane/powerpreview/preview_handler.h b/src/modules/previewpane/powerpreview/preview_handler.h new file mode 100644 index 0000000000..c2d79d9b84 --- /dev/null +++ b/src/modules/previewpane/powerpreview/preview_handler.h @@ -0,0 +1,31 @@ +#pragma once +#include "settings.h" + +namespace PowerPreviewSettings +{ + class PreviewHandlerSettings : + public FileExplorerPreviewSettings + { + private: + // Relative(HKLM/HKCU) sub key path of Preview Handlers list in registry. Registry key for Preview Handlers is generally HKLM\Software\Microsoft\Windows\CurrentVersion\PreviewHandlers, and the value name with CLSID of the handler in it is set to the name of the handler + static const LPCWSTR preview_handlers_subkey; + + public: + PreviewHandlerSettings(bool toggleSettingEnabled, const std::wstring& toggleSettingName, const std::wstring& toggleSettingDescription, LPCWSTR clsid, const std::wstring& registryValueData, RegistryWrapperIface* registryWrapper) : + FileExplorerPreviewSettings(toggleSettingEnabled, toggleSettingName, toggleSettingDescription, clsid, registryValueData, registryWrapper) + { + } + + // Function to enable the preview handler in registry + LONG Enable(); + + // Function to disable the preview handler in registry + LONG Disable(); + + // Function to check if the preview handler is enabled in registry + bool CheckRegistryState(); + + // Function to retrieve the registry subkey + static LPCWSTR GetSubkey(); + }; +} diff --git a/src/modules/previewpane/powerpreview/registry_wrapper.cpp b/src/modules/previewpane/powerpreview/registry_wrapper.cpp index 18ea8dd800..0a247f5f6c 100644 --- a/src/modules/previewpane/powerpreview/registry_wrapper.cpp +++ b/src/modules/previewpane/powerpreview/registry_wrapper.cpp @@ -23,7 +23,7 @@ namespace PowerPreviewSettings return err; } - LONG RegistryWrapper::GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) + LONG RegistryWrapper::GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) { HKEY OpenResult; LONG err = RegOpenKeyExW(keyScope, subKey, 0, KEY_READ, &OpenResult); @@ -61,4 +61,3 @@ namespace PowerPreviewSettings return err; } } - \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/registry_wrapper.h b/src/modules/previewpane/powerpreview/registry_wrapper.h index 906040bce8..40fcb8350e 100644 --- a/src/modules/previewpane/powerpreview/registry_wrapper.h +++ b/src/modules/previewpane/powerpreview/registry_wrapper.h @@ -8,6 +8,6 @@ namespace PowerPreviewSettings public: virtual LONG SetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, CONST BYTE* data, DWORD cbData); virtual LONG DeleteRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName); - virtual LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData); + virtual LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData); }; } \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/registry_wrapper_interface.h b/src/modules/previewpane/powerpreview/registry_wrapper_interface.h index 2b1485271d..767d0e8080 100644 --- a/src/modules/previewpane/powerpreview/registry_wrapper_interface.h +++ b/src/modules/previewpane/powerpreview/registry_wrapper_interface.h @@ -10,5 +10,5 @@ public: virtual LONG DeleteRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName) = 0; // Reads a registry value. - virtual LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) = 0; + virtual LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) = 0; }; \ No newline at end of file diff --git a/src/modules/previewpane/powerpreview/settings.cpp b/src/modules/previewpane/powerpreview/settings.cpp index 93ffd501a2..aa017f3492 100644 --- a/src/modules/previewpane/powerpreview/settings.cpp +++ b/src/modules/previewpane/powerpreview/settings.cpp @@ -11,12 +11,6 @@ namespace PowerPreviewSettings { extern "C" IMAGE_DOS_HEADER __ImageBase; - // Relative(HKLM/HKCU) sub key path of Preview Handlers list in registry. - static LPCWSTR preview_handlers_subkey = L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers"; - - // Relative HKCR sub key path of SVG thumbnail provider in registry - static LPCWSTR svg_thumbnail_provider_subkey = L".svg\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}"; - // Base Settings Class Implementation FileExplorerPreviewSettings::FileExplorerPreviewSettings(bool toggleSettingEnabled, const std::wstring& toggleSettingName, const std::wstring& toggleSettingDescription, LPCWSTR clsid, const std::wstring& registryValueData, RegistryWrapperIface* registryWrapper) : m_toggleSettingEnabled(toggleSettingEnabled), @@ -28,7 +22,7 @@ namespace PowerPreviewSettings { } - FileExplorerPreviewSettings::~FileExplorerPreviewSettings() + FileExplorerPreviewSettings::~FileExplorerPreviewSettings() { if (this->m_registryWrapper != NULL) { @@ -66,7 +60,7 @@ namespace PowerPreviewSettings return this->m_registryValueData; } - // Load initial state of the Preview Handler. If no inital state present initialize setting with default value. + // Load initial state of the file explorer module. If no inital state present initialize setting with default value. void FileExplorerPreviewSettings::LoadState(PowerToysSettings::PowerToyValues& settings) { auto toggle = settings.get_bool_value(this->GetToggleSettingName()); @@ -77,8 +71,8 @@ namespace PowerPreviewSettings } } - // Manage change in state of Preview Handler settings. - void FileExplorerPreviewSettings::UpdateState(PowerToysSettings::PowerToyValues& settings, bool enabled) + // Manage change in state of file explorer module settings. + bool FileExplorerPreviewSettings::UpdateState(PowerToysSettings::PowerToyValues& settings, bool enabled, bool isElevated) { auto toggle = settings.get_bool_value(this->GetToggleSettingName()); if (toggle) @@ -89,27 +83,40 @@ namespace PowerPreviewSettings { this->UpdateToggleSettingState(newState); - // If global setting is enable. Add or remove the preview handler otherwise just change the UI and save the updated config. + // If global setting is enable. Add or remove the file explorer module otherwise just change the UI and save the updated config. if (enabled) { - LONG err; - if (lastState) + // Check if the registry state does not match the new state, registry needs to be modified + if (this->CheckRegistryState() != newState) { - err = this->DisablePreview(); - } - else - { - err = this->EnablePreview(); - } + if (isElevated) + { + LONG err; + if (lastState) + { + err = this->Disable(); + } + else + { + err = this->Enable(); + } - if (err == ERROR_SUCCESS) - { - Trace::PowerPreviewSettingsUpdated(this->GetToggleSettingName().c_str(), lastState, newState, enabled); - } - else - { - Trace::PowerPreviewSettingsUpdateFailed(this->GetToggleSettingName().c_str(), lastState, newState, enabled); + if (err == ERROR_SUCCESS) + { + Trace::PowerPreviewSettingsUpdated(this->GetToggleSettingName().c_str(), lastState, newState, enabled); + } + else + { + Trace::PowerPreviewSettingsUpdateFailed(this->GetToggleSettingName().c_str(), lastState, newState, enabled); + } + } + // If process is not elevated, return false as it is not possible to update the registry + else + { + return false; + } } + // If it matches the new state, no update to registry is required } else { @@ -117,29 +124,7 @@ namespace PowerPreviewSettings } } } - } - LONG FileExplorerPreviewSettings::EnablePreview() - { - // Add registry value to enable preview. - return this->m_registryWrapper->SetRegistryValue(HKEY_CURRENT_USER, preview_handlers_subkey, this->GetCLSID(), REG_SZ, (LPBYTE)this->GetRegistryValueData().c_str(), (DWORD)(this->GetRegistryValueData().length() * sizeof(wchar_t))); - } - - LONG FileExplorerPreviewSettings::DisablePreview() - { - // Delete the registry key to disable preview. - return this->m_registryWrapper->DeleteRegistryValue(HKEY_CURRENT_USER, preview_handlers_subkey, this->GetCLSID()); - } - - LONG FileExplorerPreviewSettings::EnableThumbnailProvider() - { - // Add registry value to enable thumbnail provider. - return this->m_registryWrapper->SetRegistryValue(HKEY_CURRENT_USER, svg_thumbnail_provider_subkey, this->GetCLSID(), REG_SZ, (LPBYTE)this->GetRegistryValueData().c_str(), (DWORD)(this->GetRegistryValueData().length() * sizeof(wchar_t))); - } - - LONG FileExplorerPreviewSettings::DisableThumbnailProvider() - { - // Delete the registry key to disable thumbnail provider. - return this->m_registryWrapper->DeleteRegistryValue(HKEY_CURRENT_USER, svg_thumbnail_provider_subkey, this->GetCLSID()); + return true; } } diff --git a/src/modules/previewpane/powerpreview/settings.h b/src/modules/previewpane/powerpreview/settings.h index 3b52256021..431c93feb1 100644 --- a/src/modules/previewpane/powerpreview/settings.h +++ b/src/modules/previewpane/powerpreview/settings.h @@ -15,12 +15,14 @@ namespace PowerPreviewSettings std::wstring m_toggleSettingName; std::wstring m_toggleSettingDescription; std::wstring m_registryValueData; - RegistryWrapperIface * m_registryWrapper; LPCWSTR m_clsid; + protected: + RegistryWrapperIface* m_registryWrapper; + public: FileExplorerPreviewSettings(bool toggleSettingEnabled, const std::wstring& toggleSettingName, const std::wstring& toggleSettingDescription, LPCWSTR clsid, const std::wstring& registryValueData, RegistryWrapperIface* registryWrapper); - ~ FileExplorerPreviewSettings(); + ~FileExplorerPreviewSettings(); virtual bool GetToggleSettingState() const; virtual void UpdateToggleSettingState(bool state); @@ -29,10 +31,9 @@ namespace PowerPreviewSettings virtual LPCWSTR GetCLSID() const; virtual std::wstring GetRegistryValueData() const; virtual void LoadState(PowerToysSettings::PowerToyValues& settings); - virtual void UpdateState(PowerToysSettings::PowerToyValues& settings, bool enabled); - virtual LONG EnablePreview(); - virtual LONG DisablePreview(); - virtual LONG EnableThumbnailProvider(); - virtual LONG DisableThumbnailProvider(); + virtual bool UpdateState(PowerToysSettings::PowerToyValues& settings, bool enabled, bool isElevated); + virtual LONG Enable() = 0; + virtual LONG Disable() = 0; + virtual bool CheckRegistryState() = 0; }; } diff --git a/src/modules/previewpane/powerpreview/thumbnail_provider.cpp b/src/modules/previewpane/powerpreview/thumbnail_provider.cpp new file mode 100644 index 0000000000..e7540ed8b2 --- /dev/null +++ b/src/modules/previewpane/powerpreview/thumbnail_provider.cpp @@ -0,0 +1,51 @@ +#include "pch.h" +#include "thumbnail_provider.h" + +namespace PowerPreviewSettings +{ + // Function to enable the thumbnail provider in registry + LONG ThumbnailProviderSettings::Enable() + { + // Add registry value to enable thumbnail provider. + return this->m_registryWrapper->SetRegistryValue(HKEY_CLASSES_ROOT, thumbnail_provider_subkey, nullptr, REG_SZ, (LPBYTE)this->GetCLSID(), (DWORD)(wcslen(this->GetCLSID()) * sizeof(wchar_t))); + } + + // Function to disable the thumbnail provider in registry + LONG ThumbnailProviderSettings::Disable() + { + // Delete the registry key to disable thumbnail provider. + return this->m_registryWrapper->DeleteRegistryValue(HKEY_CLASSES_ROOT, thumbnail_provider_subkey, nullptr); + } + + // Function to check if the thumbnail provider is enabled in registry + bool ThumbnailProviderSettings::CheckRegistryState() + { + DWORD dataType; + DWORD byteCount = 255; + wchar_t regValue[255] = { 0 }; + + LONG errorCode = this->m_registryWrapper->GetRegistryValue(HKEY_CLASSES_ROOT, thumbnail_provider_subkey, nullptr, &dataType, regValue, &byteCount); + + // Registry value was found + if (errorCode == ERROR_SUCCESS) + { + // Check if the value type is string + if (dataType == REG_SZ) + { + // Check if the current registry value matches the expected value + if (wcscmp(regValue, this->GetCLSID()) == 0) + { + return true; + } + } + } + + return false; + } + + // Function to retrieve the registry subkey + LPCWSTR ThumbnailProviderSettings::GetSubkey() + { + return thumbnail_provider_subkey; + } +} diff --git a/src/modules/previewpane/powerpreview/thumbnail_provider.h b/src/modules/previewpane/powerpreview/thumbnail_provider.h new file mode 100644 index 0000000000..03eba9e306 --- /dev/null +++ b/src/modules/previewpane/powerpreview/thumbnail_provider.h @@ -0,0 +1,31 @@ +#pragma once +#include "settings.h" + +namespace PowerPreviewSettings +{ + class ThumbnailProviderSettings : + public FileExplorerPreviewSettings + { + private: + // Relative HKCR sub key path of thumbnail provider in registry. Registry key for Thumbnail Providers is generally HKCR\fileExtension\{E357FCCD-A995-4576-B01F-234630154E96}, and the default value in it is set to the CLSID of the provider + LPCWSTR thumbnail_provider_subkey; + + public: + ThumbnailProviderSettings(bool toggleSettingEnabled, const std::wstring& toggleSettingName, const std::wstring& toggleSettingDescription, LPCWSTR clsid, const std::wstring& registryValueData, RegistryWrapperIface* registryWrapper, LPCWSTR subkey) : + FileExplorerPreviewSettings(toggleSettingEnabled, toggleSettingName, toggleSettingDescription, clsid, registryValueData, registryWrapper), thumbnail_provider_subkey(subkey) + { + } + + // Function to enable the thumbnail provider in registry + LONG Enable(); + + // Function to disable the thumbnail provider in registry + LONG Disable(); + + // Function to check if the thumbnail provider is enabled in registry + bool CheckRegistryState(); + + // Function to retrieve the registry subkey + LPCWSTR GetSubkey(); + }; +} diff --git a/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp b/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp index d2cbd27351..2e9c784639 100644 --- a/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp +++ b/src/modules/previewpane/powerpreviewTest/FileExplorerPreviewSettingsTest.cpp @@ -5,12 +5,14 @@ #include #include #include +#include +#include using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace PowerToysSettings; using namespace PowerPreviewSettings; -namespace PreviewHandlerSettingsTest +namespace FileExplorerPreviewSettingsTest { extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -22,10 +24,14 @@ namespace PreviewHandlerSettingsTest HKEY Scope = NULL; LPCWSTR SubKey; LPCWSTR ValueName; + wchar_t ValueData[255] = { 0 }; }; class RegistryMock : public RegistryWrapperIface { + private: + wchar_t mockData[255] = { 0 }; + public: FunctionProperties SetRegistryMockProperties; FunctionProperties DeleteRegistryMockProperties; @@ -37,6 +43,7 @@ namespace PreviewHandlerSettingsTest SetRegistryMockProperties.Scope = keyScope; SetRegistryMockProperties.SubKey = subKey; SetRegistryMockProperties.ValueName = valueName; + wcscpy_s(SetRegistryMockProperties.ValueData, cbData, (WCHAR*)data); return SetRegistryMockProperties.ReturnValue; } @@ -49,26 +56,32 @@ namespace PreviewHandlerSettingsTest return DeleteRegistryMockProperties.ReturnValue; } - LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, DWORD dwType, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) + LONG GetRegistryValue(HKEY keyScope, LPCWSTR subKey, LPCWSTR valueName, LPDWORD pdwType, PVOID pvData, LPDWORD pcbData) { GetRegistryMockProperties.NumOfCalls++; GetRegistryMockProperties.Scope = keyScope; GetRegistryMockProperties.SubKey = subKey; GetRegistryMockProperties.ValueName = valueName; + *pdwType = REG_SZ; + wcscpy_s((LPWSTR)pvData, 255, mockData); return GetRegistryMockProperties.ReturnValue; } + + void SetMockData(std::wstring data) + { + wcscpy_s(mockData, data.c_str()); + } }; - TEST_CLASS(BaseSettingsTest) - { - public: - + TEST_CLASS (BaseSettingsTest) + { + public: TEST_METHOD (LoadState_ShouldLoadValidState_IfInitalStateIsPresent) { // Arrange bool defaultState = true; RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(defaultState, mockRegistryWrapper); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(defaultState, mockRegistryWrapper); auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); // Act @@ -83,7 +96,7 @@ namespace PreviewHandlerSettingsTest // Arrange bool defaultState = true; RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(defaultState, mockRegistryWrapper); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(defaultState, mockRegistryWrapper); auto settings = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\"}"); // Act @@ -93,50 +106,223 @@ namespace PreviewHandlerSettingsTest Assert::AreEqual(previewSettings.GetToggleSettingState(), defaultState); } - TEST_METHOD (UpdateState_ShouldDisablePreview_IfPreviewsAreEnabledAndNewSettingsStateIsFalse) + TEST_METHOD (PreviewHandlerSettingsUpdateState_ShouldDisableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsFalseAndPowerToysIsElevatedAndRegistryContainsThePreview) { // Arrange bool enabled = true; + bool elevated = true; RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(true, mockRegistryWrapper); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); previewSettings.UpdateToggleSettingState(true); + // Add expected data in registry + mockRegistryWrapper->SetMockData(previewSettings.GetRegistryValueData()); // Act - previewSettings.UpdateState(settings, enabled); + previewSettings.UpdateState(settings, enabled, elevated); // Assert Assert::IsFalse(previewSettings.GetToggleSettingState()); Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 1); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); } - TEST_METHOD (UpdateState_ShouldEnablePreview_IfPreviewsAreEnabledAndNewSettingsStateIsTrue) + TEST_METHOD (ThumbnailProviderSettingsUpdateState_ShouldDisableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsFalseAndPowerToysIsElevatedAndRegistryContainsThePreview) { // Arrange bool enabled = true; + bool elevated = true; RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(true, mockRegistryWrapper); + ThumbnailProviderSettings thumbnailSettings = GetThumbnailProviderSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(thumbnailSettings.GetToggleSettingName(), L"false")); + thumbnailSettings.UpdateToggleSettingState(true); + // Add expected data in registry + mockRegistryWrapper->SetMockData(thumbnailSettings.GetCLSID()); + + // Act + thumbnailSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::IsFalse(thumbnailSettings.GetToggleSettingState()); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 1); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); + } + + TEST_METHOD (UpdateState_ShouldNotDisableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsFalseAndPowerToysIsElevatedAndRegistryDoesNotContainThePreview) + { + // Arrange + bool enabled = true; + bool elevated = true; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); + previewSettings.UpdateToggleSettingState(true); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::IsFalse(previewSettings.GetToggleSettingState()); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 0); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); + } + + TEST_METHOD (UpdateState_ShouldNotDisableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsFalseAndPowerToysIsNotElevated) + { + // Arrange + bool enabled = true; + bool elevated = false; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); + previewSettings.UpdateToggleSettingState(true); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::IsFalse(previewSettings.GetToggleSettingState()); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 0); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); + } + + TEST_METHOD (UpdateState_ShouldEnableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsTrueAndPowerToysIsElevatedAndRegistryDoesNotContainThePreview) + { + // Arrange + bool enabled = true; + bool elevated = true; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"true")); previewSettings.UpdateToggleSettingState(false); // Act - previewSettings.UpdateState(settings, enabled); + previewSettings.UpdateState(settings, enabled, elevated); // Assert - Assert::IsTrue(previewSettings.GetToggleSettingState()); Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 1); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); } - TEST_METHOD (UpdateState_ShouldOnlyUpdateToggleSettingState_IfPreviewsAreDisabled) + TEST_METHOD (PreviewHandlerSettingsUpdateState_ShouldNotEnableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsTrueAndPowerToysIsElevatedAndRegistryContainsThePreview) + { + // Arrange + bool enabled = true; + bool elevated = true; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"true")); + previewSettings.UpdateToggleSettingState(false); + // Add expected data in registry + mockRegistryWrapper->SetMockData(previewSettings.GetRegistryValueData()); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 0); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); + } + + TEST_METHOD (ThumbnailProviderSettingsUpdateState_ShouldNotEnableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsTrueAndPowerToysIsElevatedAndRegistryContainsThePreview) + { + // Arrange + bool enabled = true; + bool elevated = true; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + ThumbnailProviderSettings thumbnailSettings = GetThumbnailProviderSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(thumbnailSettings.GetToggleSettingName(), L"true")); + thumbnailSettings.UpdateToggleSettingState(false); + // Add expected data in registry + mockRegistryWrapper->SetMockData(thumbnailSettings.GetCLSID()); + + // Act + thumbnailSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 0); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); + } + + TEST_METHOD (UpdateState_ShouldNotEnableInRegistry_IfPreviewsAreEnabledAndNewSettingsStateIsTrueAndPowerToysIsNotElevated) + { + // Arrange + bool enabled = true; + bool elevated = false; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"true")); + previewSettings.UpdateToggleSettingState(false); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 0); + Assert::AreEqual(mockRegistryWrapper->GetRegistryMockProperties.NumOfCalls, 1); + } + + TEST_METHOD (UpdateState_ShouldUpdateToggleSettingState_IfPreviewsAreEnabledAndPowerToysIsElevated) { // Arrange bool enabled = false; + bool elevated = true; RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(true, mockRegistryWrapper); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); // Act - previewSettings.UpdateState(settings, enabled); + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::IsFalse(previewSettings.GetToggleSettingState()); + } + + TEST_METHOD (UpdateState_ShouldUpdateToggleSettingState_IfPreviewsAreEnabledAndPowerToysIsNotElevated) + { + // Arrange + bool enabled = false; + bool elevated = false; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::IsFalse(previewSettings.GetToggleSettingState()); + } + + TEST_METHOD (UpdateState_ShouldOnlyUpdateToggleSettingState_IfPreviewsAreDisabledAndPowerToysIsElevated) + { + // Arrange + bool enabled = false; + bool elevated = true; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); + + // Assert + Assert::IsFalse(previewSettings.GetToggleSettingState()); + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 0); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 0); + } + + TEST_METHOD (UpdateState_ShouldOnlyUpdateToggleSettingState_IfPreviewsAreDisabledAndPowerToysIsNotElevated) + { + // Arrange + bool enabled = false; + bool elevated = false; + RegistryMock* mockRegistryWrapper = new RegistryMock(); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); + auto settings = PowerToyValues::from_json_string(GetJSONSettings(previewSettings.GetToggleSettingName(), L"false")); + + // Act + previewSettings.UpdateState(settings, enabled, elevated); // Assert Assert::IsFalse(previewSettings.GetToggleSettingState()); @@ -148,7 +334,7 @@ namespace PreviewHandlerSettingsTest { // Arrange bool updatedState = false; - FileExplorerPreviewSettings previewSettings = GetSettingsObject(true, new RegistryMock()); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, new RegistryMock()); // Act previewSettings.UpdateToggleSettingState(updatedState); @@ -157,52 +343,98 @@ namespace PreviewHandlerSettingsTest Assert::AreEqual(previewSettings.GetToggleSettingState(), updatedState); } - TEST_METHOD(EnablePreview_ShouldCallSetRegistryValueWithValidArguments_WhenCalled) + TEST_METHOD (PreviewHandlerSettingsEnable_ShouldCallSetRegistryValueWithValidArguments_WhenCalled) { // Arrange RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(true, mockRegistryWrapper); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); // Act - previewSettings.EnablePreview(); + previewSettings.Enable(); // Assert Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 1); - Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.SubKey, preview_handlers_subkey); + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.SubKey, PreviewHandlerSettings::GetSubkey()); Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.ValueName, previewSettings.GetCLSID()); - Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->SetRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_CURRENT_USER)); + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.ValueData, previewSettings.GetRegistryValueData().c_str()); + Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->SetRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_LOCAL_MACHINE)); } - TEST_METHOD(DisablePreview_ShouldCallDeleteRegistryValueWithValidArguments_WhenCalled) + TEST_METHOD (PreviewHandlerDisable_ShouldCallDeleteRegistryValueWithValidArguments_WhenCalled) { // Arrange RegistryMock* mockRegistryWrapper = new RegistryMock(); - FileExplorerPreviewSettings previewSettings = GetSettingsObject(true, mockRegistryWrapper); + PreviewHandlerSettings previewSettings = GetPreviewHandlerSettingsObject(true, mockRegistryWrapper); // Act - previewSettings.DisablePreview(); + previewSettings.Disable(); // Assert Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 1); - Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.SubKey, preview_handlers_subkey); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.SubKey, PreviewHandlerSettings::GetSubkey()); Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.ValueName, previewSettings.GetCLSID()); - Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->DeleteRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_CURRENT_USER)); + Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->DeleteRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_LOCAL_MACHINE)); } - FileExplorerPreviewSettings GetSettingsObject(bool defaultState, RegistryWrapperIface* registryMock) - { - return FileExplorerPreviewSettings( + TEST_METHOD (ThumbnailProviderSettingsEnable_ShouldCallSetRegistryValueWithValidArguments_WhenCalled) + { + // Arrange + RegistryMock* mockRegistryWrapper = new RegistryMock(); + ThumbnailProviderSettings thumbnailSettings = GetThumbnailProviderSettingsObject(true, mockRegistryWrapper); + + // Act + thumbnailSettings.Enable(); + + // Assert + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.NumOfCalls, 1); + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.SubKey, thumbnailSettings.GetSubkey()); + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.ValueName, nullptr); + Assert::AreEqual(mockRegistryWrapper->SetRegistryMockProperties.ValueData, thumbnailSettings.GetCLSID()); + Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->SetRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_CLASSES_ROOT)); + } + + TEST_METHOD (ThumbnailProviderSettingsDisable_ShouldCallDeleteRegistryValueWithValidArguments_WhenCalled) + { + // Arrange + RegistryMock* mockRegistryWrapper = new RegistryMock(); + ThumbnailProviderSettings thumbnailSettings = GetThumbnailProviderSettingsObject(true, mockRegistryWrapper); + + // Act + thumbnailSettings.Disable(); + + // Assert + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.NumOfCalls, 1); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.SubKey, thumbnailSettings.GetSubkey()); + Assert::AreEqual(mockRegistryWrapper->DeleteRegistryMockProperties.ValueName, nullptr); + Assert::AreEqual((ULONG_PTR)(mockRegistryWrapper->DeleteRegistryMockProperties.Scope), (ULONG_PTR)(HKEY_CLASSES_ROOT)); + } + + PreviewHandlerSettings GetPreviewHandlerSettingsObject(bool defaultState, RegistryWrapperIface* registryMock) + { + return PreviewHandlerSettings( defaultState, L"valid-name", L"valid-description", L"valid-guid", L"valid-handler", registryMock); - } + } - std::wstring GetJSONSettings(const std::wstring &_settingsNameId, const std::wstring &_value) const - { - return L"{\"name\":\"Module Name\",\"properties\" : {\"" + _settingsNameId + L"\":{\"value\":" + _value + L"}},\"version\" : \"1.0\" }"; - } - }; + ThumbnailProviderSettings GetThumbnailProviderSettingsObject(bool defaultState, RegistryWrapperIface* registryMock) + { + return ThumbnailProviderSettings( + defaultState, + L"valid-name", + L"valid-description", + L"valid-guid", + L"valid-handler", + registryMock, + L"valid-subkey"); + } + + std::wstring GetJSONSettings(const std::wstring& _settingsNameId, const std::wstring& _value) const + { + return L"{\"name\":\"Module Name\",\"properties\" : {\"" + _settingsNameId + L"\":{\"value\":" + _value + L"}},\"version\" : \"1.0\" }"; + } + }; }