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 13 typedef BOOL (WINAPI *FN_SetProcessShutdownParameters)(DWORD, DWORD); 14 FN_SetProcessShutdownParameters g_fnSetProcessShutdownParameters = NULL; 15 16 // kernel32!GetSystemWow64DirectoryA 17 typedef UINT (WINAPI *FN_GetSystemWow64DirectoryA)(LPSTR, UINT); 18 FN_GetSystemWow64DirectoryA g_fnGetSystemWow64DirectoryA = NULL; 19 // kernel32!GetSystemWow64DirectoryW 20 typedef UINT (WINAPI *FN_GetSystemWow64DirectoryW)(LPWSTR, UINT); 21 FN_GetSystemWow64DirectoryW g_fnGetSystemWow64DirectoryW = NULL; 22 23 HINSTANCE g_hInst = NULL; // The application instance 24 HINSTANCE g_hKernel32 = NULL; // The "kernel32.dll" instance 25 UINT g_uACP = CP_ACP; // The active codepage 26 BOOL g_fWinLogon = FALSE; // Is it a log-on process? 27 HANDLE g_hCicMutex = NULL; // The Cicero mutex 28 BOOL g_bOnWow64 = FALSE; // Is the app running on WoW64? 29 BOOL g_fNoRunKey = FALSE; // Don't write registry key "Run"? 30 BOOL g_fJustRunKey = FALSE; // Just write registry key "Run"? 31 DWORD g_dwOsInfo = 0; // The OS version info. See cicGetOSInfo 32 CLoaderWnd* g_pLoaderWnd = NULL; // Tipbar loader window 33 34 static VOID 35 ParseCommandLine( 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 72 static VOID 73 WriteRegRun(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 97 static HRESULT 98 GetGlobalCompartment( 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 117 static HRESULT 118 SetGlobalCompartmentDWORD( 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 138 static BOOL 139 CheckX64System( 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 187 static BOOL 188 InitApp( 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 // Initialize Cicero 198 TFInitLib(); 199 200 // Create a mutex for Cicero 201 g_hCicMutex = TF_CreateCicLoadMutex(&g_fWinLogon); 202 if (!g_hCicMutex) 203 return FALSE; 204 205 // Write to "Run" registry key for starting up 206 WriteRegRun(); 207 208 // Call SetProcessShutdownParameters if possible 209 if (g_dwOsInfo & CIC_OSINFO_NT) 210 { 211 g_hKernel32 = cicGetSystemModuleHandle(TEXT("kernel32.dll"), FALSE); 212 g_fnSetProcessShutdownParameters = 213 (FN_SetProcessShutdownParameters) 214 ::GetProcAddress(g_hKernel32, "SetProcessShutdownParameters"); 215 if (g_fnSetProcessShutdownParameters) 216 g_fnSetProcessShutdownParameters(0xF0, SHUTDOWN_NORETRY); 217 } 218 219 // Start text framework 220 TF_InitSystem(); 221 222 // Start watching registry if x86/x64 native 223 if (!g_bOnWow64) 224 CRegWatcher::Init(); 225 226 // Create Tipbar loader window 227 g_pLoaderWnd = new CLoaderWnd(); 228 if (!g_pLoaderWnd || !g_pLoaderWnd->Init()) 229 return FALSE; 230 231 if (g_pLoaderWnd->CreateWnd()) 232 { 233 // Go to the bottom of the hell 234 ::SetWindowPos(g_pLoaderWnd->m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, 235 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 236 } 237 238 // Display Tipbar Popup if x86/x64 native and necessary 239 if (!g_bOnWow64) 240 GetPopupTipbar(g_pLoaderWnd->m_hWnd, g_fWinLogon); 241 242 // Do x64 stuffs 243 CheckX64System(lpCmdLine); 244 245 return TRUE; 246 } 247 248 VOID 249 UninitApp(VOID) 250 { 251 // Close Tipbar Popup 252 ClosePopupTipbar(); 253 254 // Release Cicero 255 TFUninitLib(); 256 257 // Close the mutex 258 ::CloseHandle(g_hCicMutex); 259 g_hCicMutex = NULL; 260 261 // Quit watching registry if x86/x64 native 262 if (!g_bOnWow64) 263 CRegWatcher::Uninit(); 264 } 265 266 static INT 267 DoMainLoop(VOID) 268 { 269 MSG msg; 270 271 if (g_bOnWow64) // Is the current process on WoW64? 272 { 273 // Just a simple message loop 274 while (::GetMessage(&msg, NULL, 0, 0)) 275 { 276 ::TranslateMessage(&msg); 277 ::DispatchMessage(&msg); 278 } 279 return (INT)msg.wParam; 280 } 281 282 // Open the existing event by the name 283 HANDLE hSwitchEvent = ::OpenEvent(SYNCHRONIZE, FALSE, TEXT("WinSta0_DesktopSwitch")); 284 285 // The target events to watch 286 HANDLE ahEvents[WATCHENTRY_MAX + 1]; 287 288 // Borrow some handles from CRegWatcher 289 CopyMemory(ahEvents, CRegWatcher::s_ahWatchEvents, WATCHENTRY_MAX * sizeof(HANDLE)); 290 291 ahEvents[WI_DESKTOP_SWITCH] = hSwitchEvent; // Add it 292 293 // Another message loop 294 for (;;) 295 { 296 // Wait for target signal 297 DWORD dwWait = ::MsgWaitForMultipleObjects(_countof(ahEvents), ahEvents, 0, INFINITE, 298 QS_ALLINPUT); 299 if (dwWait == (WAIT_OBJECT_0 + _countof(ahEvents))) // Is input available? 300 { 301 // Do the events 302 while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 303 { 304 if (msg.message == WM_QUIT) 305 goto Quit; 306 307 ::TranslateMessage(&msg); 308 ::DispatchMessage(&msg); 309 } 310 } 311 else if (dwWait == (WAIT_OBJECT_0 + WI_DESKTOP_SWITCH)) // Desktop switch? 312 { 313 SetGlobalCompartmentDWORD(GUID_COMPARTMENT_SPEECH_OPENCLOSE, 0); 314 ::ResetEvent(hSwitchEvent); 315 } 316 else // Do the other events 317 { 318 CRegWatcher::OnEvent(dwWait - WAIT_OBJECT_0); 319 } 320 } 321 322 Quit: 323 ::CloseHandle(hSwitchEvent); 324 325 return (INT)msg.wParam; 326 } 327 328 // The main function for Unicode Win32 329 EXTERN_C INT WINAPI 330 _tWinMain( 331 HINSTANCE hInstance, 332 HINSTANCE hPrevInst, 333 LPTSTR lpCmdLine, 334 INT nCmdShow) 335 { 336 UNREFERENCED_PARAMETER(hPrevInst); 337 UNREFERENCED_PARAMETER(nCmdShow); 338 339 // Parse command line 340 ParseCommandLine(lpCmdLine); 341 342 if (g_fJustRunKey) // If "/R" option is specified 343 { 344 // Just write registry and exit 345 WriteRegRun(); 346 return 1; 347 } 348 349 // Initialize the application 350 if (!InitApp(hInstance, lpCmdLine)) 351 return 0; 352 353 // The main loop 354 INT ret = DoMainLoop(); 355 356 // Clean up the loader 357 if (g_pLoaderWnd) 358 { 359 delete g_pLoaderWnd; 360 g_pLoaderWnd = NULL; 361 } 362 363 // Un-initialize app and text framework 364 if (!CLoaderWnd::s_bUninitedSystem) 365 { 366 UninitApp(); 367 TF_UninitSystem(); 368 } 369 370 return ret; 371 } 372