main.cpp
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 #include <Windows.h> 6 #include <Tlhelp32.h> 7 #include <iostream> 8 #include <string> 9 #include <filesystem> 10 #include "ModuleLoader.h" 11 #include "SettingsLoader.h" 12 #include "HotkeyManager.h" 13 #include "ConsoleHost.h" 14 15 namespace 16 { 17 void PrintUsage() 18 { 19 std::wcout << L"PowerToys Module Loader - Standalone utility for loading and testing PowerToy modules\n\n"; 20 std::wcout << L"Usage: ModuleLoader.exe <module_dll_path>\n\n"; 21 std::wcout << L"Arguments:\n"; 22 std::wcout << L" module_dll_path Path to the PowerToy module DLL (e.g., CursorWrap.dll)\n\n"; 23 std::wcout << L"Behavior:\n"; 24 std::wcout << L" - Automatically discovers settings from %%LOCALAPPDATA%%\\Microsoft\\PowerToys\\<ModuleName>\\settings.json\n"; 25 std::wcout << L" - Loads and enables the module\n"; 26 std::wcout << L" - Registers module hotkeys\n"; 27 std::wcout << L" - Runs until Ctrl+C is pressed\n\n"; 28 std::wcout << L"Examples:\n"; 29 std::wcout << L" ModuleLoader.exe x64\\Debug\\modules\\CursorWrap.dll\n"; 30 std::wcout << L" ModuleLoader.exe \"C:\\Program Files\\PowerToys\\modules\\MouseHighlighter.dll\"\n\n"; 31 std::wcout << L"Notes:\n"; 32 std::wcout << L" - Only non-UI modules are supported\n"; 33 std::wcout << L" - Module must have a valid settings.json file\n"; 34 std::wcout << L" - Debug output is written to module's log directory\n"; 35 } 36 37 std::wstring ExtractModuleName(const std::wstring& dllPath) 38 { 39 std::filesystem::path path(dllPath); 40 std::wstring filename = path.stem().wstring(); 41 42 // Remove "PowerToys." prefix if present (case-insensitive) 43 const std::wstring powerToysPrefix = L"PowerToys."; 44 if (filename.length() >= powerToysPrefix.length()) 45 { 46 // Check if filename starts with "PowerToys." (case-insensitive) 47 if (_wcsnicmp(filename.c_str(), powerToysPrefix.c_str(), powerToysPrefix.length()) == 0) 48 { 49 filename = filename.substr(powerToysPrefix.length()); 50 } 51 } 52 53 // Common PowerToys module naming patterns 54 // Remove common suffixes if present 55 const std::wstring suffixes[] = { L"Module", L"ModuleInterface", L"Interface" }; 56 for (const auto& suffix : suffixes) 57 { 58 if (filename.size() > suffix.size()) 59 { 60 size_t pos = filename.rfind(suffix); 61 if (pos != std::wstring::npos && pos + suffix.size() == filename.size()) 62 { 63 filename = filename.substr(0, pos); 64 break; 65 } 66 } 67 } 68 69 return filename; 70 } 71 } 72 73 int wmain(int argc, wchar_t* argv[]) 74 { 75 std::wcout << L"PowerToys Module Loader v1.0\n"; 76 std::wcout << L"=============================\n\n"; 77 78 // Check if PowerToys.exe is running 79 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 80 if (hSnapshot != INVALID_HANDLE_VALUE) 81 { 82 PROCESSENTRY32W pe32; 83 pe32.dwSize = sizeof(PROCESSENTRY32W); 84 85 bool powerToysRunning = false; 86 if (Process32FirstW(hSnapshot, &pe32)) 87 { 88 do 89 { 90 if (_wcsicmp(pe32.szExeFile, L"PowerToys.exe") == 0) 91 { 92 powerToysRunning = true; 93 break; 94 } 95 } while (Process32NextW(hSnapshot, &pe32)); 96 } 97 CloseHandle(hSnapshot); 98 99 if (powerToysRunning) 100 { 101 // Display warning with VT100 colors 102 // Yellow background (43m), black text (30m), bold (1m) 103 std::wcout << L"\033[1;43;30m WARNING \033[0m PowerToys.exe is currently running!\n\n"; 104 105 // Red text for important message 106 std::wcout << L"\033[1;31m"; 107 std::wcout << L"Running ModuleLoader while PowerToys is active may cause conflicts:\n"; 108 std::wcout << L" - Duplicate hotkey registrations\n"; 109 std::wcout << L" - Conflicting module instances\n"; 110 std::wcout << L" - Unexpected behavior\n"; 111 std::wcout << L"\033[0m\n"; // Reset color 112 113 // Cyan text for recommendation 114 std::wcout << L"\033[1;36m"; 115 std::wcout << L"RECOMMENDATION: Exit PowerToys before continuing.\n"; 116 std::wcout << L"\033[0m\n"; // Reset color 117 118 // Yellow text for prompt 119 std::wcout << L"\033[1;33m"; 120 std::wcout << L"Do you want to continue anyway? (y/N): "; 121 std::wcout << L"\033[0m"; // Reset color 122 123 wchar_t response = L'\0'; 124 std::wcin >> response; 125 126 if (response != L'y' && response != L'Y') 127 { 128 std::wcout << L"\nExiting. Please close PowerToys and try again.\n"; 129 return 1; 130 } 131 132 std::wcout << L"\n"; 133 } 134 } 135 136 // Parse command-line arguments 137 if (argc < 2) 138 { 139 std::wcerr << L"Error: Missing required argument <module_dll_path>\n\n"; 140 PrintUsage(); 141 return 1; 142 } 143 144 const std::wstring dllPath = argv[1]; 145 146 // Validate DLL exists 147 if (!std::filesystem::exists(dllPath)) 148 { 149 std::wcerr << L"Error: Module DLL not found: " << dllPath << L"\n"; 150 return 1; 151 } 152 153 std::wcout << L"Loading module: " << dllPath << L"\n"; 154 155 // Extract module name from DLL path 156 std::wstring moduleName = ExtractModuleName(dllPath); 157 std::wcout << L"Detected module name: " << moduleName << L"\n\n"; 158 159 try 160 { 161 // Load settings for the module 162 std::wcout << L"Loading settings...\n"; 163 SettingsLoader settingsLoader; 164 std::wstring settingsJson = settingsLoader.LoadSettings(moduleName, dllPath); 165 166 if (settingsJson.empty()) 167 { 168 std::wcerr << L"Error: Could not load settings for module '" << moduleName << L"'\n"; 169 std::wcerr << L"Expected location: %LOCALAPPDATA%\\Microsoft\\PowerToys\\" << moduleName << L"\\settings.json\n"; 170 return 1; 171 } 172 173 std::wcout << L"Settings loaded successfully.\n\n"; 174 175 // Load the module DLL 176 std::wcout << L"Loading module DLL...\n"; 177 ModuleLoader moduleLoader; 178 if (!moduleLoader.Load(dllPath)) 179 { 180 std::wcerr << L"Error: Failed to load module DLL\n"; 181 return 1; 182 } 183 184 std::wcout << L"Module DLL loaded successfully.\n"; 185 std::wcout << L"Module key: " << moduleLoader.GetModuleKey() << L"\n"; 186 std::wcout << L"Module name: " << moduleLoader.GetModuleName() << L"\n\n"; 187 188 // Apply settings to the module 189 std::wcout << L"Applying settings to module...\n"; 190 moduleLoader.SetConfig(settingsJson); 191 std::wcout << L"Settings applied.\n\n"; 192 193 // Register hotkeys 194 std::wcout << L"Registering module hotkeys...\n"; 195 HotkeyManager hotkeyManager; 196 if (!hotkeyManager.RegisterModuleHotkeys(moduleLoader)) 197 { 198 std::wcerr << L"Warning: Failed to register some hotkeys\n"; 199 } 200 std::wcout << L"Hotkeys registered: " << hotkeyManager.GetRegisteredCount() << L"\n\n"; 201 202 // Enable the module 203 std::wcout << L"Enabling module...\n"; 204 moduleLoader.Enable(); 205 std::wcout << L"Module enabled.\n\n"; 206 207 // Display status 208 std::wcout << L"=============================\n"; 209 std::wcout << L"Module is now running!\n"; 210 std::wcout << L"=============================\n\n"; 211 std::wcout << L"Module Status:\n"; 212 std::wcout << L" - Name: " << moduleLoader.GetModuleName() << L"\n"; 213 std::wcout << L" - Key: " << moduleLoader.GetModuleKey() << L"\n"; 214 std::wcout << L" - Enabled: " << (moduleLoader.IsEnabled() ? L"Yes" : L"No") << L"\n"; 215 std::wcout << L" - Hotkeys: " << hotkeyManager.GetRegisteredCount() << L" registered\n\n"; 216 217 if (hotkeyManager.GetRegisteredCount() > 0) 218 { 219 std::wcout << L"Registered Hotkeys:\n"; 220 hotkeyManager.PrintHotkeys(); 221 std::wcout << L"\n"; 222 } 223 224 std::wcout << L"Press Ctrl+C to exit.\n"; 225 std::wcout << L"You can press the module's hotkey to toggle its functionality.\n\n"; 226 227 // Run the message loop 228 ConsoleHost consoleHost(moduleLoader, hotkeyManager); 229 consoleHost.Run(); 230 231 // Cleanup 232 std::wcout << L"\nShutting down...\n"; 233 moduleLoader.Disable(); 234 hotkeyManager.UnregisterAll(); 235 236 std::wcout << L"Module unloaded successfully.\n"; 237 return 0; 238 } 239 catch (const std::exception& ex) 240 { 241 std::wcerr << L"Fatal error: " << ex.what() << L"\n"; 242 return 1; 243 } 244 }