xref: /reactos/dll/win32/imm32/imm.c (revision 1734f297)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing Far-Eastern languages input
5  * COPYRIGHT:   Copyright 1998 Patrik Stridvall
6  *              Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
7  *              Copyright 2017 James Tabor <james.tabor@reactos.org>
8  *              Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
9  *              Copyright 2020-2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include "precomp.h"
13 #include <ndk/exfuncs.h>
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(imm);
16 
17 HMODULE ghImm32Inst = NULL; // Win: ghInst
18 PSERVERINFO gpsi = NULL; // Win: gpsi
19 SHAREDINFO gSharedInfo = { NULL }; // Win: gSharedInfo
20 BYTE gfImmInitialized = FALSE; // Win: gfInitialized
21 ULONG_PTR gHighestUserAddress = 0;
22 
23 // Win: ImmInitializeGlobals
24 static BOOL APIENTRY ImmInitializeGlobals(HMODULE hMod)
25 {
26     NTSTATUS status;
27     SYSTEM_BASIC_INFORMATION SysInfo;
28 
29     if (hMod)
30         ghImm32Inst = hMod;
31 
32     if (gfImmInitialized)
33         return TRUE;
34 
35     status = RtlInitializeCriticalSection(&gcsImeDpi);
36     if (NT_ERROR(status))
37     {
38         ERR("\n");
39         return FALSE;
40     }
41 
42     status = NtQuerySystemInformation(SystemBasicInformation, &SysInfo, sizeof(SysInfo), NULL);
43     if (NT_ERROR(status))
44     {
45         ERR("\n");
46         return FALSE;
47     }
48     gHighestUserAddress = SysInfo.MaximumUserModeAddress;
49 
50     gfImmInitialized = TRUE;
51     return TRUE;
52 }
53 
54 /***********************************************************************
55  *		ImmRegisterClient(IMM32.@)
56  *       ( Undocumented, called from user32.dll )
57  */
58 BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod)
59 {
60     gSharedInfo = *ptr;
61     gpsi = gSharedInfo.psi;
62     return ImmInitializeGlobals(hMod);
63 }
64 
65 /***********************************************************************
66  *		ImmLoadLayout (IMM32.@)
67  */
68 BOOL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
69 {
70     DWORD cbData, dwType;
71     HKEY hLayoutKey;
72     LONG error;
73     WCHAR szLayout[MAX_PATH];
74 
75     TRACE("(%p, %p)\n", hKL, pImeInfoEx);
76 
77     ZeroMemory(pImeInfoEx, sizeof(IMEINFOEX));
78 
79     if (IS_IME_HKL(hKL) || !IS_CICERO_MODE() || IS_16BIT_MODE())
80     {
81         StringCchPrintfW(szLayout, _countof(szLayout), L"%s\\%08lX",
82                          REGKEY_KEYBOARD_LAYOUTS, HandleToUlong(hKL));
83 
84         error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szLayout, 0, KEY_READ, &hLayoutKey);
85         if (IS_ERROR_UNEXPECTEDLY(error))
86             return FALSE;
87     }
88     else
89     {
90         error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REGKEY_IMM, 0, KEY_READ, &hLayoutKey);
91         if (IS_ERROR_UNEXPECTEDLY(error))
92             return FALSE;
93     }
94 
95     cbData = sizeof(pImeInfoEx->wszImeFile);
96     error = RegQueryValueExW(hLayoutKey, L"Ime File", NULL, &dwType,
97                              (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
98     pImeInfoEx->wszImeFile[_countof(pImeInfoEx->wszImeFile) - 1] = UNICODE_NULL;
99 
100     RegCloseKey(hLayoutKey);
101 
102     pImeInfoEx->fLoadFlag = 0;
103 
104     if (IS_ERROR_UNEXPECTEDLY(error))
105         return FALSE;
106 
107     if (dwType != REG_SZ)
108     {
109         ERR("\n");
110         return FALSE;
111     }
112 
113     pImeInfoEx->hkl = hKL;
114     return Imm32LoadImeVerInfo(pImeInfoEx);
115 }
116 
117 /***********************************************************************
118  *		ImmFreeLayout (IMM32.@)
119  */
120 BOOL WINAPI ImmFreeLayout(DWORD dwUnknown)
121 {
122     WCHAR szKBD[KL_NAMELENGTH];
123     UINT iKL, cKLs;
124     HKL hOldKL, hNewKL, *pList;
125     PIMEDPI pImeDpi;
126     LANGID LangID;
127 
128     TRACE("(0x%lX)\n", dwUnknown);
129 
130     hOldKL = GetKeyboardLayout(0);
131 
132     if (dwUnknown == 1)
133     {
134         if (!IS_IME_HKL(hOldKL))
135             return TRUE;
136 
137         LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
138 
139         cKLs = GetKeyboardLayoutList(0, NULL);
140         if (cKLs)
141         {
142             pList = ImmLocalAlloc(0, cKLs * sizeof(HKL));
143             if (IS_NULL_UNEXPECTEDLY(pList))
144                 return FALSE;
145 
146             cKLs = GetKeyboardLayoutList(cKLs, pList);
147             for (iKL = 0; iKL < cKLs; ++iKL)
148             {
149                 if (!IS_IME_HKL(pList[iKL]))
150                 {
151                     LangID = LOWORD(pList[iKL]);
152                     break;
153                 }
154             }
155 
156             ImmLocalFree(pList);
157         }
158 
159         StringCchPrintfW(szKBD, _countof(szKBD), L"%08X", LangID);
160         if (!LoadKeyboardLayoutW(szKBD, KLF_ACTIVATE))
161         {
162             WARN("Default to English US\n");
163             LoadKeyboardLayoutW(L"00000409", KLF_ACTIVATE | 0x200);
164         }
165     }
166     else if (dwUnknown == 2)
167     {
168         RtlEnterCriticalSection(&gcsImeDpi);
169 Retry:
170         for (pImeDpi = gpImeDpiList; pImeDpi; pImeDpi = pImeDpi->pNext)
171         {
172             if (Imm32ReleaseIME(pImeDpi->hKL))
173                 goto Retry;
174         }
175         RtlLeaveCriticalSection(&gcsImeDpi);
176     }
177     else
178     {
179         hNewKL = (HKL)(DWORD_PTR)dwUnknown;
180         if (IS_IME_HKL(hNewKL) && hNewKL != hOldKL)
181             Imm32ReleaseIME(hNewKL);
182     }
183 
184     return TRUE;
185 }
186 
187 // Win: SelectInputContext
188 VOID APIENTRY Imm32SelectInputContext(HKL hNewKL, HKL hOldKL, HIMC hIMC)
189 {
190     PCLIENTIMC pClientImc;
191     LPINPUTCONTEXTDX pIC;
192     LPGUIDELINE pGL;
193     LPCANDIDATEINFO pCI;
194     LPCOMPOSITIONSTRING pCS;
195     LOGFONTA LogFontA;
196     LOGFONTW LogFontW;
197     BOOL fOldOpen, bIsNewHKLIme = TRUE, bIsOldHKLIme = TRUE, bClientWide, bNewDpiWide;
198     DWORD cbNewPrivate = 0, cbOldPrivate = 0, dwOldConversion, dwOldSentence, dwSize, dwNewSize;
199     PIMEDPI pNewImeDpi = NULL, pOldImeDpi = NULL;
200     HANDLE hPrivate;
201     PIME_STATE pNewState = NULL, pOldState = NULL;
202 
203     pClientImc = ImmLockClientImc(hIMC);
204     if (IS_NULL_UNEXPECTEDLY(pClientImc))
205         return;
206 
207     pNewImeDpi = ImmLockImeDpi(hNewKL);
208 
209     if (hNewKL != hOldKL)
210         pOldImeDpi = ImmLockImeDpi(hOldKL);
211 
212     if (pNewImeDpi)
213     {
214         cbNewPrivate = pNewImeDpi->ImeInfo.dwPrivateDataSize;
215         pClientImc->uCodePage = pNewImeDpi->uCodePage;
216     }
217     else
218     {
219         pClientImc->uCodePage = CP_ACP;
220     }
221 
222     if (pOldImeDpi)
223         cbOldPrivate = pOldImeDpi->ImeInfo.dwPrivateDataSize;
224 
225     cbNewPrivate = max(cbNewPrivate, sizeof(DWORD));
226     cbOldPrivate = max(cbOldPrivate, sizeof(DWORD));
227 
228     if (pClientImc->hKL == hOldKL)
229     {
230         if (pOldImeDpi)
231         {
232             if (IS_IME_HKL(hOldKL))
233                 pOldImeDpi->ImeSelect(hIMC, FALSE);
234             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
235                 pOldImeDpi->CtfImeSelectEx(hIMC, FALSE, hOldKL);
236         }
237         pClientImc->hKL = NULL;
238     }
239 
240     if (CtfImmIsTextFrameServiceDisabled() && IS_CICERO_MODE() && !IS_16BIT_MODE())
241     {
242         bIsNewHKLIme = IS_IME_HKL(hNewKL);
243         bIsOldHKLIme = IS_IME_HKL(hOldKL);
244     }
245 
246     pIC = (LPINPUTCONTEXTDX)Imm32InternalLockIMC(hIMC, FALSE);
247     if (!pIC)
248     {
249         if (pNewImeDpi)
250         {
251             if (IS_IME_HKL(hNewKL))
252                 pNewImeDpi->ImeSelect(hIMC, TRUE);
253             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
254                 pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL);
255 
256             pClientImc->hKL = hNewKL;
257         }
258     }
259     else
260     {
261         dwOldConversion = pIC->fdwConversion;
262         dwOldSentence = pIC->fdwSentence;
263         fOldOpen = pIC->fOpen;
264 
265         if (pNewImeDpi)
266         {
267             bClientWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
268             bNewDpiWide = ImeDpi_IsUnicode(pNewImeDpi);
269             if (bClientWide && !bNewDpiWide)
270             {
271                 if (pIC->fdwInit & INIT_LOGFONT)
272                 {
273                     LogFontWideToAnsi(&pIC->lfFont.W, &LogFontA);
274                     pIC->lfFont.A = LogFontA;
275                 }
276                 pClientImc->dwFlags &= ~CLIENTIMC_WIDE;
277             }
278             else if (!bClientWide && bNewDpiWide)
279             {
280                 if (pIC->fdwInit & INIT_LOGFONT)
281                 {
282                     LogFontAnsiToWide(&pIC->lfFont.A, &LogFontW);
283                     pIC->lfFont.W = LogFontW;
284                 }
285                 pClientImc->dwFlags |= CLIENTIMC_WIDE;
286             }
287         }
288 
289         if (cbOldPrivate != cbNewPrivate)
290         {
291             hPrivate = ImmReSizeIMCC(pIC->hPrivate, cbNewPrivate);
292             if (!hPrivate)
293             {
294                 ImmDestroyIMCC(pIC->hPrivate);
295                 hPrivate = ImmCreateIMCC(cbNewPrivate);
296             }
297             pIC->hPrivate = hPrivate;
298         }
299 
300 #define MAX_IMCC_SIZE 0x1000
301         dwSize = ImmGetIMCCSize(pIC->hMsgBuf);
302         if (ImmGetIMCCLockCount(pIC->hMsgBuf) || dwSize > MAX_IMCC_SIZE)
303         {
304             ImmDestroyIMCC(pIC->hMsgBuf);
305             pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT));
306             pIC->dwNumMsgBuf = 0;
307         }
308 
309         dwSize = ImmGetIMCCSize(pIC->hGuideLine);
310         dwNewSize = sizeof(GUIDELINE);
311         if (ImmGetIMCCLockCount(pIC->hGuideLine) ||
312             dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
313         {
314             ImmDestroyIMCC(pIC->hGuideLine);
315             pIC->hGuideLine = ImmCreateIMCC(dwNewSize);
316             pGL = ImmLockIMCC(pIC->hGuideLine);
317             if (pGL)
318             {
319                 pGL->dwSize = dwNewSize;
320                 ImmUnlockIMCC(pIC->hGuideLine);
321             }
322         }
323 
324         dwSize = ImmGetIMCCSize(pIC->hCandInfo);
325         dwNewSize = sizeof(CANDIDATEINFO);
326         if (ImmGetIMCCLockCount(pIC->hCandInfo) ||
327             dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
328         {
329             ImmDestroyIMCC(pIC->hCandInfo);
330             pIC->hCandInfo = ImmCreateIMCC(dwNewSize);
331             pCI = ImmLockIMCC(pIC->hCandInfo);
332             if (pCI)
333             {
334                 pCI->dwSize = dwNewSize;
335                 ImmUnlockIMCC(pIC->hCandInfo);
336             }
337         }
338 
339         dwSize = ImmGetIMCCSize(pIC->hCompStr);
340         dwNewSize = sizeof(COMPOSITIONSTRING);
341         if (ImmGetIMCCLockCount(pIC->hCompStr) ||
342             dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
343         {
344             ImmDestroyIMCC(pIC->hCompStr);
345             pIC->hCompStr = ImmCreateIMCC(dwNewSize);
346             pCS = ImmLockIMCC(pIC->hCompStr);
347             if (pCS)
348             {
349                 pCS->dwSize = dwNewSize;
350                 ImmUnlockIMCC(pIC->hCompStr);
351             }
352         }
353 #undef MAX_IMCC_SIZE
354 
355         if (pOldImeDpi && bIsOldHKLIme)
356         {
357             pOldState = Imm32FetchImeState(pIC, hOldKL);
358             if (pOldState)
359                 Imm32SaveImeStateSentence(pIC, pOldState, hOldKL);
360         }
361 
362         if (pNewImeDpi && bIsNewHKLIme)
363             pNewState = Imm32FetchImeState(pIC, hNewKL);
364 
365         if (pOldState != pNewState)
366         {
367             if (pOldState)
368             {
369                 pOldState->fOpen = !!pIC->fOpen;
370                 pOldState->dwConversion = pIC->fdwConversion;
371                 pOldState->dwConversion &= ~IME_CMODE_EUDC;
372                 pOldState->dwSentence = pIC->fdwSentence;
373                 pOldState->dwInit = pIC->fdwInit;
374             }
375 
376             if (pNewState)
377             {
378                 if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_FORCE_OPEN)
379                 {
380                     pIC->dwChange &= ~INPUTCONTEXTDX_CHANGE_FORCE_OPEN;
381                     pIC->fOpen = TRUE;
382                 }
383                 else
384                 {
385                     pIC->fOpen = pNewState->fOpen;
386                 }
387 
388                 pIC->fdwConversion = pNewState->dwConversion;
389                 pIC->fdwConversion &= ~IME_CMODE_EUDC;
390                 pIC->fdwSentence = pNewState->dwSentence;
391                 pIC->fdwInit = pNewState->dwInit;
392             }
393         }
394 
395         if (pNewState)
396             Imm32LoadImeStateSentence(pIC, pNewState, hNewKL);
397 
398         if (pNewImeDpi)
399         {
400             if (IS_IME_HKL(hNewKL))
401                 pNewImeDpi->ImeSelect(hIMC, TRUE);
402             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
403                 pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL);
404 
405             pClientImc->hKL = hNewKL;
406         }
407 
408         pIC->dwChange = 0;
409         if (pIC->fOpen != fOldOpen)
410             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_OPEN;
411         if (pIC->fdwConversion != dwOldConversion)
412             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_CONVERSION;
413         if (pIC->fdwSentence != dwOldSentence)
414             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_SENTENCE;
415 
416         ImmUnlockIMC(hIMC);
417     }
418 
419     ImmUnlockImeDpi(pOldImeDpi);
420     ImmUnlockImeDpi(pNewImeDpi);
421     ImmUnlockClientImc(pClientImc);
422 }
423 
424 typedef struct SELECT_LAYOUT
425 {
426     HKL hNewKL;
427     HKL hOldKL;
428 } SELECT_LAYOUT, *LPSELECT_LAYOUT;
429 
430 // Win: SelectContextProc
431 static BOOL CALLBACK Imm32SelectContextProc(HIMC hIMC, LPARAM lParam)
432 {
433     LPSELECT_LAYOUT pSelect = (LPSELECT_LAYOUT)lParam;
434     Imm32SelectInputContext(pSelect->hNewKL, pSelect->hOldKL, hIMC);
435     return TRUE;
436 }
437 
438 // Win: NotifyIMEProc
439 static BOOL CALLBACK Imm32NotifyIMEProc(HIMC hIMC, LPARAM lParam)
440 {
441     ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, (DWORD)lParam, 0);
442     return TRUE;
443 }
444 
445 /***********************************************************************
446  *		ImmActivateLayout (IMM32.@)
447  */
448 BOOL WINAPI ImmActivateLayout(HKL hKL)
449 {
450     PIMEDPI pImeDpi;
451     HKL hOldKL;
452     LPARAM lParam;
453     HWND hwndDefIME = NULL;
454     SELECT_LAYOUT SelectLayout;
455 
456     hOldKL = GetKeyboardLayout(0);
457 
458     if (hOldKL == hKL && !(GetWin32ClientInfo()->CI_flags & CI_IMMACTIVATE))
459         return TRUE;
460 
461     ImmLoadIME(hKL);
462 
463     if (hOldKL != hKL)
464     {
465         pImeDpi = ImmLockImeDpi(hOldKL);
466         if (pImeDpi)
467         {
468             if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT)
469                 lParam = CPS_COMPLETE;
470             else
471                 lParam = CPS_CANCEL;
472             ImmUnlockImeDpi(pImeDpi);
473 
474             ImmEnumInputContext(0, Imm32NotifyIMEProc, lParam);
475         }
476 
477         hwndDefIME = ImmGetDefaultIMEWnd(NULL);
478         if (IsWindow(hwndDefIME))
479             SendMessageW(hwndDefIME, WM_IME_SELECT, FALSE, (LPARAM)hOldKL);
480 
481         NtUserSetThreadLayoutHandles(hKL, hOldKL);
482     }
483 
484     SelectLayout.hNewKL = hKL;
485     SelectLayout.hOldKL = hOldKL;
486     ImmEnumInputContext(0, Imm32SelectContextProc, (LPARAM)&SelectLayout);
487 
488     if (IsWindow(hwndDefIME))
489         SendMessageW(hwndDefIME, WM_IME_SELECT, TRUE, (LPARAM)hKL);
490 
491     return TRUE;
492 }
493 
494 /* Win: Internal_CtfImeSetActiveContextAlways */
495 static VOID APIENTRY Imm32CiceroSetActiveContext(HIMC hIMC, BOOL fActive, HWND hWnd, HKL hKL)
496 {
497     TRACE("We have to do something\n");
498 }
499 
500 /***********************************************************************
501  *		ImmAssociateContext (IMM32.@)
502  */
503 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
504 {
505     PWND pWnd;
506     HWND hwndFocus;
507     DWORD dwValue;
508     HIMC hOldIMC;
509 
510     TRACE("(%p, %p)\n", hWnd, hIMC);
511 
512     if (!IS_IMM_MODE())
513     {
514         TRACE("\n");
515         return NULL;
516     }
517 
518     pWnd = ValidateHwnd(hWnd);
519     if (IS_NULL_UNEXPECTEDLY(pWnd))
520         return NULL;
521 
522     if (hIMC && IS_CROSS_THREAD_HIMC(hIMC))
523         return NULL;
524 
525     hOldIMC = pWnd->hImc;
526     if (hOldIMC == hIMC)
527         return hIMC;
528 
529     dwValue = NtUserAssociateInputContext(hWnd, hIMC, 0);
530     switch (dwValue)
531     {
532         case 0:
533             return hOldIMC;
534 
535         case 1:
536             hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS);
537             if (hwndFocus == hWnd)
538             {
539                 ImmSetActiveContext(hWnd, hOldIMC, FALSE);
540                 ImmSetActiveContext(hWnd, hIMC, TRUE);
541             }
542             return hOldIMC;
543 
544         default:
545             return NULL;
546     }
547 }
548 
549 /***********************************************************************
550  *              ImmAssociateContextEx (IMM32.@)
551  */
552 BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
553 {
554     HWND hwndFocus;
555     PWND pFocusWnd;
556     HIMC hOldIMC = NULL;
557     DWORD dwValue;
558 
559     TRACE("(%p, %p, 0x%lX)\n", hWnd, hIMC, dwFlags);
560 
561     if (!IS_IMM_MODE())
562     {
563         TRACE("\n");
564         return FALSE;
565     }
566 
567     if (hIMC && !(dwFlags & IACE_DEFAULT) && IS_CROSS_THREAD_HIMC(hIMC))
568         return FALSE;
569 
570     hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS);
571     pFocusWnd = ValidateHwnd(hwndFocus);
572     if (pFocusWnd)
573         hOldIMC = pFocusWnd->hImc;
574 
575     dwValue = NtUserAssociateInputContext(hWnd, hIMC, dwFlags);
576     switch (dwValue)
577     {
578         case 0:
579             return TRUE;
580 
581         case 1:
582             pFocusWnd = ValidateHwnd(hwndFocus);
583             if (pFocusWnd)
584             {
585                 hIMC = pFocusWnd->hImc;
586                 if (hIMC != hOldIMC)
587                 {
588                     ImmSetActiveContext(hwndFocus, hOldIMC, FALSE);
589                     ImmSetActiveContext(hwndFocus, hIMC, TRUE);
590                 }
591             }
592             return TRUE;
593 
594         default:
595             return FALSE;
596     }
597 }
598 
599 /***********************************************************************
600  *		ImmCreateContext (IMM32.@)
601  */
602 HIMC WINAPI ImmCreateContext(void)
603 {
604     PCLIENTIMC pClientImc;
605     HIMC hIMC;
606 
607     TRACE("()\n");
608 
609     if (!IS_IMM_MODE())
610     {
611         TRACE("\n");
612         return NULL;
613     }
614 
615     pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
616     if (IS_NULL_UNEXPECTEDLY(pClientImc))
617         return NULL;
618 
619     hIMC = NtUserCreateInputContext((ULONG_PTR)pClientImc);
620     if (IS_NULL_UNEXPECTEDLY(hIMC))
621     {
622         ImmLocalFree(pClientImc);
623         return NULL;
624     }
625 
626     RtlInitializeCriticalSection(&pClientImc->cs);
627 
628     pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS);
629 
630     return hIMC;
631 }
632 
633 // Win: DestroyImeModeSaver
634 static VOID APIENTRY Imm32DestroyImeModeSaver(LPINPUTCONTEXTDX pIC)
635 {
636     PIME_STATE pState, pNext;
637     PIME_SUBSTATE pSubState, pSubNext;
638 
639     for (pState = pIC->pState; pState; pState = pNext)
640     {
641         pNext = pState->pNext;
642 
643         for (pSubState = pState->pSubState; pSubState; pSubState = pSubNext)
644         {
645             pSubNext = pSubState->pNext;
646             ImmLocalFree(pSubState);
647         }
648 
649         ImmLocalFree(pState);
650     }
651 
652     pIC->pState = NULL;
653 }
654 
655 // Win: DestroyInputContext
656 BOOL APIENTRY Imm32DestroyInputContext(HIMC hIMC, HKL hKL, BOOL bKeep)
657 {
658     PIMEDPI pImeDpi;
659     LPINPUTCONTEXTDX pIC;
660     PCLIENTIMC pClientImc;
661     PIMC pIMC;
662 
663     if (hIMC == NULL)
664         return FALSE;
665 
666     if (!IS_IMM_MODE())
667     {
668         TRACE("\n");
669         return FALSE;
670     }
671 
672     pIMC = ValidateHandle(hIMC, TYPE_INPUTCONTEXT);
673     if (IS_NULL_UNEXPECTEDLY(pIMC))
674         return FALSE;
675 
676     if (pIMC->head.pti != Imm32CurrentPti())
677     {
678         ERR("Thread mismatch\n");
679         return FALSE;
680     }
681 
682     pClientImc = (PCLIENTIMC)pIMC->dwClientImcData;
683     if (pClientImc == NULL)
684     {
685         TRACE("pClientImc == NULL\n");
686         goto Finish;
687     }
688 
689     if ((pClientImc->dwFlags & CLIENTIMC_UNKNOWN2) && !bKeep)
690     {
691         ERR("Can't destroy for CLIENTIMC_UNKNOWN2\n");
692         return FALSE;
693     }
694 
695     if (pClientImc->dwFlags & CLIENTIMC_DESTROY)
696         return TRUE;
697 
698     InterlockedIncrement(&pClientImc->cLockObj);
699 
700     if (IS_NULL_UNEXPECTEDLY(pClientImc->hInputContext))
701         goto Quit;
702 
703     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
704     if (IS_NULL_UNEXPECTEDLY(pIC))
705     {
706         ImmUnlockClientImc(pClientImc);
707         return FALSE;
708     }
709 
710     CtfImmTIMDestroyInputContext(hIMC);
711 
712     if (pClientImc->hKL == hKL)
713     {
714         pImeDpi = ImmLockImeDpi(hKL);
715         if (pImeDpi)
716         {
717             if (IS_IME_HKL(hKL))
718                 pImeDpi->ImeSelect(hIMC, FALSE);
719             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
720                 pImeDpi->CtfImeSelectEx(hIMC, FALSE, hKL);
721 
722             ImmUnlockImeDpi(pImeDpi);
723         }
724 
725         pClientImc->hKL = NULL;
726     }
727 
728     ImmDestroyIMCC(pIC->hPrivate);
729     ImmDestroyIMCC(pIC->hMsgBuf);
730     ImmDestroyIMCC(pIC->hGuideLine);
731     ImmDestroyIMCC(pIC->hCandInfo);
732     ImmDestroyIMCC(pIC->hCompStr);
733     Imm32DestroyImeModeSaver(pIC);
734     ImmUnlockIMC(hIMC);
735 
736 Quit:
737     pClientImc->dwFlags |= CLIENTIMC_DESTROY;
738     ImmUnlockClientImc(pClientImc);
739 
740 Finish:
741     if (bKeep)
742         return TRUE;
743     return NtUserDestroyInputContext(hIMC);
744 }
745 
746 // NOTE: Windows does recursive call ImmLockIMC here but we don't do so.
747 // Win: BOOL CreateInputContext(HIMC hIMC, HKL hKL, BOOL fSelect)
748 BOOL APIENTRY
749 Imm32CreateInputContext(HIMC hIMC, LPINPUTCONTEXT pIC, PCLIENTIMC pClientImc, HKL hKL, BOOL fSelect)
750 {
751     DWORD dwIndex, cbPrivate;
752     PIMEDPI pImeDpi = NULL;
753     LPCOMPOSITIONSTRING pCS;
754     LPCANDIDATEINFO pCI;
755     LPGUIDELINE pGL;
756 
757     /* Create IC components */
758     pIC->hCompStr = ImmCreateIMCC(sizeof(COMPOSITIONSTRING));
759     pIC->hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO));
760     pIC->hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE));
761     pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT));
762     if (IS_NULL_UNEXPECTEDLY(pIC->hCompStr) ||
763         IS_NULL_UNEXPECTEDLY(pIC->hCandInfo) ||
764         IS_NULL_UNEXPECTEDLY(pIC->hGuideLine) ||
765         IS_NULL_UNEXPECTEDLY(pIC->hMsgBuf))
766     {
767         goto Fail;
768     }
769 
770     /* Initialize IC components */
771     pCS = ImmLockIMCC(pIC->hCompStr);
772     if (IS_NULL_UNEXPECTEDLY(pCS))
773         goto Fail;
774     pCS->dwSize = sizeof(COMPOSITIONSTRING);
775     ImmUnlockIMCC(pIC->hCompStr);
776 
777     pCI = ImmLockIMCC(pIC->hCandInfo);
778     if (IS_NULL_UNEXPECTEDLY(pCI))
779         goto Fail;
780     pCI->dwSize = sizeof(CANDIDATEINFO);
781     ImmUnlockIMCC(pIC->hCandInfo);
782 
783     pGL = ImmLockIMCC(pIC->hGuideLine);
784     if (IS_NULL_UNEXPECTEDLY(pGL))
785         goto Fail;
786     pGL->dwSize = sizeof(GUIDELINE);
787     ImmUnlockIMCC(pIC->hGuideLine);
788 
789     pIC->dwNumMsgBuf = 0;
790     pIC->fOpen = FALSE;
791     pIC->fdwConversion = pIC->fdwSentence = 0;
792 
793     for (dwIndex = 0; dwIndex < MAX_CANDIDATEFORM; ++dwIndex)
794         pIC->cfCandForm[dwIndex].dwIndex = IMM_INVALID_CANDFORM;
795 
796     /* Get private data size */
797     pImeDpi = ImmLockImeDpi(hKL);
798     if (!pImeDpi)
799     {
800         cbPrivate = sizeof(DWORD);
801     }
802     else
803     {
804         /* Update CLIENTIMC */
805         pClientImc->uCodePage = pImeDpi->uCodePage;
806         if (ImeDpi_IsUnicode(pImeDpi))
807             pClientImc->dwFlags |= CLIENTIMC_WIDE;
808 
809         cbPrivate = pImeDpi->ImeInfo.dwPrivateDataSize;
810     }
811 
812     /* Create private data */
813     pIC->hPrivate = ImmCreateIMCC(cbPrivate);
814     if (IS_NULL_UNEXPECTEDLY(pIC->hPrivate))
815         goto Fail;
816 
817     CtfImmTIMCreateInputContext(hIMC);
818 
819     if (pImeDpi)
820     {
821         /* Select the IME */
822         if (fSelect)
823         {
824             if (IS_IME_HKL(hKL))
825                 pImeDpi->ImeSelect(hIMC, TRUE);
826             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
827                 pImeDpi->CtfImeSelectEx(hIMC, TRUE, hKL);
828         }
829 
830         /* Set HKL */
831         pClientImc->hKL = hKL;
832 
833         ImmUnlockImeDpi(pImeDpi);
834     }
835 
836     return TRUE;
837 
838 Fail:
839     if (pImeDpi)
840         ImmUnlockImeDpi(pImeDpi);
841 
842     pIC->hMsgBuf = ImmDestroyIMCC(pIC->hMsgBuf);
843     pIC->hGuideLine = ImmDestroyIMCC(pIC->hGuideLine);
844     pIC->hCandInfo = ImmDestroyIMCC(pIC->hCandInfo);
845     pIC->hCompStr = ImmDestroyIMCC(pIC->hCompStr);
846     return FALSE;
847 }
848 
849 // Win: InternalImmLockIMC
850 LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect)
851 {
852     HANDLE hIC;
853     LPINPUTCONTEXT pIC = NULL;
854     PCLIENTIMC pClientImc;
855     WORD LangID;
856     DWORD dwThreadId;
857     HKL hOldKL, hNewKL;
858     PIMEDPI pImeDpi = NULL;
859 
860     pClientImc = ImmLockClientImc(hIMC);
861     if (IS_NULL_UNEXPECTEDLY(pClientImc))
862         return NULL;
863 
864     RtlEnterCriticalSection(&pClientImc->cs);
865 
866     if (pClientImc->hInputContext)
867     {
868         pIC = LocalLock(pClientImc->hInputContext);
869         if (IS_NULL_UNEXPECTEDLY(pIC))
870             goto Failure;
871 
872         CtfImmTIMCreateInputContext(hIMC);
873         goto Success;
874     }
875 
876     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
877     if (dwThreadId == GetCurrentThreadId() && IS_CICERO_MODE() && !IS_16BIT_MODE())
878     {
879         hOldKL = GetKeyboardLayout(0);
880         LangID = LOWORD(hOldKL);
881         hNewKL = (HKL)(DWORD_PTR)MAKELONG(LangID, LangID);
882 
883         pImeDpi = Imm32FindOrLoadImeDpi(hNewKL);
884         if (pImeDpi)
885         {
886             CtfImmTIMActivate(hNewKL);
887         }
888     }
889 
890     if (!NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME))
891     {
892         ERR("No default IME window\n");
893         goto Failure;
894     }
895 
896     hIC = LocalAlloc(LHND, sizeof(INPUTCONTEXTDX));
897     pIC = LocalLock(hIC);
898     if (IS_NULL_UNEXPECTEDLY(pIC))
899     {
900         LocalFree(hIC);
901         goto Failure;
902     }
903     pClientImc->hInputContext = hIC;
904 
905     hNewKL = GetKeyboardLayout(dwThreadId);
906     if (!Imm32CreateInputContext(hIMC, pIC, pClientImc, hNewKL, fSelect))
907     {
908         LocalUnlock(hIC);
909         pClientImc->hInputContext = LocalFree(hIC);
910         goto Failure;
911     }
912 
913 Success:
914     RtlLeaveCriticalSection(&pClientImc->cs);
915     InterlockedIncrement(&pClientImc->cLockObj);
916     ImmUnlockClientImc(pClientImc);
917     return pIC;
918 
919 Failure:
920     RtlLeaveCriticalSection(&pClientImc->cs);
921     ImmUnlockClientImc(pClientImc);
922     return NULL;
923 }
924 
925 /***********************************************************************
926  *		ImmDestroyContext (IMM32.@)
927  */
928 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
929 {
930     HKL hKL;
931 
932     TRACE("(%p)\n", hIMC);
933 
934     if (!IS_IMM_MODE())
935     {
936         TRACE("\n");
937         return FALSE;
938     }
939 
940     if (IS_CROSS_THREAD_HIMC(hIMC))
941         return FALSE;
942 
943     hKL = GetKeyboardLayout(0);
944     return Imm32DestroyInputContext(hIMC, hKL, FALSE);
945 }
946 
947 /***********************************************************************
948  *		ImmLockClientImc (IMM32.@)
949  */
950 PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
951 {
952     PIMC pIMC;
953     PCLIENTIMC pClientImc;
954 
955     TRACE("(%p)\n", hImc);
956 
957     if (IS_NULL_UNEXPECTEDLY(hImc))
958         return NULL;
959 
960     pIMC = ValidateHandle(hImc, TYPE_INPUTCONTEXT);
961     if (IS_NULL_UNEXPECTEDLY(pIMC) || !Imm32CheckImcProcess(pIMC))
962         return NULL;
963 
964     pClientImc = (PCLIENTIMC)pIMC->dwClientImcData;
965     if (pClientImc)
966     {
967         if (pClientImc->dwFlags & CLIENTIMC_DESTROY)
968             return NULL;
969         goto Finish;
970     }
971 
972     pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
973     if (IS_NULL_UNEXPECTEDLY(pClientImc))
974         return NULL;
975 
976     RtlInitializeCriticalSection(&pClientImc->cs);
977     pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS);
978 
979     if (!NtUserUpdateInputContext(hImc, UIC_CLIENTIMCDATA, (DWORD_PTR)pClientImc))
980     {
981         ERR("\n");
982         ImmLocalFree(pClientImc);
983         return NULL;
984     }
985 
986     pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
987 
988 Finish:
989     InterlockedIncrement(&pClientImc->cLockObj);
990     return pClientImc;
991 }
992 
993 /***********************************************************************
994  *		ImmUnlockClientImc (IMM32.@)
995  */
996 VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc)
997 {
998     LONG cLocks;
999     HANDLE hInputContext;
1000 
1001     TRACE("(%p)\n", pClientImc);
1002 
1003     cLocks = InterlockedDecrement(&pClientImc->cLockObj);
1004     if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_DESTROY))
1005         return;
1006 
1007     hInputContext = pClientImc->hInputContext;
1008     if (hInputContext)
1009         LocalFree(hInputContext);
1010 
1011     RtlDeleteCriticalSection(&pClientImc->cs);
1012     ImmLocalFree(pClientImc);
1013 }
1014 
1015 // Win: ImmGetSaveContext
1016 static HIMC APIENTRY ImmGetSaveContext(HWND hWnd, DWORD dwContextFlags)
1017 {
1018     HIMC hIMC;
1019     PCLIENTIMC pClientImc;
1020     PWND pWnd;
1021 
1022     if (!IS_IMM_MODE())
1023     {
1024         TRACE("Not IMM mode.\n");
1025         return NULL;
1026     }
1027 
1028     if (!hWnd)
1029     {
1030         hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT);
1031         goto Quit;
1032     }
1033 
1034     pWnd = ValidateHwnd(hWnd);
1035     if (IS_NULL_UNEXPECTEDLY(pWnd) || IS_CROSS_PROCESS_HWND(hWnd))
1036         return NULL;
1037 
1038     hIMC = pWnd->hImc;
1039     if (!hIMC && (dwContextFlags & 1))
1040         hIMC = (HIMC)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_ICONTEXT);
1041 
1042 Quit:
1043     pClientImc = ImmLockClientImc(hIMC);
1044     if (IS_NULL_UNEXPECTEDLY(pClientImc))
1045         return NULL;
1046 
1047     if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_DISABLEIME))
1048         hIMC = NULL;
1049 
1050     ImmUnlockClientImc(pClientImc);
1051     return hIMC;
1052 }
1053 
1054 /***********************************************************************
1055  *		ImmGetContext (IMM32.@)
1056  */
1057 HIMC WINAPI ImmGetContext(HWND hWnd)
1058 {
1059     TRACE("(%p)\n", hWnd);
1060     if (IS_NULL_UNEXPECTEDLY(hWnd))
1061         return NULL;
1062     return ImmGetSaveContext(hWnd, 2);
1063 }
1064 
1065 /***********************************************************************
1066  *		ImmLockIMC(IMM32.@)
1067  *
1068  * NOTE: This is not ImmLockIMCC. Don't confuse.
1069  */
1070 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
1071 {
1072     TRACE("(%p)\n", hIMC);
1073     return Imm32InternalLockIMC(hIMC, TRUE);
1074 }
1075 
1076 /***********************************************************************
1077 *		ImmUnlockIMC(IMM32.@)
1078 */
1079 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
1080 {
1081     PCLIENTIMC pClientImc;
1082 
1083     pClientImc = ImmLockClientImc(hIMC);
1084     if (IS_NULL_UNEXPECTEDLY(pClientImc))
1085         return FALSE;
1086 
1087     if (pClientImc->hInputContext)
1088         LocalUnlock(pClientImc->hInputContext);
1089 
1090     InterlockedDecrement(&pClientImc->cLockObj);
1091     ImmUnlockClientImc(pClientImc);
1092     return TRUE;
1093 }
1094 
1095 /***********************************************************************
1096  *		ImmReleaseContext (IMM32.@)
1097  */
1098 BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
1099 {
1100     TRACE("(%p, %p)\n", hWnd, hIMC);
1101     UNREFERENCED_PARAMETER(hWnd);
1102     UNREFERENCED_PARAMETER(hIMC);
1103     return TRUE; // Do nothing. This is correct.
1104 }
1105 
1106 /***********************************************************************
1107  *              ImmCreateSoftKeyboard(IMM32.@)
1108  */
1109 HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
1110 {
1111     FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
1112     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1113     return 0;
1114 }
1115 
1116 /***********************************************************************
1117  *              ImmDestroySoftKeyboard(IMM32.@)
1118  */
1119 BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
1120 {
1121     TRACE("(%p)\n", hSoftWnd);
1122     return DestroyWindow(hSoftWnd);
1123 }
1124 
1125 /***********************************************************************
1126  *              ImmShowSoftKeyboard(IMM32.@)
1127  */
1128 BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
1129 {
1130     TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
1131     if (hSoftWnd)
1132         return ShowWindow(hSoftWnd, nCmdShow);
1133     return FALSE;
1134 }
1135 
1136 /***********************************************************************
1137 *		ImmDisableTextFrameService(IMM32.@)
1138 */
1139 BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
1140 {
1141     FIXME("Stub\n");
1142     return FALSE;
1143 }
1144 
1145 /***********************************************************************
1146  *              ImmEnumInputContext(IMM32.@)
1147  */
1148 BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
1149 {
1150     HIMC *phList;
1151     DWORD dwIndex, dwCount;
1152     BOOL ret = TRUE;
1153     HIMC hIMC;
1154 
1155     TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
1156 
1157     dwCount = Imm32BuildHimcList(dwThreadId, &phList);
1158     if (IS_ZERO_UNEXPECTEDLY(dwCount))
1159         return FALSE;
1160 
1161     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
1162     {
1163         hIMC = phList[dwIndex];
1164         ret = (*lpfn)(hIMC, lParam);
1165         if (!ret)
1166             break;
1167     }
1168 
1169     ImmLocalFree(phList);
1170     return ret;
1171 }
1172 
1173 /***********************************************************************
1174  *              ImmSetActiveContext(IMM32.@)
1175  */
1176 BOOL WINAPI ImmSetActiveContext(HWND hWnd, HIMC hIMC, BOOL fActive)
1177 {
1178     PCLIENTIMC pClientImc;
1179     LPINPUTCONTEXTDX pIC;
1180     PIMEDPI pImeDpi;
1181     HIMC hOldIMC;
1182     HKL hKL;
1183     BOOL fOpen = FALSE;
1184     DWORD dwConversion = 0, dwShowFlags = ISC_SHOWUIALL;
1185     HWND hwndDefIME;
1186 
1187     TRACE("(%p, %p, %d)\n", hWnd, hIMC, fActive);
1188 
1189     if (!IS_IMM_MODE())
1190     {
1191         TRACE("\n");
1192         return FALSE;
1193     }
1194 
1195     pClientImc = ImmLockClientImc(hIMC);
1196 
1197     if (!fActive)
1198     {
1199         if (pClientImc)
1200             pClientImc->dwFlags &= ~CLIENTIMC_ACTIVE;
1201     }
1202     else if (hIMC)
1203     {
1204         if (IS_NULL_UNEXPECTEDLY(pClientImc))
1205             return FALSE;
1206 
1207         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
1208         if (IS_NULL_UNEXPECTEDLY(pIC))
1209         {
1210             ImmUnlockClientImc(pClientImc);
1211             return FALSE;
1212         }
1213 
1214         pIC->hWnd = hWnd;
1215         pClientImc->dwFlags |= CLIENTIMC_ACTIVE;
1216 
1217         if (pIC->dwUIFlags & 2)
1218             dwShowFlags = (ISC_SHOWUIGUIDELINE | ISC_SHOWUIALLCANDIDATEWINDOW);
1219 
1220         fOpen = pIC->fOpen;
1221         dwConversion = pIC->fdwConversion;
1222 
1223         ImmUnlockIMC(hIMC);
1224     }
1225     else
1226     {
1227         hOldIMC = ImmGetSaveContext(hWnd, 1);
1228         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hOldIMC);
1229         if (pIC)
1230         {
1231             pIC->hWnd = hWnd;
1232             ImmUnlockIMC(hOldIMC);
1233         }
1234     }
1235 
1236     hKL = GetKeyboardLayout(0);
1237     if (IS_CICERO_MODE() && !IS_16BIT_MODE())
1238     {
1239         Imm32CiceroSetActiveContext(hIMC, fActive, hWnd, hKL);
1240         hKL = GetKeyboardLayout(0);
1241     }
1242 
1243     pImeDpi = ImmLockImeDpi(hKL);
1244     if (pImeDpi)
1245     {
1246         if (IS_IME_HKL(hKL))
1247             pImeDpi->ImeSetActiveContext(hIMC, fActive);
1248         ImmUnlockImeDpi(pImeDpi);
1249     }
1250 
1251     if (IsWindow(hWnd))
1252     {
1253         SendMessageW(hWnd, WM_IME_SETCONTEXT, fActive, dwShowFlags);
1254         if (fActive)
1255             NtUserNotifyIMEStatus(hWnd, fOpen, dwConversion);
1256     }
1257     else if (!fActive)
1258     {
1259         hwndDefIME = ImmGetDefaultIMEWnd(NULL);
1260         if (hwndDefIME)
1261             SendMessageW(hwndDefIME, WM_IME_SETCONTEXT, 0, dwShowFlags);
1262     }
1263 
1264     if (pClientImc)
1265         ImmUnlockClientImc(pClientImc);
1266 
1267     return TRUE;
1268 }
1269 
1270 /***********************************************************************
1271  *              ImmWINNLSGetEnableStatus (IMM32.@)
1272  */
1273 
1274 BOOL WINAPI ImmWINNLSGetEnableStatus(HWND hWnd)
1275 {
1276     if (!Imm32IsSystemJapaneseOrKorean())
1277     {
1278         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1279         return FALSE;
1280     }
1281 
1282     return !!ImmGetSaveContext(hWnd, 2);
1283 }
1284 
1285 /***********************************************************************
1286  *              ImmSetActiveContextConsoleIME(IMM32.@)
1287  */
1288 BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
1289 {
1290     HIMC hIMC;
1291     TRACE("(%p, %d)\n", hwnd, fFlag);
1292 
1293     hIMC = ImmGetContext(hwnd);
1294     if (IS_NULL_UNEXPECTEDLY(hIMC))
1295         return FALSE;
1296     return ImmSetActiveContext(hwnd, hIMC, fFlag);
1297 }
1298 
1299 #ifndef NDEBUG
1300 VOID APIENTRY Imm32UnitTest(VOID)
1301 {
1302     if (0)
1303     {
1304         DWORD dwValue;
1305         WCHAR szText[64];
1306 
1307         Imm32StrToUInt(L"123", &dwValue, 10);
1308         ASSERT(dwValue == 123);
1309         Imm32StrToUInt(L"100", &dwValue, 16);
1310         ASSERT(dwValue == 0x100);
1311 
1312         Imm32UIntToStr(123, 10, szText, _countof(szText));
1313         ASSERT(lstrcmpW(szText, L"123") == 0);
1314         Imm32UIntToStr(0x100, 16, szText, _countof(szText));
1315         ASSERT(lstrcmpW(szText, L"100") == 0);
1316     }
1317 }
1318 #endif
1319 
1320 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
1321 
1322 BOOL
1323 WINAPI
1324 ImmDllInitialize(
1325     _In_ HINSTANCE hDll,
1326     _In_ ULONG dwReason,
1327     _In_opt_ PVOID pReserved)
1328 {
1329     HKL hKL;
1330     HIMC hIMC;
1331 
1332     TRACE("(%p, 0x%X, %p)\n", hDll, dwReason, pReserved);
1333 
1334     switch (dwReason)
1335     {
1336         case DLL_PROCESS_ATTACH:
1337             if (!ImmInitializeGlobals(hDll))
1338             {
1339                 ERR("ImmInitializeGlobals failed\n");
1340                 return FALSE;
1341             }
1342             if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
1343             {
1344                 ERR("User32InitializeImmEntryTable failed\n");
1345                 return FALSE;
1346             }
1347 #ifndef NDEBUG
1348             Imm32UnitTest();
1349 #endif
1350             break;
1351 
1352         case DLL_THREAD_ATTACH:
1353             break;
1354 
1355         case DLL_THREAD_DETACH:
1356             if (!IS_IMM_MODE() || NtCurrentTeb()->Win32ThreadInfo == NULL)
1357                 return TRUE;
1358 
1359             hKL = GetKeyboardLayout(0);
1360             hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT);
1361             Imm32DestroyInputContext(hIMC, hKL, TRUE);
1362             break;
1363 
1364         case DLL_PROCESS_DETACH:
1365             RtlDeleteCriticalSection(&gcsImeDpi);
1366             TRACE("imm32.dll is unloaded\n");
1367             break;
1368     }
1369 
1370     return TRUE;
1371 }
1372