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