/* * PROJECT: ReactOS msctfime.ime * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: Supporting IME interface of Text Input Processors (TIPs) * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ */ #include "msctfime.h" #include /* for RtlDllShutdownInProgress */ WINE_DEFAULT_DEBUG_CHANNEL(msctfime); typedef CicArray CDispAttrPropCache; HINSTANCE g_hInst = NULL; /* The instance of this module */ BOOL g_bWinLogon = FALSE; UINT g_uACP = CP_ACP; DWORD g_dwOSInfo = 0; BOOL gfTFInitLib = FALSE; CRITICAL_SECTION g_csLock; CDispAttrPropCache *g_pPropCache = NULL; EXTERN_C void __cxa_pure_virtual(void) { ERR("__cxa_pure_virtual\n"); } typedef BOOLEAN (WINAPI *FN_DllShutdownInProgress)(VOID); /// This function calls ntdll!RtlDllShutdownInProgress. /// It can detect the system is shutting down or not. /// @implemented EXTERN_C BOOLEAN WINAPI DllShutdownInProgress(VOID) { HMODULE hNTDLL; static FN_DllShutdownInProgress s_fnDllShutdownInProgress = NULL; if (s_fnDllShutdownInProgress) return s_fnDllShutdownInProgress(); hNTDLL = cicGetSystemModuleHandle(L"ntdll.dll", FALSE); s_fnDllShutdownInProgress = (FN_DllShutdownInProgress)GetProcAddress(hNTDLL, "RtlDllShutdownInProgress"); if (!s_fnDllShutdownInProgress) return FALSE; return s_fnDllShutdownInProgress(); } /// This function checks if the current user logon session is interactive. /// @implemented static BOOL IsInteractiveUserLogon(VOID) { BOOL bOK, IsMember = FALSE; PSID pSid; SID_IDENTIFIER_AUTHORITY IdentAuth = { SECURITY_NT_AUTHORITY }; if (!AllocateAndInitializeSid(&IdentAuth, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &pSid)) { ERR("Error: %ld\n", GetLastError()); return FALSE; } bOK = CheckTokenMembership(NULL, pSid, &IsMember); if (pSid) FreeSid(pSid); return bOK && IsMember; } /// @implemented ITfCategoryMgr *GetUIMCat(PCIC_LIBTHREAD pLibThread) { if (!pLibThread) return NULL; if (pLibThread->m_pCategoryMgr) return pLibThread->m_pCategoryMgr; if (FAILED(cicCoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void **)&pLibThread->m_pCategoryMgr))) { return NULL; } return pLibThread->m_pCategoryMgr; } /// @implemented HRESULT LibEnumItemsInCategory(PCIC_LIBTHREAD pLibThread, REFGUID rguid, IEnumGUID **ppEnum) { ITfCategoryMgr *pCat = GetUIMCat(pLibThread); if (!pCat) return E_FAIL; return pCat->EnumItemsInCategory(rguid, ppEnum); } /// @implemented HRESULT InitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread) { if (!pLibThread) return E_FAIL; if (pLibThread->m_pDisplayAttrMgr) { pLibThread->m_pDisplayAttrMgr->Release(); pLibThread->m_pDisplayAttrMgr = NULL; } if (FAILED(cicCoCreateInstance(CLSID_TF_DisplayAttributeMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr, (void **)&pLibThread->m_pDisplayAttrMgr))) { return E_FAIL; } IEnumGUID *pEnumGuid; LibEnumItemsInCategory(pLibThread, GUID_TFCAT_DISPLAYATTRIBUTEPROPERTY, &pEnumGuid); HRESULT hr = E_OUTOFMEMORY; ::EnterCriticalSection(&g_csLock); if (pEnumGuid && !g_pPropCache) { g_pPropCache = new(cicNoThrow) CDispAttrPropCache(); if (g_pPropCache) { g_pPropCache->Add(GUID_PROP_ATTRIBUTE); GUID guid; while (pEnumGuid->Next(1, &guid, NULL) == S_OK) { if (!IsEqualGUID(guid, GUID_PROP_ATTRIBUTE)) g_pPropCache->Add(guid); } hr = S_OK; } } ::LeaveCriticalSection(&g_csLock); return hr; } HIMC GetActiveContext(VOID) { HWND hwndFocus = ::GetFocus(); if (!hwndFocus) hwndFocus = ::GetActiveWindow(); return ::ImmGetContext(hwndFocus); } /// @implemented HRESULT UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread) { if (!pLibThread) return E_FAIL; if (pLibThread->m_pDisplayAttrMgr) { pLibThread->m_pDisplayAttrMgr->Release(); pLibThread->m_pDisplayAttrMgr = NULL; } return S_OK; } /// Gets the charset from a language ID. /// @implemented BYTE GetCharsetFromLangId(_In_ DWORD dwValue) { CHARSETINFO info; if (!::TranslateCharsetInfo((DWORD*)(DWORD_PTR)dwValue, &info, TCI_SRCLOCALE)) return 0; return info.ciCharset; } /// Selects or unselects the input context. /// @implemented HRESULT InternalSelectEx( _In_ HIMC hIMC, _In_ BOOL fSelect, _In_ LANGID LangID) { CicIMCLock imcLock(hIMC); if (!imcLock) imcLock.m_hr = E_FAIL; if (FAILED(imcLock.m_hr)) return imcLock.m_hr; if (PRIMARYLANGID(LangID) == LANG_CHINESE) { imcLock.get().cfCandForm[0].dwStyle = 0; imcLock.get().cfCandForm[0].dwIndex = (DWORD)-1; } if (!fSelect) { imcLock.get().fdwInit &= ~INIT_GUIDMAP; return imcLock.m_hr; } if (!imcLock.ClearCand()) return imcLock.m_hr; // Populate conversion mode if (!(imcLock.get().fdwInit & INIT_CONVERSION)) { DWORD dwConv = (imcLock.get().fdwConversion & IME_CMODE_SOFTKBD); if (LangID) { if (PRIMARYLANGID(LangID) == LANG_JAPANESE) { dwConv |= IME_CMODE_ROMAN | IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE; } else if (PRIMARYLANGID(LangID) != LANG_KOREAN) { dwConv |= IME_CMODE_NATIVE; } } imcLock.get().fdwConversion |= dwConv; imcLock.get().fdwInit |= INIT_CONVERSION; } // Populate sentence mode imcLock.get().fdwSentence |= IME_SMODE_PHRASEPREDICT; // Populate LOGFONT if (!(imcLock.get().fdwInit & INIT_LOGFONT)) { // Get logical font LOGFONTW lf; HDC hDC = ::GetDC(imcLock.get().hWnd); HGDIOBJ hFont = GetCurrentObject(hDC, OBJ_FONT); ::GetObjectW(hFont, sizeof(LOGFONTW), &lf); ::ReleaseDC(imcLock.get().hWnd, hDC); imcLock.get().lfFont.W = lf; imcLock.get().fdwInit |= INIT_LOGFONT; } imcLock.get().lfFont.W.lfCharSet = GetCharsetFromLangId(LangID); imcLock.InitContext(); return imcLock.m_hr; } class TLS; /*********************************************************************** * ImeInquire (MSCTFIME.@) * * MSCTFIME's ImeInquire does nothing. * * @implemented * @see CtfImeInquireExW */ EXTERN_C BOOL WINAPI ImeInquire( _Out_ LPIMEINFO lpIMEInfo, _Out_ LPWSTR lpszWndClass, _In_ DWORD dwSystemInfoFlags) { TRACE("(%p, %p, 0x%lX)\n", lpIMEInfo, lpszWndClass, dwSystemInfoFlags); return FALSE; } /*********************************************************************** * ImeConversionList (MSCTFIME.@) * * MSCTFIME's ImeConversionList does nothing. * * @implemented * @see ImmGetConversionListW */ EXTERN_C DWORD WINAPI ImeConversionList( _In_ HIMC hIMC, _In_ LPCWSTR lpSrc, _Out_ LPCANDIDATELIST lpDst, _In_ DWORD dwBufLen, _In_ UINT uFlag) { TRACE("(%p, %s, %p, 0x%lX, %u)\n", hIMC, debugstr_w(lpSrc), lpDst, dwBufLen, uFlag); return 0; } /*********************************************************************** * ImeRegisterWord (MSCTFIME.@) * * MSCTFIME's ImeRegisterWord does nothing. * * @implemented * @see ImeUnregisterWord */ EXTERN_C BOOL WINAPI ImeRegisterWord( _In_ LPCWSTR lpszReading, _In_ DWORD dwStyle, _In_ LPCWSTR lpszString) { TRACE("(%s, 0x%lX, %s)\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString)); return FALSE; } /*********************************************************************** * ImeUnregisterWord (MSCTFIME.@) * * MSCTFIME's ImeUnregisterWord does nothing. * * @implemented * @see ImeRegisterWord */ EXTERN_C BOOL WINAPI ImeUnregisterWord( _In_ LPCWSTR lpszReading, _In_ DWORD dwStyle, _In_ LPCWSTR lpszString) { TRACE("(%s, 0x%lX, %s)\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString)); return FALSE; } /*********************************************************************** * ImeGetRegisterWordStyle (MSCTFIME.@) * * MSCTFIME's ImeGetRegisterWordStyle does nothing. * * @implemented * @see ImeRegisterWord */ EXTERN_C UINT WINAPI ImeGetRegisterWordStyle( _In_ UINT nItem, _Out_ LPSTYLEBUFW lpStyleBuf) { TRACE("(%u, %p)\n", nItem, lpStyleBuf); return 0; } /*********************************************************************** * ImeEnumRegisterWord (MSCTFIME.@) * * MSCTFIME's ImeEnumRegisterWord does nothing. * * @implemented * @see ImeRegisterWord */ EXTERN_C UINT WINAPI ImeEnumRegisterWord( _In_ REGISTERWORDENUMPROCW lpfnEnumProc, _In_opt_ LPCWSTR lpszReading, _In_ DWORD dwStyle, _In_opt_ LPCWSTR lpszString, _In_opt_ LPVOID lpData) { TRACE("(%p, %s, %lu, %s, %p)\n", lpfnEnumProc, debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString), lpData); return 0; } EXTERN_C BOOL WINAPI ImeConfigure( _In_ HKL hKL, _In_ HWND hWnd, _In_ DWORD dwMode, _Inout_opt_ LPVOID lpData) { TRACE("(%p, %p, %lu, %p)\n", hKL, hWnd, dwMode, lpData); TLS *pTLS = TLS::GetTLS(); if (!pTLS || !pTLS->m_pBridge || !pTLS->m_pThreadMgr) return FALSE; CicBridge *pBridge = pTLS->m_pBridge; ITfThreadMgr *pThreadMgr = pTLS->m_pThreadMgr; if (dwMode & 1) return (pBridge->ConfigureGeneral(pTLS, pThreadMgr, hKL, hWnd) == S_OK); if (dwMode & 2) return (pBridge->ConfigureRegisterWord(pTLS, pThreadMgr, hKL, hWnd, lpData) == S_OK); return FALSE; } /*********************************************************************** * ImeDestroy (MSCTFIME.@) * * @implemented */ EXTERN_C BOOL WINAPI ImeDestroy( _In_ UINT uReserved) { TRACE("(%u)\n", uReserved); TLS *pTLS = TLS::PeekTLS(); if (pTLS) return FALSE; if (!pTLS->m_pBridge || !pTLS->m_pThreadMgr) return FALSE; if (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON) return TRUE; if (pTLS->m_pBridge->DeactivateIMMX(pTLS, pTLS->m_pThreadMgr) != S_OK) return FALSE; return pTLS->m_pBridge->UnInitIMMX(pTLS); } /*********************************************************************** * ImeEscape (MSCTFIME.@) * * MSCTFIME's ImeEscape does nothing. * * @implemented * @see CtfImeEscapeEx */ EXTERN_C LRESULT WINAPI ImeEscape( _In_ HIMC hIMC, _In_ UINT uEscape, _Inout_opt_ LPVOID lpData) { TRACE("(%p, %u, %p)\n", hIMC, uEscape, lpData); return 0; } EXTERN_C BOOL WINAPI ImeProcessKey( _In_ HIMC hIMC, _In_ UINT uVirKey, _In_ LPARAM lParam, _In_ CONST LPBYTE lpbKeyState) { FIXME("stub:(%p, %u, %p, lpbKeyState)\n", hIMC, uVirKey, lParam, lpbKeyState); return FALSE; } /*********************************************************************** * ImeSelect (MSCTFIME.@) * * MSCTFIME's ImeSelect does nothing. * * @implemented * @see CtfImeSelectEx */ EXTERN_C BOOL WINAPI ImeSelect( _In_ HIMC hIMC, _In_ BOOL fSelect) { TRACE("(%p, %u)\n", hIMC, fSelect); return FALSE; } /*********************************************************************** * ImeSetActiveContext (MSCTFIME.@) * * MSCTFIME's ImeSetActiveContext does nothing. * * @implemented * @see CtfImeSetActiveContextAlways */ EXTERN_C BOOL WINAPI ImeSetActiveContext( _In_ HIMC hIMC, _In_ BOOL fFlag) { TRACE("(%p, %u)\n", hIMC, fFlag); return FALSE; } EXTERN_C UINT WINAPI ImeToAsciiEx( _In_ UINT uVirKey, _In_ UINT uScanCode, _In_ CONST LPBYTE lpbKeyState, _Out_ LPTRANSMSGLIST lpTransMsgList, _In_ UINT fuState, _In_ HIMC hIMC) { FIXME("stub:(%u, %u, %p, %p, %u, %p)\n", uVirKey, uScanCode, lpbKeyState, lpTransMsgList, fuState, hIMC); return 0; } EXTERN_C BOOL WINAPI NotifyIME( _In_ HIMC hIMC, _In_ DWORD dwAction, _In_ DWORD dwIndex, _In_ DWORD_PTR dwValue) { FIXME("stub:(%p, 0x%lX, 0x%lX, %p)\n", hIMC, dwAction, dwIndex, dwValue); return FALSE; } EXTERN_C BOOL WINAPI ImeSetCompositionString( _In_ HIMC hIMC, _In_ DWORD dwIndex, _In_opt_ LPCVOID lpComp, _In_ DWORD dwCompLen, _In_opt_ LPCVOID lpRead, _In_ DWORD dwReadLen) { FIXME("stub:(%p, 0x%lX, %p, 0x%lX, %p, 0x%lX)\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); return FALSE; } EXTERN_C DWORD WINAPI ImeGetImeMenuItems( _In_ HIMC hIMC, _In_ DWORD dwFlags, _In_ DWORD dwType, _Inout_opt_ LPIMEMENUITEMINFOW lpImeParentMenu, _Inout_opt_ LPIMEMENUITEMINFOW lpImeMenu, _In_ DWORD dwSize) { FIXME("stub:(%p, 0x%lX, 0x%lX, %p, %p, 0x%lX)\n", hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); return 0; } /*********************************************************************** * CtfImeInquireExW (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeInquireExW( _Out_ LPIMEINFO lpIMEInfo, _Out_ LPWSTR lpszWndClass, _In_ DWORD dwSystemInfoFlags, _In_ HKL hKL) { TRACE("(%p, %p, 0x%lX, %p)\n", lpIMEInfo, lpszWndClass, dwSystemInfoFlags, hKL); TLS *pTLS = TLS::GetTLS(); if (!pTLS) return E_OUTOFMEMORY; if (!IsInteractiveUserLogon()) { dwSystemInfoFlags |= IME_SYSINFO_WINLOGON; g_bWinLogon = TRUE; } pTLS->m_dwSystemInfoFlags = dwSystemInfoFlags; return Inquire(lpIMEInfo, lpszWndClass, dwSystemInfoFlags, hKL); } /*********************************************************************** * CtfImeSelectEx (MSCTFIME.@) * * @implemented */ EXTERN_C BOOL WINAPI CtfImeSelectEx( _In_ HIMC hIMC, _In_ BOOL fSelect, _In_ HKL hKL) { TRACE("(%p, %d, %p)\n", hIMC, fSelect, hKL); TLS *pTLS = TLS::PeekTLS(); if (!pTLS) return E_OUTOFMEMORY; InternalSelectEx(hIMC, fSelect, LOWORD(hKL)); if (!pTLS->m_pBridge || !pTLS->m_pThreadMgr) return E_OUTOFMEMORY; return pTLS->m_pBridge->SelectEx(pTLS, pTLS->m_pThreadMgr, hIMC, fSelect, hKL); } EXTERN_C LRESULT WINAPI CtfImeEscapeEx( _In_ HIMC hIMC, _In_ UINT uSubFunc, _Inout_opt_ LPVOID lpData, _In_ HKL hKL) { FIXME("stub:(%p, %u, %p, %p)\n", hIMC, uSubFunc, lpData, hKL); return 0; } /*********************************************************************** * CtfImeGetGuidAtom (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeGetGuidAtom( _In_ HIMC hIMC, _In_ DWORD dwUnknown, _Out_opt_ LPDWORD pdwGuidAtom) { TRACE("(%p, 0x%lX, %p)\n", hIMC, dwUnknown, pdwGuidAtom); CicIMCLock imcLock(hIMC); HRESULT hr = imcLock.m_hr; if (!imcLock) hr = E_FAIL; if (FAILED(hr)) return hr; CicIMCCLock imccLock(imcLock.get().hCtfImeContext); hr = imccLock.m_hr; if (!imccLock) hr = E_FAIL; if (FAILED(hr)) return hr; if (!imccLock.get().m_pCicIC) return E_OUTOFMEMORY; hr = imccLock.get().m_pCicIC->GetGuidAtom(imcLock, dwUnknown, pdwGuidAtom); return hr; } /*********************************************************************** * CtfImeIsGuidMapEnable (MSCTFIME.@) * * @implemented */ EXTERN_C BOOL WINAPI CtfImeIsGuidMapEnable( _In_ HIMC hIMC) { TRACE("(%p)\n", hIMC); BOOL ret = FALSE; HRESULT hr; CicIMCLock imcLock(hIMC); hr = imcLock.m_hr; if (!imcLock) hr = E_FAIL; if (SUCCEEDED(hr)) ret = !!(imcLock.get().fdwInit & INIT_GUIDMAP); return ret; } /*********************************************************************** * CtfImeCreateThreadMgr (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeCreateThreadMgr(VOID) { TRACE("()\n"); TLS *pTLS = TLS::GetTLS(); if (!pTLS) return E_OUTOFMEMORY; if (!pTLS->m_pBridge) { pTLS->m_pBridge = new(cicNoThrow) CicBridge(); if (!pTLS->m_pBridge) return E_OUTOFMEMORY; } HRESULT hr = S_OK; if (!g_bWinLogon && !(pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)) { hr = pTLS->m_pBridge->InitIMMX(pTLS); if (SUCCEEDED(hr)) { if (!pTLS->m_pThreadMgr) return E_OUTOFMEMORY; hr = pTLS->m_pBridge->ActivateIMMX(pTLS, pTLS->m_pThreadMgr); if (FAILED(hr)) pTLS->m_pBridge->UnInitIMMX(pTLS); } } return hr; } /*********************************************************************** * CtfImeDestroyThreadMgr (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeDestroyThreadMgr(VOID) { TRACE("()\n"); TLS *pTLS = TLS::PeekTLS(); if (!pTLS) return E_OUTOFMEMORY; if (pTLS->m_pBridge) { pTLS->m_pBridge = new(cicNoThrow) CicBridge(); if (!pTLS->m_pBridge) return E_OUTOFMEMORY; } if (!pTLS->m_pThreadMgr) return E_OUTOFMEMORY; if (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON) return S_OK; HRESULT hr = pTLS->m_pBridge->DeactivateIMMX(pTLS, pTLS->m_pThreadMgr); if (hr == S_OK) pTLS->m_pBridge->UnInitIMMX(pTLS); return hr; } EXTERN_C HRESULT WINAPI CtfImeCreateInputContext( _In_ HIMC hIMC) { return E_NOTIMPL; } /*********************************************************************** * CtfImeDestroyInputContext (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeDestroyInputContext( _In_ HIMC hIMC) { TRACE("(%p)\n", hIMC); TLS *pTLS = TLS::PeekTLS(); if (!pTLS || !pTLS->m_pBridge) return E_OUTOFMEMORY; return pTLS->m_pBridge->DestroyInputContext(pTLS, hIMC); } EXTERN_C HRESULT WINAPI CtfImeSetActiveContextAlways( _In_ HIMC hIMC, _In_ BOOL fActive, _In_ HWND hWnd, _In_ HKL hKL) { FIXME("stub:(%p, %d, %p, %p)\n", hIMC, fActive, hWnd, hKL); return E_NOTIMPL; } /*********************************************************************** * CtfImeProcessCicHotkey (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeProcessCicHotkey( _In_ HIMC hIMC, _In_ UINT vKey, _In_ LPARAM lParam) { TRACE("(%p, %u, %p)\n", hIMC, vKey, lParam); TLS *pTLS = TLS::GetTLS(); if (!pTLS) return S_OK; HRESULT hr = S_OK; ITfThreadMgr *pThreadMgr = NULL; ITfThreadMgr_P *pThreadMgr_P = NULL; if ((TF_GetThreadMgr(&pThreadMgr) == S_OK) && (pThreadMgr->QueryInterface(IID_ITfThreadMgr_P, (void**)&pThreadMgr_P) == S_OK) && CtfImmIsCiceroStartedInThread()) { HRESULT hr2; if (SUCCEEDED(pThreadMgr_P->CallImm32HotkeyHandler(vKey, lParam, &hr2))) hr = hr2; } if (pThreadMgr) pThreadMgr->Release(); if (pThreadMgr_P) pThreadMgr_P->Release(); return hr; } /*********************************************************************** * CtfImeDispatchDefImeMessage (MSCTFIME.@) * * @implemented */ EXTERN_C LRESULT WINAPI CtfImeDispatchDefImeMessage( _In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) { TRACE("(%p, %u, %p, %p)\n", hWnd, uMsg, wParam, lParam); TLS *pTLS = TLS::GetTLS(); if (pTLS) { if (uMsg == WM_CREATE) ++pTLS->m_cWnds; else if (uMsg == WM_DESTROY) --pTLS->m_cWnds; } if (!IsMsImeMessage(uMsg)) return 0; HKL hKL = GetKeyboardLayout(0); if (IS_IME_HKL(hKL)) return 0; HWND hImeWnd = (HWND)SendMessageW(hWnd, WM_IME_NOTIFY, 0x17, 0); if (!IsWindow(hImeWnd)) return 0; return SendMessageW(hImeWnd, uMsg, wParam, lParam); } EXTERN_C BOOL WINAPI CtfImeIsIME( _In_ HKL hKL) { FIXME("stub:(%p)\n", hKL); return FALSE; } /*********************************************************************** * CtfImeThreadDetach (MSCTFIME.@) * * @implemented */ EXTERN_C HRESULT WINAPI CtfImeThreadDetach(VOID) { ImeDestroy(0); return S_OK; } /// @implemented BOOL AttachIME(VOID) { return RegisterImeClass() && RegisterMSIMEMessage(); } /// @implemented VOID DetachIME(VOID) { UnregisterImeClass(); } EXTERN_C VOID TFUninitLib(VOID) { if (g_pPropCache) { delete g_pPropCache; g_pPropCache = NULL; } } /// @implemented BOOL ProcessAttach(HINSTANCE hinstDLL) { g_hInst = hinstDLL; InitializeCriticalSectionAndSpinCount(&g_csLock, 0); if (!TLS::Initialize()) return FALSE; cicGetOSInfo(&g_uACP, &g_dwOSInfo); cicInitUIFLib(); if (!TFInitLib()) return FALSE; gfTFInitLib = TRUE; return AttachIME(); } /// @implemented VOID ProcessDetach(HINSTANCE hinstDLL) { TF_DllDetachInOther(); if (gfTFInitLib) { DetachIME(); TFUninitLib(); } DeleteCriticalSection(&g_csLock); TLS::InternalDestroyTLS(); TLS::Uninitialize(); cicDoneUIFLib(); } /// @implemented EXTERN_C BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, _In_ DWORD dwReason, _Inout_opt_ LPVOID lpvReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: { TRACE("(%p, %lu, %p)\n", hinstDLL, dwReason, lpvReserved); if (!ProcessAttach(hinstDLL)) { ProcessDetach(hinstDLL); return FALSE; } break; } case DLL_PROCESS_DETACH: { ProcessDetach(hinstDLL); break; } case DLL_THREAD_DETACH: { TF_DllDetachInOther(); CtfImeThreadDetach(); TLS::InternalDestroyTLS(); break; } } return TRUE; }