dllmain.cpp
  1  // dllmain.cpp : Defines the entry point for the DLL application.
  2  #include "pch.h"
  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  #include <common/utils/resources.h>
 12  #include <common/utils/modulesRegistry.h>
 13  #include <common/utils/process_path.h>
 14  
 15  #include "resource.h"
 16  #include "Constants.h"
 17  
 18  extern "C" IMAGE_DOS_HEADER __ImageBase;
 19  
 20  BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
 21  {
 22      switch (ul_reason_for_call)
 23      {
 24      case DLL_PROCESS_ATTACH:
 25          Trace::RegisterProvider();
 26          break;
 27      case DLL_THREAD_ATTACH:
 28      case DLL_THREAD_DETACH:
 29          break;
 30      case DLL_PROCESS_DETACH:
 31          Trace::UnregisterProvider();
 32          break;
 33      }
 34      return TRUE;
 35  }
 36  
 37  const static wchar_t* MODULE_NAME = L"RegistryPreview";
 38  const static wchar_t* MODULE_DESC = L"A quick little utility to visualize and edit complex Windows Registry files.";
 39  
 40  namespace
 41  {
 42      const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
 43      const wchar_t JSON_KEY_ENABLED[] = L"enabled";
 44      const wchar_t JSON_KEY_DEFAULT_APP[] = L"default_reg_app";
 45  }
 46  
 47  class RegistryPreviewModule : public PowertoyModuleIface
 48  {
 49  
 50  private:
 51      bool m_enabled = false;
 52      bool m_default_app = false;
 53  
 54      //Hotkey m_hotkey;
 55      HANDLE m_hProcess;
 56  
 57      HANDLE triggerEvent;
 58      EventWaiter triggerEventWaiter;
 59  
 60      bool is_process_running()
 61      {
 62          return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
 63      }
 64  
 65      void launch_process()
 66      {
 67          if (m_enabled)
 68          {
 69              Logger::trace(L"Starting Registry Preview process");
 70              unsigned long powertoys_pid = GetCurrentProcessId();
 71  
 72              std::wstring executable_args = L"";
 73              executable_args.append(std::to_wstring(powertoys_pid));
 74  
 75              SHELLEXECUTEINFOW sei{ sizeof(sei) };
 76              sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
 77              sei.lpFile = L"WinUI3Apps\\PowerToys.RegistryPreview.exe";
 78              sei.nShow = SW_SHOWNORMAL;
 79              sei.lpParameters = executable_args.data();
 80              if (ShellExecuteExW(&sei))
 81              {
 82                  Logger::trace("Successfully started the Registry Preview process");
 83              }
 84              else
 85              {
 86                  Logger::error(L"Registry Preview failed to start. {}", get_last_error_or_default(GetLastError()));
 87              }
 88  
 89              m_hProcess = sei.hProcess;
 90          }
 91      }
 92  
 93      void terminate_process()
 94      {
 95          TerminateProcess(m_hProcess, 1);
 96      }
 97  
 98      void parse_default_app_settings(PowerToysSettings::PowerToyValues settings)
 99      {
100          const std::wstring installationDir = get_module_folderpath();
101          auto changeSet = getRegistryPreviewSetDefaultAppChangeSet(installationDir, true);
102  
103          auto settingsObject = settings.get_raw_json();
104          if (settingsObject.GetView().Size())
105          {
106              auto default_app = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedBoolean(JSON_KEY_DEFAULT_APP);
107  
108              if (default_app != m_default_app)
109              {
110                  m_default_app = default_app;
111                  auto result = default_app ? changeSet.apply() : changeSet.unApply();
112  
113                  if (!result)
114                  {
115                      Logger::error(L"Failed to {} default app registry change set.", default_app ? L"apply" : L"unapply");
116                  }
117  
118                  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
119              }
120          }
121          else
122          {
123              Logger::info("Registry Preview settings are empty");
124          }
125      }
126  
127      // Load the settings file.
128      void init_settings()
129      {
130          try
131          {
132              // Load and parse the settings file for this PowerToy.
133              PowerToysSettings::PowerToyValues settings =
134                  PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
135              
136              auto enabled = settings.get_bool_value(JSON_KEY_ENABLED);
137              auto result = true;
138              const std::wstring installationDir = get_module_folderpath();
139              auto enabledChangeSet = getRegistryPreviewChangeSet(installationDir, true);
140  
141              result = enabled ? enabledChangeSet.apply() : enabledChangeSet.unApply();
142              if (!result)
143              {
144                  Logger::error(L"Failed to {} enabled registry change set.", enabled ? L"apply" : L"unapply");
145              }
146  
147              parse_default_app_settings(settings);
148          }
149          catch (std::exception&)
150          {
151              Logger::error("Invalid json when trying to load the Registry Preview settings json from file.");
152          }
153      }
154  
155  public:
156      RegistryPreviewModule()
157      {
158          LoggerHelpers::init_logger(GET_RESOURCE_STRING(IDS_REGISTRYPREVIEW_NAME), L"ModuleInterface", "RegistryPreview");
159          Logger::info("Registry Preview object is constructing");
160  
161          init_settings();
162  
163          triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT);
164          triggerEventWaiter.start(CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT, [this](DWORD) {
165              on_hotkey(0);
166          });
167      }
168  
169      ~RegistryPreviewModule()
170      {
171          if (m_enabled)
172          {
173              terminate_process();
174          }
175          m_enabled = false;
176      }
177  
178      // Destroy the powertoy and free memory
179      virtual void destroy() override
180      {
181          delete this;
182      }
183  
184      // Return the localized display name of the powertoy
185      virtual const wchar_t* get_name() override
186      {
187          return MODULE_NAME;
188      }
189  
190      // Return the non localized key of the powertoy, this will be cached by the runner
191      virtual const wchar_t* get_key() override
192      {
193          return MODULE_NAME;
194      }
195  
196      // Return the configured status for the gpo policy for the module
197      virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
198      {
199          return powertoys_gpo::getConfiguredRegistryPreviewEnabledValue();
200      }
201  
202      // Return JSON with the configuration options.
203      virtual bool get_config(wchar_t* buffer, int* buffer_size) override
204      {
205          HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
206  
207          // Create a Settings object.
208          PowerToysSettings::Settings settings(hinstance, get_name());
209          settings.set_description(MODULE_DESC);
210  
211          return settings.serialize_to_buffer(buffer, buffer_size);
212      }
213  
214      // Pop open the app, if the OOBE page asks it to
215      virtual void call_custom_action(const wchar_t* action) override
216      {
217          try
218          {
219              PowerToysSettings::CustomActionObject action_object =
220                  PowerToysSettings::CustomActionObject::from_json_string(action);
221  
222              if (action_object.get_name() == L"Launch")
223              {
224                  launch_process();
225                  Trace::ActivateEditor();
226              }
227          }
228          catch (std::exception&)
229          {
230              Logger::error(L"Failed to parse action. {}", action);
231          }
232      }
233  
234      // Called by the runner to pass the updated settings values as a serialized JSON.
235      virtual void set_config(const wchar_t* config) override
236      {
237          try
238          {
239              // Parse the input JSON string.
240              PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
241  
242              parse_default_app_settings(values);
243  
244              values.save_to_settings_file();
245          }
246          catch (std::exception&)
247          {
248              Logger::error(L"Invalid json when trying to parse Registry Preview settings json.");
249          }
250      }
251  
252      // Enable the powertoy
253      virtual void enable()
254      {
255          const std::wstring installationDir = get_module_folderpath();
256  
257          if (!getRegistryPreviewChangeSet(installationDir, true).apply())
258          {
259              Logger::error(L"Applying registry changes failed");
260          }
261  
262          // let the DLL enable the app
263          m_enabled = true;
264          Trace::EnableRegistryPreview(true);
265      };
266  
267      virtual void disable()
268      {
269          if (m_enabled)
270          {
271              // let the DLL disable the app
272              terminate_process();
273  
274              Trace::EnableRegistryPreview(false);
275              Logger::trace(L"Disabling Registry Preview...");
276  
277              // Remove the Registry setting so preview doesn't work anymore
278              const std::wstring installationDir = get_module_folderpath();
279  
280              if (!getRegistryPreviewChangeSet(installationDir, true).unApply())
281              {
282                  Logger::error(L"Unapplying registry changes failed");
283              }
284  
285              SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
286          }
287  
288          m_enabled = false;
289      }
290  
291      // Returns if the powertoys is enabled
292      virtual bool is_enabled() override
293      {
294          return m_enabled;
295      }
296  
297      // Respond to a "click" from the launcher
298      virtual bool on_hotkey(size_t /*hotkeyId*/) override
299      {
300          if (m_enabled)
301          {
302              Logger::trace(L"Registry Preview hotkey pressed");
303              if (is_process_running())
304              {
305                  terminate_process();
306              }
307              else
308              {
309                  launch_process();
310              }
311  
312              return true;
313          }
314  
315          return false;
316      }
317  };
318  
319  extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
320  {
321      return new RegistryPreviewModule();
322  }