/ src / runner / settings_window.cpp
settings_window.cpp
  1  #include "pch.h"
  2  #include <WinSafer.h>
  3  #include <Sddl.h>
  4  #include <sstream>
  5  #include <aclapi.h>
  6  
  7  #include "powertoy_module.h"
  8  #include <common/interop/two_way_pipe_message_ipc.h>
  9  #include <common/interop/shared_constants.h>
 10  #include "tray_icon.h"
 11  #include "general_settings.h"
 12  #include "restart_elevated.h"
 13  #include "UpdateUtils.h"
 14  #include "centralized_kb_hook.h"
 15  #include "Generated files/resource.h"
 16  #include "hotkey_conflict_detector.h"
 17  
 18  #include <common/utils/json.h>
 19  #include <common/SettingsAPI/settings_helpers.cpp>
 20  #include <common/version/version.h>
 21  #include <common/version/helper.h>
 22  #include <common/logger/logger.h>
 23  #include <common/utils/resources.h>
 24  #include <common/utils/elevation.h>
 25  #include <common/utils/process_path.h>
 26  #include <common/utils/timeutil.h>
 27  #include <common/utils/winapi_error.h>
 28  #include <common/updating/updateState.h>
 29  #include <common/themes/windows_colors.h>
 30  #include "settings_window.h"
 31  #include "bug_report.h"
 32  
 33  #define BUFSIZE 1024
 34  
 35  TwoWayPipeMessageIPC* current_settings_ipc = NULL;
 36  std::mutex ipc_mutex;
 37  std::atomic_bool g_isLaunchInProgress = false;
 38  std::atomic_bool isUpdateCheckThreadRunning = false;
 39  HANDLE g_terminateSettingsEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::TERMINATE_SETTINGS_SHARED_EVENT);
 40  
 41  json::JsonObject get_power_toys_settings()
 42  {
 43      json::JsonObject result;
 44      for (const auto& [name, powertoy] : modules())
 45      {
 46          try
 47          {
 48              result.SetNamedValue(name, powertoy.json_config());
 49          }
 50          catch (...)
 51          {
 52              Logger::error(L"get_power_toys_settings(): got malformed json for {} module", name);
 53          }
 54      }
 55      return result;
 56  }
 57  
 58  json::JsonObject get_all_settings()
 59  {
 60      json::JsonObject result;
 61  
 62      result.SetNamedValue(L"general", get_general_settings().to_json());
 63      result.SetNamedValue(L"powertoys", get_power_toys_settings());
 64      return result;
 65  }
 66  
 67  std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObject& powertoys_configs)
 68  {
 69      std::optional<std::wstring> result;
 70      for (const auto& powertoy_element : powertoys_configs)
 71      {
 72          const std::wstring name{ powertoy_element.Key().c_str() };
 73          // Currently, there is only one custom action in the general settings screen,
 74          // so it has to be the "restart as (non-)elevated" button.
 75          if (name == L"general")
 76          {
 77              try
 78              {
 79                  const auto value = powertoy_element.Value().GetObjectW();
 80                  const auto action = value.GetNamedString(L"action_name");
 81                  if (action == L"restart_elevation")
 82                  {
 83                      if (is_process_elevated())
 84                      {
 85                          schedule_restart_as_non_elevated();
 86                          PostQuitMessage(0);
 87                      }
 88                      else
 89                      {
 90                          schedule_restart_as_elevated(true);
 91                          PostQuitMessage(0);
 92                      }
 93                  }
 94                  else if (action == L"restart_maintain_elevation")
 95                  {
 96                      // this was added to restart and maintain elevation, which is needed after settings are change from outside the normal process.
 97                      // since a normal PostQuitMessage(0) would usually cause this process to save its in memory settings to disk, we need to
 98                      // send a PostQuitMessage(1) and check for that on exit, and skip the settings-flush.
 99                      auto loaded = PTSettingsHelper::load_general_settings();
100  
101                      if (is_process_elevated())
102                      {
103                          schedule_restart_as_elevated(true);
104                          PostQuitMessage(1);
105                      }
106                      else
107                      {
108                          schedule_restart_as_non_elevated(true);
109                          PostQuitMessage(1);
110                      }
111                  }
112                  else if (action == L"check_for_updates")
113                  {
114                      bool expected_isUpdateCheckThreadRunning = false;
115                      if (isUpdateCheckThreadRunning.compare_exchange_strong(expected_isUpdateCheckThreadRunning, true))
116                      {
117                          std::thread([]() {
118                              CheckForUpdatesCallback();
119                              isUpdateCheckThreadRunning.store(false);
120                          }).detach();
121                      }
122                  }
123                  else if (action == L"request_update_state_date")
124                  {
125                      json::JsonObject json;
126  
127                      auto update_state = UpdateState::read();
128                      if (update_state.githubUpdateLastCheckedDate)
129                      {
130                          const time_t date = *update_state.githubUpdateLastCheckedDate;
131                          json.SetNamedValue(L"updateStateDate", json::value(std::to_wstring(date)));
132                      }
133  
134                      result.emplace(json.Stringify());
135                  }
136              }
137              catch (...)
138              {
139              }
140          }
141          else if (modules().find(name) != modules().end())
142          {
143              const auto element = powertoy_element.Value().Stringify();
144              modules().at(name)->call_custom_action(element.c_str());
145          }
146      }
147  
148      return result;
149  }
150  
151  void send_json_config_to_module(const std::wstring& module_key, const std::wstring& settings, bool hotkeyUpdated)
152  {
153      auto moduleIt = modules().find(module_key);
154      if (moduleIt != modules().end())
155      {
156          moduleIt->second->set_config(settings.c_str());
157  
158          if (hotkeyUpdated)
159          {
160              moduleIt->second.remove_hotkey_records();
161              moduleIt->second.update_hotkeys();
162              moduleIt->second.UpdateHotkeyEx();
163          }
164      }
165  }
166  
167  void dispatch_json_config_to_modules(const json::JsonObject& powertoys_configs)
168  {
169      for (const auto& powertoy_element : powertoys_configs)
170      {
171          const auto element = powertoy_element.Value().Stringify();
172  
173          /* As PowerToys Run hotkeys are not registered by the runner, hotkey updates are
174           * triggered only when hotkey properties change to avoid incorrect conflict detection; 
175           * otherwise, the existing logic remains.
176           */
177          auto settings = powertoy_element.Value().GetObjectW();
178          bool hotkeyUpdated = true;
179          if (settings.HasKey(L"properties"))
180          {
181              const auto properties = settings.GetNamedObject(L"properties");
182  
183              // Currently, only PowerToys Run settings use the 'hotkey_changed' property.
184              if (properties.HasKey(L"hotkey_changed"))
185              {
186                  json::get(properties, L"hotkey_changed", hotkeyUpdated, true);
187              }
188          }
189          
190          send_json_config_to_module(powertoy_element.Key().c_str(), element.c_str(), hotkeyUpdated);
191      }
192  };
193  
194  void dispatch_received_json(const std::wstring& json_to_parse)
195  {
196      json::JsonObject j;
197      const bool ok = json::JsonObject::TryParse(json_to_parse, j);
198      if (!ok)
199      {
200          Logger::error(L"dispatch_received_json: got malformed json: {}", json_to_parse);
201          return;
202      }
203  
204      Logger::info(L"dispatch_received_json: {}", json_to_parse);
205  
206      for (const auto& base_element : j)
207      {
208          const auto name = base_element.Key();
209          const auto value = base_element.Value();
210  
211          if (name == L"general")
212          {
213              apply_general_settings(value.GetObjectW());
214              // const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
215              // {
216              //     std::unique_lock lock{ ipc_mutex };
217              //     if (current_settings_ipc)
218              //         current_settings_ipc->send(settings_string);
219              // }
220          }
221          else if (name == L"module_status")
222          {
223              // Handle single module enable/disable update
224              // Expected format: {"module_status": {"ModuleName": true/false}}
225              apply_module_status_update(value.GetObjectW());
226          }
227          else if (name == L"powertoys")
228          {
229              dispatch_json_config_to_modules(value.GetObjectW());
230              const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
231              {
232                  std::unique_lock lock{ ipc_mutex };
233                  if (current_settings_ipc)
234                      current_settings_ipc->send(settings_string);
235              }
236          }
237          else if (name == L"refresh")
238          {
239              const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
240              {
241                  std::unique_lock lock{ ipc_mutex };
242                  if (current_settings_ipc)
243                      current_settings_ipc->send(settings_string);
244              }
245          }
246          else if (name == L"action")
247          {
248              auto result = dispatch_json_action_to_module(value.GetObjectW());
249              if (result.has_value())
250              {
251                  {
252                      std::unique_lock lock{ ipc_mutex };
253                      if (current_settings_ipc)
254                          current_settings_ipc->send(result.value());
255                  }
256              }
257          }
258          else if (name == L"bugreport")
259          {
260              launch_bug_report();
261          }
262          else if (name == L"bug_report_status")
263          {
264              json::JsonObject result;
265              result.SetNamedValue(L"bug_report_running", winrt::Windows::Data::Json::JsonValue::CreateBooleanValue(is_bug_report_running()));
266              std::unique_lock lock{ ipc_mutex };
267              if (current_settings_ipc)
268                  current_settings_ipc->send(result.Stringify().c_str());
269          }
270          else if (name == L"killrunner")
271          {
272              const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr);
273              if (pt_main_window != nullptr)
274              {
275                  SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
276              }
277          }
278          else if (name == L"language")
279          {
280              constexpr const wchar_t* language_filename = L"\\language.json";
281              const std::wstring save_file_location = PTSettingsHelper::get_root_save_folder_location() + language_filename;
282              json::to_file(save_file_location, j);
283          }
284          else if (name == L"check_hotkey_conflict")
285          {
286              try
287              {
288                  PowertoyModuleIface::Hotkey hotkey;
289                  hotkey.win = value.GetObjectW().GetNamedBoolean(L"win", false);
290                  hotkey.ctrl = value.GetObjectW().GetNamedBoolean(L"ctrl", false);
291                  hotkey.shift = value.GetObjectW().GetNamedBoolean(L"shift", false);
292                  hotkey.alt = value.GetObjectW().GetNamedBoolean(L"alt", false);
293                  hotkey.key = static_cast<unsigned char>(value.GetObjectW().GetNamedNumber(L"key", 0));
294  
295                  std::wstring requestId = value.GetObjectW().GetNamedString(L"request_id", L"").c_str();
296  
297                  auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
298                  bool hasConflict = hkmng.HasConflict(hotkey);
299  
300                  json::JsonObject response;
301                  response.SetNamedValue(L"response_type", json::JsonValue::CreateStringValue(L"hotkey_conflict_result"));
302                  response.SetNamedValue(L"request_id", json::JsonValue::CreateStringValue(requestId));
303                  response.SetNamedValue(L"has_conflict", json::JsonValue::CreateBooleanValue(hasConflict));
304  
305                  if (hasConflict)
306                  {
307                      auto conflicts = hkmng.GetAllConflicts(hotkey);
308                      if (!conflicts.empty())
309                      {
310                          // Include all conflicts in the response
311                          json::JsonArray allConflicts;
312                          for (const auto& conflict : conflicts)
313                          {
314                              json::JsonObject conflictObj;
315                              conflictObj.SetNamedValue(L"module", json::JsonValue::CreateStringValue(conflict.moduleName));
316                              conflictObj.SetNamedValue(L"hotkeyID", json::JsonValue::CreateNumberValue(conflict.hotkeyID));
317                              allConflicts.Append(conflictObj);
318                          }
319                          response.SetNamedValue(L"all_conflicts", allConflicts);
320                      }
321                  }
322  
323                  std::unique_lock lock{ ipc_mutex };
324                  if (current_settings_ipc)
325                  {
326                      current_settings_ipc->send(response.Stringify().c_str());
327                  }
328              }
329              catch (...)
330              {
331                  Logger::error(L"Failed to process hotkey conflict check request");
332              }
333          }
334          else if (name == L"get_all_hotkey_conflicts")
335          {
336              try
337              {
338                  auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
339                  auto conflictsJson = hkmng.GetHotkeyConflictsAsJson();
340  
341                  // Add response type identifier
342                  conflictsJson.SetNamedValue(L"response_type", json::JsonValue::CreateStringValue(L"all_hotkey_conflicts"));
343  
344                  std::unique_lock lock{ ipc_mutex };
345                  if (current_settings_ipc)
346                  {
347                      current_settings_ipc->send(conflictsJson.Stringify().c_str());
348                  }
349              }
350              catch (...)
351              {
352                  Logger::error(L"Failed to process get all hotkey conflicts request");
353              }
354          }
355      }
356      return;
357  }
358  
359  void dispatch_received_json_callback(PVOID data)
360  {
361      std::wstring* msg = static_cast<std::wstring*>(data);
362      dispatch_received_json(*msg);
363      delete msg;
364  }
365  
366  void receive_json_send_to_main_thread(const std::wstring& msg)
367  {
368      std::wstring* copy = new std::wstring(msg);
369      dispatch_run_on_main_ui_thread(dispatch_received_json_callback, copy);
370  }
371  
372  // Try to run the Settings process with non-elevated privileges.
373  BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args, PROCESS_INFORMATION* process_info)
374  {
375      HWND hwnd = GetShellWindow();
376      if (!hwnd)
377      {
378          return false;
379      }
380  
381      DWORD pid;
382      GetWindowThreadProcessId(hwnd, &pid);
383  
384      winrt::handle process{ OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid) };
385      if (!process)
386      {
387          return false;
388      }
389  
390      SIZE_T size = 0;
391      InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
392      auto pproc_buffer = std::unique_ptr<char[]>{ new (std::nothrow) char[size] };
393      auto pptal = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(pproc_buffer.get());
394      if (!pptal)
395      {
396          return false;
397      }
398  
399      if (!InitializeProcThreadAttributeList(pptal, 1, 0, &size))
400      {
401          return false;
402      }
403  
404      if (!UpdateProcThreadAttribute(pptal,
405                                     0,
406                                     PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
407                                     &process,
408                                     sizeof(process),
409                                     nullptr,
410                                     nullptr))
411      {
412          return false;
413      }
414  
415      STARTUPINFOEX siex = { 0 };
416      siex.lpAttributeList = pptal;
417      siex.StartupInfo.cb = sizeof(siex);
418  
419      BOOL process_created = CreateProcessW(executable_path,
420                                            executable_args,
421                                            nullptr,
422                                            nullptr,
423                                            FALSE,
424                                            EXTENDED_STARTUPINFO_PRESENT,
425                                            nullptr,
426                                            nullptr,
427                                            &siex.StartupInfo,
428                                            process_info);
429      g_isLaunchInProgress = false;
430      return process_created;
431  }
432  
433  DWORD g_settings_process_id = 0;
434  
435  void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional<std::wstring> settings_window)
436  {
437      g_isLaunchInProgress = true;
438  
439      PROCESS_INFORMATION process_info = { 0 };
440      HANDLE hToken = nullptr;
441  
442      // Arguments for calling the settings executable:
443      // "C:\powertoys_path\PowerToysSettings.exe" powertoys_pipe settings_pipe powertoys_pid settings_theme
444      // powertoys_pipe: PowerToys pipe server.
445      // settings_pipe : Settings pipe server.
446      // powertoys_pid : PowerToys process pid.
447      // settings_theme: pass "dark" to start the settings window in dark mode
448  
449      // Arg 1: executable path.
450      std::wstring executable_path = get_module_folderpath();
451  
452      executable_path.append(L"\\WinUI3Apps\\PowerToys.Settings.exe");
453  
454      // Args 2,3: pipe server. Generate unique names for the pipes, if getting a UUID is possible.
455      std::wstring powertoys_pipe_name(L"\\\\.\\pipe\\powertoys_runner_");
456      std::wstring settings_pipe_name(L"\\\\.\\pipe\\powertoys_settings_");
457      UUID temp_uuid;
458      wchar_t* uuid_chars = nullptr;
459      if (UuidCreate(&temp_uuid) == RPC_S_UUID_NO_ADDRESS)
460      {
461          auto val = get_last_error_message(GetLastError());
462          Logger::warn(L"UuidCreate cannot create guid. {}", val.has_value() ? val.value() : L"");
463      }
464      else if (UuidToString(&temp_uuid, reinterpret_cast<RPC_WSTR*>(&uuid_chars)) != RPC_S_OK)
465      {
466          auto val = get_last_error_message(GetLastError());
467          Logger::warn(L"UuidToString cannot convert to string. {}", val.has_value() ? val.value() : L"");
468      }
469  
470      if (uuid_chars != nullptr)
471      {
472          powertoys_pipe_name += std::wstring(uuid_chars);
473          settings_pipe_name += std::wstring(uuid_chars);
474          RpcStringFree(reinterpret_cast<RPC_WSTR*>(&uuid_chars));
475          uuid_chars = nullptr;
476      }
477  
478      // Arg 4: process pid.
479      DWORD powertoys_pid = GetCurrentProcessId();
480  
481      GeneralSettings save_settings = get_general_settings();
482  
483      // Arg 5: settings theme.
484      const std::wstring settings_theme_setting{ save_settings.theme };
485      std::wstring settings_theme = L"system";
486      if (settings_theme_setting == L"dark" || (settings_theme_setting == L"system" && WindowsColors::is_dark_mode()))
487      {
488          settings_theme = L"dark";
489      }
490  
491      // Arg 6: elevated status
492      bool isElevated{ save_settings.isElevated };
493      std::wstring settings_elevatedStatus = isElevated ? L"true" : L"false";
494  
495      // Arg 7: is user an admin
496      bool isAdmin{ save_settings.isAdmin };
497      std::wstring settings_isUserAnAdmin = isAdmin ? L"true" : L"false";
498  
499      // Arg 8: should oobe window be shown
500      std::wstring settings_showOobe = show_oobe_window ? L"true" : L"false";
501  
502      // Arg 9: should scoobe window be shown
503      std::wstring settings_showScoobe = show_scoobe_window ? L"true" : L"false";
504  
505      // Arg 10: contains if there's a settings window argument. If true, will add one extra argument with the value to the call.
506      std::wstring settings_containsSettingsWindow = settings_window.has_value() ? L"true" : L"false";
507  
508      // Args 11, .... : Optional arguments depending on the options presented before. All by the same value.
509  
510      // create general settings file to initialize the settings file with installation configurations like :
511      // 1. Run on start up.
512      PTSettingsHelper::save_general_settings(save_settings.to_json());
513  
514      std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {}",
515                                                 executable_path,
516                                                 powertoys_pipe_name,
517                                                 settings_pipe_name,
518                                                 std::to_wstring(powertoys_pid),
519                                                 settings_theme,
520                                                 settings_elevatedStatus,
521                                                 settings_isUserAnAdmin,
522                                                 settings_showOobe,
523                                                 settings_showScoobe,
524                                                 settings_containsSettingsWindow);
525  
526      if (settings_window.has_value())
527      {
528          executable_args.append(L" ");
529          executable_args.append(settings_window.value());
530      }
531  
532      BOOL process_created = false;
533  
534      // Commented out to fix #22659
535      // Running settings non-elevated and modules elevated when PowerToys is running elevated results
536      // in settings making changes in one file (non-elevated user dir) and modules are reading settings
537      // from different (elevated user) dir
538      //if (is_process_elevated())
539      //{
540  
541      //    auto res = RunNonElevatedFailsafe(executable_path, executable_args, get_module_folderpath());
542      //    process_created = res.has_value();
543      //    if (process_created)
544      //    {
545      //        process_info.dwProcessId = res->processID;
546      //        process_info.hProcess = res->processHandle.release();
547      //        g_isLaunchInProgress = false;
548      //    }
549      //}
550  
551      if (FALSE == process_created)
552      {
553          // The runner is not elevated or we failed to create the process using the
554          // attribute list from Windows Explorer (this happens when PowerToys is executed
555          // as Administrator from a non-Administrator user or an error occur trying).
556          // In the second case the Settings process will run elevated.
557          STARTUPINFO startup_info = { sizeof(startup_info) };
558          if (!CreateProcessW(executable_path.c_str(),
559                              executable_args.data(),
560                              nullptr,
561                              nullptr,
562                              FALSE,
563                              0,
564                              nullptr,
565                              nullptr,
566                              &startup_info,
567                              &process_info))
568          {
569              goto LExit;
570          }
571          else
572          {
573              g_isLaunchInProgress = false;
574          }
575      }
576  
577      if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
578      {
579          goto LExit;
580      }
581  
582      {
583          std::unique_lock lock{ ipc_mutex };
584          current_settings_ipc = new TwoWayPipeMessageIPC(powertoys_pipe_name, settings_pipe_name, receive_json_send_to_main_thread);
585          current_settings_ipc->start(hToken);
586  
587          // Register callback for bug report status changes
588          BugReportManager::instance().register_callback([](bool isRunning) {
589              json::JsonObject result;
590              result.SetNamedValue(L"bug_report_running", winrt::Windows::Data::Json::JsonValue::CreateBooleanValue(isRunning));
591  
592              std::unique_lock lock{ ipc_mutex };
593              if (current_settings_ipc)
594                  current_settings_ipc->send(result.Stringify().c_str());
595          });
596      }
597  
598      g_settings_process_id = process_info.dwProcessId;
599  
600      if (process_info.hProcess)
601      {
602          WaitForSingleObject(process_info.hProcess, INFINITE);
603          if (WaitForSingleObject(process_info.hProcess, INFINITE) != WAIT_OBJECT_0)
604          {
605              show_last_error_message(L"Couldn't wait on the Settings Window to close.", GetLastError(), L"PowerToys - runner");
606          }
607      }
608      else
609      {
610          auto val = get_last_error_message(GetLastError());
611          Logger::error(L"Process handle is empty. {}", val.has_value() ? val.value() : L"");
612      }
613  
614  LExit:
615  
616      if (process_info.hProcess)
617      {
618          CloseHandle(process_info.hProcess);
619      }
620  
621      if (process_info.hThread)
622      {
623          CloseHandle(process_info.hThread);
624      }
625      {
626          std::unique_lock lock{ ipc_mutex };
627          if (current_settings_ipc)
628          {
629              current_settings_ipc->end();
630              delete current_settings_ipc;
631              current_settings_ipc = nullptr;
632          }
633      }
634  
635      if (hToken)
636      {
637          CloseHandle(hToken);
638      }
639  
640      g_settings_process_id = 0;
641  }
642  
643  #define MAX_TITLE_LENGTH 100
644  void bring_settings_to_front()
645  {
646      auto callback = [](HWND hwnd, LPARAM /*data*/) -> BOOL {
647          DWORD processId;
648          if (GetWindowThreadProcessId(hwnd, &processId) && processId == g_settings_process_id)
649          {
650              std::wstring windowTitle = L"PowerToys Settings";
651  
652              WCHAR title[MAX_TITLE_LENGTH];
653              int len = GetWindowTextW(hwnd, title, MAX_TITLE_LENGTH);
654              if (len <= 0)
655              {
656                  return TRUE;
657              }
658              if (wcsncmp(title, windowTitle.c_str(), len) == 0)
659              {
660                  auto lStyles = GetWindowLong(hwnd, GWL_STYLE);
661  
662                  if (lStyles & WS_MAXIMIZE)
663                  {
664                      ShowWindow(hwnd, SW_MAXIMIZE);
665                  }
666                  else
667                  {
668                      ShowWindow(hwnd, SW_RESTORE);
669                  }
670  
671                  SetForegroundWindow(hwnd);
672                  return FALSE;
673              }
674          }
675  
676          return TRUE;
677      };
678  
679      EnumWindows(callback, 0);
680  }
681  
682  void open_settings_window(std::optional<std::wstring> settings_window)
683  {
684      if (g_settings_process_id != 0)
685      {
686          // nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated)
687          // bring_settings_to_front();
688          if (current_settings_ipc)
689          {
690              if (settings_window.has_value())
691              {
692                  std::wstring msg = L"{\"ShowYourself\":\"" + settings_window.value() + L"\"}";
693                  current_settings_ipc->send(msg);
694              }
695              else
696              {
697                  current_settings_ipc->send(L"{\"ShowYourself\":\"Dashboard\"}");
698              }
699          }
700      }
701      else
702      {
703          if (!g_isLaunchInProgress)
704          {
705              std::thread([settings_window]() {
706                  run_settings_window(false, false, settings_window);
707              }).detach();
708          }
709      }
710  }
711  
712  void close_settings_window()
713  {
714      if (g_settings_process_id != 0)
715      {
716          SetEvent(g_terminateSettingsEvent);
717          wil::unique_handle proc{ OpenProcess(PROCESS_ALL_ACCESS, false, g_settings_process_id) };
718          if (proc)
719          {
720              WaitForSingleObject(proc.get(), 1500);
721              TerminateProcess(proc.get(), 0);
722          }
723      }
724  }
725  
726  void open_oobe_window()
727  {
728      std::thread([]() {
729          run_settings_window(true, false, std::nullopt);
730      }).detach();
731  }
732  
733  void open_scoobe_window()
734  {
735      std::thread([]() {
736          run_settings_window(false, true, std::nullopt);
737      }).detach();
738  }
739  
740  std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
741  {
742      switch (value)
743      {
744      case ESettingsWindowNames::Dashboard:
745          return "Dashboard";
746      case ESettingsWindowNames::Overview:
747          return "Overview";
748      case ESettingsWindowNames::AlwaysOnTop:
749          return "AlwaysOnTop";
750      case ESettingsWindowNames::Awake:
751          return "Awake";
752      case ESettingsWindowNames::ColorPicker:
753          return "ColorPicker";
754      case ESettingsWindowNames::CmdNotFound:
755          return "CmdNotFound";
756      case ESettingsWindowNames::LightSwitch:
757          return "LightSwitch";
758      case ESettingsWindowNames::FancyZones:
759          return "FancyZones";
760      case ESettingsWindowNames::FileLocksmith:
761          return "FileLocksmith";
762      case ESettingsWindowNames::Run:
763          return "Run";
764      case ESettingsWindowNames::ImageResizer:
765          return "ImageResizer";
766      case ESettingsWindowNames::KBM:
767          return "KBM";
768      case ESettingsWindowNames::MouseUtils:
769          return "MouseUtils";
770      case ESettingsWindowNames::MouseWithoutBorders:
771          return "MouseWithoutBorders";
772      case ESettingsWindowNames::Peek:
773          return "Peek";
774      case ESettingsWindowNames::PowerAccent:
775          return "PowerAccent";
776      case ESettingsWindowNames::PowerLauncher:
777          return "PowerLauncher";
778      case ESettingsWindowNames::PowerPreview:
779          return "PowerPreview";
780      case ESettingsWindowNames::PowerRename:
781          return "PowerRename";
782      case ESettingsWindowNames::FileExplorer:
783          return "FileExplorer";
784      case ESettingsWindowNames::ShortcutGuide:
785          return "ShortcutGuide";
786      case ESettingsWindowNames::Hosts:
787          return "Hosts";
788      case ESettingsWindowNames::MeasureTool:
789          return "MeasureTool";
790      case ESettingsWindowNames::PowerOCR:
791          return "PowerOcr";
792      case ESettingsWindowNames::Workspaces:
793          return "Workspaces";
794      case ESettingsWindowNames::RegistryPreview:
795          return "RegistryPreview";
796      case ESettingsWindowNames::CropAndLock:
797          return "CropAndLock";
798      case ESettingsWindowNames::EnvironmentVariables:
799          return "EnvironmentVariables";
800      case ESettingsWindowNames::AdvancedPaste:
801          return "AdvancedPaste";
802      case ESettingsWindowNames::NewPlus:
803          return "NewPlus";
804      case ESettingsWindowNames::CmdPal:
805          return "CmdPal";
806      case ESettingsWindowNames::ZoomIt:
807          return "ZoomIt";
808      case ESettingsWindowNames::PowerDisplay:
809          return "PowerDisplay";
810      default:
811      {
812          Logger::error(L"Can't convert ESettingsWindowNames value={} to string", static_cast<int>(value));
813          assert(false);
814      }
815      }
816      return "";
817  }
818  
819  ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
820  {
821      if (value == "Dashboard")
822      {
823          return ESettingsWindowNames::Dashboard;
824      }
825      else if (value == "Overview")
826      {
827          return ESettingsWindowNames::Overview;
828      }
829      else if (value == "AlwaysOnTop")
830      {
831          return ESettingsWindowNames::AlwaysOnTop;
832      }
833      else if (value == "Awake")
834      {
835          return ESettingsWindowNames::Awake;
836      }
837      else if (value == "ColorPicker")
838      {
839          return ESettingsWindowNames::ColorPicker;
840      }
841      else if (value == "CmdNotFound")
842      {
843          return ESettingsWindowNames::CmdNotFound;
844      }
845      else if (value == "LightSwitch")
846      {
847          return ESettingsWindowNames::LightSwitch;
848      }
849      else if (value == "FancyZones")
850      {
851          return ESettingsWindowNames::FancyZones;
852      }
853      else if (value == "FileLocksmith")
854      {
855          return ESettingsWindowNames::FileLocksmith;
856      }
857      else if (value == "Run")
858      {
859          return ESettingsWindowNames::Run;
860      }
861      else if (value == "ImageResizer")
862      {
863          return ESettingsWindowNames::ImageResizer;
864      }
865      else if (value == "KBM")
866      {
867          return ESettingsWindowNames::KBM;
868      }
869      else if (value == "MouseUtils")
870      {
871          return ESettingsWindowNames::MouseUtils;
872      }
873      else if (value == "MouseWithoutBorders")
874      {
875          return ESettingsWindowNames::MouseWithoutBorders;
876      }
877      else if (value == "Peek")
878      {
879          return ESettingsWindowNames::Peek;
880      }
881      else if (value == "PowerAccent")
882      {
883          return ESettingsWindowNames::PowerAccent;
884      }
885      else if (value == "PowerLauncher")
886      {
887          return ESettingsWindowNames::PowerLauncher;
888      }
889      else if (value == "PowerPreview")
890      {
891          return ESettingsWindowNames::PowerPreview;
892      }
893      else if (value == "PowerRename")
894      {
895          return ESettingsWindowNames::PowerRename;
896      }
897      else if (value == "FileExplorer")
898      {
899          return ESettingsWindowNames::FileExplorer;
900      }
901      else if (value == "ShortcutGuide")
902      {
903          return ESettingsWindowNames::ShortcutGuide;
904      }
905      else if (value == "Hosts")
906      {
907          return ESettingsWindowNames::Hosts;
908      }
909      else if (value == "MeasureTool")
910      {
911          return ESettingsWindowNames::MeasureTool;
912      }
913      else if (value == "PowerOcr")
914      {
915          return ESettingsWindowNames::PowerOCR;
916      }
917      else if (value == "Workspaces")
918      {
919          return ESettingsWindowNames::Workspaces;
920      }
921      else if (value == "RegistryPreview")
922      {
923          return ESettingsWindowNames::RegistryPreview;
924      }
925      else if (value == "CropAndLock")
926      {
927          return ESettingsWindowNames::CropAndLock;
928      }
929      else if (value == "EnvironmentVariables")
930      {
931          return ESettingsWindowNames::EnvironmentVariables;
932      }
933      else if (value == "AdvancedPaste")
934      {
935          return ESettingsWindowNames::AdvancedPaste;
936      }
937      else if (value == "NewPlus")
938      {
939          return ESettingsWindowNames::NewPlus;
940      }
941      else if (value == "CmdPal")
942      {
943          return ESettingsWindowNames::CmdPal;
944      }
945      else if (value == "ZoomIt")
946      {
947          return ESettingsWindowNames::ZoomIt;
948      }
949      else if (value == "PowerDisplay")
950      {
951          return ESettingsWindowNames::PowerDisplay;
952      }
953      else
954      {
955          Logger::error(L"Can't convert string value={} to ESettingsWindowNames", winrt::to_hstring(value));
956          assert(false);
957      }
958  
959      return ESettingsWindowNames::Dashboard;
960  }