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