[MMIXER] Fix additional data size initialization for different audio formats (#6753)
[reactos.git] / dll / win32 / imm32 / imm.c
index 0351cda..175432c 100644 (file)
@@ -6,34 +6,47 @@
  *              Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
  *              Copyright 2017 James Tabor <james.tabor@reactos.org>
  *              Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
- *              Copyright 2020 Oleg Dubinskiy <oleg.dubinskij2013@yandex.ua>
- *              Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
+ *              Copyright 2020-2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
  */
 
 #include "precomp.h"
+#include <ndk/exfuncs.h>
 
 WINE_DEFAULT_DEBUG_CHANNEL(imm);
 
-HMODULE g_hImm32Inst = NULL;
-PSERVERINFO g_psi = NULL;
-SHAREDINFO g_SharedInfo = { NULL };
-BYTE g_bClientRegd = FALSE;
+HMODULE ghImm32Inst = NULL; /* The IMM32 instance */
+PSERVERINFO gpsi = NULL;
+SHAREDINFO gSharedInfo = { NULL };
+BYTE gfImmInitialized = FALSE; /* Is IMM32 initialized? */
+ULONG_PTR gHighestUserAddress = 0;
 
-static BOOL APIENTRY Imm32InitInstance(HMODULE hMod)
+static BOOL APIENTRY ImmInitializeGlobals(HMODULE hMod)
 {
     NTSTATUS status;
+    SYSTEM_BASIC_INFORMATION SysInfo;
 
     if (hMod)
-        g_hImm32Inst = hMod;
+        ghImm32Inst = hMod;
 
-    if (g_bClientRegd)
+    if (gfImmInitialized)
         return TRUE;
 
-    status = RtlInitializeCriticalSection(&g_csImeDpi);
+    status = RtlInitializeCriticalSection(&gcsImeDpi);
     if (NT_ERROR(status))
+    {
+        ERR("\n");
+        return FALSE;
+    }
+
+    status = NtQuerySystemInformation(SystemBasicInformation, &SysInfo, sizeof(SysInfo), NULL);
+    if (NT_ERROR(status))
+    {
+        ERR("\n");
         return FALSE;
+    }
+    gHighestUserAddress = SysInfo.MaximumUserModeAddress;
 
-    g_bClientRegd = TRUE;
+    gfImmInitialized = TRUE;
     return TRUE;
 }
 
@@ -43,154 +56,433 @@ static BOOL APIENTRY Imm32InitInstance(HMODULE hMod)
  */
 BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod)
 {
-    g_SharedInfo = *ptr;
-    g_psi = g_SharedInfo.psi;
-    return Imm32InitInstance(hMod);
+    gSharedInfo = *ptr;
+    gpsi = gSharedInfo.psi;
+    return ImmInitializeGlobals(hMod);
 }
 
 /***********************************************************************
  *             ImmLoadLayout (IMM32.@)
  */
-HKL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
+BOOL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
 {
-    DWORD cbData;
-    UNICODE_STRING UnicodeString;
-    HKEY hLayoutKey = NULL, hLayoutsKey = NULL;
-    LONG error;
-    NTSTATUS Status;
+    DWORD cbData, dwType;
+    HKEY hKey;
+    LSTATUS error;
     WCHAR szLayout[MAX_PATH];
+    LPCWSTR pszSubKey;
 
     TRACE("(%p, %p)\n", hKL, pImeInfoEx);
 
-    if (IS_IME_HKL(hKL) ||
-        !g_psi || !(g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED) ||
-        ((PW32CLIENTINFO)NtCurrentTeb()->Win32ClientInfo)->W32ClientInfo[0] & 2)
+    /* Choose a key */
+    if (IS_IME_HKL(hKL) || !IS_CICERO_MODE() || IS_16BIT_MODE()) /* Non-Cicero? */
     {
-        UnicodeString.Buffer = szLayout;
-        UnicodeString.MaximumLength = sizeof(szLayout);
-        Status = RtlIntegerToUnicodeString((DWORD_PTR)hKL, 16, &UnicodeString);
-        if (!NT_SUCCESS(Status))
-            return NULL;
-
-        error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hLayoutsKey);
-        if (error)
-            return NULL;
-
-        error = RegOpenKeyW(hLayoutsKey, szLayout, &hLayoutKey);
+        StringCchPrintfW(szLayout, _countof(szLayout), L"%s\\%08lX",
+                         REGKEY_KEYBOARD_LAYOUTS, HandleToUlong(hKL));
+        pszSubKey = szLayout;
     }
-    else
+    else /* Cicero */
     {
-        error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_IMM, &hLayoutKey);
+        pszSubKey = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\IMM";
     }
 
-    if (error)
+    /* Open the key */
+    error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszSubKey, 0, KEY_READ, &hKey);
+    if (IS_ERROR_UNEXPECTEDLY(error))
+        return FALSE;
+
+    /* Load "IME File" value */
+    cbData = sizeof(pImeInfoEx->wszImeFile);
+    error = RegQueryValueExW(hKey, L"IME File", NULL, &dwType,
+                             (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
+
+    /* Avoid buffer overrun */
+    pImeInfoEx->wszImeFile[_countof(pImeInfoEx->wszImeFile) - 1] = UNICODE_NULL;
+
+    RegCloseKey(hKey);
+
+    if (error != ERROR_SUCCESS || dwType != REG_SZ)
+        return FALSE; /* Failed */
+
+    pImeInfoEx->hkl = hKL;
+    pImeInfoEx->fLoadFlag = 0;
+    return Imm32LoadImeVerInfo(pImeInfoEx);
+}
+
+/***********************************************************************
+ *             ImmFreeLayout (IMM32.@)
+ */
+BOOL WINAPI ImmFreeLayout(DWORD dwUnknown)
+{
+    WCHAR szKBD[KL_NAMELENGTH];
+    UINT iKL, cKLs;
+    HKL hOldKL, hNewKL, *pList;
+    PIMEDPI pImeDpi;
+    LANGID LangID;
+
+    TRACE("(0x%lX)\n", dwUnknown);
+
+    hOldKL = GetKeyboardLayout(0);
+
+    if (dwUnknown == 1)
+    {
+        if (!IS_IME_HKL(hOldKL))
+            return TRUE;
+
+        LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
+
+        cKLs = GetKeyboardLayoutList(0, NULL);
+        if (cKLs)
+        {
+            pList = ImmLocalAlloc(0, cKLs * sizeof(HKL));
+            if (IS_NULL_UNEXPECTEDLY(pList))
+                return FALSE;
+
+            cKLs = GetKeyboardLayoutList(cKLs, pList);
+            for (iKL = 0; iKL < cKLs; ++iKL)
+            {
+                if (!IS_IME_HKL(pList[iKL]))
+                {
+                    LangID = LOWORD(pList[iKL]);
+                    break;
+                }
+            }
+
+            ImmLocalFree(pList);
+        }
+
+        StringCchPrintfW(szKBD, _countof(szKBD), L"%08X", LangID);
+        if (!LoadKeyboardLayoutW(szKBD, KLF_ACTIVATE))
+        {
+            WARN("Default to English US\n");
+            LoadKeyboardLayoutW(L"00000409", KLF_ACTIVATE | 0x200);
+        }
+    }
+    else if (dwUnknown == 2)
     {
-        ERR("RegOpenKeyW error: 0x%08lX\n", error);
-        hKL = NULL;
+        RtlEnterCriticalSection(&gcsImeDpi);
+Retry:
+        for (pImeDpi = gpImeDpiList; pImeDpi; pImeDpi = pImeDpi->pNext)
+        {
+            if (Imm32ReleaseIME(pImeDpi->hKL))
+                goto Retry;
+        }
+        RtlLeaveCriticalSection(&gcsImeDpi);
     }
     else
     {
-        cbData = sizeof(pImeInfoEx->wszImeFile);
-        error = RegQueryValueExW(hLayoutKey, L"Ime File", 0, 0,
-                                 (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
-        if (error)
-            hKL = NULL;
+        hNewKL = (HKL)(DWORD_PTR)dwUnknown;
+        if (IS_IME_HKL(hNewKL) && hNewKL != hOldKL)
+            Imm32ReleaseIME(hNewKL);
     }
 
-    RegCloseKey(hLayoutKey);
-    if (hLayoutsKey)
-        RegCloseKey(hLayoutsKey);
-    return hKL;
+    return TRUE;
 }
 
-typedef struct _tagImmHkl
+VOID APIENTRY Imm32SelectInputContext(HKL hNewKL, HKL hOldKL, HIMC hIMC)
 {
-    struct list entry;
-    HKL         hkl;
-    HMODULE     hIME;
-    IMEINFO     imeInfo;
-    WCHAR       imeClassName[17]; /* 16 character max */
-    ULONG       uSelected;
-    HWND        UIWnd;
-
-    /* Function Pointers */
-    BOOL (WINAPI *pImeInquire)(IMEINFO *, WCHAR *, const WCHAR *);
-    BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *);
-    BOOL (WINAPI *pImeDestroy)(UINT);
-    LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *);
-    BOOL (WINAPI *pImeSelect)(HIMC, BOOL);
-    BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL);
-    UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, DWORD *, UINT, HIMC);
-    BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD);
-    BOOL (WINAPI *pImeRegisterWord)(const WCHAR *, DWORD, const WCHAR *);
-    BOOL (WINAPI *pImeUnregisterWord)(const WCHAR *, DWORD, const WCHAR *);
-    UINT (WINAPI *pImeEnumRegisterWord)(REGISTERWORDENUMPROCW, const WCHAR *, DWORD, const WCHAR *, void *);
-    BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void *, DWORD, const void *, DWORD);
-    DWORD (WINAPI *pImeConversionList)(HIMC, const WCHAR *, CANDIDATELIST *, DWORD, UINT);
-    BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *);
-    UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *);
-    DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD);
-} ImmHkl;
-
-typedef struct tagInputContextData
-{
-    DWORD           dwLock;
-    INPUTCONTEXT    IMC;
-    DWORD           threadID;
+    PCLIENTIMC pClientImc;
+    LPINPUTCONTEXTDX pIC;
+    LPGUIDELINE pGL;
+    LPCANDIDATEINFO pCI;
+    LPCOMPOSITIONSTRING pCS;
+    LOGFONTA LogFontA;
+    LOGFONTW LogFontW;
+    BOOL fOldOpen, bIsNewHKLIme = TRUE, bIsOldHKLIme = TRUE, bClientWide, bNewDpiWide;
+    DWORD cbNewPrivate = 0, cbOldPrivate = 0, dwOldConversion, dwOldSentence, dwSize, dwNewSize;
+    PIMEDPI pNewImeDpi = NULL, pOldImeDpi = NULL;
+    HANDLE hPrivate;
+    PIME_STATE pNewState = NULL, pOldState = NULL;
 
-    ImmHkl          *immKbd;
-    UINT            lastVK;
-    BOOL            threadDefault;
-    DWORD           magic;
-} InputContextData;
+    pClientImc = ImmLockClientImc(hIMC);
+    if (IS_NULL_UNEXPECTEDLY(pClientImc))
+        return;
 
-#define WINE_IMC_VALID_MAGIC 0x56434D49
+    pNewImeDpi = ImmLockImeDpi(hNewKL);
 
-static const WCHAR szwWineIMCProperty[] = {'W','i','n','e','I','m','m','H','I','M','C','P','r','o','p','e','r','t','y',0};
-static const WCHAR szImeFileW[] = {'I','m','e',' ','F','i','l','e',0};
-static const WCHAR szLayoutTextW[] = {'L','a','y','o','u','t',' ','T','e','x','t',0};
-static const WCHAR szImeRegFmt[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s','\\','%','0','8','l','x',0};
+    if (hNewKL != hOldKL)
+        pOldImeDpi = ImmLockImeDpi(hOldKL);
 
-static inline BOOL is_himc_ime_unicode(const InputContextData *data)
-{
-    return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE);
-}
+    if (pNewImeDpi)
+    {
+        cbNewPrivate = pNewImeDpi->ImeInfo.dwPrivateDataSize;
+        pClientImc->uCodePage = pNewImeDpi->uCodePage;
+    }
+    else
+    {
+        pClientImc->uCodePage = CP_ACP;
+    }
 
-static InputContextData* get_imc_data(HIMC hIMC)
-{
-    InputContextData *data = (InputContextData *)hIMC;
+    if (pOldImeDpi)
+        cbOldPrivate = pOldImeDpi->ImeInfo.dwPrivateDataSize;
 
-    if (hIMC == NULL)
-        return NULL;
+    cbNewPrivate = max(cbNewPrivate, sizeof(DWORD));
+    cbOldPrivate = max(cbOldPrivate, sizeof(DWORD));
 
-    if(IsBadReadPtr(data, sizeof(InputContextData)) || data->magic != WINE_IMC_VALID_MAGIC)
+    if (pClientImc->hKL == hOldKL)
     {
-        SetLastError(ERROR_INVALID_HANDLE);
-        return NULL;
+        if (pOldImeDpi)
+        {
+            if (IS_IME_HKL(hOldKL))
+                pOldImeDpi->ImeSelect(hIMC, FALSE);
+            else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
+                pOldImeDpi->CtfImeSelectEx(hIMC, FALSE, hOldKL);
+        }
+        pClientImc->hKL = NULL;
+    }
+
+    if (CtfImmIsTextFrameServiceDisabled() && IS_CICERO_MODE() && !IS_16BIT_MODE())
+    {
+        bIsNewHKLIme = IS_IME_HKL(hNewKL);
+        bIsOldHKLIme = IS_IME_HKL(hOldKL);
+    }
+
+    pIC = (LPINPUTCONTEXTDX)Imm32InternalLockIMC(hIMC, FALSE);
+    if (!pIC)
+    {
+        if (pNewImeDpi)
+        {
+            if (IS_IME_HKL(hNewKL))
+                pNewImeDpi->ImeSelect(hIMC, TRUE);
+            else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
+                pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL);
+
+            pClientImc->hKL = hNewKL;
+        }
+    }
+    else
+    {
+        dwOldConversion = pIC->fdwConversion;
+        dwOldSentence = pIC->fdwSentence;
+        fOldOpen = pIC->fOpen;
+
+        if (pNewImeDpi)
+        {
+            bClientWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
+            bNewDpiWide = ImeDpi_IsUnicode(pNewImeDpi);
+            if (bClientWide && !bNewDpiWide)
+            {
+                if (pIC->fdwInit & INIT_LOGFONT)
+                {
+                    LogFontWideToAnsi(&pIC->lfFont.W, &LogFontA);
+                    pIC->lfFont.A = LogFontA;
+                }
+                pClientImc->dwFlags &= ~CLIENTIMC_WIDE;
+            }
+            else if (!bClientWide && bNewDpiWide)
+            {
+                if (pIC->fdwInit & INIT_LOGFONT)
+                {
+                    LogFontAnsiToWide(&pIC->lfFont.A, &LogFontW);
+                    pIC->lfFont.W = LogFontW;
+                }
+                pClientImc->dwFlags |= CLIENTIMC_WIDE;
+            }
+        }
+
+        if (cbOldPrivate != cbNewPrivate)
+        {
+            hPrivate = ImmReSizeIMCC(pIC->hPrivate, cbNewPrivate);
+            if (!hPrivate)
+            {
+                ImmDestroyIMCC(pIC->hPrivate);
+                hPrivate = ImmCreateIMCC(cbNewPrivate);
+            }
+            pIC->hPrivate = hPrivate;
+        }
+
+#define MAX_IMCC_SIZE 0x1000
+        dwSize = ImmGetIMCCSize(pIC->hMsgBuf);
+        if (ImmGetIMCCLockCount(pIC->hMsgBuf) || dwSize > MAX_IMCC_SIZE)
+        {
+            ImmDestroyIMCC(pIC->hMsgBuf);
+            pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT));
+            pIC->dwNumMsgBuf = 0;
+        }
+
+        dwSize = ImmGetIMCCSize(pIC->hGuideLine);
+        dwNewSize = sizeof(GUIDELINE);
+        if (ImmGetIMCCLockCount(pIC->hGuideLine) ||
+            dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
+        {
+            ImmDestroyIMCC(pIC->hGuideLine);
+            pIC->hGuideLine = ImmCreateIMCC(dwNewSize);
+            pGL = ImmLockIMCC(pIC->hGuideLine);
+            if (pGL)
+            {
+                pGL->dwSize = dwNewSize;
+                ImmUnlockIMCC(pIC->hGuideLine);
+            }
+        }
+
+        dwSize = ImmGetIMCCSize(pIC->hCandInfo);
+        dwNewSize = sizeof(CANDIDATEINFO);
+        if (ImmGetIMCCLockCount(pIC->hCandInfo) ||
+            dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
+        {
+            ImmDestroyIMCC(pIC->hCandInfo);
+            pIC->hCandInfo = ImmCreateIMCC(dwNewSize);
+            pCI = ImmLockIMCC(pIC->hCandInfo);
+            if (pCI)
+            {
+                pCI->dwSize = dwNewSize;
+                ImmUnlockIMCC(pIC->hCandInfo);
+            }
+        }
+
+        dwSize = ImmGetIMCCSize(pIC->hCompStr);
+        dwNewSize = sizeof(COMPOSITIONSTRING);
+        if (ImmGetIMCCLockCount(pIC->hCompStr) ||
+            dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
+        {
+            ImmDestroyIMCC(pIC->hCompStr);
+            pIC->hCompStr = ImmCreateIMCC(dwNewSize);
+            pCS = ImmLockIMCC(pIC->hCompStr);
+            if (pCS)
+            {
+                pCS->dwSize = dwNewSize;
+                ImmUnlockIMCC(pIC->hCompStr);
+            }
+        }
+#undef MAX_IMCC_SIZE
+
+        if (pOldImeDpi && bIsOldHKLIme)
+        {
+            pOldState = Imm32FetchImeState(pIC, hOldKL);
+            if (pOldState)
+                Imm32SaveImeStateSentence(pIC, pOldState, hOldKL);
+        }
+
+        if (pNewImeDpi && bIsNewHKLIme)
+            pNewState = Imm32FetchImeState(pIC, hNewKL);
+
+        if (pOldState != pNewState)
+        {
+            if (pOldState)
+            {
+                pOldState->fOpen = !!pIC->fOpen;
+                pOldState->dwConversion = pIC->fdwConversion;
+                pOldState->dwConversion &= ~IME_CMODE_EUDC;
+                pOldState->dwSentence = pIC->fdwSentence;
+                pOldState->dwInit = pIC->fdwInit;
+            }
+
+            if (pNewState)
+            {
+                if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_FORCE_OPEN)
+                {
+                    pIC->dwChange &= ~INPUTCONTEXTDX_CHANGE_FORCE_OPEN;
+                    pIC->fOpen = TRUE;
+                }
+                else
+                {
+                    pIC->fOpen = pNewState->fOpen;
+                }
+
+                pIC->fdwConversion = pNewState->dwConversion;
+                pIC->fdwConversion &= ~IME_CMODE_EUDC;
+                pIC->fdwSentence = pNewState->dwSentence;
+                pIC->fdwInit = pNewState->dwInit;
+            }
+        }
+
+        if (pNewState)
+            Imm32LoadImeStateSentence(pIC, pNewState, hNewKL);
+
+        if (pNewImeDpi)
+        {
+            if (IS_IME_HKL(hNewKL))
+                pNewImeDpi->ImeSelect(hIMC, TRUE);
+            else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
+                pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL);
+
+            pClientImc->hKL = hNewKL;
+        }
+
+        pIC->dwChange = 0;
+        if (pIC->fOpen != fOldOpen)
+            pIC->dwChange |= INPUTCONTEXTDX_CHANGE_OPEN;
+        if (pIC->fdwConversion != dwOldConversion)
+            pIC->dwChange |= INPUTCONTEXTDX_CHANGE_CONVERSION;
+        if (pIC->fdwSentence != dwOldSentence)
+            pIC->dwChange |= INPUTCONTEXTDX_CHANGE_SENTENCE;
+
+        ImmUnlockIMC(hIMC);
     }
-    return data;
+
+    ImmUnlockImeDpi(pOldImeDpi);
+    ImmUnlockImeDpi(pNewImeDpi);
+    ImmUnlockClientImc(pClientImc);
 }
 
-static HIMC get_default_context( HWND hwnd )
+typedef struct SELECT_LAYOUT
 {
-    FIXME("Don't use this function\n");
-    return FALSE;
+    HKL hNewKL;
+    HKL hOldKL;
+} SELECT_LAYOUT, *LPSELECT_LAYOUT;
+
+// Win: SelectContextProc
+static BOOL CALLBACK Imm32SelectContextProc(HIMC hIMC, LPARAM lParam)
+{
+    LPSELECT_LAYOUT pSelect = (LPSELECT_LAYOUT)lParam;
+    Imm32SelectInputContext(pSelect->hNewKL, pSelect->hOldKL, hIMC);
+    return TRUE;
+}
+
+// Win: NotifyIMEProc
+static BOOL CALLBACK Imm32NotifyIMEProc(HIMC hIMC, LPARAM lParam)
+{
+    ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, (DWORD)lParam, 0);
+    return TRUE;
 }
 
-static BOOL IMM_IsCrossThreadAccess(HWND hWnd,  HIMC hIMC)
+/***********************************************************************
+ *             ImmActivateLayout (IMM32.@)
+ */
+BOOL WINAPI ImmActivateLayout(HKL hKL)
 {
-    InputContextData *data;
+    PIMEDPI pImeDpi;
+    HKL hOldKL;
+    LPARAM lParam;
+    HWND hwndDefIME = NULL;
+    SELECT_LAYOUT SelectLayout;
+
+    hOldKL = GetKeyboardLayout(0);
+
+    if (hOldKL == hKL && !(GetWin32ClientInfo()->CI_flags & CI_IMMACTIVATE))
+        return TRUE;
 
-    if (hWnd)
+    ImmLoadIME(hKL);
+
+    if (hOldKL != hKL)
     {
-        DWORD thread = GetWindowThreadProcessId(hWnd, NULL);
-        if (thread != GetCurrentThreadId()) return TRUE;
+        pImeDpi = ImmLockImeDpi(hOldKL);
+        if (pImeDpi)
+        {
+            if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT)
+                lParam = CPS_COMPLETE;
+            else
+                lParam = CPS_CANCEL;
+            ImmUnlockImeDpi(pImeDpi);
+
+            ImmEnumInputContext(0, Imm32NotifyIMEProc, lParam);
+        }
+
+        hwndDefIME = ImmGetDefaultIMEWnd(NULL);
+        if (IsWindow(hwndDefIME))
+            SendMessageW(hwndDefIME, WM_IME_SELECT, FALSE, (LPARAM)hOldKL);
+
+        NtUserSetThreadLayoutHandles(hKL, hOldKL);
     }
-    data = get_imc_data(hIMC);
-    if (data && data->threadID != GetCurrentThreadId())
-        return TRUE;
 
-    return FALSE;
+    SelectLayout.hNewKL = hKL;
+    SelectLayout.hOldKL = hOldKL;
+    ImmEnumInputContext(0, Imm32SelectContextProc, (LPARAM)&SelectLayout);
+
+    if (IsWindow(hwndDefIME))
+        SendMessageW(hwndDefIME, WM_IME_SELECT, TRUE, (LPARAM)hKL);
+
+    return TRUE;
 }
 
 /***********************************************************************
@@ -198,60 +490,48 @@ static BOOL IMM_IsCrossThreadAccess(HWND hWnd,  HIMC hIMC)
  */
 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
 {
-    HIMC old = NULL;
-    InputContextData *data = get_imc_data(hIMC);
+    PWND pWnd;
+    HWND hwndFocus;
+    DWORD dwValue;
+    HIMC hOldIMC;
 
-    TRACE("(%p, %p):\n", hWnd, hIMC);
+    TRACE("(%p, %p)\n", hWnd, hIMC);
 
-    if(hIMC && !data)
+    if (!IS_IMM_MODE())
+    {
+        TRACE("\n");
         return NULL;
+    }
 
-    /*
-     * If already associated just return
-     */
-    if (hIMC && data->IMC.hWnd == hWnd)
-        return hIMC;
-
-    if (hIMC && IMM_IsCrossThreadAccess(hWnd, hIMC))
+    pWnd = ValidateHwnd(hWnd);
+    if (IS_NULL_UNEXPECTEDLY(pWnd))
         return NULL;
 
-    if (hWnd)
-    {
-        HIMC defaultContext = get_default_context( hWnd );
-        old = RemovePropW(hWnd,szwWineIMCProperty);
-
-        if (old == NULL)
-            old = defaultContext;
-        else if (old == (HIMC)-1)
-            old = NULL;
+    if (hIMC && IS_CROSS_THREAD_HIMC(hIMC))
+        return NULL;
 
-        if (hIMC != defaultContext)
-        {
-            if (hIMC == NULL) /* Meaning disable imm for that window*/
-                SetPropW(hWnd,szwWineIMCProperty,(HANDLE)-1);
-            else
-                SetPropW(hWnd,szwWineIMCProperty,hIMC);
-        }
+    hOldIMC = pWnd->hImc;
+    if (hOldIMC == hIMC)
+        return hIMC;
 
-        if (old)
-        {
-            InputContextData *old_data = (InputContextData *)old;
-            if (old_data->IMC.hWnd == hWnd)
-                old_data->IMC.hWnd = NULL;
-        }
-    }
+    dwValue = NtUserAssociateInputContext(hWnd, hIMC, 0);
+    switch (dwValue)
+    {
+        case 0:
+            return hOldIMC;
 
-    if (!hIMC)
-        return old;
+        case 1:
+            hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS);
+            if (hwndFocus == hWnd)
+            {
+                ImmSetActiveContext(hWnd, hOldIMC, FALSE);
+                ImmSetActiveContext(hWnd, hIMC, TRUE);
+            }
+            return hOldIMC;
 
-    if(GetActiveWindow() == data->IMC.hWnd)
-    {
-        SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, FALSE, ISC_SHOWUIALL);
-        data->IMC.hWnd = hWnd;
-        SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, TRUE, ISC_SHOWUIALL);
+        default:
+            return NULL;
     }
-
-    return old;
 }
 
 /***********************************************************************
@@ -266,14 +546,17 @@ BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
 
     TRACE("(%p, %p, 0x%lX)\n", hWnd, hIMC, dwFlags);
 
-    if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
+    if (!IS_IMM_MODE())
+    {
+        TRACE("\n");
         return FALSE;
+    }
 
-    if (hIMC && !(dwFlags & IACE_DEFAULT) && Imm32IsCrossThreadAccess(hIMC))
+    if (hIMC && !(dwFlags & IACE_DEFAULT) && IS_CROSS_THREAD_HIMC(hIMC))
         return FALSE;
 
     hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS);
-    pFocusWnd = ValidateHwndNoErr(hwndFocus);
+    pFocusWnd = ValidateHwnd(hwndFocus);
     if (pFocusWnd)
         hOldIMC = pFocusWnd->hImc;
 
@@ -284,7 +567,7 @@ BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
             return TRUE;
 
         case 1:
-            pFocusWnd = ValidateHwndNoErr(hwndFocus);
+            pFocusWnd = ValidateHwnd(hwndFocus);
             if (pFocusWnd)
             {
                 hIMC = pFocusWnd->hImc;
@@ -311,86 +594,122 @@ HIMC WINAPI ImmCreateContext(void)
 
     TRACE("()\n");
 
-    if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
+    if (!IS_IMM_MODE())
+    {
+        TRACE("\n");
         return NULL;
+    }
 
-    pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
-    if (pClientImc == NULL)
+    pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
+    if (IS_NULL_UNEXPECTEDLY(pClientImc))
         return NULL;
 
-    hIMC = NtUserCreateInputContext(pClientImc);
-    if (hIMC == NULL)
+    hIMC = NtUserCreateInputContext((ULONG_PTR)pClientImc);
+    if (IS_NULL_UNEXPECTEDLY(hIMC))
     {
-        Imm32HeapFree(pClientImc);
+        ImmLocalFree(pClientImc);
         return NULL;
     }
 
     RtlInitializeCriticalSection(&pClientImc->cs);
 
-    // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
-    pClientImc->unknown = NtUserGetThreadState(13);
+    pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS);
 
     return hIMC;
 }
 
-static VOID APIENTRY Imm32CleanupContextExtra(LPINPUTCONTEXT pIC)
+// Win: DestroyImeModeSaver
+static VOID APIENTRY Imm32DestroyImeModeSaver(LPINPUTCONTEXTDX pIC)
 {
-    FIXME("We have to do something do here");
-}
+    PIME_STATE pState, pNext;
+    PIME_SUBSTATE pSubState, pSubNext;
 
-static PCLIENTIMC APIENTRY Imm32FindClientImc(HIMC hIMC)
-{
-    // FIXME
-    return NULL;
+    for (pState = pIC->pState; pState; pState = pNext)
+    {
+        pNext = pState->pNext;
+
+        for (pSubState = pState->pSubState; pSubState; pSubState = pSubNext)
+        {
+            pSubNext = pSubState->pNext;
+            ImmLocalFree(pSubState);
+        }
+
+        ImmLocalFree(pState);
+    }
+
+    pIC->pState = NULL;
 }
 
-BOOL APIENTRY Imm32CleanupContext(HIMC hIMC, HKL hKL, BOOL bKeep)
+// Win: DestroyInputContext
+BOOL APIENTRY Imm32DestroyInputContext(HIMC hIMC, HKL hKL, BOOL bKeep)
 {
     PIMEDPI pImeDpi;
-    LPINPUTCONTEXT pIC;
+    LPINPUTCONTEXTDX pIC;
     PCLIENTIMC pClientImc;
+    PIMC pIMC;
 
-    if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32) || hIMC == NULL)
+    if (hIMC == NULL)
         return FALSE;
 
-    FIXME("We have do something to do here\n");
-    pClientImc = Imm32FindClientImc(hIMC);
-    if (!pClientImc)
+    if (!IS_IMM_MODE())
+    {
+        TRACE("\n");
+        return FALSE;
+    }
+
+    pIMC = ValidateHandle(hIMC, TYPE_INPUTCONTEXT);
+    if (IS_NULL_UNEXPECTEDLY(pIMC))
         return FALSE;
 
-    if (pClientImc->hImc == NULL)
+    if (pIMC->head.pti != Imm32CurrentPti())
     {
-        pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
-        ImmUnlockClientImc(pClientImc);
-        if (!bKeep)
-            return NtUserDestroyInputContext(hIMC);
-        return TRUE;
+        ERR("Thread mismatch\n");
+        return FALSE;
+    }
+
+    pClientImc = (PCLIENTIMC)pIMC->dwClientImcData;
+    if (pClientImc == NULL)
+    {
+        TRACE("pClientImc == NULL\n");
+        goto Finish;
+    }
+
+    if ((pClientImc->dwFlags & CLIENTIMC_UNKNOWN2) && !bKeep)
+    {
+        ERR("Can't destroy for CLIENTIMC_UNKNOWN2\n");
+        return FALSE;
     }
 
-    pIC = ImmLockIMC(hIMC);
-    if (pIC == NULL)
+    if (pClientImc->dwFlags & CLIENTIMC_DESTROY)
+        return TRUE;
+
+    InterlockedIncrement(&pClientImc->cLockObj);
+
+    if (IS_NULL_UNEXPECTEDLY(pClientImc->hInputContext))
+        goto Quit;
+
+    pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
+    if (IS_NULL_UNEXPECTEDLY(pIC))
     {
         ImmUnlockClientImc(pClientImc);
         return FALSE;
     }
 
-    FIXME("We have do something to do here\n");
+    CtfImmTIMDestroyInputContext(hIMC);
 
     if (pClientImc->hKL == hKL)
     {
         pImeDpi = ImmLockImeDpi(hKL);
-        if (pImeDpi != NULL)
+        if (pImeDpi)
         {
             if (IS_IME_HKL(hKL))
-            {
                 pImeDpi->ImeSelect(hIMC, FALSE);
-            }
-            else if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
-            {
-                FIXME("We have do something to do here\n");
-            }
+            else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
+                pImeDpi->CtfImeSelectEx(hIMC, FALSE, hKL);
+
             ImmUnlockImeDpi(pImeDpi);
         }
+
         pClientImc->hKL = NULL;
     }
 
@@ -399,83 +718,261 @@ BOOL APIENTRY Imm32CleanupContext(HIMC hIMC, HKL hKL, BOOL bKeep)
     ImmDestroyIMCC(pIC->hGuideLine);
     ImmDestroyIMCC(pIC->hCandInfo);
     ImmDestroyIMCC(pIC->hCompStr);
-
-    Imm32CleanupContextExtra(pIC);
-
+    Imm32DestroyImeModeSaver(pIC);
     ImmUnlockIMC(hIMC);
 
-    pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
+Quit:
+    pClientImc->dwFlags |= CLIENTIMC_DESTROY;
     ImmUnlockClientImc(pClientImc);
 
-    if (!bKeep)
-        return NtUserDestroyInputContext(hIMC);
-
-    return TRUE;
+Finish:
+    if (bKeep)
+        return TRUE;
+    return NtUserDestroyInputContext(hIMC);
 }
 
-/***********************************************************************
- *             ImmDestroyContext (IMM32.@)
- */
-BOOL WINAPI ImmDestroyContext(HIMC hIMC)
+// NOTE: Windows does recursive call ImmLockIMC here but we don't do so.
+// Win: BOOL CreateInputContext(HIMC hIMC, HKL hKL, BOOL fSelect)
+BOOL APIENTRY
+Imm32CreateInputContext(HIMC hIMC, LPINPUTCONTEXT pIC, PCLIENTIMC pClientImc, HKL hKL, BOOL fSelect)
 {
-    HKL hKL;
+    DWORD dwIndex, cbPrivate;
+    PIMEDPI pImeDpi = NULL;
+    LPCOMPOSITIONSTRING pCS;
+    LPCANDIDATEINFO pCI;
+    LPGUIDELINE pGL;
+
+    /* Create IC components */
+    pIC->hCompStr = ImmCreateIMCC(sizeof(COMPOSITIONSTRING));
+    pIC->hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO));
+    pIC->hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE));
+    pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT));
+    if (IS_NULL_UNEXPECTEDLY(pIC->hCompStr) ||
+        IS_NULL_UNEXPECTEDLY(pIC->hCandInfo) ||
+        IS_NULL_UNEXPECTEDLY(pIC->hGuideLine) ||
+        IS_NULL_UNEXPECTEDLY(pIC->hMsgBuf))
+    {
+        goto Fail;
+    }
 
-    TRACE("(%p)\n", hIMC);
+    /* Initialize IC components */
+    pCS = ImmLockIMCC(pIC->hCompStr);
+    if (IS_NULL_UNEXPECTEDLY(pCS))
+        goto Fail;
+    pCS->dwSize = sizeof(COMPOSITIONSTRING);
+    ImmUnlockIMCC(pIC->hCompStr);
+
+    pCI = ImmLockIMCC(pIC->hCandInfo);
+    if (IS_NULL_UNEXPECTEDLY(pCI))
+        goto Fail;
+    pCI->dwSize = sizeof(CANDIDATEINFO);
+    ImmUnlockIMCC(pIC->hCandInfo);
+
+    pGL = ImmLockIMCC(pIC->hGuideLine);
+    if (IS_NULL_UNEXPECTEDLY(pGL))
+        goto Fail;
+    pGL->dwSize = sizeof(GUIDELINE);
+    ImmUnlockIMCC(pIC->hGuideLine);
+
+    pIC->dwNumMsgBuf = 0;
+    pIC->fOpen = FALSE;
+    pIC->fdwConversion = pIC->fdwSentence = 0;
+
+    for (dwIndex = 0; dwIndex < MAX_CANDIDATEFORM; ++dwIndex)
+        pIC->cfCandForm[dwIndex].dwIndex = IMM_INVALID_CANDFORM;
+
+    /* Get private data size */
+    pImeDpi = ImmLockImeDpi(hKL);
+    if (!pImeDpi)
+    {
+        cbPrivate = sizeof(DWORD);
+    }
+    else
+    {
+        /* Update CLIENTIMC */
+        pClientImc->uCodePage = pImeDpi->uCodePage;
+        if (ImeDpi_IsUnicode(pImeDpi))
+            pClientImc->dwFlags |= CLIENTIMC_WIDE;
 
-    if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
-        return FALSE;
+        cbPrivate = pImeDpi->ImeInfo.dwPrivateDataSize;
+    }
 
-    if (Imm32IsCrossThreadAccess(hIMC))
-        return FALSE;
+    /* Create private data */
+    pIC->hPrivate = ImmCreateIMCC(cbPrivate);
+    if (IS_NULL_UNEXPECTEDLY(pIC->hPrivate))
+        goto Fail;
 
-    hKL = GetKeyboardLayout(0);
-    return Imm32CleanupContext(hIMC, hKL, FALSE);
+    CtfImmTIMCreateInputContext(hIMC);
+
+    if (pImeDpi)
+    {
+        /* Select the IME */
+        if (fSelect)
+        {
+            if (IS_IME_HKL(hKL))
+                pImeDpi->ImeSelect(hIMC, TRUE);
+            else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
+                pImeDpi->CtfImeSelectEx(hIMC, TRUE, hKL);
+        }
+
+        /* Set HKL */
+        pClientImc->hKL = hKL;
+
+        ImmUnlockImeDpi(pImeDpi);
+    }
+
+    return TRUE;
+
+Fail:
+    if (pImeDpi)
+        ImmUnlockImeDpi(pImeDpi);
+
+    pIC->hMsgBuf = ImmDestroyIMCC(pIC->hMsgBuf);
+    pIC->hGuideLine = ImmDestroyIMCC(pIC->hGuideLine);
+    pIC->hCandInfo = ImmDestroyIMCC(pIC->hCandInfo);
+    pIC->hCompStr = ImmDestroyIMCC(pIC->hCompStr);
+    return FALSE;
 }
 
-static PCLIENTIMC APIENTRY Imm32GetClientImcCache(void)
+LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect)
 {
-    // FIXME: Do something properly here
+    HANDLE hIC;
+    LPINPUTCONTEXT pIC = NULL;
+    PCLIENTIMC pClientImc;
+    WORD LangID;
+    DWORD dwThreadId;
+    HKL hOldKL, hNewKL;
+    PIMEDPI pImeDpi = NULL;
+
+    pClientImc = ImmLockClientImc(hIMC);
+    if (!pClientImc)
+        return NULL;
+
+    RtlEnterCriticalSection(&pClientImc->cs);
+
+    if (pClientImc->hInputContext)
+    {
+        pIC = LocalLock(pClientImc->hInputContext);
+        if (IS_NULL_UNEXPECTEDLY(pIC))
+            goto Failure;
+
+        CtfImmTIMCreateInputContext(hIMC);
+        goto Success;
+    }
+
+    dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
+    if (dwThreadId == GetCurrentThreadId() && IS_CICERO_MODE() && !IS_16BIT_MODE())
+    {
+        hOldKL = GetKeyboardLayout(0);
+        LangID = LOWORD(hOldKL);
+        hNewKL = (HKL)(DWORD_PTR)MAKELONG(LangID, LangID);
+
+        pImeDpi = Imm32FindOrLoadImeDpi(hNewKL);
+        if (pImeDpi)
+        {
+            CtfImmTIMActivate(hNewKL);
+        }
+    }
+
+    if (!NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME))
+    {
+        ERR("No default IME window\n");
+        goto Failure;
+    }
+
+    hIC = LocalAlloc(LHND, sizeof(INPUTCONTEXTDX));
+    pIC = LocalLock(hIC);
+    if (IS_NULL_UNEXPECTEDLY(pIC))
+    {
+        LocalFree(hIC);
+        goto Failure;
+    }
+    pClientImc->hInputContext = hIC;
+
+    hNewKL = GetKeyboardLayout(dwThreadId);
+    if (!Imm32CreateInputContext(hIMC, pIC, pClientImc, hNewKL, fSelect))
+    {
+        LocalUnlock(hIC);
+        pClientImc->hInputContext = LocalFree(hIC);
+        goto Failure;
+    }
+
+Success:
+    RtlLeaveCriticalSection(&pClientImc->cs);
+    InterlockedIncrement(&pClientImc->cLockObj);
+    ImmUnlockClientImc(pClientImc);
+    return pIC;
+
+Failure:
+    RtlLeaveCriticalSection(&pClientImc->cs);
+    ImmUnlockClientImc(pClientImc);
     return NULL;
 }
 
+/***********************************************************************
+ *             ImmDestroyContext (IMM32.@)
+ */
+BOOL WINAPI ImmDestroyContext(HIMC hIMC)
+{
+    HKL hKL;
+
+    TRACE("(%p)\n", hIMC);
+
+    if (!IS_IMM_MODE())
+    {
+        TRACE("\n");
+        return FALSE;
+    }
+
+    if (IS_CROSS_THREAD_HIMC(hIMC))
+        return FALSE;
+
+    hKL = GetKeyboardLayout(0);
+    return Imm32DestroyInputContext(hIMC, hKL, FALSE);
+}
+
 /***********************************************************************
  *             ImmLockClientImc (IMM32.@)
  */
 PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
 {
+    PIMC pIMC;
     PCLIENTIMC pClientImc;
 
     TRACE("(%p)\n", hImc);
 
-    if (hImc == NULL)
+    if (IS_NULL_UNEXPECTEDLY(hImc))
         return NULL;
 
-    pClientImc = Imm32GetClientImcCache();
-    if (!pClientImc)
+    pIMC = ValidateHandle(hImc, TYPE_INPUTCONTEXT);
+    if (!pIMC || !Imm32CheckImcProcess(pIMC))
+        return NULL;
+
+    pClientImc = (PCLIENTIMC)pIMC->dwClientImcData;
+    if (pClientImc)
     {
-        pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
-        if (!pClientImc)
+        if (pClientImc->dwFlags & CLIENTIMC_DESTROY)
             return NULL;
+        goto Finish;
+    }
 
-        RtlInitializeCriticalSection(&pClientImc->cs);
-
-        // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
-        pClientImc->unknown = NtUserGetThreadState(13);
+    pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
+    if (IS_NULL_UNEXPECTEDLY(pClientImc))
+        return NULL;
 
-        if (!NtUserUpdateInputContext(hImc, 0, pClientImc))
-        {
-            Imm32HeapFree(pClientImc);
-            return NULL;
-        }
+    RtlInitializeCriticalSection(&pClientImc->cs);
+    pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS);
 
-        pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
-    }
-    else
+    if (!NtUserUpdateInputContext(hImc, UIC_CLIENTIMCDATA, (DWORD_PTR)pClientImc))
     {
-        if (pClientImc->dwFlags & CLIENTIMC_UNKNOWN1)
-            return NULL;
+        ERR("\n");
+        ImmLocalFree(pClientImc);
+        return NULL;
     }
 
+    pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
+
+Finish:
     InterlockedIncrement(&pClientImc->cLockObj);
     return pClientImc;
 }
@@ -486,40 +983,43 @@ PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
 VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc)
 {
     LONG cLocks;
-    HIMC hImc;
+    HANDLE hInputContext;
 
     TRACE("(%p)\n", pClientImc);
 
     cLocks = InterlockedDecrement(&pClientImc->cLockObj);
-    if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_UNKNOWN1))
+    if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_DESTROY))
         return;
 
-    hImc = pClientImc->hImc;
-    if (hImc)
-        LocalFree(hImc);
+    hInputContext = pClientImc->hInputContext;
+    if (hInputContext)
+        LocalFree(hInputContext);
 
     RtlDeleteCriticalSection(&pClientImc->cs);
-    Imm32HeapFree(pClientImc);
+    ImmLocalFree(pClientImc);
 }
 
-static HIMC APIENTRY Imm32GetContextEx(HWND hWnd, DWORD dwContextFlags)
+// Win: ImmGetSaveContext
+static HIMC APIENTRY ImmGetSaveContext(HWND hWnd, DWORD dwContextFlags)
 {
     HIMC hIMC;
     PCLIENTIMC pClientImc;
     PWND pWnd;
 
-    if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
+    if (!IS_IMM_MODE())
+    {
+        TRACE("Not IMM mode.\n");
         return NULL;
+    }
 
     if (!hWnd)
     {
-        // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
-        hIMC = (HIMC)NtUserGetThreadState(4);
+        hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT);
         goto Quit;
     }
 
-    pWnd = ValidateHwndNoErr(hWnd);
-    if (!pWnd || Imm32IsCrossProcessAccess(hWnd))
+    pWnd = ValidateHwnd(hWnd);
+    if (IS_NULL_UNEXPECTEDLY(pWnd) || IS_CROSS_PROCESS_HWND(hWnd))
         return NULL;
 
     hIMC = pWnd->hImc;
@@ -528,418 +1028,36 @@ static HIMC APIENTRY Imm32GetContextEx(HWND hWnd, DWORD dwContextFlags)
 
 Quit:
     pClientImc = ImmLockClientImc(hIMC);
-    if (pClientImc == NULL)
+    if (IS_NULL_UNEXPECTEDLY(pClientImc))
         return NULL;
-    if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_UNKNOWN3))
+
+    if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_DISABLEIME))
         hIMC = NULL;
+
     ImmUnlockClientImc(pClientImc);
     return hIMC;
 }
 
-
-/* Helpers for the GetCompositionString functions */
-
-/* Source encoding is defined by context, source length is always given in respective characters. Destination buffer
-   length is always in bytes. */
-static INT
-CopyCompStringIMEtoClient(const InputContextData *data, const void *src, INT src_len, void *dst,
-                          INT dst_len, BOOL unicode)
-{
-    int char_size = unicode ? sizeof(WCHAR) : sizeof(char);
-    INT ret;
-
-    if (is_himc_ime_unicode(data) ^ unicode)
-    {
-        if (unicode)
-            ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR));
-        else
-            ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL);
-        ret *= char_size;
-    }
-    else
-    {
-        if (dst_len)
-        {
-            ret = min(src_len * char_size, dst_len);
-            memcpy(dst, src, ret);
-        }
-        else
-            ret = src_len * char_size;
-    }
-
-    return ret;
-}
-
-/* Composition string encoding is defined by context, returned attributes correspond to string, converted according to
-   passed mode. String length is in characters, attributes are in byte arrays. */
-static INT
-CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src, INT src_len, const void *comp_string,
-                        INT str_len, BYTE *dst, INT dst_len, BOOL unicode)
-{
-    union
-    {
-        const void *str;
-        const WCHAR *strW;
-        const char *strA;
-    } string;
-    INT rc;
-
-    string.str = comp_string;
-
-    if (is_himc_ime_unicode(data) && !unicode)
-    {
-        rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL);
-        if (dst_len)
-        {
-            int i, j = 0, k = 0;
-
-            if (rc < dst_len)
-                dst_len = rc;
-            for (i = 0; i < str_len; ++i)
-            {
-                int len;
-
-                len = WideCharToMultiByte(CP_ACP, 0, string.strW + i, 1, NULL, 0, NULL, NULL);
-                for (; len > 0; --len)
-                {
-                    dst[j++] = src[k];
-
-                    if (j >= dst_len)
-                        goto end;
-                }
-                ++k;
-            }
-        end:
-            rc = j;
-        }
-    }
-    else if (!is_himc_ime_unicode(data) && unicode)
-    {
-        rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0);
-        if (dst_len)
-        {
-            int i, j = 0;
-
-            if (rc < dst_len)
-                dst_len = rc;
-            for (i = 0; i < str_len; ++i)
-            {
-                if (IsDBCSLeadByte(string.strA[i]))
-                    continue;
-
-                dst[j++] = src[i];
-
-                if (j >= dst_len)
-                    break;
-            }
-            rc = j;
-        }
-    }
-    else
-    {
-        memcpy(dst, src, min(src_len, dst_len));
-        rc = src_len;
-    }
-
-    return rc;
-}
-
-static INT
-CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource,
-                          LPBYTE target, INT tlen, BOOL unicode )
-{
-    INT rc;
-
-    if (is_himc_ime_unicode(data) && !unicode)
-    {
-        if (tlen)
-        {
-            int i;
-
-            if (slen < tlen)
-                tlen = slen;
-            tlen /= sizeof (DWORD);
-            for (i = 0; i < tlen; ++i)
-            {
-                ((DWORD *)target)[i] = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource,
-                                                          ((DWORD *)source)[i],
-                                                          NULL, 0,
-                                                          NULL, NULL);
-            }
-            rc = sizeof (DWORD) * i;
-        }
-        else
-            rc = slen;
-    }
-    else if (!is_himc_ime_unicode(data) && unicode)
-    {
-        if (tlen)
-        {
-            int i;
-
-            if (slen < tlen)
-                tlen = slen;
-            tlen /= sizeof (DWORD);
-            for (i = 0; i < tlen; ++i)
-            {
-                ((DWORD *)target)[i] = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource,
-                                                          ((DWORD *)source)[i],
-                                                          NULL, 0);
-            }
-            rc = sizeof (DWORD) * i;
-        }
-        else
-            rc = slen;
-    }
-    else
-    {
-        memcpy( target, source, min(slen,tlen));
-        rc = slen;
-    }
-
-    return rc;
-}
-
-static INT
-CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYTE ssource, BOOL unicode)
-{
-    int rc;
-
-    if (is_himc_ime_unicode(data) && !unicode)
-    {
-        rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL);
-    }
-    else if (!is_himc_ime_unicode(data) && unicode)
-    {
-        rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0);
-    }
-    else
-        rc = offset;
-
-    return rc;
-}
-
-static LONG
-ImmGetCompositionStringT(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf,
-                         DWORD dwBufLen, BOOL unicode)
-{
-    LONG rc = 0;
-    InputContextData *data = get_imc_data(hIMC);
-    LPCOMPOSITIONSTRING compstr;
-    LPBYTE compdata;
-
-    TRACE("(%p, 0x%x, %p, %d)\n", hIMC, dwIndex, lpBuf, dwBufLen);
-
-    if (!data)
-       return FALSE;
-
-    if (!data->IMC.hCompStr)
-       return FALSE;
-
-    compdata = ImmLockIMCC(data->IMC.hCompStr);
-    compstr = (LPCOMPOSITIONSTRING)compdata;
-
-    switch (dwIndex)
-    {
-    case GCS_RESULTSTR:
-        TRACE("GCS_RESULTSTR\n");
-        rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_COMPSTR:
-        TRACE("GCS_COMPSTR\n");
-        rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_COMPATTR:
-        TRACE("GCS_COMPATTR\n");
-        rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen,
-                                     compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen,
-                                     lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_COMPCLAUSE:
-        TRACE("GCS_COMPCLAUSE\n");
-        rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen,
-                                       compdata + compstr->dwCompStrOffset,
-                                       lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_RESULTCLAUSE:
-        TRACE("GCS_RESULTCLAUSE\n");
-        rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen,
-                                       compdata + compstr->dwResultStrOffset,
-                                       lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_RESULTREADSTR:
-        TRACE("GCS_RESULTREADSTR\n");
-        rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_RESULTREADCLAUSE:
-        TRACE("GCS_RESULTREADCLAUSE\n");
-        rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen,
-                                       compdata + compstr->dwResultStrOffset,
-                                       lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_COMPREADSTR:
-        TRACE("GCS_COMPREADSTR\n");
-        rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_COMPREADATTR:
-        TRACE("GCS_COMPREADATTR\n");
-        rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen,
-                                     compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen,
-                                     lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_COMPREADCLAUSE:
-        TRACE("GCS_COMPREADCLAUSE\n");
-        rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen,
-                                       compdata + compstr->dwCompStrOffset,
-                                       lpBuf, dwBufLen, unicode);
-        break;
-    case GCS_CURSORPOS:
-        TRACE("GCS_CURSORPOS\n");
-        rc = CopyCompOffsetIMEtoClient(data, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode);
-        break;
-    case GCS_DELTASTART:
-        TRACE("GCS_DELTASTART\n");
-        rc = CopyCompOffsetIMEtoClient(data, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode);
-        break;
-    default:
-        FIXME("Unhandled index 0x%x\n",dwIndex);
-        break;
-    }
-
-    ImmUnlockIMCC(data->IMC.hCompStr);
-
-    return rc;
-}
-
-/***********************************************************************
- *             ImmGetCompositionStringA (IMM32.@)
- */
-LONG WINAPI ImmGetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
-{
-    return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, FALSE);
-}
-
-/***********************************************************************
- *             ImmGetCompositionStringW (IMM32.@)
- */
-LONG WINAPI ImmGetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
-{
-    return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, TRUE);
-}
-
 /***********************************************************************
  *             ImmGetContext (IMM32.@)
  */
 HIMC WINAPI ImmGetContext(HWND hWnd)
 {
     TRACE("(%p)\n", hWnd);
-    if (hWnd == NULL)
+    if (IS_NULL_UNEXPECTEDLY(hWnd))
         return NULL;
-    return Imm32GetContextEx(hWnd, 2);
+    return ImmGetSaveContext(hWnd, 2);
 }
 
 /***********************************************************************
- *             CtfImmIsCiceroEnabled (IMM32.@)
+ *             ImmLockIMC(IMM32.@)
+ *
+ * NOTE: This is not ImmLockIMCC. Don't confuse.
  */
-BOOL WINAPI CtfImmIsCiceroEnabled(VOID)
-{
-    return (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED));
-}
-
-/***********************************************************************
- *             ImmInstallIMEA (IMM32.@)
- */
-HKL WINAPI ImmInstallIMEA(LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText)
-{
-    HKL hKL = NULL;
-    LPWSTR pszFileNameW = NULL, pszLayoutTextW = NULL;
-
-    TRACE("(%s, %s)\n", debugstr_a(lpszIMEFileName), debugstr_a(lpszLayoutText));
-
-    pszFileNameW = Imm32WideFromAnsi(lpszIMEFileName);
-    if (pszFileNameW == NULL)
-        goto Quit;
-
-    pszLayoutTextW = Imm32WideFromAnsi(lpszLayoutText);
-    if (pszLayoutTextW == NULL)
-        goto Quit;
-
-    hKL = ImmInstallIMEW(pszFileNameW, pszLayoutTextW);
-
-Quit:
-    Imm32HeapFree(pszFileNameW);
-    Imm32HeapFree(pszLayoutTextW);
-    return hKL;
-}
-
-/***********************************************************************
- *             ImmInstallIMEW (IMM32.@)
- */
-HKL WINAPI ImmInstallIMEW(LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText)
-{
-    INT lcid = GetUserDefaultLCID();
-    INT count;
-    HKL hkl;
-    DWORD rc;
-    HKEY hkey;
-    WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8];
-
-    TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName),
-                          debugstr_w(lpszLayoutText));
-
-    /* Start with 2.  e001 will be blank and so default to the wine internal IME */
-    count = 2;
-
-    while (count < 0xfff)
-    {
-        DWORD disposition = 0;
-
-        hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count );
-        wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hkl);
-
-        rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, regKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition);
-        if (rc == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
-            break;
-        else if (rc == ERROR_SUCCESS)
-            RegCloseKey(hkey);
-
-        count++;
-    }
-
-    if (count == 0xfff)
-    {
-        WARN("Unable to find slot to install IME\n");
-        return 0;
-    }
-
-    if (rc == ERROR_SUCCESS)
-    {
-        rc = RegSetValueExW(hkey, szImeFileW, 0, REG_SZ, (const BYTE*)lpszIMEFileName,
-                            (lstrlenW(lpszIMEFileName) + 1) * sizeof(WCHAR));
-        if (rc == ERROR_SUCCESS)
-            rc = RegSetValueExW(hkey, szLayoutTextW, 0, REG_SZ, (const BYTE*)lpszLayoutText,
-                                (lstrlenW(lpszLayoutText) + 1) * sizeof(WCHAR));
-        RegCloseKey(hkey);
-        return hkl;
-    }
-    else
-    {
-        WARN("Unable to set IME registry values\n");
-        return 0;
-    }
-}
-
-/***********************************************************************
-*              ImmLockIMC(IMM32.@)
-*/
 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
 {
-    InputContextData *data = get_imc_data(hIMC);
-
-    if (!data)
-        return NULL;
-    data->dwLock++;
-    return &data->IMC;
+    TRACE("(%p)\n", hIMC);
+    return Imm32InternalLockIMC(hIMC, TRUE);
 }
 
 /***********************************************************************
@@ -948,51 +1066,19 @@ LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
 {
     PCLIENTIMC pClientImc;
-    HIMC hClientImc;
 
     pClientImc = ImmLockClientImc(hIMC);
-    if (pClientImc == NULL)
+    if (IS_NULL_UNEXPECTEDLY(pClientImc))
         return FALSE;
 
-    hClientImc = pClientImc->hImc;
-    if (hClientImc)
-        LocalUnlock(hClientImc);
+    if (pClientImc->hInputContext)
+        LocalUnlock(pClientImc->hInputContext);
 
     InterlockedDecrement(&pClientImc->cLockObj);
     ImmUnlockClientImc(pClientImc);
     return TRUE;
 }
 
-/***********************************************************************
- *              ImmRequestMessageA(IMM32.@)
- */
-LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
-{
-    InputContextData *data = get_imc_data(hIMC);
-
-    TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
-
-    if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
-
-    SetLastError(ERROR_INVALID_HANDLE);
-    return 0;
-}
-
-/***********************************************************************
- *              ImmRequestMessageW(IMM32.@)
- */
-LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
-{
-    InputContextData *data = get_imc_data(hIMC);
-
-    TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
-
-    if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
-
-    SetLastError(ERROR_INVALID_HANDLE);
-    return 0;
-}
-
 /***********************************************************************
  *             ImmReleaseContext (IMM32.@)
  */
@@ -1005,193 +1091,143 @@ BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
 }
 
 /***********************************************************************
- *             ImmSetCompositionStringA (IMM32.@)
+ *              ImmEnumInputContext(IMM32.@)
  */
-BOOL WINAPI
-ImmSetCompositionStringA(HIMC hIMC, DWORD dwIndex,
-                         LPCVOID lpComp, DWORD dwCompLen,
-                         LPCVOID lpRead, DWORD dwReadLen)
+BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
 {
-    DWORD comp_len;
-    DWORD read_len;
-    WCHAR *CompBuffer = NULL;
-    WCHAR *ReadBuffer = NULL;
-    BOOL rc;
-    InputContextData *data = get_imc_data(hIMC);
-
-    TRACE("(%p, %d, %p, %d, %p, %d):\n",
-            hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
+    HIMC *phList;
+    DWORD dwIndex, dwCount;
+    BOOL ret = TRUE;
+    HIMC hIMC;
 
-    if (!data)
-        return FALSE;
+    TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
 
-    if (!(dwIndex == SCS_SETSTR ||
-          dwIndex == SCS_CHANGEATTR ||
-          dwIndex == SCS_CHANGECLAUSE ||
-          dwIndex == SCS_SETRECONVERTSTRING ||
-          dwIndex == SCS_QUERYRECONVERTSTRING))
+    dwCount = Imm32BuildHimcList(dwThreadId, &phList);
+    if (IS_ZERO_UNEXPECTEDLY(dwCount))
         return FALSE;
 
-    if (!is_himc_ime_unicode(data))
-        return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
-                        dwCompLen, lpRead, dwReadLen);
-
-    comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0);
-    if (comp_len)
-    {
-        CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len * sizeof(WCHAR));
-        MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len);
-    }
-
-    read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0);
-    if (read_len)
+    for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
     {
-        ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len * sizeof(WCHAR));
-        MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len);
+        hIMC = phList[dwIndex];
+        ret = (*lpfn)(hIMC, lParam);
+        if (!ret)
+            break;
     }
 
-    rc =  ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len,
-                                   ReadBuffer, read_len);
-
-    HeapFree(GetProcessHeap(), 0, CompBuffer);
-    HeapFree(GetProcessHeap(), 0, ReadBuffer);
-
-    return rc;
+    ImmLocalFree(phList);
+    return ret;
 }
 
 /***********************************************************************
- *             ImmSetCompositionStringW (IMM32.@)
+ *              ImmSetActiveContext(IMM32.@)
  */
-BOOL WINAPI
-ImmSetCompositionStringW(HIMC hIMC, DWORD dwIndex,
-                         LPCVOID lpComp, DWORD dwCompLen,
-                         LPCVOID lpRead, DWORD dwReadLen)
+BOOL WINAPI ImmSetActiveContext(HWND hWnd, HIMC hIMC, BOOL fActive)
 {
-    DWORD comp_len;
-    DWORD read_len;
-    CHAR *CompBuffer = NULL;
-    CHAR *ReadBuffer = NULL;
-    BOOL rc;
-    InputContextData *data = get_imc_data(hIMC);
-
-    TRACE("(%p, %d, %p, %d, %p, %d):\n",
-            hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
+    PCLIENTIMC pClientImc;
+    LPINPUTCONTEXTDX pIC;
+    PIMEDPI pImeDpi;
+    HIMC hOldIMC;
+    HKL hKL;
+    BOOL fOpen = FALSE;
+    DWORD dwConversion = 0, dwShowFlags = ISC_SHOWUIALL;
+    HWND hwndDefIME;
 
-    if (!data)
-        return FALSE;
+    TRACE("(%p, %p, %d)\n", hWnd, hIMC, fActive);
 
-    if (!(dwIndex == SCS_SETSTR ||
-          dwIndex == SCS_CHANGEATTR ||
-          dwIndex == SCS_CHANGECLAUSE ||
-          dwIndex == SCS_SETRECONVERTSTRING ||
-          dwIndex == SCS_QUERYRECONVERTSTRING))
+    if (!IS_IMM_MODE())
+    {
+        TRACE("\n");
         return FALSE;
+    }
 
-    if (is_himc_ime_unicode(data))
-        return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
-                        dwCompLen, lpRead, dwReadLen);
+    pClientImc = ImmLockClientImc(hIMC);
 
-    comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL,
-                                   NULL);
-    if (comp_len)
+    if (!fActive)
     {
-        CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len);
-        WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len,
-                            NULL, NULL);
+        if (pClientImc)
+            pClientImc->dwFlags &= ~CLIENTIMC_ACTIVE;
     }
-
-    read_len = WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, NULL, 0, NULL,
-                                   NULL);
-    if (read_len)
+    else if (hIMC)
     {
-        ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len);
-        WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len,
-                            NULL, NULL);
-    }
-
-    rc =  ImmSetCompositionStringA(hIMC, dwIndex, CompBuffer, comp_len,
-                                   ReadBuffer, read_len);
-
-    HeapFree(GetProcessHeap(), 0, CompBuffer);
-    HeapFree(GetProcessHeap(), 0, ReadBuffer);
-
-    return rc;
-}
+        if (IS_NULL_UNEXPECTEDLY(pClientImc))
+            return FALSE;
 
-/***********************************************************************
- *              ImmCreateSoftKeyboard(IMM32.@)
- */
-HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
-{
-    FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return 0;
-}
+        pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
+        if (IS_NULL_UNEXPECTEDLY(pIC))
+        {
+            ImmUnlockClientImc(pClientImc);
+            return FALSE;
+        }
 
-/***********************************************************************
- *              ImmDestroySoftKeyboard(IMM32.@)
- */
-BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
-{
-    TRACE("(%p)\n", hSoftWnd);
-    return DestroyWindow(hSoftWnd);
-}
+        pIC->hWnd = hWnd;
+        pClientImc->dwFlags |= CLIENTIMC_ACTIVE;
 
-/***********************************************************************
- *              ImmShowSoftKeyboard(IMM32.@)
- */
-BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
-{
-    TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
-    if (hSoftWnd)
-        return ShowWindow(hSoftWnd, nCmdShow);
-    return FALSE;
-}
+        if (pIC->dwUIFlags & 2)
+            dwShowFlags = (ISC_SHOWUIGUIDELINE | ISC_SHOWUIALLCANDIDATEWINDOW);
 
-/***********************************************************************
-*              ImmDisableTextFrameService(IMM32.@)
-*/
-BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
-{
-    FIXME("Stub\n");
-    return FALSE;
-}
+        fOpen = pIC->fOpen;
+        dwConversion = pIC->fdwConversion;
 
-/***********************************************************************
- *              ImmEnumInputContext(IMM32.@)
- */
-BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
-{
-    HIMC *phList;
-    DWORD dwIndex, dwCount;
-    BOOL ret = TRUE;
-    HIMC hIMC;
+        ImmUnlockIMC(hIMC);
+    }
+    else
+    {
+        hOldIMC = ImmGetSaveContext(hWnd, 1);
+        pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hOldIMC);
+        if (pIC)
+        {
+            pIC->hWnd = hWnd;
+            ImmUnlockIMC(hOldIMC);
+        }
+    }
 
-    TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
+    hKL = GetKeyboardLayout(0);
+    if (IS_CICERO_MODE() && !IS_16BIT_MODE())
+    {
+        CtfImeSetActiveContextAlways(hIMC, fActive, hWnd, hKL);
+        hKL = GetKeyboardLayout(0);
+    }
 
-    dwCount = Imm32AllocAndBuildHimcList(dwThreadId, &phList);
-    if (!dwCount)
-        return FALSE;
+    pImeDpi = ImmLockImeDpi(hKL);
+    if (pImeDpi)
+    {
+        if (IS_IME_HKL(hKL))
+            pImeDpi->ImeSetActiveContext(hIMC, fActive);
+        ImmUnlockImeDpi(pImeDpi);
+    }
 
-    for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
+    if (IsWindow(hWnd))
     {
-        hIMC = phList[dwIndex];
-        ret = (*lpfn)(hIMC, lParam);
-        if (!ret)
-            break;
+        SendMessageW(hWnd, WM_IME_SETCONTEXT, fActive, dwShowFlags);
+        if (fActive)
+            NtUserNotifyIMEStatus(hWnd, fOpen, dwConversion);
+    }
+    else if (!fActive)
+    {
+        hwndDefIME = ImmGetDefaultIMEWnd(NULL);
+        if (hwndDefIME)
+            SendMessageW(hwndDefIME, WM_IME_SETCONTEXT, 0, dwShowFlags);
     }
 
-    Imm32HeapFree(phList);
-    return ret;
+    if (pClientImc)
+        ImmUnlockClientImc(pClientImc);
+
+    return TRUE;
 }
 
 /***********************************************************************
- *              ImmSetActiveContext(IMM32.@)
+ *              ImmWINNLSGetEnableStatus (IMM32.@)
  */
-BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC hIMC, BOOL fFlag)
+
+BOOL WINAPI ImmWINNLSGetEnableStatus(HWND hWnd)
 {
-    FIXME("(%p, %p, %d): stub\n", hwnd, hIMC, fFlag);
-    return FALSE;
+    if (!Imm32IsSystemJapaneseOrKorean())
+    {
+        SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+        return FALSE;
+    }
+
+    return !!ImmGetSaveContext(hWnd, 2);
 }
 
 /***********************************************************************
@@ -1203,170 +1239,75 @@ BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
     TRACE("(%p, %d)\n", hwnd, fFlag);
 
     hIMC = ImmGetContext(hwnd);
-    if (hIMC)
-        return ImmSetActiveContext(hwnd, hIMC, fFlag);
-    return FALSE;
+    if (IS_NULL_UNEXPECTEDLY(hIMC))
+        return FALSE;
+    return ImmSetActiveContext(hwnd, hIMC, fFlag);
 }
 
 /***********************************************************************
- *             ImmGetImeMenuItemsA (IMM32.@)
+ *              GetKeyboardLayoutCP (IMM32.@)
  */
-DWORD WINAPI
-ImmGetImeMenuItemsA(HIMC hIMC, DWORD dwFlags, DWORD dwType,
-                    LPIMEMENUITEMINFOA lpImeParentMenu,
-                    LPIMEMENUITEMINFOA lpImeMenu, DWORD dwSize)
+UINT WINAPI GetKeyboardLayoutCP(_In_ LANGID wLangId)
 {
-    InputContextData *data = get_imc_data(hIMC);
-    TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
-        lpImeParentMenu, lpImeMenu, dwSize);
+    WCHAR szText[8];
+    static LANGID s_wKeyboardLangIdCache = 0;
+    static UINT s_uKeyboardLayoutCPCache = 0;
 
-    if (!data)
-    {
-        SetLastError(ERROR_INVALID_HANDLE);
-        return 0;
-    }
+    TRACE("(%u)\n", wLangId);
 
-    if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
-    {
-        if (!is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
-            return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
-                                (IMEMENUITEMINFOW*)lpImeParentMenu,
-                                (IMEMENUITEMINFOW*)lpImeMenu, dwSize);
-        else
-        {
-            IMEMENUITEMINFOW lpImeParentMenuW;
-            IMEMENUITEMINFOW *lpImeMenuW, *parent = NULL;
-            DWORD rc;
-
-            if (lpImeParentMenu)
-                parent = &lpImeParentMenuW;
-            if (lpImeMenu)
-            {
-                int count = dwSize / sizeof(LPIMEMENUITEMINFOA);
-                dwSize = count * sizeof(IMEMENUITEMINFOW);
-                lpImeMenuW = HeapAlloc(GetProcessHeap(), 0, dwSize);
-            }
-            else
-                lpImeMenuW = NULL;
+    if (wLangId == s_wKeyboardLangIdCache)
+        return s_uKeyboardLayoutCPCache;
 
-            rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
-                                parent, lpImeMenuW, dwSize);
-
-            if (lpImeParentMenu)
-            {
-                memcpy(lpImeParentMenu,&lpImeParentMenuW,sizeof(IMEMENUITEMINFOA));
-                lpImeParentMenu->hbmpItem = lpImeParentMenuW.hbmpItem;
-                WideCharToMultiByte(CP_ACP, 0, lpImeParentMenuW.szString,
-                    -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE,
-                    NULL, NULL);
-            }
-            if (lpImeMenu && rc)
-            {
-                unsigned int i;
-                for (i = 0; i < rc; i++)
-                {
-                    memcpy(&lpImeMenu[i],&lpImeMenuW[1],sizeof(IMEMENUITEMINFOA));
-                    lpImeMenu[i].hbmpItem = lpImeMenuW[i].hbmpItem;
-                    WideCharToMultiByte(CP_ACP, 0, lpImeMenuW[i].szString,
-                        -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE,
-                        NULL, NULL);
-                }
-            }
-            HeapFree(GetProcessHeap(),0,lpImeMenuW);
-            return rc;
-        }
-    }
-    else
+    if (!GetLocaleInfoW(wLangId, LOCALE_IDEFAULTANSICODEPAGE, szText, _countof(szText)))
         return 0;
+
+    s_wKeyboardLangIdCache = wLangId;
+    szText[_countof(szText) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
+    s_uKeyboardLayoutCPCache = wcstol(szText, NULL, 10);
+    return s_uKeyboardLayoutCPCache;
 }
 
-/***********************************************************************
- *             ImmGetImeMenuItemsW (IMM32.@)
- */
-DWORD WINAPI
-ImmGetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType,
-                    LPIMEMENUITEMINFOW lpImeParentMenu,
-                    LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize)
+#ifndef NDEBUG
+VOID APIENTRY Imm32UnitTest(VOID)
 {
-    InputContextData *data = get_imc_data(hIMC);
-    TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
-        lpImeParentMenu, lpImeMenu, dwSize);
-
-    if (!data)
-    {
-        SetLastError(ERROR_INVALID_HANDLE);
-        return 0;
-    }
-
-    if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
+    if (0)
     {
-        if (is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
-            return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
-                                lpImeParentMenu, lpImeMenu, dwSize);
-        else
-        {
-            IMEMENUITEMINFOA lpImeParentMenuA;
-            IMEMENUITEMINFOA *lpImeMenuA, *parent = NULL;
-            DWORD rc;
-
-            if (lpImeParentMenu)
-                parent = &lpImeParentMenuA;
-            if (lpImeMenu)
-            {
-                int count = dwSize / sizeof(LPIMEMENUITEMINFOW);
-                dwSize = count * sizeof(IMEMENUITEMINFOA);
-                lpImeMenuA = HeapAlloc(GetProcessHeap(), 0, dwSize);
-            }
-            else
-                lpImeMenuA = NULL;
-
-            rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
-                                (IMEMENUITEMINFOW*)parent,
-                                (IMEMENUITEMINFOW*)lpImeMenuA, dwSize);
-
-            if (lpImeParentMenu)
-            {
-                memcpy(lpImeParentMenu,&lpImeParentMenuA,sizeof(IMEMENUITEMINFOA));
-                lpImeParentMenu->hbmpItem = lpImeParentMenuA.hbmpItem;
-                MultiByteToWideChar(CP_ACP, 0, lpImeParentMenuA.szString,
-                    -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE);
-            }
-            if (lpImeMenu && rc)
-            {
-                unsigned int i;
-                for (i = 0; i < rc; i++)
-                {
-                    memcpy(&lpImeMenu[i],&lpImeMenuA[1],sizeof(IMEMENUITEMINFOA));
-                    lpImeMenu[i].hbmpItem = lpImeMenuA[i].hbmpItem;
-                    MultiByteToWideChar(CP_ACP, 0, lpImeMenuA[i].szString,
-                        -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE);
-                }
-            }
-            HeapFree(GetProcessHeap(),0,lpImeMenuA);
-            return rc;
-        }
+        DWORD dwValue;
+        WCHAR szText[64];
+
+        Imm32StrToUInt(L"123", &dwValue, 10);
+        ASSERT(dwValue == 123);
+        Imm32StrToUInt(L"100", &dwValue, 16);
+        ASSERT(dwValue == 0x100);
+
+        Imm32UIntToStr(123, 10, szText, _countof(szText));
+        ASSERT(lstrcmpW(szText, L"123") == 0);
+        Imm32UIntToStr(0x100, 16, szText, _countof(szText));
+        ASSERT(lstrcmpW(szText, L"100") == 0);
     }
-    else
-        return 0;
 }
+#endif
 
 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
 
-BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
+BOOL
+WINAPI
+ImmDllInitialize(
+    _In_ HINSTANCE hDll,
+    _In_ ULONG dwReason,
+    _In_opt_ PVOID pReserved)
 {
     HKL hKL;
     HIMC hIMC;
-    PTEB pTeb;
 
-    TRACE("(%p, 0x%X, %p)\n", hinstDLL, fdwReason, lpReserved);
+    TRACE("(%p, 0x%X, %p)\n", hDll, dwReason, pReserved);
 
-    switch (fdwReason)
+    switch (dwReason)
     {
         case DLL_PROCESS_ATTACH:
-            //Imm32GenerateRandomSeed(hinstDLL, 1, lpReserved); // Non-sense
-            if (!Imm32InitInstance(hinstDLL))
+            if (!ImmInitializeGlobals(hDll))
             {
-                ERR("Imm32InitInstance failed\n");
+                ERR("ImmInitializeGlobals failed\n");
                 return FALSE;
             }
             if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
@@ -1374,27 +1315,25 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
                 ERR("User32InitializeImmEntryTable failed\n");
                 return FALSE;
             }
+#ifndef NDEBUG
+            Imm32UnitTest();
+#endif
             break;
 
         case DLL_THREAD_ATTACH:
             break;
 
         case DLL_THREAD_DETACH:
-            if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
-                return TRUE;
-
-            pTeb = NtCurrentTeb();
-            if (pTeb->Win32ThreadInfo == NULL)
+            if (!IS_IMM_MODE() || NtCurrentTeb()->Win32ThreadInfo == NULL)
                 return TRUE;
 
             hKL = GetKeyboardLayout(0);
-            // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
-            hIMC = (HIMC)NtUserGetThreadState(4);
-            Imm32CleanupContext(hIMC, hKL, TRUE);
+            hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT);
+            Imm32DestroyInputContext(hIMC, hKL, TRUE);
             break;
 
         case DLL_PROCESS_DETACH:
-            RtlDeleteCriticalSection(&g_csImeDpi);
+            RtlDeleteCriticalSection(&gcsImeDpi);
             TRACE("imm32.dll is unloaded\n");
             break;
     }