/* * PROJECT: ReactOS msctf.dll * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: Text Framework Services * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ */ #include #define WIN32_LEAN_AND_MEAN #define WIN32_NO_STATUS #define COBJMACROS #define INITGUID #define _EXTYPES_H #include #include #include #include #include #include #include #include #include #include #include #include #include WINE_DEFAULT_DEBUG_CHANNEL(msctf); BOOL gf_CRT_INIT = FALSE; BOOL g_fDllProcessDetached = FALSE; CRITICAL_SECTION g_cs; CRITICAL_SECTION g_csInDllMain; CRITICAL_SECTION g_csDelayLoad; HINSTANCE g_hInst = NULL; BOOL g_bOnWow64 = FALSE; UINT g_uACP = CP_ACP; // Active Code Page DWORD g_dwOSInfo = 0; // See cicGetOSInfo HKL g_hklDefault = NULL; DWORD g_dwTLSIndex = (DWORD)-1; BOOL gfSharedMemory = FALSE; LONG g_cRefDll = -1; BOOL g_fCUAS = FALSE; TCHAR g_szCUASImeFile[16] = { 0 }; // Messages UINT g_msgPrivate = 0; UINT g_msgSetFocus = 0; UINT g_msgThreadTerminate = 0; UINT g_msgThreadItemChange = 0; UINT g_msgLBarModal = 0; UINT g_msgRpcSendReceive = 0; UINT g_msgThreadMarshal = 0; UINT g_msgCheckThreadInputIdel = 0; UINT g_msgStubCleanUp = 0; UINT g_msgShowFloating = 0; UINT g_msgLBUpdate = 0; UINT g_msgNuiMgrDirtyUpdate = 0; // Unique names BOOL g_fUserSidString = FALSE; TCHAR g_szUserSidString[MAX_PATH] = { 0 }; TCHAR g_szUserUnique[MAX_PATH] = { 0 }; TCHAR g_szAsmListCache[MAX_PATH] = { 0 }; TCHAR g_szTimListCache[MAX_PATH] = { 0 }; TCHAR g_szLayoutsCache[MAX_PATH] = { 0 }; // Mutexes CicMutex g_mutexLBES; CicMutex g_mutexCompart; CicMutex g_mutexAsm; CicMutex g_mutexLayouts; CicMutex g_mutexTMD; // File mapping CicFileMappingStatic g_SharedMemory; // Hot-Keys UINT g_uLangHotKeyModifiers = 0; UINT g_uLangHotKeyVKey = 0; UINT g_uLangHotKeyVKey2 = 0; UINT g_uKeyTipHotKeyModifiers = 0; UINT g_uKeyTipHotKeyVKey = 0; UINT g_uKeyTipHotKeyVKey2 = 0; /** * @implemented */ LPTSTR GetUserSIDString(void) { HANDLE hToken = NULL; OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); if (!hToken) return NULL; DWORD dwLengthNeeded = 0; GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLengthNeeded); PTOKEN_USER pTokenUser = (PTOKEN_USER)cicMemAllocClear(dwLengthNeeded); if (!pTokenUser) { CloseHandle(hToken); return NULL; } LPTSTR StringSid = NULL; if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwLengthNeeded, &dwLengthNeeded) || !ConvertSidToStringSid(pTokenUser->User.Sid, &StringSid)) { if (StringSid) { LocalFree(StringSid); StringSid = NULL; } } cicMemFree(pTokenUser); CloseHandle(hToken); return StringSid; } /** * @implemented */ BOOL InitUserSidString(void) { if (g_fUserSidString) return TRUE; LPTSTR pszUserSID = GetUserSIDString(); if (!pszUserSID) return FALSE; StringCchCopy(g_szUserSidString, _countof(g_szUserSidString), pszUserSID); g_fUserSidString = TRUE; LocalFree(pszUserSID); return TRUE; } /** * @implemented */ BOOL InitUniqueString(void) { g_szUserUnique[0] = TEXT('\0'); DWORD dwThreadId = GetCurrentThreadId(); HDESK hDesk = GetThreadDesktop(dwThreadId); DWORD nLengthNeeded; TCHAR szName[MAX_PATH]; if (hDesk && GetUserObjectInformation(hDesk, UOI_NAME, szName, _countof(szName), &nLengthNeeded)) StringCchCat(g_szUserUnique, _countof(g_szUserUnique), szName); if (!InitUserSidString()) return FALSE; StringCchCat(g_szUserUnique, _countof(g_szUserUnique), g_szUserSidString); return TRUE; } void GetDesktopUniqueName( _In_ LPCTSTR pszName, _Out_ LPTSTR pszBuff, _In_ UINT cchBuff) { StringCchCopy(pszBuff, cchBuff, pszName); StringCchCat(pszBuff, cchBuff, g_szUserUnique); } BOOL StringFromGUID2A(REFGUID rguid, LPSTR pszGUID, INT cchGUID) { pszGUID[0] = ANSI_NULL; WCHAR szWide[40]; szWide[0] = UNICODE_NULL; BOOL ret = StringFromGUID2(rguid, szWide, _countof(szWide)); ::WideCharToMultiByte(CP_ACP, 0, szWide, -1, pszGUID, cchGUID, NULL, NULL); return ret; } #ifdef UNICODE #define StringFromGUID2T StringFromGUID2 #define debugstr_t debugstr_w #else #define StringFromGUID2T StringFromGUID2A #define debugstr_t debugstr_a #endif BOOL FullPathExec(LPCTSTR pszExeFile, LPCTSTR pszCmdLine, UINT nCmdShow, BOOL bSysWinDir) { STARTUPINFO si; PROCESS_INFORMATION pi; CicSystemModulePath ModPath; TCHAR szCommandLine[2 * MAX_PATH]; ModPath.Init(pszExeFile, bSysWinDir); if (!ModPath.m_cchPath) { ERR("%s\n", debugstr_t(pszExeFile)); return FALSE; } StringCchCopy(szCommandLine, _countof(szCommandLine), pszCmdLine); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.wShowWindow = nCmdShow; si.dwFlags = STARTF_USESHOWWINDOW; if (!CreateProcess(ModPath.m_szPath, szCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { ERR("%s, %s\n", debugstr_t(ModPath.m_szPath), debugstr_t(szCommandLine)); return FALSE; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return TRUE; } static inline BOOL RunCPLSetting(LPCTSTR pszCmdLine) { if (!pszCmdLine) return FALSE; return FullPathExec(TEXT("rundll32.exe"), pszCmdLine, SW_SHOWMINNOACTIVE, FALSE); } /*********************************************************************** * TF_RegisterLangBarAddIn (MSCTF.@) * * @implemented */ EXTERN_C HRESULT WINAPI TF_RegisterLangBarAddIn( _In_ REFGUID rguid, _In_ LPCWSTR pszFilePath, _In_ DWORD dwFlags) { TRACE("(%s, %s, 0x%lX)\n", debugstr_guid(&rguid), debugstr_w(pszFilePath), dwFlags); if (!pszFilePath || IsEqualGUID(rguid, GUID_NULL)) { ERR("E_INVALIDARG\n"); return E_INVALIDARG; } TCHAR szBuff[MAX_PATH], szGUID[40]; StringCchCopy(szBuff, _countof(szBuff), TEXT("SOFTWARE\\Microsoft\\CTF\\LangBarAddIn\\")); StringFromGUID2T(rguid, szGUID, _countof(szGUID)); StringCchCat(szBuff, _countof(szBuff), szGUID); CicRegKey regKey; HKEY hBaseKey = ((dwFlags & 1) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER); LSTATUS error = regKey.Create(hBaseKey, szBuff); if (error == ERROR_SUCCESS) { error = regKey.SetSzW(L"FilePath", pszFilePath); if (error == ERROR_SUCCESS) error = regKey.SetDword(TEXT("Enable"), !!(dwFlags & 4)); } return ((error == ERROR_SUCCESS) ? S_OK : E_FAIL); } /*********************************************************************** * TF_UnregisterLangBarAddIn (MSCTF.@) * * @implemented */ EXTERN_C HRESULT WINAPI TF_UnregisterLangBarAddIn( _In_ REFGUID rguid, _In_ DWORD dwFlags) { TRACE("(%s, 0x%lX)\n", debugstr_guid(&rguid), dwFlags); if (IsEqualGUID(rguid, GUID_NULL)) { ERR("E_INVALIDARG\n"); return E_INVALIDARG; } TCHAR szSubKey[MAX_PATH]; StringCchCopy(szSubKey, _countof(szSubKey), TEXT("SOFTWARE\\Microsoft\\CTF\\LangBarAddIn\\")); CicRegKey regKey; HKEY hBaseKey = ((dwFlags & 1) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER); LSTATUS error = regKey.Open(hBaseKey, szSubKey, KEY_ALL_ACCESS); HRESULT hr = E_FAIL; if (error == ERROR_SUCCESS) { TCHAR szGUID[40]; StringFromGUID2T(rguid, szGUID, _countof(szGUID)); regKey.RecurseDeleteKey(szGUID); hr = S_OK; } return hr; } /*********************************************************************** * TF_RunInputCPL (MSCTF.@) * * @implemented */ EXTERN_C HRESULT WINAPI TF_RunInputCPL(VOID) { CicSystemModulePath ModPath; TCHAR szCmdLine[2 * MAX_PATH]; TRACE("()\n"); // NOTE: We don't support Win95/98/Me if (g_dwOSInfo & CIC_OSINFO_XPPLUS) ModPath.Init(TEXT("input.dll"), FALSE); else ModPath.Init(TEXT("input.cpl"), FALSE); if (!ModPath.m_cchPath) return E_FAIL; StringCchPrintf(szCmdLine, _countof(szCmdLine), TEXT("rundll32.exe shell32.dll,Control_RunDLL %s"), ModPath.m_szPath); if (!RunCPLSetting(szCmdLine)) return E_FAIL; return S_OK; } /*********************************************************************** * TF_IsCtfmonRunning (MSCTF.@) * * @implemented */ EXTERN_C BOOL WINAPI TF_IsCtfmonRunning(VOID) { TCHAR szName[MAX_PATH]; GetDesktopUniqueName(TEXT("CtfmonInstMutex"), szName, _countof(szName)); HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, szName); if (!hMutex) return FALSE; ::CloseHandle(hMutex); return TRUE; } /** * @implemented */ BOOL InitLangChangeHotKey(VOID) { CicRegKey regKey; TCHAR szLanguage[2], szLayout[2]; LSTATUS error; szLanguage[0] = szLayout[0] = TEXT('3'); szLanguage[1] = szLayout[1] = TEXT('\0'); error = regKey.Open(HKEY_CURRENT_USER, TEXT("Keyboard Layout\\Toggle")); if (error == ERROR_SUCCESS) { error = regKey.QuerySz(TEXT("Language Hotkey"), szLanguage, _countof(szLanguage)); if (error != ERROR_SUCCESS) { if (g_dwOSInfo & CIC_OSINFO_NT) { error = regKey.QuerySz(TEXT("Hotkey"), szLanguage, _countof(szLanguage)); if (error != ERROR_SUCCESS) szLanguage[0] = TEXT('1'); } else { error = regKey.QuerySz(NULL, szLanguage, _countof(szLanguage)); if (error != ERROR_SUCCESS) szLanguage[0] = TEXT('1'); } if (PRIMARYLANGID(GetSystemDefaultLCID()) == LANG_CHINESE) szLanguage[0] = TEXT('1'); } error = regKey.QuerySz(TEXT("Layout Hotkey"), szLayout, _countof(szLayout)); if (error != ERROR_SUCCESS) { szLayout[0] = TEXT('1'); if (szLanguage[0] != TEXT('2')) szLayout[0] = TEXT('2'); if (GetSystemMetrics(SM_MIDEASTENABLED)) szLayout[0] = TEXT('3'); } szLanguage[1] = TEXT('\0'); szLayout[1] = TEXT('\0'); } if (szLanguage[0] == szLayout[0]) { if (szLanguage[0] == TEXT('1')) szLayout[0] = TEXT('2'); else if (szLanguage[0] == TEXT('2')) szLayout[0] = TEXT('1'); else szLayout[0] = TEXT('3'); } ::EnterCriticalSection(&g_csInDllMain); switch (szLanguage[0]) { case TEXT('2'): g_uLangHotKeyModifiers = MOD_SHIFT | MOD_CONTROL; g_uLangHotKeyVKey2 = VK_CONTROL; g_uLangHotKeyVKey = VK_SHIFT; break; case TEXT('3'): g_uLangHotKeyVKey = 0; g_uLangHotKeyModifiers = 0; g_uLangHotKeyVKey2 = 0; break; case TEXT('4'): g_uLangHotKeyVKey = VK_NUMPAD0; g_uLangHotKeyModifiers = 0; g_uLangHotKeyVKey2 = 0; break; case TEXT('1'): default: g_uLangHotKeyModifiers = MOD_SHIFT | MOD_ALT; g_uLangHotKeyVKey2 = VK_MENU; g_uLangHotKeyVKey = VK_SHIFT; break; } switch (szLayout[0]) { case TEXT('2'): g_uKeyTipHotKeyModifiers = MOD_SHIFT | MOD_CONTROL; g_uKeyTipHotKeyVKey = VK_SHIFT; g_uKeyTipHotKeyVKey2 = VK_CONTROL; break; case TEXT('3'): g_uKeyTipHotKeyModifiers = 0; g_uKeyTipHotKeyVKey = 0; g_uKeyTipHotKeyVKey2 = 0; break; case TEXT('4'): g_uKeyTipHotKeyModifiers = 0; g_uKeyTipHotKeyVKey = VK_OEM_3; g_uKeyTipHotKeyVKey2 = 0; break; case TEXT('1'): default: g_uKeyTipHotKeyModifiers = 0x40 | MOD_SHIFT; g_uKeyTipHotKeyVKey = VK_SHIFT; g_uKeyTipHotKeyVKey2 = VK_MENU; break; } ::LeaveCriticalSection(&g_csInDllMain); TRACE("HotKey: %c, %c\n", szLanguage[0], szLayout[0]); return TRUE; } /** * @unimplemented */ VOID CheckAnchorStores(VOID) { //FIXME } VOID InitCUASFlag(VOID) { CicRegKey regKey1; LSTATUS error; error = regKey1.Open(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\CTF\\SystemShared\\")); if (error == ERROR_SUCCESS) { DWORD dwValue; error = regKey1.QueryDword(TEXT("CUAS"), &dwValue); if (error == ERROR_SUCCESS) g_fCUAS = !!dwValue; } g_szCUASImeFile[0] = TEXT('\0'); if (!g_fCUAS) return; TCHAR szImeFile[16]; CicRegKey regKey2; error = regKey2.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\IMM")); if (error == ERROR_SUCCESS) { error = regKey2.QuerySz(TEXT("IME File"), szImeFile, _countof(szImeFile)); if (error == ERROR_SUCCESS) { g_szCUASImeFile[_countof(g_szCUASImeFile) - 1] = TEXT('\0'); // Avoid buffer overrun StringCchCopy(g_szCUASImeFile, _countof(g_szCUASImeFile), szImeFile); } } } EXTERN_C VOID TFUninitLib(VOID) { // Do nothing } /** * @unimplemented */ BOOL ProcessAttach(HINSTANCE hinstDLL) // FIXME: Call me from DllMain { gf_CRT_INIT = TRUE; ::InitializeCriticalSectionAndSpinCount(&g_cs, 0); ::InitializeCriticalSectionAndSpinCount(&g_csInDllMain, 0); ::InitializeCriticalSectionAndSpinCount(&g_csDelayLoad, 0); g_bOnWow64 = cicIsWow64(); g_hInst = hinstDLL; g_hklDefault = ::GetKeyboardLayout(0); g_dwTLSIndex = ::TlsAlloc(); if (g_dwTLSIndex == (DWORD)-1) return FALSE; g_msgPrivate = ::RegisterWindowMessageA("MSUIM.Msg.Private"); g_msgSetFocus = ::RegisterWindowMessageA("MSUIM.Msg.SetFocus"); g_msgThreadTerminate = ::RegisterWindowMessageA("MSUIM.Msg.ThreadTerminate"); g_msgThreadItemChange = ::RegisterWindowMessageA("MSUIM.Msg.ThreadItemChange"); g_msgLBarModal = ::RegisterWindowMessageA("MSUIM.Msg.LangBarModal"); g_msgRpcSendReceive = ::RegisterWindowMessageA("MSUIM.Msg.RpcSendReceive"); g_msgThreadMarshal = ::RegisterWindowMessageA("MSUIM.Msg.ThreadMarshal"); g_msgCheckThreadInputIdel = ::RegisterWindowMessageA("MSUIM.Msg.CheckThreadInputIdel"); g_msgStubCleanUp = ::RegisterWindowMessageA("MSUIM.Msg.StubCleanUp"); g_msgShowFloating = ::RegisterWindowMessageA("MSUIM.Msg.ShowFloating"); g_msgLBUpdate = ::RegisterWindowMessageA("MSUIM.Msg.LBUpdate"); g_msgNuiMgrDirtyUpdate = ::RegisterWindowMessageA("MSUIM.Msg.MuiMgrDirtyUpdate"); if (!g_msgPrivate || !g_msgSetFocus || !g_msgThreadTerminate || !g_msgThreadItemChange || !g_msgLBarModal || !g_msgRpcSendReceive || !g_msgThreadMarshal || !g_msgCheckThreadInputIdel || !g_msgStubCleanUp || !g_msgShowFloating || !g_msgLBUpdate || !g_msgNuiMgrDirtyUpdate) { return FALSE; } cicGetOSInfo(&g_uACP, &g_dwOSInfo); TRACE("cicGetOSInfo: %u, 0x%lX\n", g_uACP, g_dwOSInfo); InitUniqueString(); //FIXME gfSharedMemory = TRUE; //FIXME InitCUASFlag(); //FIXME GetDesktopUniqueName(TEXT("CTF.AsmListCache.FMP"), g_szAsmListCache, _countof(g_szAsmListCache)); GetDesktopUniqueName(TEXT("CTF.TimListCache.FMP"), g_szTimListCache, _countof(g_szTimListCache)); GetDesktopUniqueName(TEXT("CTF.LayoutsCache.FMP"), g_szLayoutsCache, _countof(g_szLayoutsCache)); //FIXME InitLangChangeHotKey(); //FIXME CheckAnchorStores(); return TRUE; } /** * @unimplemented */ VOID ProcessDetach(HINSTANCE hinstDLL) // FIXME: Call me from DllMain { if (!gf_CRT_INIT) { g_fDllProcessDetached = TRUE; return; } if (gfSharedMemory) { if (g_cRefDll != -1 ) TFUninitLib(); //FIXME } //FIXME //TF_UninitThreadSystem(); //FIXME if (g_dwTLSIndex != (DWORD)-1) { ::TlsFree(g_dwTLSIndex); g_dwTLSIndex = (DWORD)-1; } //FIXME if (gfSharedMemory) { g_mutexLBES.Uninit(); g_mutexCompart.Uninit(); g_mutexAsm.Uninit(); //FIXME g_mutexLayouts.Uninit(); g_mutexTMD.Uninit(); //FIXME g_SharedMemory.Close(); } g_SharedMemory.Finalize(); ::DeleteCriticalSection(&g_cs); ::DeleteCriticalSection(&g_csInDllMain); ::DeleteCriticalSection(&g_csDelayLoad); g_fDllProcessDetached = TRUE; }