/ tools / module_loader / src / main.cpp
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  }