xref: /reactos/dll/win32/imm32/keymsg.c (revision 3fb5957d)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing IMM32 keys and messages
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-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include "precomp.h"
13 #include <jpnvkeys.h>
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(imm);
16 
17 /* Win: IMENonIMEToggle */
18 BOOL APIENTRY Imm32ImeNonImeToggle(HIMC hIMC, HKL hKL, HWND hWnd, BOOL bNowIME, LANGID LangID)
19 {
20     HKL hOldKL, LayoutList[32], hFoundKL = NULL;
21     UINT iLayout, nLayoutCount;
22 
23     /* Get the previous layout */
24     hOldKL = (HKL)NtUserGetThreadState(THREADSTATE_OLDKEYBOARDLAYOUT);
25 
26     /* Get the layout list */
27     nLayoutCount = GetKeyboardLayoutList(_countof(LayoutList), LayoutList);
28 
29     /* Is there hOldKL in the list in the specified language ID? */
30     if (hOldKL && (LangID == 0 || LOWORD(hOldKL) == LangID))
31     {
32         for (iLayout = 0; iLayout < nLayoutCount; ++iLayout)
33         {
34             if (LayoutList[iLayout] == hOldKL)
35             {
36                 hFoundKL = hOldKL;
37                 break;
38             }
39         }
40     }
41 
42     if (hFoundKL == NULL) /* Not found? */
43     {
44         /* Is there the keyboard layout of another kind in LangID? */
45         for (iLayout = 0; iLayout < nLayoutCount; ++iLayout)
46         {
47             if (bNowIME == ImmIsIME(LayoutList[iLayout])) /* Same kind? */
48                 continue;
49 
50             if (LangID == 0 || LangID == LOWORD(LayoutList[iLayout]))
51             {
52                 hFoundKL = LayoutList[iLayout];
53                 break;
54             }
55         }
56     }
57 
58     if (hFoundKL && hKL != hFoundKL) /* Found and different layout */
59     {
60         PostMessageW(hWnd, WM_INPUTLANGCHANGEREQUEST, 1, (LPARAM)hFoundKL);
61     }
62 
63     return ImmIsIME(hFoundKL);
64 }
65 
66 /* Open or close the IME on Chinese or Taiwanese */
67 /* Win: CIMENonIMEToggle */
68 BOOL APIENTRY Imm32CImeNonImeToggle(HIMC hIMC, HKL hKL, HWND hWnd, LANGID LangID)
69 {
70     LPINPUTCONTEXT pIC;
71     BOOL fOpen;
72 
73     if (IS_NULL_UNEXPECTEDLY(hWnd))
74         return FALSE;
75 
76     if (LOWORD(hKL) != LangID || !ImmIsIME(hKL))
77     {
78         Imm32ImeNonImeToggle(hIMC, hKL, hWnd, FALSE, LangID);
79         return TRUE;
80     }
81 
82     pIC = ImmLockIMC(hIMC);
83     if (IS_NULL_UNEXPECTEDLY(pIC))
84         return TRUE;
85 
86     fOpen = pIC->fOpen;
87     ImmUnlockIMC(hIMC);
88 
89     if (fOpen)
90         Imm32ImeNonImeToggle(hIMC, hKL, hWnd, TRUE, 0);
91     else
92         ImmSetOpenStatus(hIMC, TRUE);
93 
94     return TRUE;
95 }
96 
97 /* Toggle shape mode on Chinese or Taiwanese */
98 /* Win: TShapeToggle */
99 BOOL APIENTRY Imm32CShapeToggle(HIMC hIMC, HKL hKL, HWND hWnd)
100 {
101     LPINPUTCONTEXT pIC;
102     BOOL fOpen;
103     DWORD dwConversion, dwSentence;
104 
105     if (hWnd == NULL || !ImmIsIME(hKL))
106         return FALSE;
107 
108     pIC = ImmLockIMC(hIMC);
109     if (IS_NULL_UNEXPECTEDLY(pIC))
110         return TRUE;
111 
112     fOpen = pIC->fOpen;
113     if (fOpen)
114     {
115         dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
116         dwSentence = pIC->fdwSentence;
117     }
118 
119     ImmUnlockIMC(hIMC);
120 
121     if (fOpen)
122         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
123     else
124         ImmSetOpenStatus(hIMC, TRUE);
125 
126     return TRUE;
127 }
128 
129 /* Win: CSymbolToggle */
130 BOOL APIENTRY Imm32CSymbolToggle(HIMC hIMC, HKL hKL, HWND hWnd)
131 {
132     LPINPUTCONTEXT pIC;
133     BOOL fOpen;
134     DWORD dwConversion, dwSentence;
135 
136     if (hWnd == NULL || !ImmIsIME(hKL))
137         return FALSE;
138 
139     pIC = ImmLockIMC(hIMC);
140     if (IS_NULL_UNEXPECTEDLY(pIC))
141         return TRUE;
142 
143     fOpen = pIC->fOpen;
144     if (fOpen)
145     {
146         dwConversion = (pIC->fdwConversion ^ IME_CMODE_SYMBOL);
147         dwSentence = pIC->fdwSentence;
148     }
149 
150     ImmUnlockIMC(hIMC);
151 
152     if (fOpen)
153         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
154     else
155         ImmSetOpenStatus(hIMC, TRUE);
156 
157     return TRUE;
158 }
159 
160 /* Open or close Japanese IME */
161 /* Win: JCloseOpen */
162 BOOL APIENTRY Imm32JCloseOpen(HIMC hIMC, HKL hKL, HWND hWnd)
163 {
164     BOOL fOpen;
165     LPINPUTCONTEXTDX pIC;
166 
167     if (LOWORD(hKL) == LANGID_JAPANESE && ImmIsIME(hKL)) /* Japanese IME is selected */
168     {
169         fOpen = ImmGetOpenStatus(hIMC);
170         ImmSetOpenStatus(hIMC, !fOpen);
171         return TRUE;
172     }
173 
174     /* Japanese IME is not selected. Select now */
175     if (Imm32ImeNonImeToggle(hIMC, hKL, hWnd, FALSE, LANGID_JAPANESE))
176     {
177         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
178         if (pIC)
179         {
180             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_OPEN; /* Notify open change */
181             ImmUnlockIMC(hIMC);
182         }
183     }
184 
185     return TRUE;
186 }
187 
188 /* Win: KShapeToggle */
189 BOOL APIENTRY Imm32KShapeToggle(HIMC hIMC)
190 {
191     LPINPUTCONTEXT pIC;
192     DWORD dwConversion, dwSentence;
193 
194     pIC = ImmLockIMC(hIMC);
195     if (IS_NULL_UNEXPECTEDLY(pIC))
196         return FALSE;
197 
198     dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
199     dwSentence = pIC->fdwSentence;
200     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
201 
202     if (pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE))
203         ImmSetOpenStatus(hIMC, TRUE);
204     else
205         ImmSetOpenStatus(hIMC, FALSE);
206 
207     ImmUnlockIMC(hIMC);
208     return TRUE;
209 }
210 
211 /* Win: KHanjaConvert */
212 BOOL APIENTRY Imm32KHanjaConvert(HIMC hIMC)
213 {
214     LPINPUTCONTEXT pIC;
215     DWORD dwConversion, dwSentence;
216 
217     pIC = ImmLockIMC(hIMC);
218     if (IS_NULL_UNEXPECTEDLY(pIC))
219         return FALSE;
220 
221     dwConversion = (pIC->fdwConversion ^ IME_CMODE_HANJACONVERT);
222     dwSentence = pIC->fdwSentence;
223     ImmUnlockIMC(hIMC);
224 
225     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
226     return TRUE;
227 }
228 
229 /* Win: KEnglishHangul */
230 BOOL APIENTRY Imm32KEnglish(HIMC hIMC)
231 {
232     LPINPUTCONTEXT pIC;
233     DWORD dwConversion, dwSentence;
234     BOOL fOpen;
235 
236     pIC = ImmLockIMC(hIMC);
237     if (IS_NULL_UNEXPECTEDLY(pIC))
238         return FALSE;
239 
240     dwConversion = (pIC->fdwConversion ^ IME_CMODE_NATIVE);
241     dwSentence = pIC->fdwSentence;
242     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
243 
244     fOpen = ((pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE)) != 0);
245     ImmSetOpenStatus(hIMC, fOpen);
246 
247     ImmUnlockIMC(hIMC);
248     return TRUE;
249 }
250 
251 /* Win: HotKeyIDDispatcher */
252 BOOL APIENTRY Imm32ProcessHotKey(HWND hWnd, HIMC hIMC, HKL hKL, DWORD dwHotKeyID)
253 {
254     PIMEDPI pImeDpi;
255     BOOL ret;
256 
257     if (hIMC && IS_CROSS_THREAD_HIMC(hIMC))
258         return FALSE;
259 
260     switch (dwHotKeyID)
261     {
262         case IME_CHOTKEY_IME_NONIME_TOGGLE:
263             return Imm32CImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_SIMPLIFIED);
264 
265         case IME_CHOTKEY_SHAPE_TOGGLE:
266             return Imm32CShapeToggle(hIMC, hKL, hWnd);
267 
268         case IME_CHOTKEY_SYMBOL_TOGGLE:
269             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
270 
271         case IME_JHOTKEY_CLOSE_OPEN:
272             return Imm32JCloseOpen(hIMC, hKL, hWnd);
273 
274         case IME_KHOTKEY_SHAPE_TOGGLE:
275             return Imm32KShapeToggle(hIMC);
276 
277         case IME_KHOTKEY_HANJACONVERT:
278             return Imm32KHanjaConvert(hIMC);
279 
280         case IME_KHOTKEY_ENGLISH:
281             return Imm32KEnglish(hIMC);
282 
283         case IME_THOTKEY_IME_NONIME_TOGGLE:
284             return Imm32CImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_TRADITIONAL);
285 
286         case IME_THOTKEY_SHAPE_TOGGLE:
287             return Imm32CShapeToggle(hIMC, hKL, hWnd);
288 
289         case IME_THOTKEY_SYMBOL_TOGGLE:
290             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
291 
292         default:
293             WARN("0x%X\n", dwHotKeyID);
294             break;
295     }
296 
297     if (dwHotKeyID < IME_HOTKEY_PRIVATE_FIRST || IME_HOTKEY_PRIVATE_LAST < dwHotKeyID)
298         return FALSE;
299 
300     pImeDpi = ImmLockImeDpi(hKL);
301     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
302         return FALSE;
303 
304     ret = (BOOL)pImeDpi->ImeEscape(hIMC, IME_ESC_PRIVATE_HOTKEY, &dwHotKeyID);
305     ImmUnlockImeDpi(pImeDpi);
306     return ret;
307 }
308 
309 /* Win: ImmIsUIMessageWorker */
310 static BOOL APIENTRY
311 ImmIsUIMessageAW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
312 {
313     switch (msg)
314     {
315         case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION:
316         case WM_IME_COMPOSITION: case WM_IME_SETCONTEXT: case WM_IME_NOTIFY:
317         case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: case WM_IME_SYSTEM:
318             break;
319         default:
320             return FALSE;
321     }
322 
323     if (IS_NULL_UNEXPECTEDLY(hWndIME))
324         return TRUE;
325 
326     if (bAnsi)
327         SendMessageA(hWndIME, msg, wParam, lParam);
328     else
329         SendMessageW(hWndIME, msg, wParam, lParam);
330 
331     return TRUE;
332 }
333 
334 typedef struct IMM_DELAY_SET_LANG_BAND
335 {
336     HWND hWnd;
337     BOOL fSet;
338 } IMM_DELAY_SET_LANG_BAND, *PIMM_DELAY_SET_LANG_BAND;
339 
340 /* Sends a message to set the language band with delay. */
341 /* Win: DelaySetLangBand */
342 static DWORD APIENTRY Imm32DelaySetLangBandProc(LPVOID arg)
343 {
344     HWND hwndDefIME;
345     WPARAM wParam;
346     DWORD_PTR lResult;
347     PIMM_DELAY_SET_LANG_BAND pSetBand = arg;
348 
349     Sleep(3000); /* Delay 3 seconds! */
350 
351     hwndDefIME = ImmGetDefaultIMEWnd(pSetBand->hWnd);
352     if (hwndDefIME)
353     {
354         wParam = (pSetBand->fSet ? IMS_SETLANGBAND : IMS_UNSETLANGBAND);
355         SendMessageTimeoutW(hwndDefIME, WM_IME_SYSTEM, wParam, (LPARAM)pSetBand->hWnd,
356                             SMTO_BLOCK | SMTO_ABORTIFHUNG, 5000, &lResult);
357     }
358     ImmLocalFree(pSetBand);
359     return FALSE;
360 }
361 
362 /* Updates the language band. */
363 /* Win: CtfImmSetLangBand */
364 LRESULT APIENTRY CtfImmSetLangBand(HWND hWnd, BOOL fSet)
365 {
366     HANDLE hThread;
367     PWND pWnd = NULL;
368     PIMM_DELAY_SET_LANG_BAND pSetBand;
369     DWORD_PTR lResult = 0;
370 
371     if (hWnd && gpsi)
372         pWnd = ValidateHwndNoErr(hWnd);
373 
374     if (IS_NULL_UNEXPECTEDLY(pWnd))
375         return 0;
376 
377     if (pWnd->state2 & WNDS2_WMCREATEMSGPROCESSED)
378     {
379         SendMessageTimeoutW(hWnd, WM_USER + 0x105, 0, fSet, SMTO_BLOCK | SMTO_ABORTIFHUNG,
380                             5000, &lResult);
381         return lResult;
382     }
383 
384     pSetBand = ImmLocalAlloc(0, sizeof(IMM_DELAY_SET_LANG_BAND));
385     if (IS_NULL_UNEXPECTEDLY(pSetBand))
386         return 0;
387 
388     pSetBand->hWnd = hWnd;
389     pSetBand->fSet = fSet;
390 
391     hThread = CreateThread(NULL, 0, Imm32DelaySetLangBandProc, pSetBand, 0, NULL);
392     if (hThread)
393         CloseHandle(hThread);
394     return 0;
395 }
396 
397 /* Win: SendNotificationProc */
398 static BOOL CALLBACK Imm32SendNotificationProc(HIMC hIMC, LPARAM lParam)
399 {
400     HWND hWnd;
401     LPINPUTCONTEXTDX pIC;
402 
403     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
404     if (IS_NULL_UNEXPECTEDLY(pIC))
405         return TRUE;
406 
407     hWnd = pIC->hWnd;
408     if (hWnd == NULL || !IsWindow(hWnd))
409     {
410         ERR("\n");
411         goto Quit;
412     }
413 
414     TRACE("dwChange: 0x%08X\n", pIC->dwChange);
415 
416     if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_OPEN)
417         SendMessageW(hWnd, WM_IME_NOTIFY, IMN_SETOPENSTATUS, 0);
418     if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_CONVERSION)
419         SendMessageW(hWnd, WM_IME_NOTIFY, IMN_SETCONVERSIONMODE, 0);
420     if (pIC->dwChange & (INPUTCONTEXTDX_CHANGE_OPEN | INPUTCONTEXTDX_CHANGE_CONVERSION))
421         NtUserNotifyIMEStatus(hWnd, pIC->fOpen, pIC->fdwConversion);
422     if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_SENTENCE)
423         SendMessageW(hWnd, WM_IME_NOTIFY, IMN_SETSENTENCEMODE, 0);
424 Quit:
425     pIC->dwChange = 0;
426     ImmUnlockIMC(hIMC); // ??? Windows doesn't unlock here
427     return TRUE;
428 }
429 
430 /* Win: ImmSendNotification */
431 BOOL APIENTRY Imm32SendNotification(BOOL bProcess)
432 {
433     return ImmEnumInputContext((bProcess ? -1 : 0), Imm32SendNotificationProc, 0);
434 }
435 
436 LRESULT APIENTRY
437 Imm32ProcessRequest(HIMC hIMC, PWND pWnd, DWORD dwCommand, LPVOID pData, BOOL bAnsiAPI)
438 {
439     HWND hWnd;
440     DWORD ret = 0, dwCharPos, cchCompStr, dwSize;
441     LPVOID pCS, pTempData = pData;
442     LPRECONVERTSTRING pRS;
443     LPIMECHARPOSITION pICP;
444     PCLIENTIMC pClientImc;
445     UINT uCodePage = CP_ACP;
446     BOOL bAnsiWnd = !!(pWnd->state & WNDS_ANSIWINDOWPROC);
447     static const size_t acbData[7 * 2] =
448     {
449         /* UNICODE */
450         sizeof(COMPOSITIONFORM), sizeof(CANDIDATEFORM), sizeof(LOGFONTW),
451         sizeof(RECONVERTSTRING), sizeof(RECONVERTSTRING),
452         sizeof(IMECHARPOSITION), sizeof(RECONVERTSTRING),
453         /* ANSI */
454         sizeof(COMPOSITIONFORM), sizeof(CANDIDATEFORM), sizeof(LOGFONTA),
455         sizeof(RECONVERTSTRING), sizeof(RECONVERTSTRING),
456         sizeof(IMECHARPOSITION), sizeof(RECONVERTSTRING),
457     };
458 
459     if (dwCommand == 0 || dwCommand > IMR_DOCUMENTFEED)
460     {
461         ERR("Out of boundary\n");
462         return 0; /* Out of range */
463     }
464 
465     dwSize = acbData[bAnsiAPI * 7 + dwCommand - 1];
466     if (pData && IsBadWritePtr(pData, dwSize))
467     {
468         ERR("\n");
469         return 0; /* Invalid pointer */
470     }
471 
472     /* Sanity check */
473     switch (dwCommand)
474     {
475         case IMR_RECONVERTSTRING: case IMR_DOCUMENTFEED:
476             pRS = pData;
477             if (pRS && (pRS->dwVersion != 0 || pRS->dwSize < sizeof(RECONVERTSTRING)))
478             {
479                 ERR("Invalid pRS\n");
480                 return 0;
481             }
482             break;
483 
484         case IMR_CONFIRMRECONVERTSTRING:
485             pRS = pData;
486             if (!pRS || pRS->dwVersion != 0)
487             {
488                 ERR("Invalid pRS\n");
489                 return 0;
490             }
491             break;
492 
493         default:
494             if (IS_NULL_UNEXPECTEDLY(pData))
495                 return 0;
496             break;
497     }
498 
499     pClientImc = ImmLockClientImc(hIMC);
500     if (pClientImc)
501     {
502         uCodePage = pClientImc->uCodePage;
503         ImmUnlockClientImc(pClientImc);
504     }
505 
506     /* Prepare */
507     switch (dwCommand)
508     {
509         case IMR_COMPOSITIONFONT:
510             if (bAnsiAPI == bAnsiWnd)
511                 goto DoIt; /* No conversion needed */
512 
513             if (bAnsiWnd)
514                 pTempData = ImmLocalAlloc(0, sizeof(LOGFONTA));
515             else
516                 pTempData = ImmLocalAlloc(0, sizeof(LOGFONTW));
517 
518             if (IS_NULL_UNEXPECTEDLY(pTempData))
519                 return 0;
520             break;
521 
522         case IMR_RECONVERTSTRING: case IMR_CONFIRMRECONVERTSTRING: case IMR_DOCUMENTFEED:
523             if (bAnsiAPI == bAnsiWnd || !pData)
524                 goto DoIt; /* No conversion needed */
525 
526             if (bAnsiWnd)
527                 ret = Imm32ReconvertAnsiFromWide(NULL, pData, uCodePage);
528             else
529                 ret = Imm32ReconvertWideFromAnsi(NULL, pData, uCodePage);
530 
531             pTempData = ImmLocalAlloc(0, ret + sizeof(WCHAR));
532             if (IS_NULL_UNEXPECTEDLY(pTempData))
533                 return 0;
534 
535             pRS = pTempData;
536             pRS->dwSize = ret;
537             pRS->dwVersion = 0;
538 
539             if (dwCommand == IMR_CONFIRMRECONVERTSTRING)
540             {
541                 if (bAnsiWnd)
542                     ret = Imm32ReconvertAnsiFromWide(pTempData, pData, uCodePage);
543                 else
544                     ret = Imm32ReconvertWideFromAnsi(pTempData, pData, uCodePage);
545             }
546             break;
547 
548         case IMR_QUERYCHARPOSITION:
549             if (bAnsiAPI == bAnsiWnd)
550                 goto DoIt; /* No conversion needed */
551 
552             pICP = pData;
553             dwCharPos = pICP->dwCharPos;
554 
555             if (bAnsiAPI)
556             {
557                 cchCompStr = ImmGetCompositionStringA(hIMC, GCS_COMPSTR, NULL, 0);
558                 if (IS_ZERO_UNEXPECTEDLY(cchCompStr))
559                     return 0;
560 
561                 pCS = ImmLocalAlloc(0, (cchCompStr + 1) * sizeof(CHAR));
562                 if (IS_NULL_UNEXPECTEDLY(pCS))
563                     return 0;
564 
565                 ImmGetCompositionStringA(hIMC, GCS_COMPSTR, pCS, cchCompStr);
566                 pICP->dwCharPos = IchWideFromAnsi(pICP->dwCharPos, pCS, uCodePage);
567             }
568             else
569             {
570                 cchCompStr = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
571                 if (IS_ZERO_UNEXPECTEDLY(cchCompStr))
572                     return 0;
573 
574                 pCS = ImmLocalAlloc(0, (cchCompStr + 1) * sizeof(WCHAR));
575                 if (IS_NULL_UNEXPECTEDLY(pCS))
576                     return 0;
577 
578                 ImmGetCompositionStringW(hIMC, GCS_COMPSTR, pCS, cchCompStr);
579                 pICP->dwCharPos = IchAnsiFromWide(pICP->dwCharPos, pCS, uCodePage);
580             }
581 
582             ImmLocalFree(pCS);
583             break;
584 
585         default:
586             WARN("0x%X\n", dwCommand);
587             break;
588     }
589 
590 DoIt:
591     /* The main task */
592     hWnd = pWnd->head.h;
593     if (bAnsiWnd)
594         ret = SendMessageA(hWnd, WM_IME_REQUEST, dwCommand, (LPARAM)pTempData);
595     else
596         ret = SendMessageW(hWnd, WM_IME_REQUEST, dwCommand, (LPARAM)pTempData);
597 
598     if (bAnsiAPI == bAnsiWnd)
599         goto Quit; /* No conversion needed */
600 
601     /* Get back to caller */
602     switch (dwCommand)
603     {
604         case IMR_COMPOSITIONFONT:
605             if (bAnsiAPI)
606                 LogFontWideToAnsi(pTempData, pData);
607             else
608                 LogFontAnsiToWide(pTempData, pData);
609             break;
610 
611         case IMR_RECONVERTSTRING: case IMR_DOCUMENTFEED:
612             if (!ret)
613                 break;
614 
615             if (ret < sizeof(RECONVERTSTRING))
616             {
617                 ret = 0;
618                 break;
619             }
620 
621             if (pTempData)
622             {
623                 if (bAnsiWnd)
624                     ret = Imm32ReconvertWideFromAnsi(pData, pTempData, uCodePage);
625                 else
626                     ret = Imm32ReconvertAnsiFromWide(pData, pTempData, uCodePage);
627             }
628             break;
629 
630         case IMR_QUERYCHARPOSITION:
631             pICP->dwCharPos = dwCharPos;
632             break;
633 
634         default:
635             WARN("0x%X\n", dwCommand);
636             break;
637     }
638 
639 Quit:
640     if (pTempData != pData)
641         ImmLocalFree(pTempData);
642     return ret;
643 }
644 
645 /* Win: ImmRequestMessageWorker */
646 LRESULT APIENTRY ImmRequestMessageAW(HIMC hIMC, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
647 {
648     LRESULT ret = 0;
649     LPINPUTCONTEXT pIC;
650     HWND hWnd;
651     PWND pWnd = NULL;
652 
653     if (IS_NULL_UNEXPECTEDLY(hIMC) || IS_CROSS_THREAD_HIMC(hIMC))
654         return FALSE;
655 
656     pIC = ImmLockIMC(hIMC);
657     if (IS_NULL_UNEXPECTEDLY(pIC))
658         return FALSE;
659 
660     hWnd = pIC->hWnd;
661     if (hWnd)
662         pWnd = ValidateHwnd(hWnd);
663 
664     if (pWnd && pWnd->head.pti == Imm32CurrentPti())
665         ret = Imm32ProcessRequest(hIMC, pWnd, (DWORD)wParam, (LPVOID)lParam, bAnsi);
666 
667     ImmUnlockIMC(hIMC);
668     return ret;
669 }
670 
671 /***********************************************************************
672  *		ImmIsUIMessageA (IMM32.@)
673  */
674 BOOL WINAPI ImmIsUIMessageA(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
675 {
676     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
677     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, TRUE);
678 }
679 
680 /***********************************************************************
681  *		ImmIsUIMessageW (IMM32.@)
682  */
683 BOOL WINAPI ImmIsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
684 {
685     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
686     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, FALSE);
687 }
688 
689 /***********************************************************************
690  *              ImmGetHotKey(IMM32.@)
691  */
692 BOOL WINAPI
693 ImmGetHotKey(IN DWORD dwHotKey, OUT LPUINT lpuModifiers, OUT LPUINT lpuVKey,
694              OUT LPHKL lphKL)
695 {
696     TRACE("(0x%lX, %p, %p, %p)\n", dwHotKey, lpuModifiers, lpuVKey, lphKL);
697     if (lpuModifiers && lpuVKey)
698         return NtUserGetImeHotKey(dwHotKey, lpuModifiers, lpuVKey, lphKL);
699     return FALSE;
700 }
701 
702 /***********************************************************************
703  *              ImmWINNLSGetIMEHotkey (IMM32.@)
704  */
705 UINT WINAPI ImmWINNLSGetIMEHotkey(HWND hwndIme)
706 {
707     TRACE("(%p)\n", hwndIme);
708     UNREFERENCED_PARAMETER(hwndIme);
709     return 0; /* This is correct. This function of Windows just returns zero. */
710 }
711 
712 /***********************************************************************
713  *		ImmSimulateHotKey (IMM32.@)
714  */
715 BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
716 {
717     HIMC hIMC;
718     DWORD dwThreadId;
719     HKL hKL;
720     BOOL ret;
721 
722     TRACE("(%p, 0x%lX)\n", hWnd, dwHotKeyID);
723 
724     hIMC = ImmGetContext(hWnd);
725     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
726     hKL = GetKeyboardLayout(dwThreadId);
727     ret = Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID);
728     ImmReleaseContext(hWnd, hIMC);
729     return ret;
730 }
731 
732 /***********************************************************************
733  *		ImmGetVirtualKey (IMM32.@)
734  */
735 UINT WINAPI ImmGetVirtualKey(HWND hWnd)
736 {
737     HIMC hIMC;
738     LPINPUTCONTEXTDX pIC;
739     UINT ret = VK_PROCESSKEY;
740 
741     TRACE("(%p)\n", hWnd);
742 
743     hIMC = ImmGetContext(hWnd);
744     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
745     if (IS_NULL_UNEXPECTEDLY(pIC))
746         return ret;
747 
748     if (pIC->bNeedsTrans)
749         ret = pIC->nVKey;
750 
751     ImmUnlockIMC(hIMC);
752     return ret;
753 }
754 
755 /***********************************************************************
756  *		ImmGetAppCompatFlags (IMM32.@)
757  */
758 DWORD WINAPI ImmGetAppCompatFlags(HIMC hIMC)
759 {
760     PCLIENTIMC pClientIMC;
761     DWORD dwFlags;
762 
763     TRACE("(%p)\n", hIMC);
764 
765     pClientIMC = ImmLockClientImc(hIMC);
766     if (IS_NULL_UNEXPECTEDLY(pClientIMC))
767         return 0;
768 
769     dwFlags = pClientIMC->dwCompatFlags;
770     ImmUnlockClientImc(pClientIMC);
771     return (dwFlags | g_aimm_compat_flags);
772 }
773 
774 /***********************************************************************
775  *		ImmProcessKey(IMM32.@)
776  *       ( Undocumented, called from user32.dll )
777  */
778 DWORD WINAPI
779 ImmProcessKey(HWND hWnd, HKL hKL, UINT vKey, LPARAM lParam, DWORD dwHotKeyID)
780 {
781     DWORD ret = 0;
782     HIMC hIMC;
783     PIMEDPI pImeDpi;
784     LPINPUTCONTEXTDX pIC;
785     BYTE KeyState[256];
786     UINT vk;
787     BOOL bUseIme = TRUE, bSkipThisKey = FALSE, bLowWordOnly = FALSE;
788 
789     TRACE("(%p, %p, 0x%X, %p, 0x%lX)\n", hWnd, hKL, vKey, lParam, dwHotKeyID);
790 
791     hIMC = ImmGetContext(hWnd);
792     pImeDpi = ImmLockImeDpi(hKL);
793     if (pImeDpi)
794     {
795         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
796         if (pIC)
797         {
798             if (LOBYTE(vKey) == VK_PACKET &&
799                 !(pImeDpi->ImeInfo.fdwProperty & IME_PROP_ACCEPT_WIDE_VKEY))
800             {
801                 if (ImeDpi_IsUnicode(pImeDpi))
802                 {
803                     bLowWordOnly = TRUE;
804                 }
805                 else
806                 {
807                     bUseIme = FALSE;
808                     if (pIC->fOpen)
809                         bSkipThisKey = TRUE;
810                 }
811             }
812 
813             if (bUseIme)
814             {
815                 if (GetKeyboardState(KeyState))
816                 {
817                     vk = (bLowWordOnly ? LOWORD(vKey) : vKey);
818                     if (pImeDpi->ImeProcessKey(hIMC, vk, lParam, KeyState))
819                     {
820                         pIC->bNeedsTrans = TRUE;
821                         pIC->nVKey = vKey;
822                         ret |= IPHK_PROCESSBYIME;
823                     }
824                 }
825             }
826             else if (bSkipThisKey)
827             {
828                 ret |= IPHK_SKIPTHISKEY;
829             }
830 
831             ImmUnlockIMC(hIMC);
832         }
833 
834         ImmUnlockImeDpi(pImeDpi);
835     }
836 
837     if (dwHotKeyID != INVALID_HOTKEY_ID) /* Valid Hot-key */
838     {
839         if (Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID))
840         {
841             if (vKey != VK_KANJI || dwHotKeyID != IME_JHOTKEY_CLOSE_OPEN)
842                 ret |= IPHK_HOTKEY;
843         }
844     }
845 
846     if ((ret & IPHK_PROCESSBYIME) && (ImmGetAppCompatFlags(hIMC) & 0x10000))
847     {
848         /* The key has been processed by IME's ImeProcessKey */
849         LANGID wLangID = LANGIDFROMLCID(GetSystemDefaultLCID());
850         if (PRIMARYLANGID(wLangID) == LANG_KOREAN &&
851             (vKey == VK_PROCESSKEY || (ret & IPHK_HOTKEY)))
852         {
853             /* Korean don't want VK_PROCESSKEY and IME hot-keys */
854         }
855         else
856         {
857             /* Add WM_KEYDOWN:VK_PROCESSKEY message */
858             ImmTranslateMessage(hWnd, WM_KEYDOWN, VK_PROCESSKEY, lParam);
859             ret &= ~IPHK_PROCESSBYIME;
860             ret |= IPHK_SKIPTHISKEY;
861         }
862     }
863 
864     ImmReleaseContext(hWnd, hIMC);
865     return ret; /* Returns IPHK_... flags */
866 }
867 
868 /***********************************************************************
869  *		ImmSystemHandler(IMM32.@)
870  */
871 LRESULT WINAPI ImmSystemHandler(HIMC hIMC, WPARAM wParam, LPARAM lParam)
872 {
873     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
874 
875     switch (wParam)
876     {
877         case IMS_SENDNOTIFICATION:
878             Imm32SendNotification((BOOL)lParam);
879             return 0;
880 
881         case IMS_COMPLETECOMPSTR:
882             ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
883             return 0;
884 
885         case IMS_SETLANGBAND:
886         case IMS_UNSETLANGBAND:
887             return CtfImmSetLangBand((HWND)lParam, (wParam == IMS_SETLANGBAND));
888 
889         default:
890             WARN("%p\n", wParam);
891             return 0;
892     }
893 }
894 
895 /***********************************************************************
896  *		ImmGenerateMessage(IMM32.@)
897  */
898 BOOL WINAPI ImmGenerateMessage(HIMC hIMC)
899 {
900     PCLIENTIMC pClientImc;
901     LPINPUTCONTEXT pIC;
902     LPTRANSMSG pMsgs, pTrans = NULL, pItem;
903     HWND hWnd;
904     DWORD dwIndex, dwCount, cbTrans;
905     HIMCC hMsgBuf = NULL;
906     BOOL bAnsi;
907 
908     TRACE("(%p)\n", hIMC);
909 
910     if (IS_CROSS_THREAD_HIMC(hIMC))
911         return FALSE;
912 
913     pClientImc = ImmLockClientImc(hIMC);
914     if (IS_NULL_UNEXPECTEDLY(pClientImc))
915         return FALSE;
916 
917     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
918     ImmUnlockClientImc(pClientImc);
919 
920     pIC = ImmLockIMC(hIMC);
921     if (IS_NULL_UNEXPECTEDLY(pIC))
922         return FALSE;
923 
924     dwCount = pIC->dwNumMsgBuf;
925     if (dwCount == 0)
926         goto Quit;
927 
928     hMsgBuf = pIC->hMsgBuf;
929     pMsgs = ImmLockIMCC(hMsgBuf);
930     if (IS_NULL_UNEXPECTEDLY(pMsgs))
931         goto Quit;
932 
933     cbTrans = dwCount * sizeof(TRANSMSG);
934     pTrans = ImmLocalAlloc(0, cbTrans);
935     if (IS_NULL_UNEXPECTEDLY(pTrans))
936         goto Quit;
937 
938     RtlCopyMemory(pTrans, pMsgs, cbTrans);
939 
940 #ifdef IMM_WIN3_SUPPORT
941     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
942     {
943         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
944         WORD wLang = PRIMARYLANGID(LangID);
945 
946         /* translate the messages if Japanese or Korean */
947         if (wLang == LANG_JAPANESE ||
948             (wLang == LANG_KOREAN && NtUserGetAppImeLevel(pIC->hWnd) == 3))
949         {
950             dwCount = WINNLSTranslateMessage(dwCount, pTrans, hIMC, bAnsi, wLang);
951         }
952     }
953 #endif
954 
955     /* send them */
956     hWnd = pIC->hWnd;
957     pItem = pTrans;
958     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
959     {
960         if (bAnsi)
961             SendMessageA(hWnd, pItem->message, pItem->wParam, pItem->lParam);
962         else
963             SendMessageW(hWnd, pItem->message, pItem->wParam, pItem->lParam);
964     }
965 
966 Quit:
967     ImmLocalFree(pTrans);
968     if (hMsgBuf)
969         ImmUnlockIMCC(hMsgBuf);
970     pIC->dwNumMsgBuf = 0; /* done */
971     ImmUnlockIMC(hIMC);
972     return TRUE;
973 }
974 
975 VOID APIENTRY
976 ImmPostMessages(HWND hwnd, HIMC hIMC, DWORD dwCount, LPTRANSMSG lpTransMsg)
977 {
978     DWORD dwIndex;
979     PCLIENTIMC pClientImc;
980     LPTRANSMSG pNewTransMsg = lpTransMsg, pItem;
981     BOOL bAnsi;
982 
983     pClientImc = ImmLockClientImc(hIMC);
984     if (IS_NULL_UNEXPECTEDLY(pClientImc))
985         return;
986 
987     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
988     ImmUnlockClientImc(pClientImc);
989 
990 #ifdef IMM_WIN3_SUPPORT
991     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
992     {
993         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
994         WORD Lang = PRIMARYLANGID(LangID);
995 
996         /* translate the messages if Japanese or Korean */
997         if (Lang == LANG_JAPANESE ||
998             (Lang == LANG_KOREAN && NtUserGetAppImeLevel(hwnd) == 3))
999         {
1000             DWORD cbTransMsg = dwCount * sizeof(TRANSMSG);
1001             pNewTransMsg = ImmLocalAlloc(0, cbTransMsg);
1002             if (pNewTransMsg)
1003             {
1004                 RtlCopyMemory(pNewTransMsg, lpTransMsg, cbTransMsg);
1005                 dwCount = WINNLSTranslateMessage(dwCount, pNewTransMsg, hIMC, bAnsi, Lang);
1006             }
1007             else
1008             {
1009                 pNewTransMsg = lpTransMsg;
1010             }
1011         }
1012     }
1013 #endif
1014 
1015     /* post them */
1016     pItem = pNewTransMsg;
1017     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
1018     {
1019         if (bAnsi)
1020             PostMessageA(hwnd, pItem->message, pItem->wParam, pItem->lParam);
1021         else
1022             PostMessageW(hwnd, pItem->message, pItem->wParam, pItem->lParam);
1023     }
1024 
1025 #ifdef IMM_WIN3_SUPPORT
1026     if (pNewTransMsg != lpTransMsg)
1027         ImmLocalFree(pNewTransMsg);
1028 #endif
1029 }
1030 
1031 /***********************************************************************
1032  *       ImmTranslateMessage(IMM32.@)
1033  *       ( Undocumented, call internally and from user32.dll )
1034  */
1035 BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData)
1036 {
1037 #define MSG_COUNT 0x100
1038     BOOL ret = FALSE;
1039     INT kret;
1040     LPINPUTCONTEXTDX pIC;
1041     PIMEDPI pImeDpi = NULL;
1042     LPTRANSMSGLIST pList = NULL;
1043     LPTRANSMSG pTransMsg;
1044     BYTE abKeyState[256];
1045     HIMC hIMC;
1046     HKL hKL;
1047     UINT vk;
1048     DWORD dwThreadId, dwCount, cbList;
1049     WCHAR wch;
1050     WORD wChar;
1051 
1052     TRACE("(%p, 0x%X, %p, %p)\n", hwnd, msg, wParam, lKeyData);
1053 
1054     /* filter the message */
1055     switch (msg)
1056     {
1057         case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP:
1058             break;
1059         default:
1060             return FALSE;
1061     }
1062 
1063     hIMC = ImmGetContext(hwnd);
1064     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
1065     if (IS_NULL_UNEXPECTEDLY(pIC))
1066     {
1067         ImmReleaseContext(hwnd, hIMC);
1068         return FALSE;
1069     }
1070 
1071     if (!pIC->bNeedsTrans) /* is translation needed? */
1072     {
1073         /* directly post them */
1074         dwCount = pIC->dwNumMsgBuf;
1075         if (dwCount == 0)
1076             goto Quit;
1077 
1078         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
1079         if (pTransMsg)
1080         {
1081             ImmPostMessages(hwnd, hIMC, dwCount, pTransMsg);
1082             ImmUnlockIMCC(pIC->hMsgBuf);
1083             ret = TRUE;
1084         }
1085         pIC->dwNumMsgBuf = 0; /* done */
1086         goto Quit;
1087     }
1088     pIC->bNeedsTrans = FALSE; /* clear the flag */
1089 
1090     dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
1091     hKL = GetKeyboardLayout(dwThreadId);
1092     pImeDpi = ImmLockImeDpi(hKL);
1093     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
1094         goto Quit;
1095 
1096     if (!GetKeyboardState(abKeyState)) /* get keyboard ON/OFF status */
1097     {
1098         WARN("\n");
1099         goto Quit;
1100     }
1101 
1102     /* convert a virtual key if IME_PROP_KBD_CHAR_FIRST */
1103     vk = pIC->nVKey;
1104     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST)
1105     {
1106         if (ImeDpi_IsUnicode(pImeDpi))
1107         {
1108             wch = 0;
1109             kret = ToUnicode(vk, HIWORD(lKeyData), abKeyState, &wch, 1, 0);
1110             if (kret == 1)
1111                 vk = MAKELONG(LOBYTE(vk), wch);
1112         }
1113         else
1114         {
1115             wChar = 0;
1116             kret = ToAsciiEx(vk, HIWORD(lKeyData), abKeyState, &wChar, 0, hKL);
1117             if (kret > 0)
1118             {
1119                 if ((BYTE)vk == VK_PACKET)
1120                 {
1121                     vk &= 0xFF;
1122                     vk |= (wChar << 8);
1123                 }
1124                 else
1125                 {
1126                     vk = MAKEWORD(vk, wChar);
1127                 }
1128             }
1129         }
1130     }
1131 
1132     /* allocate a list */
1133     cbList = offsetof(TRANSMSGLIST, TransMsg) + MSG_COUNT * sizeof(TRANSMSG);
1134     pList = ImmLocalAlloc(0, cbList);
1135     if (IS_NULL_UNEXPECTEDLY(pList))
1136         goto Quit;
1137 
1138     /* use IME conversion engine and convert the list */
1139     pList->uMsgCount = MSG_COUNT;
1140     kret = pImeDpi->ImeToAsciiEx(vk, HIWORD(lKeyData), abKeyState, pList, 0, hIMC);
1141     if (kret <= 0)
1142         goto Quit;
1143 
1144     /* post them */
1145     if (kret <= MSG_COUNT)
1146     {
1147         ImmPostMessages(hwnd, hIMC, kret, pList->TransMsg);
1148         ret = TRUE;
1149     }
1150     else
1151     {
1152         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
1153         if (IS_NULL_UNEXPECTEDLY(pTransMsg))
1154             goto Quit;
1155         ImmPostMessages(hwnd, hIMC, kret, pTransMsg);
1156         ImmUnlockIMCC(pIC->hMsgBuf);
1157     }
1158 
1159 Quit:
1160     ImmLocalFree(pList);
1161     ImmUnlockImeDpi(pImeDpi);
1162     ImmUnlockIMC(hIMC);
1163     ImmReleaseContext(hwnd, hIMC);
1164     TRACE("ret: %d\n", ret);
1165     return ret;
1166 #undef MSG_COUNT
1167 }
1168 
1169 /***********************************************************************
1170  *              ImmRequestMessageA(IMM32.@)
1171  */
1172 LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1173 {
1174     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
1175     return ImmRequestMessageAW(hIMC, wParam, lParam, TRUE);
1176 }
1177 
1178 /***********************************************************************
1179  *              ImmRequestMessageW(IMM32.@)
1180  */
1181 LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1182 {
1183     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
1184     return ImmRequestMessageAW(hIMC, wParam, lParam, FALSE);
1185 }
1186 
1187 /***********************************************************************
1188  *              ImmSendMessageToActiveDefImeWndW (IMM32.@)
1189  */
1190 LRESULT WINAPI
1191 ImmSendMessageToActiveDefImeWndW(UINT uMsg, WPARAM wParam, LPARAM lParam)
1192 {
1193     HWND hwndIME;
1194 
1195     if (uMsg != WM_COPYDATA)
1196         return 0;
1197 
1198     hwndIME = (HWND)NtUserQueryWindow((HWND)wParam, QUERY_WINDOW_DEFAULT_IME);
1199     if (IS_NULL_UNEXPECTEDLY(hwndIME))
1200         return 0;
1201 
1202     return SendMessageW(hwndIME, uMsg, wParam, lParam);
1203 }
1204 
1205 /***********************************************************************
1206  *              ImmCallImeConsoleIME (IMM32.@)
1207  */
1208 DWORD WINAPI
1209 ImmCallImeConsoleIME(
1210     _In_ HWND hWnd,
1211     _In_ UINT uMsg,
1212     _In_ WPARAM wParam,
1213     _In_ LPARAM lParam,
1214     _Out_ LPUINT puVK)
1215 {
1216     DWORD dwThreadId, ret = 0;
1217     HKL hKL;
1218     PWND pWnd = NULL;
1219     HIMC hIMC;
1220     PIMEDPI pImeDpi;
1221     UINT uVK;
1222     PIMC pIMC;
1223 
1224     switch (uMsg)
1225     {
1226         case WM_KEYDOWN:
1227         case WM_KEYUP:
1228         case WM_SYSKEYDOWN:
1229         case WM_SYSKEYUP:
1230             break;
1231 
1232         default:
1233             return 0;
1234     }
1235 
1236     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
1237     hKL = GetKeyboardLayout(dwThreadId);
1238 
1239     if (hWnd && gpsi)
1240         pWnd = ValidateHwndNoErr(hWnd);
1241     if (IS_NULL_UNEXPECTEDLY(pWnd))
1242         return 0;
1243 
1244     hIMC = ImmGetContext(hWnd);
1245     if (IS_NULL_UNEXPECTEDLY(hIMC))
1246         return 0;
1247 
1248     uVK = *puVK = (wParam & 0xFF);
1249 
1250     pIMC = ValidateHandleNoErr(hIMC, TYPE_INPUTCONTEXT);
1251     if (IS_NULL_UNEXPECTEDLY(pIMC))
1252         return 0;
1253 
1254     pImeDpi = ImmLockImeDpi(hKL);
1255     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
1256         return 0;
1257 
1258     if ((lParam & MAKELPARAM(0, KF_UP)) && (pImeDpi->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS))
1259         goto Quit;
1260 
1261     switch (uVK)
1262     {
1263         case VK_DBE_ROMAN:
1264         case VK_DBE_NOROMAN:
1265         case VK_DBE_HIRAGANA:
1266         case VK_DBE_KATAKANA:
1267         case VK_DBE_CODEINPUT:
1268         case VK_DBE_NOCODEINPUT:
1269         case VK_DBE_ENTERWORDREGISTERMODE:
1270         case VK_DBE_ENTERCONFIGMODE:
1271             break;
1272 
1273         default:
1274         {
1275             if (uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP)
1276             {
1277                 if (uVK != VK_MENU && uVK != VK_F10)
1278                     goto Quit;
1279             }
1280 
1281             if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY))
1282             {
1283                 if (uVK == VK_MENU || (lParam & MAKELPARAM(0, KF_ALTDOWN)))
1284                     goto Quit;
1285             }
1286         }
1287     }
1288 
1289     ret = ImmProcessKey(hWnd, hKL, uVK, lParam, INVALID_HOTKEY_ID);
1290 
1291 Quit:
1292     ImmUnlockImeDpi(pImeDpi);
1293     return ret;
1294 }
1295