Reactos
at listview 385 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: Providing Language Bar front-end 5 * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8#include "precomp.h" 9#include "CRegWatcher.h" 10#include "CLoaderWnd.h" 11 12// kernel32!SetProcessShutdownParameters 13typedef BOOL (WINAPI *FN_SetProcessShutdownParameters)(DWORD, DWORD); 14FN_SetProcessShutdownParameters g_fnSetProcessShutdownParameters = NULL; 15 16// kernel32!GetSystemWow64DirectoryA 17typedef UINT (WINAPI *FN_GetSystemWow64DirectoryA)(LPSTR, UINT); 18FN_GetSystemWow64DirectoryA g_fnGetSystemWow64DirectoryA = NULL; 19// kernel32!GetSystemWow64DirectoryW 20typedef UINT (WINAPI *FN_GetSystemWow64DirectoryW)(LPWSTR, UINT); 21FN_GetSystemWow64DirectoryW g_fnGetSystemWow64DirectoryW = NULL; 22 23HINSTANCE g_hInst = NULL; // The application instance 24HINSTANCE g_hKernel32 = NULL; // The "kernel32.dll" instance 25UINT g_uACP = CP_ACP; // The active codepage 26BOOL g_fWinLogon = FALSE; // Is it a log-on process? 27HANDLE g_hCicMutex = NULL; // The Cicero mutex 28BOOL g_bOnWow64 = FALSE; // Is the app running on WoW64? 29BOOL g_fNoRunKey = FALSE; // Don't write registry key "Run"? 30BOOL g_fJustRunKey = FALSE; // Just write registry key "Run"? 31DWORD g_dwOsInfo = 0; // The OS version info. See cicGetOSInfo 32CLoaderWnd* g_pLoaderWnd = NULL; // Tipbar loader window 33 34static VOID 35ParseCommandLine( 36 _In_ LPCTSTR pszCmdLine) 37{ 38 g_fNoRunKey = g_fJustRunKey = FALSE; 39 40 for (LPCTSTR pch = pszCmdLine; *pch; ++pch) 41 { 42 // Skip space 43 while (*pch == TEXT(' ')) 44 ++pch; 45 46 if (*pch == TEXT('\0')) 47 return; 48 49 if ((*pch == TEXT('-')) || (*pch == TEXT('/'))) 50 { 51 ++pch; 52 switch (*pch) 53 { 54 case TEXT('N'): case TEXT('n'): // Found "/N" option 55 g_fNoRunKey = TRUE; 56 break; 57 58 case TEXT('R'): case TEXT('r'): // Found "/R" option 59 g_fJustRunKey = TRUE; 60 break; 61 62 case UNICODE_NULL: 63 return; 64 65 default: 66 break; 67 } 68 } 69 } 70} 71 72static VOID 73WriteRegRun(VOID) 74{ 75 if (g_fNoRunKey) // If "/N" option is specified 76 return; // Don't write 77 78 // Open "Run" key 79 HKEY hKey; 80 LSTATUS error = ::RegCreateKey(HKEY_CURRENT_USER, 81 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), 82 &hKey); 83 if (error != ERROR_SUCCESS) 84 return; 85 86 // Write the module path 87 CicSystemModulePath ModPath; 88 if (ModPath.Init(TEXT("ctfmon.exe"), FALSE)) 89 { 90 DWORD cbData = (ModPath.m_cchPath + 1) * sizeof(TCHAR); 91 ::RegSetValueEx(hKey, TEXT("ctfmon.exe"), 0, REG_SZ, (BYTE*)ModPath.m_szPath, cbData); 92 } 93 94 ::RegCloseKey(hKey); 95} 96 97static HRESULT 98GetGlobalCompartment( 99 _In_ REFGUID guid, 100 _Inout_ ITfCompartment **ppComp) 101{ 102 *ppComp = NULL; 103 104 ITfCompartmentMgr *pCompMgr = NULL; 105 HRESULT hr = TF_GetGlobalCompartment(&pCompMgr); 106 if (FAILED(hr)) 107 return hr; 108 109 if (!pCompMgr) 110 return E_FAIL; 111 112 hr = pCompMgr->GetCompartment(guid, ppComp); 113 pCompMgr->Release(); 114 return hr; 115} 116 117static HRESULT 118SetGlobalCompartmentDWORD( 119 _In_ REFGUID guid, 120 _In_ DWORD dwValue) 121{ 122 HRESULT hr; 123 VARIANT vari; 124 ITfCompartment *pComp; 125 126 hr = GetGlobalCompartment(guid, &pComp); 127 if (FAILED(hr)) 128 return hr; 129 130 V_VT(&vari) = VT_I4; 131 V_I4(&vari) = dwValue; 132 hr = pComp->SetValue(0, &vari); 133 134 pComp->Release(); 135 return hr; 136} 137 138static BOOL 139CheckX64System( 140 _In_ LPTSTR lpCmdLine) 141{ 142 // Is the system x64? 143 SYSTEM_INFO SystemInfo; 144 ::GetSystemInfo(&SystemInfo); 145 if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_IA64 || 146 SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64) 147 { 148 return FALSE; 149 } 150 151 // Get GetSystemWow64DirectoryW function 152 g_hKernel32 = cicGetSystemModuleHandle(TEXT("kernel32.dll"), FALSE); 153#ifdef UNICODE 154 g_fnGetSystemWow64DirectoryW = 155 (FN_GetSystemWow64DirectoryW)::GetProcAddress(g_hKernel32, "GetSystemWow64DirectoryW"); 156 if (!g_fnGetSystemWow64DirectoryW) 157 return FALSE; 158#else 159 g_fnGetSystemWow64DirectoryA = 160 (FN_GetSystemWow64DirectoryA)::GetProcAddress(g_hKernel32, "GetSystemWow64DirectoryA"); 161 if (!g_fnGetSystemWow64DirectoryA) 162 return FALSE; 163#endif 164 165 // Build WoW64 ctfmon.exe pathname 166 TCHAR szPath[MAX_PATH]; 167#ifdef UNICODE 168 UINT cchPath = g_fnGetSystemWow64DirectoryW(szPath, _countof(szPath)); 169#else 170 UINT cchPath = g_fnGetSystemWow64DirectoryA(szPath, _countof(szPath)); 171#endif 172 if (!cchPath && FAILED(StringCchCat(szPath, _countof(szPath), TEXT("\\ctfmon.exe")))) 173 return FALSE; 174 175 // Create a WoW64 ctfmon.exe process 176 PROCESS_INFORMATION pi; 177 STARTUPINFO si = { sizeof(si) }; 178 si.wShowWindow = SW_SHOWMINNOACTIVE; 179 if (!::CreateProcess(szPath, lpCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) 180 return FALSE; 181 182 ::CloseHandle(pi.hThread); 183 ::CloseHandle(pi.hProcess); 184 return TRUE; 185} 186 187static BOOL 188InitApp( 189 _In_ HINSTANCE hInstance, 190 _In_ LPTSTR lpCmdLine) 191{ 192 g_hInst = hInstance; // Save the instance handle 193 194 g_bOnWow64 = cicIsWow64(); // Is the current process on WoW64? 195 cicGetOSInfo(&g_uACP, &g_dwOsInfo); // Get OS info 196 197 // Create a mutex for Cicero 198 g_hCicMutex = TF_CreateCicLoadMutex(&g_fWinLogon); 199 if (!g_hCicMutex) 200 return FALSE; 201 202 // Write to "Run" registry key for starting up 203 WriteRegRun(); 204 205 // Call SetProcessShutdownParameters if possible 206 if (g_dwOsInfo & CIC_OSINFO_NT) 207 { 208 g_hKernel32 = cicGetSystemModuleHandle(TEXT("kernel32.dll"), FALSE); 209 g_fnSetProcessShutdownParameters = 210 (FN_SetProcessShutdownParameters) 211 ::GetProcAddress(g_hKernel32, "SetProcessShutdownParameters"); 212 if (g_fnSetProcessShutdownParameters) 213 g_fnSetProcessShutdownParameters(0xF0, SHUTDOWN_NORETRY); 214 } 215 216 // Start text framework 217 TF_InitSystem(); 218 219 // Start watching registry if x86/x64 native 220 if (!g_bOnWow64) 221 CRegWatcher::Init(); 222 223 // Create Tipbar loader window 224 g_pLoaderWnd = new(cicNoThrow) CLoaderWnd(); 225 if (!g_pLoaderWnd || !g_pLoaderWnd->Init()) 226 return FALSE; 227 228 if (g_pLoaderWnd->CreateWnd()) 229 { 230 // Go to the bottom of the (s)hell 231 ::SetWindowPos(g_pLoaderWnd->m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, 232 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 233 } 234 235 // Display Tipbar Popup if x86/x64 native and necessary 236 if (!g_bOnWow64) 237 GetPopupTipbar(g_pLoaderWnd->m_hWnd, g_fWinLogon); 238 239 // Initialize x64-specific support 240 CheckX64System(lpCmdLine); 241 242 return TRUE; 243} 244 245VOID 246UninitApp(VOID) 247{ 248 // Close Tipbar Popup 249 ClosePopupTipbar(); 250 251 // Close the mutex 252 ::CloseHandle(g_hCicMutex); 253 g_hCicMutex = NULL; 254 255 // Quit watching registry if x86/x64 native 256 if (!g_bOnWow64) 257 CRegWatcher::Uninit(); 258} 259 260static INT 261DoMainLoop(VOID) 262{ 263 MSG msg; 264 265 if (g_bOnWow64) // Is the current process on WoW64? 266 { 267 // Simply run a message loop 268 while (::GetMessage(&msg, NULL, 0, 0)) 269 { 270 ::TranslateMessage(&msg); 271 ::DispatchMessage(&msg); 272 } 273 return (INT)msg.wParam; 274 } 275 276 // We wait on the CRegWatcher and the WinSta0 desktop-switch events. 277 HANDLE ahEvents[WI_REGEVTS_MAX + 1]; 278 DWORD dwEventCount; 279 280 // Get the CRegWatcher handles 281 CopyMemory(ahEvents, CRegWatcher::s_ahWatchEvents, WI_REGEVTS_MAX * sizeof(HANDLE)); 282 dwEventCount = WI_REGEVTS_MAX; 283 284 // Open the WinSta0 desktop-switch event and add it 285 HANDLE hSwitchEvent = ::OpenEvent(SYNCHRONIZE, FALSE, TEXT("WinSta0_DesktopSwitch")); 286 if (hSwitchEvent) 287 { 288 ahEvents[WI_DESKTOP_SWITCH] = hSwitchEvent; 289 ++dwEventCount; 290 } 291 292 // Run the event loop 293 for (;;) 294 { 295 DWORD dwWait = ::MsgWaitForMultipleObjects(dwEventCount, ahEvents, 296 FALSE, INFINITE, QS_ALLINPUT); 297 if (dwWait == (WAIT_OBJECT_0 + dwEventCount)) // Is input available? 298 { 299 // Pump available messages 300 while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 301 { 302 if (msg.message == WM_QUIT) 303 goto Quit; 304 305 ::TranslateMessage(&msg); 306 ::DispatchMessage(&msg); 307 } 308 } 309 else if (dwWait == (WAIT_OBJECT_0 + WI_DESKTOP_SWITCH)) // Desktop switch? 310 { 311 SetGlobalCompartmentDWORD(GUID_COMPARTMENT_SPEECH_OPENCLOSE, 0); 312 ::ResetEvent(hSwitchEvent); 313 } 314 else if (dwWait < (WAIT_OBJECT_0 + WI_REGEVTS_MAX)) // Do the other events 315 { 316 CRegWatcher::OnEvent(dwWait - WAIT_OBJECT_0); 317 } 318 else if (dwWait == WAIT_TIMEOUT) 319 { 320 // Ignore 321#ifndef NDEBUG 322 OutputDebugStringA("DoMainLoop: WAIT_TIMEOUT\n"); 323#endif 324 } 325 else if (dwWait == WAIT_FAILED) 326 { 327 // Something failed, bail out 328#ifndef NDEBUG 329 OutputDebugStringA("DoMainLoop: WAIT_FAILED, exiting\n"); 330#endif 331 break; 332 } 333 } 334 335Quit: 336 if (hSwitchEvent) 337 ::CloseHandle(hSwitchEvent); 338 339 return (INT)msg.wParam; 340} 341 342// The main function for Unicode Win32 343EXTERN_C INT WINAPI 344_tWinMain( 345 HINSTANCE hInstance, 346 HINSTANCE hPrevInst, 347 LPTSTR lpCmdLine, 348 INT nCmdShow) 349{ 350 UNREFERENCED_PARAMETER(hPrevInst); 351 UNREFERENCED_PARAMETER(nCmdShow); 352 353 // Parse command line 354 ParseCommandLine(lpCmdLine); 355 356 if (g_fJustRunKey) // If "/R" option is specified 357 { 358 // Just write registry and exit 359 WriteRegRun(); 360 return 1; 361 } 362 363 // Initialize the application 364 if (!InitApp(hInstance, lpCmdLine)) 365 return 0; 366 367 // The main loop 368 INT ret = DoMainLoop(); 369 370 // Clean up the loader 371 if (g_pLoaderWnd) 372 { 373 delete g_pLoaderWnd; 374 g_pLoaderWnd = NULL; 375 } 376 377 // Un-initialize app and text framework 378 if (!CLoaderWnd::s_bUninitedSystem) 379 { 380 UninitApp(); 381 TF_UninitSystem(); 382 } 383 384 return ret; 385}