Reactos
at listview 376 lines 11 kB view raw
1/* 2 * PROJECT: ReactOS CTF Monitor 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Registry watcher 5 * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8#include "precomp.h" 9#include "CRegWatcher.h" 10#include <ime/indicml.h> 11 12// The event handles to use in watching 13HANDLE CRegWatcher::s_ahWatchEvents[WI_REGEVTS_MAX] = { NULL }; 14 15// The registry entries to watch 16WATCHENTRY CRegWatcher::s_WatchEntries[WI_REGEVTS_MAX] = 17{ 18 { HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Toggle") }, // WI_TOGGLE 19 { HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\CTF\\TIP") }, // WI_MACHINE_TIF 20 { HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Preload") }, // WI_PRELOAD 21 { HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run") }, // WI_RUN 22 { HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\CTF\\TIP") }, // WI_USER_TIF 23 { HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Speech") }, // WI_USER_SPEECH 24 { HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance") }, // WI_APPEARANCE 25 { HKEY_CURRENT_USER, TEXT("Control Panel\\Colors") }, // WI_COLORS 26 { HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop\\WindowMetrics") }, // WI_WINDOW_METRICS 27 { HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Speech") }, // WI_MACHINE_SPEECH 28 { HKEY_CURRENT_USER, TEXT("Keyboard Layout") }, // WI_KEYBOARD_LAYOUT 29 { HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\CTF\\Assemblies") }, // WI_ASSEMBLIES 30}; 31 32// The timer IDs: For delaying ignitions 33UINT CRegWatcher::s_nSysColorTimerId = 0; 34UINT CRegWatcher::s_nKbdToggleTimerId = 0; 35UINT CRegWatcher::s_nRegImxTimerId = 0; 36 37// %WINDIR%/IME/sptip.dll!TF_CreateLangProfileUtil 38typedef HRESULT (WINAPI* FN_TF_CreateLangProfileUtil)(ITfFnLangProfileUtil**); 39 40BOOL 41CRegWatcher::Init() 42{ 43 // NOTE: We don't support Win95/98/Me 44#ifdef SUPPORT_WIN9X 45 if (!(g_dwOsInfo & CIC_OSINFO_NT)) 46 s_WatchEntries[WI_RUN].hRootKey = HKEY_LOCAL_MACHINE; 47#endif 48 49 // Create some nameless events and initialize them 50 for (SIZE_T iEvent = 0; iEvent < _countof(s_ahWatchEvents); ++iEvent) 51 { 52 s_ahWatchEvents[iEvent] = ::CreateEvent(NULL, TRUE, FALSE, NULL); 53 InitEvent(iEvent, FALSE); 54 } 55 56 // Internat.exe is an enemy of ctfmon.exe 57 KillInternat(); 58 59 UpdateSpTip(); 60 61 return TRUE; 62} 63 64VOID 65CRegWatcher::Uninit() 66{ 67 for (SIZE_T iEvent = 0; iEvent < _countof(s_ahWatchEvents); ++iEvent) 68 { 69 // Close the key 70 WATCHENTRY& entry = s_WatchEntries[iEvent]; 71 if (entry.hKey) 72 { 73 ::RegCloseKey(entry.hKey); 74 entry.hKey = NULL; 75 } 76 77 // Close the event handle 78 HANDLE& hEvent = s_ahWatchEvents[iEvent]; 79 if (hEvent) 80 { 81 ::CloseHandle(hEvent); 82 hEvent = NULL; 83 } 84 } 85} 86 87// advapi32!RegNotifyChangeKeyValue 88typedef LONG (WINAPI *FN_RegNotifyChangeKeyValue)(HKEY, BOOL, DWORD, HANDLE, BOOL); 89 90LONG WINAPI 91DelayedRegNotifyChangeKeyValue( 92 HKEY hKey, 93 BOOL bWatchSubtree, 94 DWORD dwNotifyFilter, 95 HANDLE hEvent, 96 BOOL fAsynchronous) 97{ 98 static FN_RegNotifyChangeKeyValue s_fnRegNotifyChangeKeyValue = NULL; 99 100 if (!s_fnRegNotifyChangeKeyValue) 101 { 102 HINSTANCE hAdvApi32 = cicGetSystemModuleHandle(TEXT("advapi32.dll"), FALSE); 103 s_fnRegNotifyChangeKeyValue = 104 (FN_RegNotifyChangeKeyValue)GetProcAddress(hAdvApi32, "RegNotifyChangeKeyValue"); 105 if (!s_fnRegNotifyChangeKeyValue) 106 return ERROR_CALL_NOT_IMPLEMENTED; 107 } 108 109 return s_fnRegNotifyChangeKeyValue(hKey, bWatchSubtree, dwNotifyFilter, hEvent, fAsynchronous); 110} 111 112BOOL 113CRegWatcher::InitEvent( 114 _In_ SIZE_T iEvent, 115 _In_ BOOL bResetEvent) 116{ 117 if (iEvent >= _countof(s_ahWatchEvents)) 118 return FALSE; 119 120 // Reset the signal status 121 if (bResetEvent) 122 ::ResetEvent(s_ahWatchEvents[iEvent]); 123 124 // Close once to re-open 125 WATCHENTRY& entry = s_WatchEntries[iEvent]; 126 if (entry.hKey) 127 { 128 ::RegCloseKey(entry.hKey); 129 entry.hKey = NULL; 130 } 131 132 // Open or create a registry key to watch registry key 133 LSTATUS error; 134 error = ::RegOpenKeyEx(entry.hRootKey, entry.pszSubKey, 0, KEY_READ, &entry.hKey); 135 if (error != ERROR_SUCCESS) 136 { 137 error = ::RegCreateKeyEx(entry.hRootKey, entry.pszSubKey, 0, NULL, 0, 138 KEY_ALL_ACCESS, NULL, &entry.hKey, NULL); 139 if (error != ERROR_SUCCESS) 140 return FALSE; 141 } 142 143 // Start registry watching 144 error = DelayedRegNotifyChangeKeyValue(entry.hKey, 145 TRUE, 146 REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME, 147 s_ahWatchEvents[iEvent], 148 TRUE); 149#ifndef NDEBUG 150 if (error != ERROR_SUCCESS) 151 OutputDebugStringA("RegNotifyChangeKeyValue failed\n"); 152#endif 153 return error == ERROR_SUCCESS; 154} 155 156VOID 157CRegWatcher::UpdateSpTip() 158{ 159 // Post message 0x8002 to "SapiTipWorkerClass" windows 160 ::EnumWindows(EnumWndProc, 0); 161 162 // Clear "ProfileInitialized" value 163 HKEY hKey; 164 LSTATUS error = ::RegOpenKeyEx(HKEY_CURRENT_USER, 165 TEXT("SOFTWARE\\Microsoft\\CTF\\Sapilayr"), 166 0, KEY_WRITE, &hKey); 167 if (error == ERROR_SUCCESS) 168 { 169 DWORD dwValue = 0, cbValue = sizeof(dwValue); 170 ::RegSetValueEx(hKey, TEXT("ProfileInitialized"), NULL, REG_DWORD, (LPBYTE)&dwValue, cbValue); 171 ::RegCloseKey(hKey); 172 } 173 174 // Get %WINDIR%/IME/sptip.dll!TF_CreateLangProfileUtil function 175 HINSTANCE hSPTIP = cicLoadSystemLibrary(TEXT("IME\\sptip.dll"), TRUE); 176 FN_TF_CreateLangProfileUtil fnTF_CreateLangProfileUtil = 177 (FN_TF_CreateLangProfileUtil)::GetProcAddress(hSPTIP, "TF_CreateLangProfileUtil"); 178 if (fnTF_CreateLangProfileUtil) 179 { 180 // Call it 181 ITfFnLangProfileUtil *pProfileUtil = NULL; 182 HRESULT hr = fnTF_CreateLangProfileUtil(&pProfileUtil); 183 if ((hr == S_OK) && pProfileUtil) // Success! 184 { 185 // Register profile 186 hr = pProfileUtil->RegisterActiveProfiles(); 187 if (hr == S_OK) 188 TF_InvalidAssemblyListCacheIfExist(); // Invalidate the assembly list cache 189 190 pProfileUtil->Release(); 191 } 192 } 193 194 if (hSPTIP) 195 ::FreeLibrary(hSPTIP); 196} 197 198VOID 199CRegWatcher::KillInternat() 200{ 201 HKEY hKey; 202 WATCHENTRY& entry = s_WatchEntries[WI_RUN]; 203 204 // Delete internat.exe from registry "Run" key 205 LSTATUS error = ::RegOpenKeyEx(entry.hRootKey, entry.pszSubKey, 0, KEY_ALL_ACCESS, &hKey); 206 if (error == ERROR_SUCCESS) 207 { 208 ::RegDeleteValue(hKey, TEXT("internat.exe")); 209 ::RegCloseKey(hKey); 210 } 211 212 // Kill the "Indicator" window (that internat.exe creates) 213 HWND hwndInternat = ::FindWindow(INDICATOR_CLASS, NULL); 214 if (hwndInternat) 215 ::PostMessage(hwndInternat, WM_CLOSE, 0, 0); 216} 217 218// Post message 0x8002 to every "SapiTipWorkerClass" window. 219// Called from CRegWatcher::UpdateSpTip 220BOOL CALLBACK 221CRegWatcher::EnumWndProc( 222 _In_ HWND hWnd, 223 _In_ LPARAM lParam) 224{ 225 TCHAR ClassName[MAX_PATH]; 226 227 UNREFERENCED_PARAMETER(lParam); 228 229 if (::GetClassName(hWnd, ClassName, _countof(ClassName)) && 230 _tcsicmp(ClassName, TEXT("SapiTipWorkerClass")) == 0) 231 { 232 PostMessage(hWnd, 0x8002, 0, 0); // FIXME: Magic number 233 } 234 235 return TRUE; 236} 237 238VOID CALLBACK 239CRegWatcher::SysColorTimerProc( 240 _In_ HWND hwnd, 241 _In_ UINT uMsg, 242 _In_ UINT_PTR idEvent, 243 _In_ DWORD dwTime) 244{ 245 UNREFERENCED_PARAMETER(hwnd); 246 UNREFERENCED_PARAMETER(uMsg); 247 UNREFERENCED_PARAMETER(idEvent); 248 UNREFERENCED_PARAMETER(dwTime); 249 250 // Cancel the timer 251 if (s_nSysColorTimerId) 252 { 253 ::KillTimer(NULL, s_nSysColorTimerId); 254 s_nSysColorTimerId = 0; 255 } 256 257 TF_PostAllThreadMsg(15, 16); 258} 259 260VOID 261CRegWatcher::StartSysColorChangeTimer() 262{ 263 // Call SysColorTimerProc 0.5 seconds later (Delayed) 264 if (s_nSysColorTimerId) 265 { 266 ::KillTimer(NULL, s_nSysColorTimerId); 267 s_nSysColorTimerId = 0; 268 } 269 s_nSysColorTimerId = ::SetTimer(NULL, 0, 500, SysColorTimerProc); 270} 271 272VOID CALLBACK 273CRegWatcher::RegImxTimerProc( 274 _In_ HWND hwnd, 275 _In_ UINT uMsg, 276 _In_ UINT_PTR idEvent, 277 _In_ DWORD dwTime) 278{ 279 UNREFERENCED_PARAMETER(hwnd); 280 UNREFERENCED_PARAMETER(uMsg); 281 UNREFERENCED_PARAMETER(idEvent); 282 UNREFERENCED_PARAMETER(dwTime); 283 284 // Cancel the timer 285 if (s_nRegImxTimerId) 286 { 287 ::KillTimer(NULL, s_nRegImxTimerId); 288 s_nRegImxTimerId = 0; 289 } 290 291 TF_InvalidAssemblyListCache(); 292 TF_PostAllThreadMsg(12, 16); 293} 294 295VOID CALLBACK 296CRegWatcher::KbdToggleTimerProc( 297 _In_ HWND hwnd, 298 _In_ UINT uMsg, 299 _In_ UINT_PTR idEvent, 300 _In_ DWORD dwTime) 301{ 302 UNREFERENCED_PARAMETER(hwnd); 303 UNREFERENCED_PARAMETER(uMsg); 304 UNREFERENCED_PARAMETER(idEvent); 305 UNREFERENCED_PARAMETER(dwTime); 306 307 // Cancel the timer 308 if (s_nKbdToggleTimerId) 309 { 310 ::KillTimer(NULL, s_nKbdToggleTimerId); 311 s_nKbdToggleTimerId = 0; 312 } 313 314 TF_PostAllThreadMsg(11, 16); 315} 316 317VOID 318CRegWatcher::OnEvent( 319 _In_ SIZE_T iEvent) 320{ 321 if (iEvent >= _countof(s_ahWatchEvents)) 322 return; 323 324 InitEvent(iEvent, TRUE); 325 326 switch (iEvent) 327 { 328 case WI_TOGGLE: 329 { 330 // Call KbdToggleTimerProc 0.5 seconds later (Delayed) 331 if (s_nKbdToggleTimerId) 332 { 333 ::KillTimer(NULL, s_nKbdToggleTimerId); 334 s_nKbdToggleTimerId = 0; 335 } 336 s_nKbdToggleTimerId = ::SetTimer(NULL, 0, 500, KbdToggleTimerProc); 337 break; 338 } 339 case WI_MACHINE_TIF: 340 case WI_PRELOAD: 341 case WI_USER_TIF: 342 case WI_MACHINE_SPEECH: 343 case WI_KEYBOARD_LAYOUT: 344 case WI_ASSEMBLIES: 345 { 346 if (iEvent == WI_MACHINE_SPEECH) 347 UpdateSpTip(); 348 349 // Call RegImxTimerProc 0.2 seconds later (Delayed) 350 if (s_nRegImxTimerId) 351 { 352 ::KillTimer(NULL, s_nRegImxTimerId); 353 s_nRegImxTimerId = 0; 354 } 355 s_nRegImxTimerId = ::SetTimer(NULL, 0, 200, RegImxTimerProc); 356 break; 357 } 358 case WI_RUN: // The "Run" key is changed 359 { 360 KillInternat(); // Deny internat.exe the right to live 361 break; 362 } 363 case WI_USER_SPEECH: 364 case WI_APPEARANCE: 365 case WI_COLORS: 366 case WI_WINDOW_METRICS: 367 { 368 StartSysColorChangeTimer(); 369 break; 370 } 371 default: 372 { 373 break; 374 } 375 } 376}