main.cpp
1 #include "pch.h" 2 #include <ShellScalingApi.h> 3 #include <lmcons.h> 4 #include <filesystem> 5 #include <sstream> 6 #include "tray_icon.h" 7 #include "powertoy_module.h" 8 #include "trace.h" 9 #include "general_settings.h" 10 #include "restart_elevated.h" 11 #include "RestartManagement.h" 12 #include "Generated files/resource.h" 13 #include "settings_telemetry.h" 14 15 #include <common/comUtils/comUtils.h> 16 #include <common/display/dpi_aware.h> 17 #include <common/Telemetry/EtwTrace/EtwTrace.h> 18 #include <common/notifications/notifications.h> 19 #include <common/notifications/dont_show_again.h> 20 #include <common/updating/installer.h> 21 #include <common/updating/updating.h> 22 #include <common/updating/updateState.h> 23 #include <common/utils/appMutex.h> 24 #include <common/utils/elevation.h> 25 #include <common/utils/os-detect.h> 26 #include <common/utils/processApi.h> 27 #include <common/utils/resources.h> 28 #include <common/utils/clean_video_conference.h> 29 30 #include "UpdateUtils.h" 31 #include "ActionRunnerUtils.h" 32 33 #include <winrt/Windows.System.h> 34 35 #include <Psapi.h> 36 #include <RestartManager.h> 37 #include <shellapi.h> 38 #include "centralized_kb_hook.h" 39 #include "centralized_hotkeys.h" 40 #include "quick_access_host.h" 41 #include "ai_detection.h" 42 #include <common/utils/package.h> 43 44 #if _DEBUG && _WIN64 45 #include "unhandled_exception_handler.h" 46 #endif 47 #include <common/logger/logger.h> 48 #include <common/SettingsAPI/settings_helpers.h> 49 #include <runner/settings_window.h> 50 #include <common/utils/process_path.h> 51 #include <common/utils/winapi_error.h> 52 #include <common/utils/window.h> 53 #include <common/version/version.h> 54 #include <common/utils/string_utils.h> 55 #include <common/utils/gpo.h> 56 57 // disabling warning 4458 - declaration of 'identifier' hides class member 58 // to avoid warnings from GDI files - can't add winRT directory to external code 59 // in the Cpp.Build.props 60 #pragma warning(push) 61 #pragma warning(disable : 4458) 62 #include <gdiplus.h> 63 #pragma warning(pop) 64 65 namespace 66 { 67 const wchar_t PT_URI_PROTOCOL_SCHEME[] = L"powertoys://"; 68 const wchar_t POWER_TOYS_MODULE_LOAD_FAIL[] = L"Failed to load "; // Module name will be appended on this message and it is not localized. 69 } 70 71 void chdir_current_executable() 72 { 73 // Change current directory to the path of the executable. 74 WCHAR executable_path[MAX_PATH]; 75 GetModuleFileName(NULL, executable_path, MAX_PATH); 76 PathRemoveFileSpec(executable_path); 77 if (!SetCurrentDirectory(executable_path)) 78 { 79 show_last_error_message(L"Change Directory to Executable Path", GetLastError(), L"PowerToys - runner"); 80 } 81 } 82 83 // Detect AI capabilities by calling ImageResizer in detection mode. 84 // This runs in a background thread to avoid blocking the main startup. 85 // ImageResizer writes the result to a cache file that it reads on normal startup. 86 void DetectAiCapabilitiesAsync(bool skipSettingsCheck) 87 { 88 std::thread([skipSettingsCheck]() { 89 try 90 { 91 // Check if ImageResizer module is enabled (skip if called from apply_general_settings) 92 if (!skipSettingsCheck) 93 { 94 auto settings = PTSettingsHelper::load_general_settings(); 95 if (json::has(settings, L"enabled", json::JsonValueType::Object)) 96 { 97 auto enabledModules = settings.GetNamedObject(L"enabled"); 98 if (json::has(enabledModules, L"Image Resizer", json::JsonValueType::Boolean)) 99 { 100 bool isEnabled = enabledModules.GetNamedBoolean(L"Image Resizer", false); 101 if (!isEnabled) 102 { 103 Logger::info(L"ImageResizer module is disabled, skipping AI detection"); 104 return; 105 } 106 } 107 } 108 } 109 110 // Get ImageResizer.exe path (located in WinUI3Apps folder) 111 std::wstring imageResizerPath = get_module_folderpath(); 112 imageResizerPath += L"\\WinUI3Apps\\PowerToys.ImageResizer.exe"; 113 114 if (!std::filesystem::exists(imageResizerPath)) 115 { 116 Logger::warn(L"ImageResizer.exe not found at {}, skipping AI detection", imageResizerPath); 117 return; 118 } 119 120 Logger::info(L"Starting AI capability detection via ImageResizer"); 121 122 // Call ImageResizer --detect-ai 123 SHELLEXECUTEINFO sei = { sizeof(sei) }; 124 sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 125 sei.lpFile = imageResizerPath.c_str(); 126 sei.lpParameters = L"--detect-ai"; 127 sei.nShow = SW_HIDE; 128 129 if (ShellExecuteExW(&sei)) 130 { 131 // Wait for detection to complete (with timeout) 132 DWORD waitResult = WaitForSingleObject(sei.hProcess, 30000); // 30 second timeout 133 CloseHandle(sei.hProcess); 134 135 if (waitResult == WAIT_OBJECT_0) 136 { 137 Logger::info(L"AI capability detection completed successfully"); 138 } 139 else if (waitResult == WAIT_TIMEOUT) 140 { 141 Logger::warn(L"AI capability detection timed out"); 142 } 143 else 144 { 145 Logger::warn(L"AI capability detection wait failed"); 146 } 147 } 148 else 149 { 150 Logger::warn(L"Failed to launch ImageResizer for AI detection, error: {}", GetLastError()); 151 } 152 } 153 catch (const std::exception& e) 154 { 155 Logger::error("Exception during AI capability detection: {}", e.what()); 156 } 157 catch (...) 158 { 159 Logger::error("Unknown exception during AI capability detection"); 160 } 161 }).detach(); 162 } 163 164 inline wil::unique_mutex_nothrow create_msi_mutex() 165 { 166 return createAppMutex(POWERTOYS_MSI_MUTEX_NAME); 167 } 168 169 void open_menu_from_another_instance(std::optional<std::string> settings_window) 170 { 171 const HWND hwnd_main = FindWindowW(L"PToyTrayIconWindow", nullptr); 172 LPARAM msg = static_cast<LPARAM>(ESettingsWindowNames::Dashboard); 173 if (settings_window.has_value() && settings_window.value() != "") 174 { 175 msg = static_cast<LPARAM>(ESettingsWindowNames_from_string(settings_window.value())); 176 } 177 PostMessageW(hwnd_main, WM_COMMAND, ID_SETTINGS_MENU_COMMAND, msg); 178 SetForegroundWindow(hwnd_main); // Bring the settings window to the front 179 } 180 181 int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow, bool openOobe, bool openScoobe, bool showRestartNotificationAfterUpdate) 182 { 183 Logger::info("Runner is starting. Elevated={} openOobe={} openScoobe={} showRestartNotificationAfterUpdate={}", isProcessElevated, openOobe, openScoobe, showRestartNotificationAfterUpdate); 184 DPIAware::EnableDPIAwarenessForThisProcess(); 185 186 #if _DEBUG && _WIN64 187 //Global error handlers to diagnose errors. 188 //We prefer this not to show any longer until there's a bug to diagnose. 189 //init_global_error_handlers(); 190 #endif 191 Trace::RegisterProvider(); 192 193 // Load settings from file before reading them 194 load_general_settings(); 195 auto const settings = get_general_settings(); 196 start_tray_icon(isProcessElevated, settings.showThemeAdaptiveTrayIcon); 197 198 if (settings.enableQuickAccess) 199 { 200 QuickAccessHost::start(); 201 } 202 update_quick_access_hotkey(settings.enableQuickAccess, settings.quickAccessShortcut); 203 set_tray_icon_visible(settings.showSystemTrayIcon); 204 CentralizedKeyboardHook::Start(); 205 206 int result = -1; 207 try 208 { 209 if (!openOobe && showRestartNotificationAfterUpdate) 210 { 211 std::thread{ 212 [] { 213 // Wait a bit, because Windows has a delay until it picks up toast notification registration in the registry 214 Sleep(10000); 215 Logger::info("Showing toast notification asking to restart PC"); 216 notifications::show_toast(GET_RESOURCE_STRING(IDS_PT_VERSION_CHANGE_ASK_FOR_COMPUTER_RESTART).c_str(), L"PowerToys"); 217 } 218 }.detach(); 219 } 220 221 std::thread{ [] { 222 PeriodicUpdateWorker(); 223 } }.detach(); 224 225 // Start AI capability detection in background (Windows 11+ only) 226 // AI Super Resolution is not supported on Windows 10 227 // This calls ImageResizer --detect-ai which writes result to cache file 228 if (package::IsWin11OrGreater()) 229 { 230 DetectAiCapabilitiesAsync(); 231 } 232 else 233 { 234 Logger::info(L"AI capability detection skipped: Windows 10 does not support AI Super Resolution"); 235 } 236 237 std::thread{ [] { 238 if (updating::uninstall_previous_msix_version_async().get()) 239 { 240 notifications::show_toast(GET_RESOURCE_STRING(IDS_OLDER_MSIX_UNINSTALLED).c_str(), L"PowerToys"); 241 } 242 } }.detach(); 243 244 chdir_current_executable(); 245 246 // We deprecated a utility called Video Conference Mute, which registered itself as a video input device. 247 // When running elevated, we try to clean up the device registration from previous installations. 248 // This is done here too because a user-scope installer won't be able to remove the driver registration due to lack of permissions. 249 if (isProcessElevated) 250 { 251 clean_video_conference(); 252 } 253 254 // Load PowerToys DLLs 255 256 std::vector<std::wstring_view> knownModules = { 257 L"PowerToys.FancyZonesModuleInterface.dll", 258 L"PowerToys.powerpreview.dll", 259 L"WinUI3Apps/PowerToys.ImageResizerExt.dll", 260 L"PowerToys.KeyboardManager.dll", 261 L"PowerToys.Launcher.dll", 262 L"WinUI3Apps/PowerToys.PowerRenameExt.dll", 263 L"PowerToys.ShortcutGuideModuleInterface.dll", 264 L"PowerToys.ColorPicker.dll", 265 L"PowerToys.AwakeModuleInterface.dll", 266 L"PowerToys.FindMyMouse.dll", 267 L"PowerToys.MouseHighlighter.dll", 268 L"PowerToys.MouseJump.dll", 269 L"PowerToys.AlwaysOnTopModuleInterface.dll", 270 L"PowerToys.MousePointerCrosshairs.dll", 271 L"PowerToys.CursorWrap.dll", 272 L"PowerToys.PowerAccentModuleInterface.dll", 273 L"PowerToys.PowerOCRModuleInterface.dll", 274 L"PowerToys.AdvancedPasteModuleInterface.dll", 275 L"WinUI3Apps/PowerToys.FileLocksmithExt.dll", 276 L"WinUI3Apps/PowerToys.RegistryPreviewExt.dll", 277 L"WinUI3Apps/PowerToys.MeasureToolModuleInterface.dll", 278 L"WinUI3Apps/PowerToys.NewPlus.ShellExtension.dll", 279 L"WinUI3Apps/PowerToys.HostsModuleInterface.dll", 280 L"WinUI3Apps/PowerToys.Peek.dll", 281 L"WinUI3Apps/PowerToys.EnvironmentVariablesModuleInterface.dll", 282 L"PowerToys.MouseWithoutBordersModuleInterface.dll", 283 L"PowerToys.CropAndLockModuleInterface.dll", 284 L"PowerToys.CmdNotFoundModuleInterface.dll", 285 L"PowerToys.WorkspacesModuleInterface.dll", 286 L"PowerToys.CmdPalModuleInterface.dll", 287 L"PowerToys.ZoomItModuleInterface.dll", 288 L"PowerToys.LightSwitchModuleInterface.dll", 289 L"PowerToys.PowerDisplayModuleInterface.dll", 290 }; 291 292 for (auto moduleSubdir : knownModules) 293 { 294 try 295 { 296 auto pt_module = load_powertoy(moduleSubdir); 297 modules().emplace(pt_module->get_key(), std::move(pt_module)); 298 } 299 catch (...) 300 { 301 std::wstring errorMessage = POWER_TOYS_MODULE_LOAD_FAIL; 302 errorMessage += moduleSubdir; 303 304 #ifdef _DEBUG 305 // In debug mode, simply log the warning and continue execution. 306 // This contrasts with the past approach where developers had to build all modules 307 // without errors before debugging—slowing down quick clone-and-fix iterations. 308 Logger::warn(L"Debug mode: {}", errorMessage); 309 #else 310 // In release mode, show error dialog as before 311 MessageBoxW(NULL, 312 errorMessage.c_str(), 313 L"PowerToys", 314 MB_OK | MB_ICONERROR); 315 #endif 316 } 317 } 318 // Start initial powertoys 319 start_enabled_powertoys(); 320 std::wstring product_version = get_product_version(); 321 Trace::EventLaunch(product_version, isProcessElevated); 322 PTSettingsHelper::save_last_version_run(product_version); 323 324 if (openSettings) 325 { 326 std::optional<std::wstring> window; 327 if (!settingsWindow.empty()) 328 { 329 window = winrt::to_hstring(settingsWindow); 330 } 331 open_settings_window(window); 332 } 333 334 if (openOobe) 335 { 336 PTSettingsHelper::save_oobe_opened_state(); 337 open_oobe_window(); 338 } 339 else if (openScoobe) 340 { 341 open_scoobe_window(); 342 } 343 344 settings_telemetry::init(); 345 result = run_message_loop(); 346 } 347 catch (std::runtime_error& err) 348 { 349 std::string err_what = err.what(); 350 MessageBoxW(nullptr, std::wstring(err_what.begin(), err_what.end()).c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR | MB_SETFOREGROUND); 351 result = -1; 352 } 353 Trace::UnregisterProvider(); 354 QuickAccessHost::stop(); 355 return result; 356 } 357 358 // If the PT runner is launched as part of some action and manually by a user, e.g. being activated as a COM server 359 // for background toast notification handling, we should execute corresponding code flow instead of the main code flow. 360 enum class SpecialMode 361 { 362 None, 363 Win32ToastNotificationCOMServer, 364 ToastNotificationHandler, 365 ReportSuccessfulUpdate 366 }; 367 368 SpecialMode should_run_in_special_mode(const int n_cmd_args, LPWSTR* cmd_arg_list) 369 { 370 for (size_t i = 1; i < n_cmd_args; ++i) 371 { 372 if (!wcscmp(notifications::TOAST_ACTIVATED_LAUNCH_ARG, cmd_arg_list[i])) 373 { 374 return SpecialMode::Win32ToastNotificationCOMServer; 375 } 376 else if (n_cmd_args == 2 && !wcsncmp(PT_URI_PROTOCOL_SCHEME, cmd_arg_list[i], wcslen(PT_URI_PROTOCOL_SCHEME))) 377 { 378 return SpecialMode::ToastNotificationHandler; 379 } 380 else if (n_cmd_args == 2 && !wcscmp(cmdArg::UPDATE_REPORT_SUCCESS, cmd_arg_list[i])) 381 { 382 return SpecialMode::ReportSuccessfulUpdate; 383 } 384 } 385 386 return SpecialMode::None; 387 } 388 389 int win32_toast_notification_COM_server_mode() 390 { 391 notifications::run_desktop_app_activator_loop(); 392 return 0; 393 } 394 395 enum class toast_notification_handler_result 396 { 397 exit_success, 398 exit_error 399 }; 400 401 toast_notification_handler_result toast_notification_handler(const std::wstring_view param) 402 { 403 const std::wstring_view cant_drag_elevated_disable = L"cant_drag_elevated_disable/"; 404 const std::wstring_view couldnt_toggle_powerpreview_modules_disable = L"couldnt_toggle_powerpreview_modules_disable/"; 405 const std::wstring_view open_settings = L"open_settings/"; 406 const std::wstring_view open_overview = L"open_overview/"; 407 const std::wstring_view update_now = L"update_now/"; 408 409 if (param == cant_drag_elevated_disable) 410 { 411 return notifications::disable_toast(notifications::ElevatedDontShowAgainRegistryPath) ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error; 412 } 413 else if (param.starts_with(update_now)) 414 { 415 std::wstring args{ cmdArg::UPDATE_NOW_LAUNCH_STAGE1 }; 416 LaunchPowerToysUpdate(args.c_str()); 417 return toast_notification_handler_result::exit_success; 418 } 419 else if (param == couldnt_toggle_powerpreview_modules_disable) 420 { 421 return notifications::disable_toast(notifications::PreviewModulesDontShowAgainRegistryPath) ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error; 422 } 423 else if (param == open_settings) 424 { 425 open_menu_from_another_instance(std::nullopt); 426 return toast_notification_handler_result::exit_success; 427 } 428 else if (param == open_overview) 429 { 430 open_menu_from_another_instance("Overview"); 431 return toast_notification_handler_result::exit_success; 432 } 433 else 434 { 435 return toast_notification_handler_result::exit_error; 436 } 437 } 438 439 int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR lpCmdLine, int /*nCmdShow*/) 440 { 441 Shared::Trace::ETWTrace trace{}; 442 trace.UpdateState(true); 443 444 Gdiplus::GdiplusStartupInput gpStartupInput; 445 ULONG_PTR gpToken; 446 GdiplusStartup(&gpToken, &gpStartupInput, NULL); 447 448 winrt::init_apartment(); 449 450 const wchar_t* securityDescriptor = 451 L"O:BA" // Owner: Builtin (local) administrator 452 L"G:BA" // Group: Builtin (local) administrator 453 L"D:" 454 L"(A;;0x7;;;PS)" // Access allowed on COM_RIGHTS_EXECUTE, _LOCAL, & _REMOTE for Personal self 455 L"(A;;0x7;;;IU)" // Access allowed on COM_RIGHTS_EXECUTE for Interactive Users 456 L"(A;;0x3;;;SY)" // Access allowed on COM_RIGHTS_EXECUTE, & _LOCAL for Local system 457 L"(A;;0x7;;;BA)" // Access allowed on COM_RIGHTS_EXECUTE, _LOCAL, & _REMOTE for Builtin (local) administrator 458 L"(A;;0x3;;;S-1-15-3-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646)" // Access allowed on COM_RIGHTS_EXECUTE, & _LOCAL for Win32WebViewHost package capability 459 L"S:" 460 L"(ML;;NX;;;LW)"; // Integrity label on No execute up for Low mandatory level 461 initializeCOMSecurity(securityDescriptor); 462 463 int n_cmd_args = 0; 464 LPWSTR* cmd_arg_list = CommandLineToArgvW(GetCommandLineW(), &n_cmd_args); 465 switch (should_run_in_special_mode(n_cmd_args, cmd_arg_list)) 466 { 467 case SpecialMode::Win32ToastNotificationCOMServer: 468 return win32_toast_notification_COM_server_mode(); 469 case SpecialMode::ToastNotificationHandler: 470 switch (toast_notification_handler(cmd_arg_list[1] + wcslen(PT_URI_PROTOCOL_SCHEME))) 471 { 472 case toast_notification_handler_result::exit_error: 473 return 1; 474 case toast_notification_handler_result::exit_success: 475 return 0; 476 } 477 [[fallthrough]]; 478 case SpecialMode::ReportSuccessfulUpdate: 479 { 480 notifications::remove_toasts_by_tag(notifications::UPDATING_PROCESS_TOAST_TAG); 481 notifications::remove_all_scheduled_toasts(); 482 notifications::show_toast(GET_RESOURCE_STRING(IDS_PT_UPDATE_MESSAGE_BOX_TEXT), 483 L"PowerToys", 484 notifications::toast_params{ notifications::UPDATING_PROCESS_TOAST_TAG }); 485 break; 486 } 487 488 case SpecialMode::None: 489 // continue as usual 490 break; 491 } 492 493 std::filesystem::path logFilePath(PTSettingsHelper::get_root_save_folder_location()); 494 logFilePath.append(LogSettings::runnerLogPath); 495 Logger::init(LogSettings::runnerLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location()); 496 497 const std::string cmdLine{ lpCmdLine }; 498 Logger::info("Running powertoys with cmd args: {}", cmdLine); 499 500 auto open_settings_it = cmdLine.find("--open-settings"); 501 const bool open_settings = open_settings_it != std::string::npos; 502 // Check if opening specific settings window 503 open_settings_it = cmdLine.find("--open-settings="); 504 std::string settings_window; 505 if (open_settings_it != std::string::npos) 506 { 507 std::string rest_of_cmd_line{ cmdLine, open_settings_it + std::string{ "--open-settings=" }.size() }; 508 std::istringstream iss(rest_of_cmd_line); 509 iss >> settings_window; 510 } 511 512 // Check if another instance is already running. 513 wil::unique_mutex_nothrow msi_mutex = create_msi_mutex(); 514 if (!msi_mutex) 515 { 516 open_menu_from_another_instance(settings_window); 517 return 0; 518 } 519 520 bool openOobe = false; 521 try 522 { 523 openOobe = !PTSettingsHelper::get_oobe_opened_state(); 524 } 525 catch (const std::exception& e) 526 { 527 Logger::error("Failed to get or save OOBE state with an exception: {}", e.what()); 528 } 529 530 bool openScoobe = false; 531 bool showRestartNotificationAfterUpdate = false; 532 try 533 { 534 std::wstring last_version_run = PTSettingsHelper::get_last_version_run(); 535 const auto product_version = get_product_version(); 536 openScoobe = product_version != last_version_run; 537 showRestartNotificationAfterUpdate = openScoobe; 538 Logger::info(L"Scoobe: product_version={} last_version_run={}", product_version, last_version_run); 539 } 540 catch (const std::exception& e) 541 { 542 Logger::error("Failed to get last version with an exception: {}", e.what()); 543 } 544 545 int result = 0; 546 try 547 { 548 // Singletons initialization order needs to be preserved, first events and 549 // then modules to guarantee the reverse destruction order. 550 modules(); 551 552 std::thread{ [] { 553 auto state = UpdateState::read(); 554 if (state.state == UpdateState::upToDate) 555 { 556 updating::cleanup_updates(); 557 } 558 } }.detach(); 559 560 auto general_settings = load_general_settings(); 561 562 // Apply the general settings but don't save it as the modules() variable has not been loaded yet 563 apply_general_settings(general_settings, false); 564 const bool elevated = is_process_elevated(); 565 const bool with_dont_elevate_arg = cmdLine.find("--dont-elevate") != std::string::npos; 566 const bool run_elevated_setting = general_settings.GetNamedBoolean(L"run_elevated", false); 567 const bool with_restartedElevated_arg = cmdLine.find("--restartedElevated") != std::string::npos; 568 569 // Update scoobe behavior based on setting and gpo 570 bool scoobeSettingDisabled = general_settings.GetNamedBoolean(L"show_whats_new_after_updates", true) == false; 571 bool scoobeDisabledByGpo = powertoys_gpo::getDisableShowWhatsNewAfterUpdatesValue() == powertoys_gpo::gpo_rule_configured_enabled; 572 if (openScoobe && (scoobeSettingDisabled || scoobeDisabledByGpo)) 573 { 574 // Scoobe should show after an update, but is disabled by policy or setting 575 Logger::info(L"Scoobe: Showing scoobe after updates is disabled by setting or by GPO."); 576 openScoobe = false; 577 } 578 579 bool dataDiagnosticsDisabledByGpo = powertoys_gpo::getAllowDataDiagnosticsValue() == powertoys_gpo::gpo_rule_configured_disabled; 580 if (dataDiagnosticsDisabledByGpo) 581 { 582 Logger::info(L"Data diagnostics: Data diagnostics is disabled by GPO."); 583 PTSettingsHelper::save_data_diagnostics(false); 584 } 585 586 if (elevated && with_dont_elevate_arg && !run_elevated_setting) 587 { 588 Logger::info("Scheduling restart as non elevated"); 589 schedule_restart_as_non_elevated(); 590 result = 0; 591 } 592 else if (elevated || !run_elevated_setting || with_dont_elevate_arg || (!elevated && with_restartedElevated_arg)) 593 { 594 // The condition (!elevated && with_restartedElevated_arg) solves issue #19307. Restart elevated loop detected, running non-elevated 595 if (!elevated && with_restartedElevated_arg) 596 { 597 Logger::info("Restart as elevated failed. Running non-elevated."); 598 } 599 600 result = runner(elevated, open_settings, settings_window, openOobe, openScoobe, showRestartNotificationAfterUpdate); 601 602 if (result == 0) 603 { 604 // Save settings on closing, if closed 'normal' 605 PTSettingsHelper::save_general_settings(get_general_settings().to_json()); 606 } 607 } 608 else 609 { 610 Logger::info("Scheduling restart as elevated"); 611 schedule_restart_as_elevated(open_settings); 612 result = 0; 613 } 614 } 615 catch (std::runtime_error& err) 616 { 617 std::string err_what = err.what(); 618 MessageBoxW(nullptr, std::wstring(err_what.begin(), err_what.end()).c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR); 619 result = -1; 620 } 621 622 trace.Flush(); 623 trace.UpdateState(false); 624 625 // We need to release the mutexes to be able to restart the application 626 if (msi_mutex) 627 { 628 msi_mutex.reset(nullptr); 629 } 630 631 if (is_restart_scheduled()) 632 { 633 modules().clear(); 634 if (!restart_if_scheduled()) 635 { 636 // If it's not possible to restart non-elevated due to some condition in the user's configuration, user should start PowerToys manually. 637 Logger::warn("Scheduled restart failed. Couldn't restart non-elevated. PowerToys exits here because retrying it would just mean failing in a loop."); 638 } 639 } 640 stop_tray_icon(); 641 642 return result; 643 }