#include "pch.h" #include #include #include "powerpreview.h" #include "trace.h" #include "settings.h" #include "Generated Files/resource.h" #include #include #include // Constructor PowerPreviewModule::PowerPreviewModule() : m_moduleName(GET_RESOURCE_STRING(IDS_MODULE_NAME)), app_key(powerpreviewConstants::ModuleKey) { // Initialize the toggle states for each module init_settings(); m_fileExplorerModules.emplace_back(std::make_unique( true, L"svg-previewer-toggle-setting", GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION), L"{ddee2b8a-6807-48a6-bb20-2338174ff779}", L"Svg Preview Handler", std::make_unique())); m_fileExplorerModules.emplace_back(std::make_unique( true, L"md-previewer-toggle-setting", GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION), L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}", L"Markdown Preview Handler", std::make_unique())); m_fileExplorerModules.emplace_back(std::make_unique( 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", std::make_unique(), L".svg\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}")); // 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(); delete this; } // Return the localized display name of the powertoy const wchar_t* PowerPreviewModule::get_name() { return m_moduleName.c_str(); } // Return the non localized key of the powertoy, this will be cached by the runner const wchar_t* PowerPreviewModule::get_key() { return app_key.c_str(); } // Return JSON with the configuration options. bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_size) { HINSTANCE hinstance = reinterpret_cast(&__ImageBase); // Create a Settings object. PowerToysSettings::Settings settings(hinstance, get_name()); // General Settings. settings.set_description(GET_RESOURCE_STRING(IDS_GENERAL_DESCRIPTION)); settings.set_icon_key(GET_RESOURCE_STRING(IDS_ICON_KEY_NAME)); settings.set_overview_link(L"https://aka.ms/PowerToysOverview_FileExplorerAddOns"); // Preview Pane: Settings Group Header. settings.add_header_szLarge( GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_HEADER_ID), GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_DESC), GET_RESOURCE_STRING(IDS_PRVPANE_FILE_PREV_STTNGS_GROUP_TEXT)); for (auto& fileExplorerModule : m_fileExplorerModules) { settings.add_bool_toggle( fileExplorerModule->GetToggleSettingName(), fileExplorerModule->GetToggleSettingDescription(), fileExplorerModule->GetToggleSettingState()); } return settings.serialize_to_buffer(buffer, buffer_size); } // Called by the runner to pass the updated settings values as a serialized JSON. void PowerPreviewModule::set_config(const wchar_t* config) { try { PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); bool updateSuccess = true; bool isElevated = is_process_elevated(false); for (auto& fileExplorerModule : m_fileExplorerModules) { // 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); } if (!updateSuccess) { show_update_warning_message(); } settings.save_to_settings_file(); } catch (std::exception const& e) { Trace::SetConfigInvalidJSON(e.what()); } } // Enable preview handlers. void PowerPreviewModule::enable() { // Should only be done for old settings as it is already done for new settings in the constructor. if (!UseNewSettings()) { update_registry_to_match_toggles(); } if (!this->m_enabled) { Trace::EnabledPowerPreview(true); } this->m_enabled = true; } // Disable active preview handlers. void PowerPreviewModule::disable() { elevation_check_wrapper([this]() { for (auto& fileExplorerModule : m_fileExplorerModules) { fileExplorerModule->Disable(); } }); if (this->m_enabled) { Trace::EnabledPowerPreview(false); } this->m_enabled = false; } // Returns if the powertoys is enabled bool PowerPreviewModule::is_enabled() { return this->m_enabled; } // Load the settings file. void PowerPreviewModule::init_settings() { try { // Load and parse the settings file for this PowerToy. PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(PowerPreviewModule::get_key()); // Load settings states. for (auto& fileExplorerModule : m_fileExplorerModules) { fileExplorerModule->LoadState(settings); } } catch (std::exception const& e) { Trace::InitSetErrorLoadingFile(e.what()); } } // Function to check if the registry states need to be updated bool PowerPreviewModule::is_registry_update_required() { for (auto& fileExplorerModule : 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() { using namespace notifications; if (!is_toast_disabled(PreviewModulesDontShowAgainRegistryPath, PreviewModulesDisableIntervalInDays)) { std::vector actions = { link_button{ GET_RESOURCE_STRING(IDS_FILEEXPLORER_ADMIN_RESTART_WARNING_OPEN_SETTINGS), L"powertoys://open_settings/" }, link_button{ GET_RESOURCE_STRING(IDS_FILEEXPLORER_ADMIN_RESTART_WARNING_DONT_SHOW_AGAIN), L"powertoys://couldnt_toggle_powerpreview_modules_disable/" } }; show_toast_with_activations(GET_RESOURCE_STRING(IDS_FILEEXPLORER_ADMIN_RESTART_WARNING_DESCRIPTION), GET_RESOURCE_STRING(IDS_FILEEXPLORER_ADMIN_RESTART_WARNING_TITLE), {}, std::move(actions)); } } // 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 : m_fileExplorerModules) { if (fileExplorerModule->GetToggleSettingState()) { // Enable all the modules with initial state set as true. fileExplorerModule->Enable(); } else { fileExplorerModule->Disable(); } } }); }