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 }