main.cpp
  1  #include "pch.h"
  2  #include <Windows.h>
  3  #include <common/utils/window.h>
  4  #include <common/SettingsAPI/settings_helpers.h>
  5  #include <common/utils/ProcessWaiter.h>
  6  #include <common/utils/winapi_error.h>
  7  #include <common/utils/UnhandledExceptionHandler.h>
  8  #include <common/utils/logger_helper.h>
  9  #include <common/utils/EventWaiter.h>
 10  #include <common/utils/gpo.h>
 11  
 12  #include <common/Telemetry/EtwTrace/EtwTrace.h>
 13  
 14  #include "shortcut_guide.h"
 15  #include "target_state.h"
 16  #include "ShortcutGuideConstants.h"
 17  #include "trace.h"
 18  
 19  const std::wstring instanceMutexName = L"Local\\PowerToys_ShortcutGuide_InstanceMutex";
 20  
 21  // set current path to the executable path
 22  bool SetCurrentPath()
 23  {
 24      TCHAR buffer[MAX_PATH] = { 0 };
 25      if (!GetModuleFileName(NULL, buffer, MAX_PATH))
 26      {
 27          Logger::error(L"Failed to get module path. {}", get_last_error_or_default(GetLastError()));
 28          return false;
 29      }
 30  
 31      if (!PathRemoveFileSpec(buffer))
 32      {
 33          Logger::error(L"Failed to remove file from module path. {}", get_last_error_or_default(GetLastError()));
 34          return false;
 35      }
 36  
 37      std::error_code err;
 38      std::filesystem::current_path(buffer, err);
 39      if (err.value())
 40      {
 41          Logger::error("Failed to set current path. {}", err.message());
 42          return false;
 43      }
 44  
 45      return true;
 46  }
 47  
 48  int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInstance*/, _In_ PWSTR lpCmdLine, _In_ int /*nCmdShow*/)
 49  {
 50      winrt::init_apartment();
 51      LoggerHelpers::init_logger(ShortcutGuideConstants::ModuleKey, L"ShortcutGuide", LogSettings::shortcutGuideLoggerName);
 52  
 53      Shared::Trace::ETWTrace trace;
 54      trace.UpdateState(true);
 55  
 56      if (powertoys_gpo::getConfiguredShortcutGuideEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
 57      {
 58          Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
 59          return 0;
 60      }
 61  
 62      InitUnhandledExceptionHandler();
 63      Logger::trace("Starting Shortcut Guide");
 64  
 65      if (!SetCurrentPath())
 66      {
 67          return false;
 68      }
 69  
 70      Trace::RegisterProvider();
 71      if (std::wstring(lpCmdLine).find(L' ') != std::wstring::npos)
 72      {
 73          Logger::trace("Sending settings telemetry");
 74          auto settings = OverlayWindow::GetSettings();
 75          Trace::SendSettings(settings);
 76          Trace::UnregisterProvider();
 77          return 0;
 78      }
 79  
 80      auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str());
 81      if (mutex == nullptr)
 82      {
 83          Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError()));
 84      }
 85  
 86      if (GetLastError() == ERROR_ALREADY_EXISTS)
 87      {
 88          Logger::warn(L"Shortcut Guide instance is already running");
 89          Trace::UnregisterProvider();
 90          return 0;
 91      }
 92  
 93      std::wstring pid = std::wstring(lpCmdLine);
 94      if (!pid.empty())
 95      {
 96          auto mainThreadId = GetCurrentThreadId();
 97          ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) {
 98              if (err != ERROR_SUCCESS)
 99              {
100                  Logger::error(L"Failed to wait for parent process exit. {}", get_last_error_or_default(err));
101              }
102              else
103              {
104                  Logger::trace(L"PowerToys runner exited.");
105              }
106  
107              Logger::trace(L"Exiting Shortcut Guide");
108              PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
109          });
110      }
111  
112      auto hwnd = GetForegroundWindow();
113      auto window = OverlayWindow(hwnd);
114      EventWaiter exitEventWaiter;
115      if (window.IsDisabled())
116      {
117          Logger::trace("SG is disabled for the current foreground app. Exiting SG");
118          Trace::UnregisterProvider();
119          return 0;
120      }
121      else
122      {
123          auto mainThreadId = GetCurrentThreadId();
124          exitEventWaiter.start(CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, [mainThreadId, &window](DWORD err) {
125              if (err != ERROR_SUCCESS)
126              {
127                  Logger::error(L"Failed to wait for {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(err));
128              }
129              else
130              {
131                  Logger::trace(L"{} event was signaled", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT);
132              }
133  
134              window.CloseWindow(HideWindowType::THE_SHORTCUT_PRESSED, mainThreadId);
135          });
136      }
137  
138      window.ShowWindow();
139      run_message_loop();
140  
141      trace.Flush();
142      Trace::UnregisterProvider();
143      return 0;
144  }