xref: /reactos/dll/win32/imm32/keymsg.c (revision bbabe248)
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 Oleg Dubinskiy <oleg.dubinskij2013@yandex.ua>
10  *              Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
11  */
12 
13 #include "precomp.h"
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(imm);
16 
17 BOOL APIENTRY Imm32ImeNonImeToggle(HIMC hIMC, HKL hKL, HWND hWnd, LANGID LangID)
18 {
19     LPINPUTCONTEXT pIC;
20     BOOL fOpen;
21 
22     if (hWnd != NULL)
23         return FALSE;
24 
25     if (!IS_IME_HKL(hKL) || LOWORD(hKL) != LangID)
26     {
27         FIXME("We have to do something here\n");
28         return TRUE;
29     }
30 
31     pIC = ImmLockIMC(hIMC);
32     if (pIC == NULL)
33         return TRUE;
34 
35     fOpen = pIC->fOpen;
36     ImmUnlockIMC(hIMC);
37 
38     if (!fOpen)
39     {
40         ImmSetOpenStatus(hIMC, TRUE);
41         return TRUE;
42     }
43 
44     FIXME("We have to do something here\n");
45     return TRUE;
46 }
47 
48 BOOL APIENTRY Imm32CShapeToggle(HIMC hIMC, HKL hKL, HWND hWnd)
49 {
50     LPINPUTCONTEXT pIC;
51     BOOL fOpen;
52     DWORD dwConversion, dwSentence;
53 
54     if (hWnd == NULL || !IS_IME_HKL(hKL))
55         return FALSE;
56 
57     pIC = ImmLockIMC(hIMC);
58     if (pIC == NULL)
59         return TRUE;
60 
61     fOpen = pIC->fOpen;
62     if (fOpen)
63     {
64         dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
65         dwSentence = pIC->fdwSentence;
66     }
67 
68     ImmUnlockIMC(hIMC);
69 
70     if (fOpen)
71         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
72     else
73         ImmSetOpenStatus(hIMC, TRUE);
74 
75     return TRUE;
76 }
77 
78 BOOL APIENTRY Imm32CSymbolToggle(HIMC hIMC, HKL hKL, HWND hWnd)
79 {
80     LPINPUTCONTEXT pIC;
81     BOOL fOpen;
82     DWORD dwConversion, dwSentence;
83 
84     if (hWnd == NULL || !IS_IME_HKL(hKL))
85         return FALSE;
86 
87     pIC = ImmLockIMC(hIMC);
88     if (pIC == NULL)
89         return TRUE;
90 
91     fOpen = pIC->fOpen;
92     if (fOpen)
93     {
94         dwConversion = (pIC->fdwConversion ^ IME_CMODE_SYMBOL);
95         dwSentence = pIC->fdwSentence;
96     }
97 
98     ImmUnlockIMC(hIMC);
99 
100     if (fOpen)
101         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
102     else
103         ImmSetOpenStatus(hIMC, TRUE);
104 
105     return TRUE;
106 }
107 
108 BOOL APIENTRY Imm32JCloseOpen(HIMC hIMC, HKL hKL, HWND hWnd)
109 {
110     BOOL fOpen;
111 
112     if (ImmIsIME(hKL) && LOWORD(hKL) == LANGID_JAPANESE)
113     {
114         fOpen = ImmGetOpenStatus(hIMC);
115         ImmSetOpenStatus(hIMC, !fOpen);
116         return TRUE;
117     }
118 
119     FIXME("We have to do something here\n");
120     return TRUE;
121 }
122 
123 BOOL APIENTRY Imm32KShapeToggle(HIMC hIMC)
124 {
125     LPINPUTCONTEXT pIC;
126     DWORD dwConversion, dwSentence;
127 
128     pIC = ImmLockIMC(hIMC);
129     if (pIC == NULL)
130         return FALSE;
131 
132     dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
133     dwSentence = pIC->fdwSentence;
134     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
135 
136     if (pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE))
137         ImmSetOpenStatus(hIMC, TRUE);
138     else
139         ImmSetOpenStatus(hIMC, FALSE);
140 
141     ImmUnlockIMC(hIMC);
142     return TRUE;
143 }
144 
145 BOOL APIENTRY Imm32KHanjaConvert(HIMC hIMC)
146 {
147     LPINPUTCONTEXT pIC;
148     DWORD dwConversion, dwSentence;
149 
150     pIC = ImmLockIMC(hIMC);
151     if (!pIC)
152         return FALSE;
153 
154     dwConversion = (pIC->fdwConversion ^ IME_CMODE_HANJACONVERT);
155     dwSentence = pIC->fdwSentence;
156     ImmUnlockIMC(hIMC);
157 
158     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
159     return TRUE;
160 }
161 
162 BOOL APIENTRY Imm32KEnglish(HIMC hIMC)
163 {
164     LPINPUTCONTEXT pIC;
165     DWORD dwConversion, dwSentence;
166     BOOL fOpen;
167 
168     pIC = ImmLockIMC(hIMC);
169     if (pIC == NULL)
170         return FALSE;
171 
172     dwConversion = (pIC->fdwConversion ^ IME_CMODE_NATIVE);
173     dwSentence = pIC->fdwSentence;
174     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
175 
176     fOpen = ((pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE)) != 0);
177     ImmSetOpenStatus(hIMC, fOpen);
178 
179     ImmUnlockIMC(hIMC);
180     return TRUE;
181 }
182 
183 BOOL APIENTRY Imm32ProcessHotKey(HWND hWnd, HIMC hIMC, HKL hKL, DWORD dwHotKeyID)
184 {
185     PIMEDPI pImeDpi;
186     BOOL ret;
187 
188     if (hIMC && Imm32IsCrossThreadAccess(hIMC))
189         return FALSE;
190 
191     switch (dwHotKeyID)
192     {
193         case IME_CHOTKEY_IME_NONIME_TOGGLE:
194             return Imm32ImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_SIMPLIFIED);
195 
196         case IME_CHOTKEY_SHAPE_TOGGLE:
197             return Imm32CShapeToggle(hIMC, hKL, hWnd);
198 
199         case IME_CHOTKEY_SYMBOL_TOGGLE:
200             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
201 
202         case IME_JHOTKEY_CLOSE_OPEN:
203             return Imm32JCloseOpen(hIMC, hKL, hWnd);
204 
205         case IME_KHOTKEY_SHAPE_TOGGLE:
206             return Imm32KShapeToggle(hIMC);
207 
208         case IME_KHOTKEY_HANJACONVERT:
209             return Imm32KHanjaConvert(hIMC);
210 
211         case IME_KHOTKEY_ENGLISH:
212             return Imm32KEnglish(hIMC);
213 
214         case IME_THOTKEY_IME_NONIME_TOGGLE:
215             return Imm32ImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_TRADITIONAL);
216 
217         case IME_THOTKEY_SHAPE_TOGGLE:
218             return Imm32CShapeToggle(hIMC, hKL, hWnd);
219 
220         case IME_THOTKEY_SYMBOL_TOGGLE:
221             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
222 
223         default:
224             break;
225     }
226 
227     if (dwHotKeyID < IME_HOTKEY_PRIVATE_FIRST || IME_HOTKEY_PRIVATE_LAST < dwHotKeyID)
228         return FALSE;
229 
230     pImeDpi = ImmLockImeDpi(hKL);
231     if (pImeDpi == NULL)
232         return FALSE;
233 
234     ret = (BOOL)pImeDpi->ImeEscape(hIMC, IME_ESC_PRIVATE_HOTKEY, &dwHotKeyID);
235     ImmUnlockImeDpi(pImeDpi);
236     return ret;
237 }
238 
239 static BOOL APIENTRY
240 ImmIsUIMessageAW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
241 {
242     switch (msg)
243     {
244         case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION:
245         case WM_IME_COMPOSITION: case WM_IME_SETCONTEXT: case WM_IME_NOTIFY:
246         case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: case WM_IME_SYSTEM:
247             break;
248         default:
249             return FALSE;
250     }
251 
252     if (!hWndIME)
253         return TRUE;
254 
255     if (bAnsi)
256         SendMessageA(hWndIME, msg, wParam, lParam);
257     else
258         SendMessageW(hWndIME, msg, wParam, lParam);
259 
260     return TRUE;
261 }
262 
263 /***********************************************************************
264  *		ImmIsUIMessageA (IMM32.@)
265  */
266 BOOL WINAPI ImmIsUIMessageA(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
267 {
268     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
269     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, TRUE);
270 }
271 
272 /***********************************************************************
273  *		ImmIsUIMessageW (IMM32.@)
274  */
275 BOOL WINAPI ImmIsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
276 {
277     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
278     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, FALSE);
279 }
280 
281 /***********************************************************************
282  *              ImmGetHotKey(IMM32.@)
283  */
284 BOOL WINAPI
285 ImmGetHotKey(IN DWORD dwHotKey, OUT LPUINT lpuModifiers, OUT LPUINT lpuVKey,
286              OUT LPHKL lphKL)
287 {
288     TRACE("(0x%lX, %p, %p, %p)\n", dwHotKey, lpuModifiers, lpuVKey, lphKL);
289     if (lpuModifiers && lpuVKey)
290         return NtUserGetImeHotKey(dwHotKey, lpuModifiers, lpuVKey, lphKL);
291     return FALSE;
292 }
293 
294 /***********************************************************************
295  *              ImmWINNLSGetIMEHotkey (IMM32.@)
296  */
297 UINT WINAPI ImmWINNLSGetIMEHotkey(HWND hwndIme)
298 {
299     TRACE("(%p)\n", hwndIme);
300     UNREFERENCED_PARAMETER(hwndIme);
301     return 0; /* This is correct. This function of Windows just returns zero. */
302 }
303 
304 /***********************************************************************
305  *		ImmSimulateHotKey (IMM32.@)
306  */
307 BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
308 {
309     HIMC hIMC;
310     DWORD dwThreadId;
311     HKL hKL;
312     BOOL ret;
313 
314     TRACE("(%p, 0x%lX)\n", hWnd, dwHotKeyID);
315 
316     hIMC = ImmGetContext(hWnd);
317     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
318     hKL = GetKeyboardLayout(dwThreadId);
319     ret = Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID);
320     ImmReleaseContext(hWnd, hIMC);
321     return ret;
322 }
323 
324 /***********************************************************************
325  *		ImmGetVirtualKey (IMM32.@)
326  */
327 UINT WINAPI ImmGetVirtualKey(HWND hWnd)
328 {
329     HIMC hIMC;
330     LPINPUTCONTEXTDX pIC;
331     UINT ret = VK_PROCESSKEY;
332 
333     TRACE("(%p)\n", hWnd);
334 
335     hIMC = ImmGetContext(hWnd);
336     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
337     if (!pIC)
338         return ret;
339 
340     if (pIC->bNeedsTrans)
341         ret = pIC->nVKey;
342 
343     ImmUnlockIMC(hIMC);
344     return ret;
345 }
346 
347 /***********************************************************************
348  *		ImmProcessKey(IMM32.@)
349  *       ( Undocumented, called from user32.dll )
350  */
351 DWORD WINAPI
352 ImmProcessKey(HWND hWnd, HKL hKL, UINT vKey, LPARAM lParam, DWORD dwHotKeyID)
353 {
354     DWORD ret = 0;
355     HIMC hIMC;
356     PIMEDPI pImeDpi;
357     LPINPUTCONTEXTDX pIC;
358     BYTE KeyState[256];
359     UINT vk;
360     BOOL bUseIme = TRUE, bSkipThisKey = FALSE, bLowWordOnly = FALSE;
361 
362     TRACE("(%p, %p, 0x%X, %p, 0x%lX)\n", hWnd, hKL, vKey, lParam, dwHotKeyID);
363 
364     hIMC = ImmGetContext(hWnd);
365     pImeDpi = ImmLockImeDpi(hKL);
366     if (pImeDpi)
367     {
368         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
369         if (pIC)
370         {
371             if (LOBYTE(vKey) == VK_PACKET &&
372                 !(pImeDpi->ImeInfo.fdwProperty & IME_PROP_ACCEPT_WIDE_VKEY))
373             {
374                 if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
375                 {
376                     bLowWordOnly = TRUE;
377                 }
378                 else
379                 {
380                     bUseIme = FALSE;
381                     if (pIC->fOpen)
382                         bSkipThisKey = TRUE;
383                 }
384             }
385 
386             if (bUseIme)
387             {
388                 if (GetKeyboardState(KeyState))
389                 {
390                     vk = (bLowWordOnly ? LOWORD(vKey) : vKey);
391                     if (pImeDpi->ImeProcessKey(hIMC, vk, lParam, KeyState))
392                     {
393                         pIC->bNeedsTrans = TRUE;
394                         pIC->nVKey = vKey;
395                         ret |= IPHK_PROCESSBYIME;
396                     }
397                 }
398             }
399             else if (bSkipThisKey)
400             {
401                 ret |= IPHK_SKIPTHISKEY;
402             }
403 
404             ImmUnlockIMC(hIMC);
405         }
406 
407         ImmUnlockImeDpi(pImeDpi);
408     }
409 
410     if (dwHotKeyID != INVALID_HOTKEY_ID)
411     {
412         if (Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID))
413         {
414             if (vKey != VK_KANJI || dwHotKeyID != IME_JHOTKEY_CLOSE_OPEN)
415                 ret |= IPHK_HOTKEY;
416         }
417     }
418 
419     if (ret & IPHK_PROCESSBYIME)
420     {
421         FIXME("TODO: We have to do something here.\n");
422     }
423 
424     ImmReleaseContext(hWnd, hIMC);
425     return ret;
426 }
427 
428 /***********************************************************************
429  *		ImmGenerateMessage(IMM32.@)
430  */
431 BOOL WINAPI ImmGenerateMessage(HIMC hIMC)
432 {
433     PCLIENTIMC pClientImc;
434     LPINPUTCONTEXT pIC;
435     LPTRANSMSG pMsgs, pTrans = NULL, pItem;
436     HWND hWnd;
437     DWORD dwIndex, dwCount, cbTrans;
438     HIMCC hMsgBuf = NULL;
439     BOOL bAnsi;
440 
441     TRACE("(%p)\n", hIMC);
442 
443     if (Imm32IsCrossThreadAccess(hIMC))
444         return FALSE;
445 
446     pClientImc = ImmLockClientImc(hIMC);
447     if (pClientImc == NULL)
448         return FALSE;
449 
450     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
451     ImmUnlockClientImc(pClientImc);
452 
453     pIC = ImmLockIMC(hIMC);
454     if (pIC == NULL)
455         return FALSE;
456 
457     dwCount = pIC->dwNumMsgBuf;
458     if (dwCount == 0)
459         goto Quit;
460 
461     hMsgBuf = pIC->hMsgBuf;
462     pMsgs = ImmLockIMCC(hMsgBuf);
463     if (pMsgs == NULL)
464         goto Quit;
465 
466     cbTrans = dwCount * sizeof(TRANSMSG);
467     pTrans = Imm32HeapAlloc(0, cbTrans);
468     if (pTrans == NULL)
469         goto Quit;
470 
471     RtlCopyMemory(pTrans, pMsgs, cbTrans);
472 
473 #ifdef IMM_NT3_SUPPORT
474     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
475     {
476         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
477         WORD wLang = PRIMARYLANGID(LangID);
478 
479         /* translate the messages if Japanese or Korean */
480         if (wLang == LANG_JAPANESE ||
481             (wLang == LANG_KOREAN && NtUserGetAppImeLevel(pIC->hWnd) == 3))
482         {
483             dwCount = ImmNt3Trans(dwCount, pTrans, hIMC, bAnsi, wLang);
484         }
485     }
486 #endif
487 
488     /* send them */
489     hWnd = pIC->hWnd;
490     pItem = pTrans;
491     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
492     {
493         if (bAnsi)
494             SendMessageA(hWnd, pItem->message, pItem->wParam, pItem->lParam);
495         else
496             SendMessageW(hWnd, pItem->message, pItem->wParam, pItem->lParam);
497     }
498 
499 Quit:
500     if (pTrans)
501         HeapFree(g_hImm32Heap, 0, pTrans);
502     if (hMsgBuf)
503         ImmUnlockIMCC(hMsgBuf);
504     pIC->dwNumMsgBuf = 0; /* done */
505     ImmUnlockIMC(hIMC);
506     return TRUE;
507 }
508 
509 VOID APIENTRY
510 Imm32PostMessages(HWND hwnd, HIMC hIMC, DWORD dwCount, LPTRANSMSG lpTransMsg)
511 {
512     DWORD dwIndex;
513     PCLIENTIMC pClientImc;
514     LPTRANSMSG pNewTransMsg = lpTransMsg, pItem;
515     BOOL bAnsi;
516 
517     pClientImc = ImmLockClientImc(hIMC);
518     if (pClientImc == NULL)
519         return;
520 
521     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
522     ImmUnlockClientImc(pClientImc);
523 
524 #ifdef IMM_NT3_SUPPORT
525     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
526     {
527         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
528         WORD Lang = PRIMARYLANGID(LangID);
529 
530         /* translate the messages if Japanese or Korean */
531         if (Lang == LANG_JAPANESE ||
532             (Lang == LANG_KOREAN && NtUserGetAppImeLevel(hwnd) == 3))
533         {
534             DWORD cbTransMsg = dwCount * sizeof(TRANSMSG);
535             pNewTransMsg = Imm32HeapAlloc(0, cbTransMsg);
536             if (pNewTransMsg)
537             {
538                 RtlCopyMemory(pNewTransMsg, lpTransMsg, cbTransMsg);
539                 dwCount = ImmNt3Trans(dwCount, pNewTransMsg, hIMC, bAnsi, Lang);
540             }
541             else
542             {
543                 pNewTransMsg = lpTransMsg;
544             }
545         }
546     }
547 #endif
548 
549     /* post them */
550     pItem = pNewTransMsg;
551     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
552     {
553         if (bAnsi)
554             PostMessageA(hwnd, pItem->message, pItem->wParam, pItem->lParam);
555         else
556             PostMessageW(hwnd, pItem->message, pItem->wParam, pItem->lParam);
557     }
558 
559 #ifdef IMM_NT3_SUPPORT
560     if (pNewTransMsg && pNewTransMsg != lpTransMsg)
561         HeapFree(g_hImm32Heap, 0, pNewTransMsg);
562 #endif
563 }
564 
565 /***********************************************************************
566  *       ImmTranslateMessage(IMM32.@)
567  *       ( Undocumented, call internally and from user32.dll )
568  */
569 BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData)
570 {
571 #define MSG_COUNT 0x100
572     BOOL ret = FALSE;
573     INT kret;
574     LPINPUTCONTEXTDX pIC;
575     PIMEDPI pImeDpi = NULL;
576     LPTRANSMSGLIST pList = NULL;
577     LPTRANSMSG pTransMsg;
578     BYTE abKeyState[256];
579     HIMC hIMC;
580     HKL hKL;
581     UINT vk;
582     DWORD dwThreadId, dwCount, cbList;
583     WCHAR wch;
584     WORD wChar;
585 
586     TRACE("(%p, 0x%X, %p, %p)\n", hwnd, msg, wParam, lKeyData);
587 
588     /* filter the message */
589     switch (msg)
590     {
591         case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP:
592             break;
593         default:
594             return FALSE;
595     }
596 
597     hIMC = ImmGetContext(hwnd);
598     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
599     if (pIC == NULL)
600     {
601         ImmReleaseContext(hwnd, hIMC);
602         return FALSE;
603     }
604 
605     if (!pIC->bNeedsTrans) /* is translation needed? */
606     {
607         /* directly post them */
608         dwCount = pIC->dwNumMsgBuf;
609         if (dwCount == 0)
610             goto Quit;
611 
612         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
613         if (pTransMsg)
614         {
615             Imm32PostMessages(hwnd, hIMC, dwCount, pTransMsg);
616             ImmUnlockIMCC(pIC->hMsgBuf);
617             ret = TRUE;
618         }
619         pIC->dwNumMsgBuf = 0; /* done */
620         goto Quit;
621     }
622     pIC->bNeedsTrans = FALSE; /* clear the flag */
623 
624     dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
625     hKL = GetKeyboardLayout(dwThreadId);
626     pImeDpi = ImmLockImeDpi(hKL);
627     if (pImeDpi == NULL)
628         goto Quit;
629 
630     if (!GetKeyboardState(abKeyState)) /* get keyboard ON/OFF status */
631         goto Quit;
632 
633     /* convert a virtual key if IME_PROP_KBD_CHAR_FIRST */
634     vk = pIC->nVKey;
635     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST)
636     {
637         if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
638         {
639             wch = 0;
640             kret = ToUnicode(vk, HIWORD(lKeyData), abKeyState, &wch, 1, 0);
641             if (kret == 1)
642                 vk = MAKELONG(LOBYTE(vk), wch);
643         }
644         else
645         {
646             wChar = 0;
647             kret = ToAsciiEx(vk, HIWORD(lKeyData), abKeyState, &wChar, 0, hKL);
648             if (kret > 0)
649                 vk = MAKEWORD(vk, wChar);
650         }
651     }
652 
653     /* allocate a list */
654     cbList = offsetof(TRANSMSGLIST, TransMsg) + MSG_COUNT * sizeof(TRANSMSG);
655     pList = Imm32HeapAlloc(0, cbList);
656     if (!pList)
657         goto Quit;
658 
659     /* use IME conversion engine and convert the list */
660     pList->uMsgCount = MSG_COUNT;
661     kret = pImeDpi->ImeToAsciiEx(vk, HIWORD(lKeyData), abKeyState, pList, 0, hIMC);
662     if (kret <= 0)
663         goto Quit;
664 
665     /* post them */
666     if (kret <= MSG_COUNT)
667     {
668         Imm32PostMessages(hwnd, hIMC, kret, pList->TransMsg);
669         ret = TRUE;
670     }
671     else
672     {
673         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
674         if (pTransMsg == NULL)
675             goto Quit;
676         Imm32PostMessages(hwnd, hIMC, kret, pTransMsg);
677         ImmUnlockIMCC(pIC->hMsgBuf);
678     }
679 
680 Quit:
681     if (pList)
682         HeapFree(g_hImm32Heap, 0, pList);
683     ImmUnlockImeDpi(pImeDpi);
684     ImmUnlockIMC(hIMC);
685     ImmReleaseContext(hwnd, hIMC);
686     return ret;
687 #undef MSG_COUNT
688 }
689