dllmain.cpp
  1  #include "pch.h"
  2  
  3  #include <interface/powertoy_module_interface.h>
  4  #include <common/SettingsAPI/settings_objects.h>
  5  #include "trace.h"
  6  #include <common/interop/shared_constants.h>
  7  #include <common/utils/string_utils.h>
  8  #include <common/utils/winapi_error.h>
  9  #include <common/utils/logger_helper.h>
 10  #include <common/utils/EventWaiter.h>
 11  
 12  extern "C" IMAGE_DOS_HEADER __ImageBase;
 13  
 14  BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
 15  {
 16      switch (ul_reason_for_call)
 17      {
 18      case DLL_PROCESS_ATTACH:
 19          Trace::RegisterProvider();
 20          break;
 21      case DLL_THREAD_ATTACH:
 22      case DLL_THREAD_DETACH:
 23          break;
 24      case DLL_PROCESS_DETACH:
 25          Trace::UnregisterProvider();
 26          break;
 27      }
 28      return TRUE;
 29  }
 30  
 31  const static wchar_t* MODULE_NAME = L"Measure Tool";
 32  const static wchar_t* MODULE_DESC = L"Measure your screen contents";
 33  
 34  namespace
 35  {
 36      const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
 37      const wchar_t JSON_KEY_WIN[] = L"win";
 38      const wchar_t JSON_KEY_ALT[] = L"alt";
 39      const wchar_t JSON_KEY_CTRL[] = L"ctrl";
 40      const wchar_t JSON_KEY_SHIFT[] = L"shift";
 41      const wchar_t JSON_KEY_CODE[] = L"code";
 42      const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
 43  }
 44  
 45  class MeasureTool : public PowertoyModuleIface
 46  {
 47  private:
 48      // The PowerToy state.
 49      bool m_enabled = false;
 50  
 51      Hotkey m_hotkey;
 52      HANDLE m_hProcess;
 53  
 54      HANDLE triggerEvent;
 55      EventWaiter triggerEventWaiter;
 56  
 57      void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
 58      {
 59          auto settingsObject = settings.get_raw_json();
 60          if (settingsObject.GetView().Size())
 61          {
 62              try
 63              {
 64                  auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT);
 65                  m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
 66                  m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
 67                  m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
 68                  m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
 69                  m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
 70              }
 71              catch (...)
 72              {
 73                  Logger::error("Failed to initialize Measure Tool start shortcut");
 74              }
 75          }
 76          else
 77          {
 78              Logger::info("MeasureTool settings are empty");
 79          }
 80  
 81          if (!m_hotkey.key)
 82          {
 83              Logger::info("MeasureTool is going to use default shortcut");
 84              m_hotkey.win = true;
 85              m_hotkey.ctrl = true;
 86              m_hotkey.alt = false;
 87              m_hotkey.shift = true;
 88              m_hotkey.key = 'M';
 89          }
 90      }
 91  
 92      bool is_process_running()
 93      {
 94          return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
 95      }
 96  
 97      void launch_process()
 98      {
 99          Logger::trace(L"Starting MeasureTool process");
100          unsigned long powertoys_pid = GetCurrentProcessId();
101  
102          std::wstring executable_args = L"";
103          executable_args.append(std::to_wstring(powertoys_pid));
104  
105          SHELLEXECUTEINFOW sei{ sizeof(sei) };
106          sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
107          sei.lpFile = L"WinUI3Apps\\PowerToys.MeasureToolUI.exe";
108          sei.nShow = SW_SHOWNORMAL;
109          sei.lpParameters = executable_args.data();
110          if (ShellExecuteExW(&sei))
111          {
112              Logger::trace("Successfully started the Measure Tool process");
113          }
114          else
115          {
116              Logger::error(L"MeasureTool failed to start. {}", get_last_error_or_default(GetLastError()));
117          }
118  
119          m_hProcess = sei.hProcess;
120      }
121  
122      // Load the settings file.
123      void init_settings()
124      {
125          try
126          {
127              // Load and parse the settings file for this PowerToy.
128              PowerToysSettings::PowerToyValues settings =
129                  PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
130  
131              parse_hotkey(settings);
132          }
133          catch (std::exception&)
134          {
135              Logger::warn(L"An exception occurred while loading the settings file");
136              // Error while loading from the settings file. Let default values stay as they are.
137          }
138      }
139  
140      void terminate_process()
141      {
142          TerminateProcess(m_hProcess, 1);
143      }
144  
145  public:
146      MeasureTool()
147      {
148          LoggerHelpers::init_logger(L"Measure Tool", L"ModuleInterface", "Measure Tool");
149          init_settings();
150  
151          triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT);
152          triggerEventWaiter.start(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT, [this](DWORD) {
153              on_hotkey(0);
154          });
155      }
156  
157      ~MeasureTool()
158      {
159          if (m_enabled)
160          {
161              terminate_process();
162          }
163          m_enabled = false;
164      }
165  
166      // Destroy the powertoy and free memory
167      virtual void destroy() override
168      {
169          delete this;
170      }
171  
172      // Return the localized display name of the powertoy
173      virtual const wchar_t* get_name() override
174      {
175          return MODULE_NAME;
176      }
177  
178      // Return the non localized key of the powertoy, this will be cached by the runner
179      virtual const wchar_t* get_key() override
180      {
181          return MODULE_NAME;
182      }
183  
184      // Return the configured status for the gpo policy for the module
185      virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
186      {
187          return powertoys_gpo::getConfiguredScreenRulerEnabledValue();
188      }
189  
190      // Return JSON with the configuration options.
191      virtual bool get_config(wchar_t* buffer, int* buffer_size) override
192      {
193          HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
194  
195          // Create a Settings object.
196          PowerToysSettings::Settings settings(hinstance, get_name());
197          settings.set_description(MODULE_DESC);
198  
199          return settings.serialize_to_buffer(buffer, buffer_size);
200      }
201  
202      // Signal from the Settings editor to call a custom action.
203      // This can be used to spawn more complex editors.
204      virtual void call_custom_action(const wchar_t*) override
205      {
206      }
207  
208      // Called by the runner to pass the updated settings values as a serialized JSON.
209      virtual void set_config(const wchar_t* config) override
210      {
211          try
212          {
213              // Parse the input JSON string.
214              PowerToysSettings::PowerToyValues values =
215                  PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
216  
217              parse_hotkey(values);
218              // If you don't need to do any custom processing of the settings, proceed
219              // to persists the values calling:
220              values.save_to_settings_file();
221              // Otherwise call a custom function to process the settings before saving them to disk:
222              // save_settings();
223          }
224          catch (std::exception&)
225          {
226              // Improper JSON.
227          }
228      }
229  
230      // Enable the powertoy
231      virtual void enable()
232      {
233          m_enabled = true;
234          Trace::EnableMeasureTool(true);
235      }
236  
237      // Disable the powertoy
238      virtual void disable()
239      {
240          if (m_enabled)
241          {
242              terminate_process();
243          }
244  
245          m_enabled = false;
246          Trace::EnableMeasureTool(false);
247      }
248  
249      // Returns if the powertoys is enabled
250      virtual bool is_enabled() override
251      {
252          return m_enabled;
253      }
254  
255      virtual bool on_hotkey(size_t /*hotkeyId*/) override
256      {
257          if (m_enabled)
258          {
259              Logger::trace(L"MeasureTool hotkey pressed");
260              if (is_process_running())
261              {
262                  terminate_process();
263              }
264              else
265              {
266                  launch_process();
267              }
268  
269              return true;
270          }
271  
272          return false;
273      }
274  
275      virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
276      {
277          if (m_hotkey.key)
278          {
279              if (hotkeys && buffer_size >= 1)
280              {
281                  hotkeys[0] = m_hotkey;
282              }
283  
284              return 1;
285          }
286          else
287          {
288              return 0;
289          }
290      }
291  };
292  
293  extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
294  {
295      return new MeasureTool();
296  }