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