reactos/base/applications/ctfmon/ctfmon.cpp
Katayama Hirofumi MZ b48e77e15b
[CTFMON][MSCTFIME][SDK] Define new(cicNoThrow) and use it (#6286)
Use new(cicNoThrow) instead of plain operator new.
JIRA issue: CORE-19360
- Define CicNoThrow structure and
  cicNoThrow macro in <cicero/cicbase.h>.
- Use new(cicNoThrow) instead of
  plain operator new.
2024-01-03 16:45:23 +09:00

372 lines
9.8 KiB
C++

/*
* 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 <katayama.hirofumi.mz@gmail.com>
*/
#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;
}