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 }