/* * PROJECT: ReactOS CTF Monitor * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: Providing Language Bar front-end * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ */ #include "precomp.h" #include "CRegWatcher.h" #include "CLoaderWnd.h" // kernel32!SetProcessShutdownParameters typedef BOOL (WINAPI *FN_SetProcessShutdownParameters)(DWORD, DWORD); FN_SetProcessShutdownParameters g_fnSetProcessShutdownParameters = NULL; // kernel32!GetSystemWow64DirectoryA typedef UINT (WINAPI *FN_GetSystemWow64DirectoryA)(LPSTR, UINT); FN_GetSystemWow64DirectoryA g_fnGetSystemWow64DirectoryA = NULL; // kernel32!GetSystemWow64DirectoryW typedef UINT (WINAPI *FN_GetSystemWow64DirectoryW)(LPWSTR, UINT); FN_GetSystemWow64DirectoryW g_fnGetSystemWow64DirectoryW = NULL; HINSTANCE g_hInst = NULL; // The application instance HINSTANCE g_hKernel32 = NULL; // The "kernel32.dll" instance UINT g_uACP = CP_ACP; // The active codepage BOOL g_fWinLogon = FALSE; // Is it a log-on process? HANDLE g_hCicMutex = NULL; // The Cicero mutex BOOL g_bOnWow64 = FALSE; // Is the app running on WoW64? BOOL g_fNoRunKey = FALSE; // Don't write registry key "Run"? BOOL g_fJustRunKey = FALSE; // Just write registry key "Run"? DWORD g_dwOsInfo = 0; // The OS version info. See cicGetOSInfo CLoaderWnd* g_pLoaderWnd = NULL; // Tipbar loader window static VOID ParseCommandLine( _In_ LPCTSTR pszCmdLine) { g_fNoRunKey = g_fJustRunKey = FALSE; for (LPCTSTR pch = pszCmdLine; *pch; ++pch) { // Skip space while (*pch == TEXT(' ')) ++pch; if (*pch == TEXT('\0')) return; if ((*pch == TEXT('-')) || (*pch == TEXT('/'))) { ++pch; switch (*pch) { case TEXT('N'): case TEXT('n'): // Found "/N" option g_fNoRunKey = TRUE; break; case TEXT('R'): case TEXT('r'): // Found "/R" option g_fJustRunKey = TRUE; break; case UNICODE_NULL: return; default: break; } } } } static VOID WriteRegRun(VOID) { if (g_fNoRunKey) // If "/N" option is specified return; // Don't write // Open "Run" key HKEY hKey; LSTATUS error = ::RegCreateKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), &hKey); if (error != ERROR_SUCCESS) return; // Write the module path CicSystemModulePath ModPath; if (ModPath.Init(TEXT("ctfmon.exe"), FALSE)) { DWORD cbData = (ModPath.m_cchPath + 1) * sizeof(TCHAR); ::RegSetValueEx(hKey, TEXT("ctfmon.exe"), 0, REG_SZ, (BYTE*)ModPath.m_szPath, cbData); } ::RegCloseKey(hKey); } static HRESULT GetGlobalCompartment( _In_ REFGUID guid, _Inout_ ITfCompartment **ppComp) { *ppComp = NULL; ITfCompartmentMgr *pCompMgr = NULL; HRESULT hr = TF_GetGlobalCompartment(&pCompMgr); if (FAILED(hr)) return hr; if (!pCompMgr) return E_FAIL; hr = pCompMgr->GetCompartment(guid, ppComp); pCompMgr->Release(); return hr; } static HRESULT SetGlobalCompartmentDWORD( _In_ REFGUID guid, _In_ DWORD dwValue) { HRESULT hr; VARIANT vari; ITfCompartment *pComp; hr = GetGlobalCompartment(guid, &pComp); if (FAILED(hr)) return hr; V_VT(&vari) = VT_I4; V_I4(&vari) = dwValue; hr = pComp->SetValue(0, &vari); pComp->Release(); return hr; } static BOOL CheckX64System( _In_ LPTSTR lpCmdLine) { // Is the system x64? SYSTEM_INFO SystemInfo; ::GetSystemInfo(&SystemInfo); if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_IA64 || SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64) { return FALSE; } // Get GetSystemWow64DirectoryW function g_hKernel32 = cicGetSystemModuleHandle(TEXT("kernel32.dll"), FALSE); #ifdef UNICODE g_fnGetSystemWow64DirectoryW = (FN_GetSystemWow64DirectoryW)::GetProcAddress(g_hKernel32, "GetSystemWow64DirectoryW"); if (!g_fnGetSystemWow64DirectoryW) return FALSE; #else g_fnGetSystemWow64DirectoryA = (FN_GetSystemWow64DirectoryA)::GetProcAddress(g_hKernel32, "GetSystemWow64DirectoryA"); if (!g_fnGetSystemWow64DirectoryA) return FALSE; #endif // Build WoW64 ctfmon.exe pathname TCHAR szPath[MAX_PATH]; #ifdef UNICODE UINT cchPath = g_fnGetSystemWow64DirectoryW(szPath, _countof(szPath)); #else UINT cchPath = g_fnGetSystemWow64DirectoryA(szPath, _countof(szPath)); #endif if (!cchPath && FAILED(StringCchCat(szPath, _countof(szPath), TEXT("\\ctfmon.exe")))) return FALSE; // Create a WoW64 ctfmon.exe process PROCESS_INFORMATION pi; STARTUPINFO si = { sizeof(si) }; si.wShowWindow = SW_SHOWMINNOACTIVE; if (!::CreateProcess(szPath, lpCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return FALSE; ::CloseHandle(pi.hThread); ::CloseHandle(pi.hProcess); return TRUE; } static BOOL InitApp( _In_ HINSTANCE hInstance, _In_ LPTSTR lpCmdLine) { g_hInst = hInstance; // Save the instance handle g_bOnWow64 = cicIsWow64(); // Is the current process on WoW64? cicGetOSInfo(&g_uACP, &g_dwOsInfo); // Get OS info // Initialize Cicero TFInitLib(); // Create a mutex for Cicero g_hCicMutex = TF_CreateCicLoadMutex(&g_fWinLogon); if (!g_hCicMutex) return FALSE; // Write to "Run" registry key for starting up WriteRegRun(); // Call SetProcessShutdownParameters if possible if (g_dwOsInfo & CIC_OSINFO_NT) { g_hKernel32 = cicGetSystemModuleHandle(TEXT("kernel32.dll"), FALSE); g_fnSetProcessShutdownParameters = (FN_SetProcessShutdownParameters) ::GetProcAddress(g_hKernel32, "SetProcessShutdownParameters"); if (g_fnSetProcessShutdownParameters) g_fnSetProcessShutdownParameters(0xF0, SHUTDOWN_NORETRY); } // Start text framework TF_InitSystem(); // Start watching registry if x86/x64 native if (!g_bOnWow64) CRegWatcher::Init(); // Create Tipbar loader window g_pLoaderWnd = new(cicNoThrow) CLoaderWnd(); if (!g_pLoaderWnd || !g_pLoaderWnd->Init()) return FALSE; if (g_pLoaderWnd->CreateWnd()) { // Go to the bottom of the hell ::SetWindowPos(g_pLoaderWnd->m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } // Display Tipbar Popup if x86/x64 native and necessary if (!g_bOnWow64) GetPopupTipbar(g_pLoaderWnd->m_hWnd, g_fWinLogon); // Do x64 stuffs CheckX64System(lpCmdLine); return TRUE; } VOID UninitApp(VOID) { // Close Tipbar Popup ClosePopupTipbar(); // Release Cicero TFUninitLib(); // Close the mutex ::CloseHandle(g_hCicMutex); g_hCicMutex = NULL; // Quit watching registry if x86/x64 native if (!g_bOnWow64) CRegWatcher::Uninit(); } static INT DoMainLoop(VOID) { MSG msg; if (g_bOnWow64) // Is the current process on WoW64? { // Just a simple message loop while (::GetMessage(&msg, NULL, 0, 0)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } return (INT)msg.wParam; } // Open the existing event by the name HANDLE hSwitchEvent = ::OpenEvent(SYNCHRONIZE, FALSE, TEXT("WinSta0_DesktopSwitch")); // The target events to watch HANDLE ahEvents[WATCHENTRY_MAX + 1]; // Borrow some handles from CRegWatcher CopyMemory(ahEvents, CRegWatcher::s_ahWatchEvents, WATCHENTRY_MAX * sizeof(HANDLE)); ahEvents[WI_DESKTOP_SWITCH] = hSwitchEvent; // Add it // Another message loop for (;;) { // Wait for target signal DWORD dwWait = ::MsgWaitForMultipleObjects(_countof(ahEvents), ahEvents, 0, INFINITE, QS_ALLINPUT); if (dwWait == (WAIT_OBJECT_0 + _countof(ahEvents))) // Is input available? { // Do the events while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) goto Quit; ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } else if (dwWait == (WAIT_OBJECT_0 + WI_DESKTOP_SWITCH)) // Desktop switch? { SetGlobalCompartmentDWORD(GUID_COMPARTMENT_SPEECH_OPENCLOSE, 0); ::ResetEvent(hSwitchEvent); } else // Do the other events { CRegWatcher::OnEvent(dwWait - WAIT_OBJECT_0); } } Quit: ::CloseHandle(hSwitchEvent); return (INT)msg.wParam; } // The main function for Unicode Win32 EXTERN_C INT WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdShow) { UNREFERENCED_PARAMETER(hPrevInst); UNREFERENCED_PARAMETER(nCmdShow); // Parse command line ParseCommandLine(lpCmdLine); if (g_fJustRunKey) // If "/R" option is specified { // Just write registry and exit WriteRegRun(); return 1; } // Initialize the application if (!InitApp(hInstance, lpCmdLine)) return 0; // The main loop INT ret = DoMainLoop(); // Clean up the loader if (g_pLoaderWnd) { delete g_pLoaderWnd; g_pLoaderWnd = NULL; } // Un-initialize app and text framework if (!CLoaderWnd::s_bUninitedSystem) { UninitApp(); TF_UninitSystem(); } return ret; }