xref: /reactos/dll/win32/imm32/imm.c (revision ea6e7740)
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 Oleg Dubinskiy <oleg.dubinskij2013@yandex.ua>
10  *              Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
11  */
12 
13 #include <stdarg.h>
14 #include <stdio.h>
15 
16 #define WIN32_NO_STATUS
17 #include <windef.h>
18 #include <winbase.h>
19 #include <wingdi.h>
20 #include <winuser.h>
21 #include <winerror.h>
22 #include <wine/debug.h>
23 #include <imm.h>
24 #include <ddk/imm.h>
25 #include <winnls.h>
26 #include <winreg.h>
27 #include <wine/list.h>
28 #include <stdlib.h>
29 #include <ndk/umtypes.h>
30 #include <ndk/pstypes.h>
31 #include <ndk/rtlfuncs.h>
32 #include "../../../win32ss/include/ntuser.h"
33 #include "../../../win32ss/include/ntwin32.h"
34 #include <undocuser.h>
35 #include <imm32_undoc.h>
36 #include <strsafe.h>
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(imm);
39 
40 #define IMM_INIT_MAGIC 0x19650412
41 #define IMM_INVALID_CANDFORM ULONG_MAX
42 #define INVALID_HOTKEY_ID 0xFFFFFFFF
43 #define MAX_CANDIDATEFORM 4
44 
45 #define LANGID_CHINESE_SIMPLIFIED MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
46 #define LANGID_CHINESE_TRADITIONAL MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
47 #define LANGID_JAPANESE MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)
48 
49 #define REGKEY_KEYBOARD_LAYOUTS \
50     L"System\\CurrentControlSet\\Control\\Keyboard Layouts"
51 #define REGKEY_IMM \
52     L"Software\\Microsoft\\Windows NT\\CurrentVersion\\IMM"
53 
54 #define ROUNDUP4(n) (((n) + 3) & ~3)  /* DWORD alignment */
55 
56 HMODULE g_hImm32Inst = NULL;
57 RTL_CRITICAL_SECTION g_csImeDpi;
58 PIMEDPI g_pImeDpiList = NULL;
59 PSERVERINFO g_psi = NULL;
60 SHAREDINFO g_SharedInfo = { NULL };
61 BYTE g_bClientRegd = FALSE;
62 HANDLE g_hImm32Heap = NULL;
63 
64 static PWND FASTCALL ValidateHwndNoErr(HWND hwnd)
65 {
66     PCLIENTINFO ClientInfo = GetWin32ClientInfo();
67     INT index;
68     PUSER_HANDLE_TABLE ht;
69     WORD generation;
70 
71     /* See if the window is cached */
72     if (hwnd == ClientInfo->CallbackWnd.hWnd)
73         return ClientInfo->CallbackWnd.pWnd;
74 
75     if (!NtUserValidateHandleSecure(hwnd))
76         return NULL;
77 
78     ht = g_SharedInfo.aheList; /* handle table */
79     index = (LOWORD(hwnd) - FIRST_USER_HANDLE) >> 1;
80     if (index < 0 || index >= ht->nb_handles || ht->handles[index].type != TYPE_WINDOW)
81         return NULL;
82 
83     generation = HIWORD(hwnd);
84     if (generation != ht->handles[index].generation && generation && generation != 0xFFFF)
85         return NULL;
86 
87     return (PWND)&ht->handles[index];
88 }
89 
90 static BOOL APIENTRY Imm32InitInstance(HMODULE hMod)
91 {
92     NTSTATUS status;
93 
94     if (hMod)
95         g_hImm32Inst = hMod;
96 
97     if (g_bClientRegd)
98         return TRUE;
99 
100     status = RtlInitializeCriticalSection(&g_csImeDpi);
101     if (NT_ERROR(status))
102         return FALSE;
103 
104     g_bClientRegd = TRUE;
105     return TRUE;
106 }
107 
108 LPVOID APIENTRY Imm32HeapAlloc(DWORD dwFlags, DWORD dwBytes)
109 {
110     if (!g_hImm32Heap)
111     {
112         g_hImm32Heap = RtlGetProcessHeap();
113         if (g_hImm32Heap == NULL)
114             return NULL;
115     }
116     return HeapAlloc(g_hImm32Heap, dwFlags, dwBytes);
117 }
118 
119 static LPWSTR APIENTRY Imm32WideFromAnsi(LPCSTR pszA)
120 {
121     INT cch = lstrlenA(pszA);
122     LPWSTR pszW = Imm32HeapAlloc(0, (cch + 1) * sizeof(WCHAR));
123     if (pszW == NULL)
124         return NULL;
125     cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszA, cch, pszW, cch + 1);
126     pszW[cch] = 0;
127     return pszW;
128 }
129 
130 static LPSTR APIENTRY Imm32AnsiFromWide(LPCWSTR pszW)
131 {
132     INT cchW = lstrlenW(pszW);
133     INT cchA = (cchW + 1) * sizeof(WCHAR);
134     LPSTR pszA = Imm32HeapAlloc(0, cchA);
135     if (!pszA)
136         return NULL;
137     cchA = WideCharToMultiByte(CP_ACP, 0, pszW, cchW, pszA, cchA, NULL, NULL);
138     pszA[cchA] = 0;
139     return pszA;
140 }
141 
142 static inline BOOL Imm32IsCrossThreadAccess(HIMC hIMC)
143 {
144     DWORD dwImeThreadId = NtUserQueryInputContext(hIMC, 1);
145     DWORD dwThreadId = GetCurrentThreadId();
146     return (dwImeThreadId != dwThreadId);
147 }
148 
149 static BOOL Imm32IsCrossProcessAccess(HWND hWnd)
150 {
151     return (NtUserQueryWindow(hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID) !=
152             (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueProcess);
153 }
154 
155 static VOID APIENTRY Imm32FreeImeDpi(PIMEDPI pImeDpi, BOOL bDestroy)
156 {
157     if (pImeDpi->hInst == NULL)
158         return;
159     if (bDestroy)
160         pImeDpi->ImeDestroy(0);
161     FreeLibrary(pImeDpi->hInst);
162     pImeDpi->hInst = NULL;
163 }
164 
165 static BOOL APIENTRY
166 Imm32NotifyAction(HIMC hIMC, HWND hwnd, DWORD dwAction, DWORD_PTR dwIndex, DWORD_PTR dwValue,
167                   DWORD_PTR dwCommand, DWORD_PTR dwData)
168 {
169     DWORD dwLayout;
170     HKL hKL;
171     PIMEDPI pImeDpi;
172 
173     if (dwAction)
174     {
175         dwLayout = NtUserQueryInputContext(hIMC, 1);
176         if (dwLayout)
177         {
178             /* find keyboard layout and lock it */
179             hKL = GetKeyboardLayout(dwLayout);
180             pImeDpi = ImmLockImeDpi(hKL);
181             if (pImeDpi)
182             {
183                 /* do notify */
184                 pImeDpi->NotifyIME(hIMC, dwAction, dwIndex, dwValue);
185 
186                 ImmUnlockImeDpi(pImeDpi); /* unlock */
187             }
188         }
189     }
190 
191     if (hwnd && dwCommand)
192         SendMessageW(hwnd, WM_IME_NOTIFY, dwCommand, dwData);
193 
194     return TRUE;
195 }
196 
197 static PIMEDPI APIENTRY Imm32FindImeDpi(HKL hKL)
198 {
199     PIMEDPI pImeDpi;
200 
201     RtlEnterCriticalSection(&g_csImeDpi);
202     for (pImeDpi = g_pImeDpiList; pImeDpi != NULL; pImeDpi = pImeDpi->pNext)
203     {
204         if (pImeDpi->hKL == hKL)
205             break;
206     }
207     RtlLeaveCriticalSection(&g_csImeDpi);
208 
209     return pImeDpi;
210 }
211 
212 static BOOL Imm32GetSystemLibraryPath(LPWSTR pszPath, DWORD cchPath, LPCWSTR pszFileName)
213 {
214     if (!pszFileName[0] || !GetSystemDirectoryW(pszPath, cchPath))
215         return FALSE;
216     StringCchCatW(pszPath, cchPath, L"\\");
217     StringCchCatW(pszPath, cchPath, pszFileName);
218     return TRUE;
219 }
220 
221 static BOOL APIENTRY Imm32InquireIme(PIMEDPI pImeDpi)
222 {
223     WCHAR szUIClass[64];
224     WNDCLASSW wcW;
225     DWORD dwSysInfoFlags = 0; // TODO: ???
226     LPIMEINFO pImeInfo = &pImeDpi->ImeInfo;
227 
228     // TODO: NtUserGetThreadState(16);
229 
230     if (!IS_IME_HKL(pImeDpi->hKL))
231     {
232         if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED) &&
233             pImeDpi->CtfImeInquireExW)
234         {
235             // TODO:
236             return FALSE;
237         }
238     }
239 
240     if (!pImeDpi->ImeInquire(pImeInfo, szUIClass, dwSysInfoFlags))
241         return FALSE;
242 
243     szUIClass[_countof(szUIClass) - 1] = 0;
244 
245     if (pImeInfo->dwPrivateDataSize == 0)
246         pImeInfo->dwPrivateDataSize = 4;
247 
248 #define VALID_IME_PROP (IME_PROP_AT_CARET              | \
249                         IME_PROP_SPECIAL_UI            | \
250                         IME_PROP_CANDLIST_START_FROM_1 | \
251                         IME_PROP_UNICODE               | \
252                         IME_PROP_COMPLETE_ON_UNSELECT  | \
253                         IME_PROP_END_UNLOAD            | \
254                         IME_PROP_KBD_CHAR_FIRST        | \
255                         IME_PROP_IGNORE_UPKEYS         | \
256                         IME_PROP_NEED_ALTKEY           | \
257                         IME_PROP_NO_KEYS_ON_CLOSE      | \
258                         IME_PROP_ACCEPT_WIDE_VKEY)
259 #define VALID_CMODE_CAPS (IME_CMODE_ALPHANUMERIC | \
260                           IME_CMODE_NATIVE       | \
261                           IME_CMODE_KATAKANA     | \
262                           IME_CMODE_LANGUAGE     | \
263                           IME_CMODE_FULLSHAPE    | \
264                           IME_CMODE_ROMAN        | \
265                           IME_CMODE_CHARCODE     | \
266                           IME_CMODE_HANJACONVERT | \
267                           IME_CMODE_SOFTKBD      | \
268                           IME_CMODE_NOCONVERSION | \
269                           IME_CMODE_EUDC         | \
270                           IME_CMODE_SYMBOL       | \
271                           IME_CMODE_FIXED)
272 #define VALID_SMODE_CAPS (IME_SMODE_NONE          | \
273                           IME_SMODE_PLAURALCLAUSE | \
274                           IME_SMODE_SINGLECONVERT | \
275                           IME_SMODE_AUTOMATIC     | \
276                           IME_SMODE_PHRASEPREDICT | \
277                           IME_SMODE_CONVERSATION)
278 #define VALID_UI_CAPS (UI_CAP_2700    | \
279                        UI_CAP_ROT90   | \
280                        UI_CAP_ROTANY  | \
281                        UI_CAP_SOFTKBD)
282 #define VALID_SCS_CAPS (SCS_CAP_COMPSTR            | \
283                         SCS_CAP_MAKEREAD           | \
284                         SCS_CAP_SETRECONVERTSTRING)
285 #define VALID_SELECT_CAPS (SELECT_CAP_CONVERSION | SELECT_CAP_SENTENCE)
286 
287     if (pImeInfo->fdwProperty & ~VALID_IME_PROP)
288         return FALSE;
289     if (pImeInfo->fdwConversionCaps & ~VALID_CMODE_CAPS)
290         return FALSE;
291     if (pImeInfo->fdwSentenceCaps & ~VALID_SMODE_CAPS)
292         return FALSE;
293     if (pImeInfo->fdwUICaps & ~VALID_UI_CAPS)
294         return FALSE;
295     if (pImeInfo->fdwSCSCaps & ~VALID_SCS_CAPS)
296         return FALSE;
297     if (pImeInfo->fdwSelectCaps & ~VALID_SELECT_CAPS)
298         return FALSE;
299 
300 #undef VALID_IME_PROP
301 #undef VALID_CMODE_CAPS
302 #undef VALID_SMODE_CAPS
303 #undef VALID_UI_CAPS
304 #undef VALID_SCS_CAPS
305 #undef VALID_SELECT_CAPS
306 
307     if (pImeInfo->fdwProperty & IME_PROP_UNICODE)
308     {
309         StringCchCopyW(pImeDpi->szUIClass, _countof(pImeDpi->szUIClass), szUIClass);
310     }
311     else
312     {
313         if (pImeDpi->uCodePage != GetACP() && pImeDpi->uCodePage)
314             return FALSE;
315 
316         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPSTR)szUIClass, -1,
317                             pImeDpi->szUIClass, _countof(pImeDpi->szUIClass));
318     }
319 
320     return GetClassInfoW(pImeDpi->hInst, pImeDpi->szUIClass, &wcW);
321 }
322 
323 static BOOL APIENTRY Imm32LoadImeInfo(PIMEINFOEX pImeInfoEx, PIMEDPI pImeDpi)
324 {
325     WCHAR szPath[MAX_PATH];
326     HINSTANCE hIME;
327     FARPROC fn;
328 
329     if (!Imm32GetSystemLibraryPath(szPath, _countof(szPath), pImeInfoEx->wszImeFile))
330         return FALSE;
331 
332     hIME = GetModuleHandleW(szPath);
333     if (hIME == NULL)
334     {
335         hIME = LoadLibraryW(szPath);
336         if (hIME == NULL)
337         {
338             ERR("Imm32LoadImeInfo: LoadLibraryW(%S) failed\n", szPath);
339             return FALSE;
340         }
341     }
342     pImeDpi->hInst = hIME;
343 
344 #define DEFINE_IME_ENTRY(type, name, params, extended) \
345     do { \
346         fn = GetProcAddress(hIME, #name); \
347         if (fn) pImeDpi->name = (FN_##name)fn; \
348         else if (!extended) goto Failed; \
349     } while (0);
350 #include "../../../win32ss/include/imetable.h"
351 #undef DEFINE_IME_ENTRY
352 
353     if (!Imm32InquireIme(pImeDpi))
354     {
355         ERR("Imm32LoadImeInfo: Imm32InquireIme failed\n");
356         goto Failed;
357     }
358 
359     if (pImeInfoEx->fLoadFlag)
360         return TRUE;
361 
362     NtUserSetImeOwnerWindow(pImeInfoEx, TRUE);
363     return TRUE;
364 
365 Failed:
366     FreeLibrary(pImeDpi->hInst);
367     pImeDpi->hInst = NULL;
368     return FALSE;
369 }
370 
371 #ifdef IMP_SUPPORT /* 3.x support */
372 static DWORD APIENTRY
373 ImpJTransCompA(LPINPUTCONTEXTDX pIC, LPCOMPOSITIONSTRING pCS,
374                const TRANSMSG *pSrc, LPTRANSMSG pDest)
375 {
376     // FIXME
377     *pDest = *pSrc;
378     return 1;
379 }
380 
381 static DWORD APIENTRY
382 ImpJTransCompW(LPINPUTCONTEXTDX pIC, LPCOMPOSITIONSTRING pCS,
383                const TRANSMSG *pSrc, LPTRANSMSG pDest)
384 {
385     // FIXME
386     *pDest = *pSrc;
387     return 1;
388 }
389 
390 typedef LRESULT (WINAPI *FN_SendMessage)(HWND, UINT, WPARAM, LPARAM);
391 
392 static DWORD APIENTRY
393 ImpJTrans(DWORD dwCount, LPTRANSMSG pTrans, LPINPUTCONTEXTDX pIC,
394           LPCOMPOSITIONSTRING pCS, BOOL bAnsi)
395 {
396     DWORD ret = 0;
397     HWND hWnd, hwndDefIME;
398     LPTRANSMSG pTempList, pEntry, pNext;
399     DWORD dwIndex, iCandForm, dwNumber, cbTempList;
400     HGLOBAL hGlobal;
401     CANDIDATEFORM CandForm;
402     FN_SendMessage pSendMessage;
403 
404     hWnd = pIC->hWnd;
405     hwndDefIME = ImmGetDefaultIMEWnd(hWnd);
406     pSendMessage = (IsWindowUnicode(hWnd) ? SendMessageW : SendMessageA);
407 
408     // clone the message list
409     cbTempList = (dwCount + 1) * sizeof(TRANSMSG);
410     pTempList = Imm32HeapAlloc(HEAP_ZERO_MEMORY, cbTempList);
411     if (pTempList == NULL)
412         return 0;
413     RtlCopyMemory(pTempList, pTrans, dwCount * sizeof(TRANSMSG));
414 
415     if (pIC->dwUIFlags & 0x2)
416     {
417         // find WM_IME_ENDCOMPOSITION
418         pEntry = pTempList;
419         for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pEntry)
420         {
421             if (pEntry->message == WM_IME_ENDCOMPOSITION)
422                 break;
423         }
424 
425         if (pEntry->message == WM_IME_ENDCOMPOSITION) // if found
426         {
427             // move WM_IME_ENDCOMPOSITION to the end of the list
428             for (pNext = pEntry + 1; pNext->message != 0; ++pEntry, ++pNext)
429                 *pEntry = *pNext;
430 
431             pEntry->message = WM_IME_ENDCOMPOSITION;
432             pEntry->wParam = 0;
433             pEntry->lParam = 0;
434         }
435     }
436 
437     for (pEntry = pTempList; pEntry->message != 0; ++pEntry)
438     {
439         switch (pEntry->message)
440         {
441             case WM_IME_STARTCOMPOSITION:
442                 if (!(pIC->dwUIFlags & 0x2))
443                 {
444                     // send IR_OPENCONVERT
445                     if (pIC->cfCompForm.dwStyle != CFS_DEFAULT)
446                         pSendMessage(hWnd, WM_IME_REPORT, IR_OPENCONVERT, 0);
447 
448                     goto DoDefault;
449                 }
450                 break;
451 
452             case WM_IME_ENDCOMPOSITION:
453                 if (pIC->dwUIFlags & 0x2)
454                 {
455                     // send IR_UNDETERMINE
456                     hGlobal = GlobalAlloc(GHND | GMEM_SHARE, sizeof(UNDETERMINESTRUCT));
457                     if (hGlobal)
458                     {
459                         pSendMessage(hWnd, WM_IME_REPORT, IR_UNDETERMINE, (LPARAM)hGlobal);
460                         GlobalFree(hGlobal);
461                     }
462                 }
463                 else
464                 {
465                     // send IR_CLOSECONVERT
466                     if (pIC->cfCompForm.dwStyle != CFS_DEFAULT)
467                         pSendMessage(hWnd, WM_IME_REPORT, IR_CLOSECONVERT, 0);
468 
469                     goto DoDefault;
470                 }
471                 break;
472 
473             case WM_IME_COMPOSITION:
474                 if (bAnsi)
475                     dwNumber = ImpJTransCompA(pIC, pCS, pEntry, pTrans);
476                 else
477                     dwNumber = ImpJTransCompW(pIC, pCS, pEntry, pTrans);
478 
479                 ret += dwNumber;
480                 pTrans += dwNumber;
481 
482                 // send IR_CHANGECONVERT
483                 if (!(pIC->dwUIFlags & 0x2))
484                 {
485                     if (pIC->cfCompForm.dwStyle != CFS_DEFAULT)
486                         pSendMessage(hWnd, WM_IME_REPORT, IR_CHANGECONVERT, 0);
487                 }
488                 break;
489 
490             case WM_IME_NOTIFY:
491                 if (pEntry->wParam == IMN_OPENCANDIDATE)
492                 {
493                     if (IsWindow(hWnd) && (pIC->dwUIFlags & 0x2))
494                     {
495                         // send IMC_SETCANDIDATEPOS
496                         for (iCandForm = 0; iCandForm < MAX_CANDIDATEFORM; ++iCandForm)
497                         {
498                             if (!(pEntry->lParam & (1 << iCandForm)))
499                                 continue;
500 
501                             CandForm.dwIndex = iCandForm;
502                             CandForm.dwStyle = CFS_EXCLUDE;
503                             CandForm.ptCurrentPos = pIC->cfCompForm.ptCurrentPos;
504                             CandForm.rcArea = pIC->cfCompForm.rcArea;
505                             pSendMessage(hwndDefIME, WM_IME_CONTROL, IMC_SETCANDIDATEPOS,
506                                          (LPARAM)&CandForm);
507                         }
508                     }
509                 }
510 
511                 if (!(pIC->dwUIFlags & 0x2))
512                     goto DoDefault;
513 
514                 // send a WM_IME_NOTIFY notification to the default ime window
515                 pSendMessage(hwndDefIME, pEntry->message, pEntry->wParam, pEntry->lParam);
516                 break;
517 
518 DoDefault:
519             default:
520                 // default processing
521                 *pTrans++ = *pEntry;
522                 ++ret;
523                 break;
524         }
525     }
526 
527     HeapFree(g_hImm32Heap, 0, pTempList);
528     return ret;
529 }
530 
531 static DWORD APIENTRY
532 ImpKTrans(DWORD dwCount, LPTRANSMSG pEntries, LPINPUTCONTEXTDX pIC,
533           LPCOMPOSITIONSTRING pCS, BOOL bAnsi)
534 {
535     return dwCount; // FIXME
536 }
537 
538 static DWORD APIENTRY
539 ImpTrans(DWORD dwCount, LPTRANSMSG pEntries, HIMC hIMC, BOOL bAnsi, WORD wLang)
540 {
541     BOOL ret = FALSE;
542     LPINPUTCONTEXTDX pIC;
543     LPCOMPOSITIONSTRING pCS;
544 
545     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
546     if (pIC == NULL)
547         return 0;
548 
549     pCS = ImmLockIMCC(pIC->hCompStr);
550     if (pCS)
551     {
552         if (wLang == LANG_JAPANESE)
553             ret = ImpJTrans(dwCount, pEntries, pIC, pCS, bAnsi);
554         else if (wLang == LANG_KOREAN)
555             ret = ImpKTrans(dwCount, pEntries, pIC, pCS, bAnsi);
556         ImmUnlockIMCC(pIC->hCompStr);
557     }
558 
559     ImmUnlockIMC(hIMC);
560     return ret;
561 }
562 #endif  /* def IMP_SUPPORT */
563 
564 static PIMEDPI APIENTRY Ime32LoadImeDpi(HKL hKL, BOOL bLock)
565 {
566     IMEINFOEX ImeInfoEx;
567     CHARSETINFO ci;
568     PIMEDPI pImeDpiNew, pImeDpiFound;
569     UINT uCodePage;
570     LCID lcid;
571 
572     if (!ImmGetImeInfoEx(&ImeInfoEx, ImeInfoExKeyboardLayout, &hKL) ||
573         ImeInfoEx.fLoadFlag == 1)
574     {
575         return NULL;
576     }
577 
578     pImeDpiNew = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(IMEDPI));
579     if (pImeDpiNew == NULL)
580         return NULL;
581 
582     pImeDpiNew->hKL = hKL;
583 
584     lcid = LOWORD(hKL);
585     if (TranslateCharsetInfo((LPDWORD)(DWORD_PTR)lcid, &ci, TCI_SRCLOCALE))
586         uCodePage = ci.ciACP;
587     else
588         uCodePage = CP_ACP;
589     pImeDpiNew->uCodePage = uCodePage;
590 
591     if (!Imm32LoadImeInfo(&ImeInfoEx, pImeDpiNew))
592     {
593         HeapFree(g_hImm32Heap, 0, pImeDpiNew);
594         return FALSE;
595     }
596 
597     RtlEnterCriticalSection(&g_csImeDpi);
598 
599     pImeDpiFound = Imm32FindImeDpi(hKL);
600     if (pImeDpiFound)
601     {
602         if (!bLock)
603             pImeDpiFound->dwFlags &= ~IMEDPI_FLAG_LOCKED;
604 
605         RtlLeaveCriticalSection(&g_csImeDpi);
606 
607         Imm32FreeImeDpi(pImeDpiNew, FALSE);
608         HeapFree(g_hImm32Heap, 0, pImeDpiNew);
609         return pImeDpiFound;
610     }
611     else
612     {
613         if (bLock)
614         {
615             pImeDpiNew->dwFlags |= IMEDPI_FLAG_LOCKED;
616             pImeDpiNew->cLockObj = 1;
617         }
618 
619         pImeDpiNew->pNext = g_pImeDpiList;
620         g_pImeDpiList = pImeDpiNew;
621 
622         RtlLeaveCriticalSection(&g_csImeDpi);
623         return pImeDpiNew;
624     }
625 }
626 
627 /***********************************************************************
628  *		ImmLoadIME (IMM32.@)
629  */
630 BOOL WINAPI ImmLoadIME(HKL hKL)
631 {
632     PW32CLIENTINFO pInfo;
633     PIMEDPI pImeDpi;
634 
635     if (!IS_IME_HKL(hKL))
636     {
637         if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
638             return FALSE;
639 
640         pInfo = (PW32CLIENTINFO)(NtCurrentTeb()->Win32ClientInfo);
641         if ((pInfo->W32ClientInfo[0] & 2))
642             return FALSE;
643     }
644 
645     pImeDpi = Imm32FindImeDpi(hKL);
646     if (pImeDpi == NULL)
647         pImeDpi = Ime32LoadImeDpi(hKL, FALSE);
648     return (pImeDpi != NULL);
649 }
650 
651 PIMEDPI APIENTRY ImmLockOrLoadImeDpi(HKL hKL)
652 {
653     PW32CLIENTINFO pInfo;
654     PIMEDPI pImeDpi;
655 
656     if (!IS_IME_HKL(hKL))
657     {
658         if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
659             return NULL;
660 
661         pInfo = (PW32CLIENTINFO)(NtCurrentTeb()->Win32ClientInfo);
662         if ((pInfo->W32ClientInfo[0] & 2))
663             return NULL;
664     }
665 
666     pImeDpi = ImmLockImeDpi(hKL);
667     if (pImeDpi == NULL)
668         pImeDpi = Ime32LoadImeDpi(hKL, TRUE);
669     return pImeDpi;
670 }
671 
672 /***********************************************************************
673  *		ImmLoadLayout (IMM32.@)
674  */
675 HKL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
676 {
677     DWORD cbData;
678     UNICODE_STRING UnicodeString;
679     HKEY hLayoutKey = NULL, hLayoutsKey = NULL;
680     LONG error;
681     NTSTATUS Status;
682     WCHAR szLayout[MAX_PATH];
683 
684     TRACE("(%p, %p)\n", hKL, pImeInfoEx);
685 
686     if (IS_IME_HKL(hKL) ||
687         !g_psi || !(g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED) ||
688         ((PW32CLIENTINFO)NtCurrentTeb()->Win32ClientInfo)->W32ClientInfo[0] & 2)
689     {
690         UnicodeString.Buffer = szLayout;
691         UnicodeString.MaximumLength = sizeof(szLayout);
692         Status = RtlIntegerToUnicodeString((DWORD_PTR)hKL, 16, &UnicodeString);
693         if (!NT_SUCCESS(Status))
694             return NULL;
695 
696         error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hLayoutsKey);
697         if (error)
698             return NULL;
699 
700         error = RegOpenKeyW(hLayoutsKey, szLayout, &hLayoutKey);
701     }
702     else
703     {
704         error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_IMM, &hLayoutKey);
705     }
706 
707     if (error)
708     {
709         ERR("RegOpenKeyW error: 0x%08lX\n", error);
710         hKL = NULL;
711     }
712     else
713     {
714         cbData = sizeof(pImeInfoEx->wszImeFile);
715         error = RegQueryValueExW(hLayoutKey, L"Ime File", 0, 0,
716                                  (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
717         if (error)
718             hKL = NULL;
719     }
720 
721     RegCloseKey(hLayoutKey);
722     if (hLayoutsKey)
723         RegCloseKey(hLayoutsKey);
724     return hKL;
725 }
726 
727 typedef struct _tagImmHkl{
728     struct list entry;
729     HKL         hkl;
730     HMODULE     hIME;
731     IMEINFO     imeInfo;
732     WCHAR       imeClassName[17]; /* 16 character max */
733     ULONG       uSelected;
734     HWND        UIWnd;
735 
736     /* Function Pointers */
737     BOOL (WINAPI *pImeInquire)(IMEINFO *, WCHAR *, const WCHAR *);
738     BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *);
739     BOOL (WINAPI *pImeDestroy)(UINT);
740     LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *);
741     BOOL (WINAPI *pImeSelect)(HIMC, BOOL);
742     BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL);
743     UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, DWORD *, UINT, HIMC);
744     BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD);
745     BOOL (WINAPI *pImeRegisterWord)(const WCHAR *, DWORD, const WCHAR *);
746     BOOL (WINAPI *pImeUnregisterWord)(const WCHAR *, DWORD, const WCHAR *);
747     UINT (WINAPI *pImeEnumRegisterWord)(REGISTERWORDENUMPROCW, const WCHAR *, DWORD, const WCHAR *, void *);
748     BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void *, DWORD, const void *, DWORD);
749     DWORD (WINAPI *pImeConversionList)(HIMC, const WCHAR *, CANDIDATELIST *, DWORD, UINT);
750     BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *);
751     UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *);
752     DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD);
753 } ImmHkl;
754 
755 typedef struct tagInputContextData
756 {
757         DWORD           dwLock;
758         INPUTCONTEXT    IMC;
759         DWORD           threadID;
760 
761         ImmHkl          *immKbd;
762         UINT            lastVK;
763         BOOL            threadDefault;
764         DWORD           magic;
765 } InputContextData;
766 
767 #define WINE_IMC_VALID_MAGIC 0x56434D49
768 
769 typedef struct _tagIMMThreadData {
770     struct list entry;
771     DWORD threadID;
772     HIMC defaultContext;
773     HWND hwndDefault;
774     BOOL disableIME;
775     DWORD windowRefs;
776 } IMMThreadData;
777 
778 static struct list ImmHklList = LIST_INIT(ImmHklList);
779 static struct list ImmThreadDataList = LIST_INIT(ImmThreadDataList);
780 
781 static const WCHAR szwWineIMCProperty[] = {'W','i','n','e','I','m','m','H','I','M','C','P','r','o','p','e','r','t','y',0};
782 
783 static const WCHAR szImeFileW[] = {'I','m','e',' ','F','i','l','e',0};
784 static const WCHAR szLayoutTextW[] = {'L','a','y','o','u','t',' ','T','e','x','t',0};
785 static const WCHAR szImeRegFmt[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s','\\','%','0','8','l','x',0};
786 
787 static const WCHAR szwIME[] = {'I','M','E',0};
788 static const WCHAR szwDefaultIME[] = {'D','e','f','a','u','l','t',' ','I','M','E',0};
789 
790 static CRITICAL_SECTION threaddata_cs;
791 static CRITICAL_SECTION_DEBUG critsect_debug =
792 {
793     0, 0, &threaddata_cs,
794     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
795       0, 0, { (DWORD_PTR)(__FILE__ ": threaddata_cs") }
796 };
797 static CRITICAL_SECTION threaddata_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
798 
799 static inline BOOL is_himc_ime_unicode(const InputContextData *data)
800 {
801     return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE);
802 }
803 
804 static inline BOOL is_kbd_ime_unicode(const ImmHkl *hkl)
805 {
806     return !!(hkl->imeInfo.fdwProperty & IME_PROP_UNICODE);
807 }
808 
809 static InputContextData* get_imc_data(HIMC hIMC);
810 
811 static inline WCHAR *strdupAtoW( const char *str )
812 {
813     WCHAR *ret = NULL;
814     if (str)
815     {
816         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
817         if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
818             MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
819     }
820     return ret;
821 }
822 
823 static inline CHAR *strdupWtoA( const WCHAR *str )
824 {
825     CHAR *ret = NULL;
826     if (str)
827     {
828         DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
829         if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
830             WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
831     }
832     return ret;
833 }
834 
835 static HMODULE load_graphics_driver(void)
836 {
837     static const WCHAR display_device_guid_propW[] = {
838         '_','_','w','i','n','e','_','d','i','s','p','l','a','y','_',
839         'd','e','v','i','c','e','_','g','u','i','d',0 };
840     static const WCHAR key_pathW[] = {
841         'S','y','s','t','e','m','\\',
842         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
843         'C','o','n','t','r','o','l','\\',
844         'V','i','d','e','o','\\','{',0};
845     static const WCHAR displayW[] = {'}','\\','0','0','0','0',0};
846     static const WCHAR driverW[] = {'G','r','a','p','h','i','c','s','D','r','i','v','e','r',0};
847 
848     HMODULE ret = 0;
849     HKEY hkey;
850     DWORD size;
851     WCHAR path[MAX_PATH];
852     WCHAR key[ARRAY_SIZE( key_pathW ) + ARRAY_SIZE( displayW ) + 40];
853     UINT guid_atom = HandleToULong( GetPropW( GetDesktopWindow(), display_device_guid_propW ));
854 
855     if (!guid_atom) return 0;
856     memcpy( key, key_pathW, sizeof(key_pathW) );
857     if (!GlobalGetAtomNameW( guid_atom, key + lstrlenW(key), 40 )) return 0;
858     lstrcatW( key, displayW );
859     if (RegOpenKeyW( HKEY_LOCAL_MACHINE, key, &hkey )) return 0;
860     size = sizeof(path);
861     if (!RegQueryValueExW( hkey, driverW, NULL, NULL, (BYTE *)path, &size )) ret = LoadLibraryW( path );
862     RegCloseKey( hkey );
863     TRACE( "%s %p\n", debugstr_w(path), ret );
864     return ret;
865 }
866 
867 /* ImmHkl loading and freeing */
868 #define LOAD_FUNCPTR(f) if((ptr->p##f = (LPVOID)GetProcAddress(ptr->hIME, #f)) == NULL){WARN("Can't find function %s in ime\n", #f);}
869 static ImmHkl *IMM_GetImmHkl(HKL hkl)
870 {
871     ImmHkl *ptr;
872     WCHAR filename[MAX_PATH];
873 
874     TRACE("Seeking ime for keyboard %p\n",hkl);
875 
876     LIST_FOR_EACH_ENTRY(ptr, &ImmHklList, ImmHkl, entry)
877     {
878         if (ptr->hkl == hkl)
879             return ptr;
880     }
881     /* not found... create it */
882 
883     ptr = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ImmHkl));
884 
885     ptr->hkl = hkl;
886     if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename);
887     if (!ptr->hIME) ptr->hIME = load_graphics_driver();
888     if (ptr->hIME)
889     {
890         LOAD_FUNCPTR(ImeInquire);
891         if (!ptr->pImeInquire || !ptr->pImeInquire(&ptr->imeInfo, ptr->imeClassName, NULL))
892         {
893             FreeLibrary(ptr->hIME);
894             ptr->hIME = NULL;
895         }
896         else
897         {
898             LOAD_FUNCPTR(ImeDestroy);
899             LOAD_FUNCPTR(ImeSelect);
900             if (!ptr->pImeSelect || !ptr->pImeDestroy)
901             {
902                 FreeLibrary(ptr->hIME);
903                 ptr->hIME = NULL;
904             }
905             else
906             {
907                 LOAD_FUNCPTR(ImeConfigure);
908                 LOAD_FUNCPTR(ImeEscape);
909                 LOAD_FUNCPTR(ImeSetActiveContext);
910                 LOAD_FUNCPTR(ImeToAsciiEx);
911                 LOAD_FUNCPTR(NotifyIME);
912                 LOAD_FUNCPTR(ImeRegisterWord);
913                 LOAD_FUNCPTR(ImeUnregisterWord);
914                 LOAD_FUNCPTR(ImeEnumRegisterWord);
915                 LOAD_FUNCPTR(ImeSetCompositionString);
916                 LOAD_FUNCPTR(ImeConversionList);
917                 LOAD_FUNCPTR(ImeProcessKey);
918                 LOAD_FUNCPTR(ImeGetRegisterWordStyle);
919                 LOAD_FUNCPTR(ImeGetImeMenuItems);
920                 /* make sure our classname is WCHAR */
921                 if (!is_kbd_ime_unicode(ptr))
922                 {
923                     WCHAR bufW[17];
924                     MultiByteToWideChar(CP_ACP, 0, (LPSTR)ptr->imeClassName,
925                                         -1, bufW, 17);
926                     lstrcpyW(ptr->imeClassName, bufW);
927                 }
928             }
929         }
930     }
931     list_add_head(&ImmHklList,&ptr->entry);
932 
933     return ptr;
934 }
935 #undef LOAD_FUNCPTR
936 
937 static InputContextData* get_imc_data(HIMC hIMC)
938 {
939     InputContextData *data = (InputContextData *)hIMC;
940 
941     if (hIMC == NULL)
942         return NULL;
943 
944     if(IsBadReadPtr(data, sizeof(InputContextData)) || data->magic != WINE_IMC_VALID_MAGIC)
945     {
946         SetLastError(ERROR_INVALID_HANDLE);
947         return NULL;
948     }
949     return data;
950 }
951 
952 static HIMC get_default_context( HWND hwnd )
953 {
954     FIXME("Don't use this function\n");
955     return FALSE;
956 }
957 
958 static BOOL IMM_IsCrossThreadAccess(HWND hWnd,  HIMC hIMC)
959 {
960     InputContextData *data;
961 
962     if (hWnd)
963     {
964         DWORD thread = GetWindowThreadProcessId(hWnd, NULL);
965         if (thread != GetCurrentThreadId()) return TRUE;
966     }
967     data = get_imc_data(hIMC);
968     if (data && data->threadID != GetCurrentThreadId())
969         return TRUE;
970 
971     return FALSE;
972 }
973 
974 /***********************************************************************
975  *		ImmAssociateContext (IMM32.@)
976  */
977 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
978 {
979     HIMC old = NULL;
980     InputContextData *data = get_imc_data(hIMC);
981 
982     TRACE("(%p, %p):\n", hWnd, hIMC);
983 
984     if(hIMC && !data)
985         return NULL;
986 
987     /*
988      * If already associated just return
989      */
990     if (hIMC && data->IMC.hWnd == hWnd)
991         return hIMC;
992 
993     if (hIMC && IMM_IsCrossThreadAccess(hWnd, hIMC))
994         return NULL;
995 
996     if (hWnd)
997     {
998         HIMC defaultContext = get_default_context( hWnd );
999         old = RemovePropW(hWnd,szwWineIMCProperty);
1000 
1001         if (old == NULL)
1002             old = defaultContext;
1003         else if (old == (HIMC)-1)
1004             old = NULL;
1005 
1006         if (hIMC != defaultContext)
1007         {
1008             if (hIMC == NULL) /* Meaning disable imm for that window*/
1009                 SetPropW(hWnd,szwWineIMCProperty,(HANDLE)-1);
1010             else
1011                 SetPropW(hWnd,szwWineIMCProperty,hIMC);
1012         }
1013 
1014         if (old)
1015         {
1016             InputContextData *old_data = (InputContextData *)old;
1017             if (old_data->IMC.hWnd == hWnd)
1018                 old_data->IMC.hWnd = NULL;
1019         }
1020     }
1021 
1022     if (!hIMC)
1023         return old;
1024 
1025     if(GetActiveWindow() == data->IMC.hWnd)
1026     {
1027         SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, FALSE, ISC_SHOWUIALL);
1028         data->IMC.hWnd = hWnd;
1029         SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, TRUE, ISC_SHOWUIALL);
1030     }
1031 
1032     return old;
1033 }
1034 
1035 
1036 /*
1037  * Helper function for ImmAssociateContextEx
1038  */
1039 static BOOL CALLBACK _ImmAssociateContextExEnumProc(HWND hwnd, LPARAM lParam)
1040 {
1041     HIMC hImc = (HIMC)lParam;
1042     ImmAssociateContext(hwnd,hImc);
1043     return TRUE;
1044 }
1045 
1046 /***********************************************************************
1047  *              ImmAssociateContextEx (IMM32.@)
1048  */
1049 BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
1050 {
1051     TRACE("(%p, %p, 0x%x):\n", hWnd, hIMC, dwFlags);
1052 
1053     if (!hWnd)
1054         return FALSE;
1055 
1056     switch (dwFlags)
1057     {
1058     case 0:
1059         ImmAssociateContext(hWnd,hIMC);
1060         return TRUE;
1061     case IACE_DEFAULT:
1062     {
1063         HIMC defaultContext = get_default_context( hWnd );
1064         if (!defaultContext) return FALSE;
1065         ImmAssociateContext(hWnd,defaultContext);
1066         return TRUE;
1067     }
1068     case IACE_IGNORENOCONTEXT:
1069         if (GetPropW(hWnd,szwWineIMCProperty))
1070             ImmAssociateContext(hWnd,hIMC);
1071         return TRUE;
1072     case IACE_CHILDREN:
1073         EnumChildWindows(hWnd,_ImmAssociateContextExEnumProc,(LPARAM)hIMC);
1074         return TRUE;
1075     default:
1076         FIXME("Unknown dwFlags 0x%x\n",dwFlags);
1077         return FALSE;
1078     }
1079 }
1080 
1081 /***********************************************************************
1082  *		ImmConfigureIMEA (IMM32.@)
1083  */
1084 BOOL WINAPI ImmConfigureIMEA(
1085   HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
1086 {
1087     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
1088 
1089     TRACE("(%p, %p, %d, %p):\n", hKL, hWnd, dwMode, lpData);
1090 
1091     if (dwMode == IME_CONFIG_REGISTERWORD && !lpData)
1092         return FALSE;
1093 
1094     if (immHkl->hIME && immHkl->pImeConfigure)
1095     {
1096         if (dwMode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode(immHkl))
1097             return immHkl->pImeConfigure(hKL,hWnd,dwMode,lpData);
1098         else
1099         {
1100             REGISTERWORDW rww;
1101             REGISTERWORDA *rwa = lpData;
1102             BOOL rc;
1103 
1104             rww.lpReading = strdupAtoW(rwa->lpReading);
1105             rww.lpWord = strdupAtoW(rwa->lpWord);
1106             rc = immHkl->pImeConfigure(hKL,hWnd,dwMode,&rww);
1107             HeapFree(GetProcessHeap(),0,rww.lpReading);
1108             HeapFree(GetProcessHeap(),0,rww.lpWord);
1109             return rc;
1110         }
1111     }
1112     else
1113         return FALSE;
1114 }
1115 
1116 /***********************************************************************
1117  *		ImmConfigureIMEW (IMM32.@)
1118  */
1119 BOOL WINAPI ImmConfigureIMEW(
1120   HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
1121 {
1122     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
1123 
1124     TRACE("(%p, %p, %d, %p):\n", hKL, hWnd, dwMode, lpData);
1125 
1126     if (dwMode == IME_CONFIG_REGISTERWORD && !lpData)
1127         return FALSE;
1128 
1129     if (immHkl->hIME && immHkl->pImeConfigure)
1130     {
1131         if (dwMode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode(immHkl))
1132             return immHkl->pImeConfigure(hKL,hWnd,dwMode,lpData);
1133         else
1134         {
1135             REGISTERWORDW *rww = lpData;
1136             REGISTERWORDA rwa;
1137             BOOL rc;
1138 
1139             rwa.lpReading = strdupWtoA(rww->lpReading);
1140             rwa.lpWord = strdupWtoA(rww->lpWord);
1141             rc = immHkl->pImeConfigure(hKL,hWnd,dwMode,&rwa);
1142             HeapFree(GetProcessHeap(),0,rwa.lpReading);
1143             HeapFree(GetProcessHeap(),0,rwa.lpWord);
1144             return rc;
1145         }
1146     }
1147     else
1148         return FALSE;
1149 }
1150 
1151 /***********************************************************************
1152  *		ImmCreateContext (IMM32.@)
1153  */
1154 HIMC WINAPI ImmCreateContext(void)
1155 {
1156     PCLIENTIMC pClientImc;
1157     HIMC hIMC;
1158 
1159     TRACE("()\n");
1160 
1161     if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
1162         return NULL;
1163 
1164     pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
1165     if (pClientImc == NULL)
1166         return NULL;
1167 
1168     hIMC = NtUserCreateInputContext(pClientImc);
1169     if (hIMC == NULL)
1170     {
1171         HeapFree(g_hImm32Heap, 0, pClientImc);
1172         return NULL;
1173     }
1174 
1175     RtlInitializeCriticalSection(&pClientImc->cs);
1176 
1177     // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
1178     pClientImc->unknown = NtUserGetThreadState(13);
1179 
1180     return hIMC;
1181 }
1182 
1183 static VOID APIENTRY Imm32CleanupContextExtra(LPINPUTCONTEXT pIC)
1184 {
1185     FIXME("We have to do something do here");
1186 }
1187 
1188 static PCLIENTIMC APIENTRY Imm32FindClientImc(HIMC hIMC)
1189 {
1190     // FIXME
1191     return NULL;
1192 }
1193 
1194 BOOL APIENTRY Imm32CleanupContext(HIMC hIMC, HKL hKL, BOOL bKeep)
1195 {
1196     PIMEDPI pImeDpi;
1197     LPINPUTCONTEXT pIC;
1198     PCLIENTIMC pClientImc;
1199 
1200     if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32) || hIMC == NULL)
1201         return FALSE;
1202 
1203     FIXME("We have do something to do here\n");
1204     pClientImc = Imm32FindClientImc(hIMC);
1205     if (!pClientImc)
1206         return FALSE;
1207 
1208     if (pClientImc->hImc == NULL)
1209     {
1210         pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
1211         ImmUnlockClientImc(pClientImc);
1212         if (!bKeep)
1213             return NtUserDestroyInputContext(hIMC);
1214         return TRUE;
1215     }
1216 
1217     pIC = ImmLockIMC(hIMC);
1218     if (pIC == NULL)
1219     {
1220         ImmUnlockClientImc(pClientImc);
1221         return FALSE;
1222     }
1223 
1224     FIXME("We have do something to do here\n");
1225 
1226     if (pClientImc->hKL == hKL)
1227     {
1228         pImeDpi = ImmLockImeDpi(hKL);
1229         if (pImeDpi != NULL)
1230         {
1231             if (IS_IME_HKL(hKL))
1232             {
1233                 pImeDpi->ImeSelect(hIMC, FALSE);
1234             }
1235             else if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
1236             {
1237                 FIXME("We have do something to do here\n");
1238             }
1239             ImmUnlockImeDpi(pImeDpi);
1240         }
1241         pClientImc->hKL = NULL;
1242     }
1243 
1244     ImmDestroyIMCC(pIC->hPrivate);
1245     ImmDestroyIMCC(pIC->hMsgBuf);
1246     ImmDestroyIMCC(pIC->hGuideLine);
1247     ImmDestroyIMCC(pIC->hCandInfo);
1248     ImmDestroyIMCC(pIC->hCompStr);
1249 
1250     Imm32CleanupContextExtra(pIC);
1251 
1252     ImmUnlockIMC(hIMC);
1253 
1254     pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
1255     ImmUnlockClientImc(pClientImc);
1256 
1257     if (!bKeep)
1258         return NtUserDestroyInputContext(hIMC);
1259 
1260     return TRUE;
1261 }
1262 
1263 /***********************************************************************
1264  *		ImmDestroyContext (IMM32.@)
1265  */
1266 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
1267 {
1268     HKL hKL;
1269 
1270     TRACE("(%p)\n", hIMC);
1271 
1272     if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
1273         return FALSE;
1274 
1275     if (Imm32IsCrossThreadAccess(hIMC))
1276         return FALSE;
1277 
1278     hKL = GetKeyboardLayout(0);
1279     return Imm32CleanupContext(hIMC, hKL, FALSE);
1280 }
1281 
1282 /***********************************************************************
1283  *		ImmDisableIME (IMM32.@)
1284  */
1285 BOOL WINAPI ImmDisableIME(DWORD dwThreadId)
1286 {
1287     return NtUserDisableThreadIme(dwThreadId);
1288 }
1289 
1290 /*
1291  * These functions absorb the difference between Ansi and Wide.
1292  */
1293 typedef struct ENUM_WORD_A2W
1294 {
1295     REGISTERWORDENUMPROCW lpfnEnumProc;
1296     LPVOID lpData;
1297     UINT ret;
1298 } ENUM_WORD_A2W, *LPENUM_WORD_A2W;
1299 
1300 typedef struct ENUM_WORD_W2A
1301 {
1302     REGISTERWORDENUMPROCA lpfnEnumProc;
1303     LPVOID lpData;
1304     UINT ret;
1305 } ENUM_WORD_W2A, *LPENUM_WORD_W2A;
1306 
1307 static INT CALLBACK
1308 Imm32EnumWordProcA2W(LPCSTR pszReadingA, DWORD dwStyle, LPCSTR pszRegisterA, LPVOID lpData)
1309 {
1310     INT ret = 0;
1311     LPENUM_WORD_A2W lpEnumData = lpData;
1312     LPWSTR pszReadingW = NULL, pszRegisterW = NULL;
1313 
1314     if (pszReadingA)
1315     {
1316         pszReadingW = Imm32WideFromAnsi(pszReadingA);
1317         if (pszReadingW == NULL)
1318             goto Quit;
1319     }
1320 
1321     if (pszRegisterA)
1322     {
1323         pszRegisterW = Imm32WideFromAnsi(pszRegisterA);
1324         if (pszRegisterW == NULL)
1325             goto Quit;
1326     }
1327 
1328     ret = lpEnumData->lpfnEnumProc(pszReadingW, dwStyle, pszRegisterW, lpEnumData->lpData);
1329     lpEnumData->ret = ret;
1330 
1331 Quit:
1332     if (pszReadingW)
1333         HeapFree(g_hImm32Heap, 0, pszReadingW);
1334     if (pszRegisterW)
1335         HeapFree(g_hImm32Heap, 0, pszRegisterW);
1336     return ret;
1337 }
1338 
1339 static INT CALLBACK
1340 Imm32EnumWordProcW2A(LPCWSTR pszReadingW, DWORD dwStyle, LPCWSTR pszRegisterW, LPVOID lpData)
1341 {
1342     INT ret = 0;
1343     LPENUM_WORD_W2A lpEnumData = lpData;
1344     LPSTR pszReadingA = NULL, pszRegisterA = NULL;
1345 
1346     if (pszReadingW)
1347     {
1348         pszReadingA = Imm32AnsiFromWide(pszReadingW);
1349         if (pszReadingW == NULL)
1350             goto Quit;
1351     }
1352 
1353     if (pszRegisterW)
1354     {
1355         pszRegisterA = Imm32AnsiFromWide(pszRegisterW);
1356         if (pszRegisterA == NULL)
1357             goto Quit;
1358     }
1359 
1360     ret = lpEnumData->lpfnEnumProc(pszReadingA, dwStyle, pszRegisterA, lpEnumData->lpData);
1361     lpEnumData->ret = ret;
1362 
1363 Quit:
1364     if (pszReadingA)
1365         HeapFree(g_hImm32Heap, 0, pszReadingA);
1366     if (pszRegisterA)
1367         HeapFree(g_hImm32Heap, 0, pszRegisterA);
1368     return ret;
1369 }
1370 
1371 /***********************************************************************
1372  *		ImmEnumRegisterWordA (IMM32.@)
1373  */
1374 UINT WINAPI ImmEnumRegisterWordA(
1375   HKL hKL, REGISTERWORDENUMPROCA lpfnEnumProc,
1376   LPCSTR lpszReading, DWORD dwStyle,
1377   LPCSTR lpszRegister, LPVOID lpData)
1378 {
1379     UINT ret = 0;
1380     LPWSTR pszReadingW = NULL, pszRegisterW = NULL;
1381     ENUM_WORD_W2A EnumDataW2A;
1382     PIMEDPI pImeDpi;
1383 
1384     TRACE("(%p, %p, %s, 0x%lX, %s, %p)", hKL, lpfnEnumProc, debugstr_a(lpszReading),
1385           dwStyle, debugstr_a(lpszRegister), lpData);
1386 
1387     pImeDpi = ImmLockOrLoadImeDpi(hKL);
1388     if (!pImeDpi)
1389         return 0;
1390 
1391     if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE))
1392     {
1393         ret = pImeDpi->ImeEnumRegisterWord(lpfnEnumProc, lpszReading, dwStyle,
1394                                            lpszRegister, lpData);
1395         ImmUnlockImeDpi(pImeDpi);
1396         return ret;
1397     }
1398 
1399     if (lpszReading)
1400     {
1401         pszReadingW = Imm32WideFromAnsi(lpszReading);
1402         if (pszReadingW == NULL)
1403             goto Quit;
1404     }
1405 
1406     if (lpszRegister)
1407     {
1408         pszRegisterW = Imm32WideFromAnsi(lpszRegister);
1409         if (pszRegisterW == NULL)
1410             goto Quit;
1411     }
1412 
1413     EnumDataW2A.lpfnEnumProc = lpfnEnumProc;
1414     EnumDataW2A.lpData = lpData;
1415     EnumDataW2A.ret = 0;
1416     pImeDpi->ImeEnumRegisterWord(Imm32EnumWordProcW2A, pszReadingW, dwStyle,
1417                                  pszRegisterW, &EnumDataW2A);
1418     ret = EnumDataW2A.ret;
1419 
1420 Quit:
1421     if (pszReadingW)
1422         HeapFree(g_hImm32Heap, 0, pszReadingW);
1423     if (pszRegisterW)
1424         HeapFree(g_hImm32Heap, 0, pszRegisterW);
1425     ImmUnlockImeDpi(pImeDpi);
1426     return ret;
1427 }
1428 
1429 /***********************************************************************
1430  *		ImmEnumRegisterWordW (IMM32.@)
1431  */
1432 UINT WINAPI ImmEnumRegisterWordW(
1433   HKL hKL, REGISTERWORDENUMPROCW lpfnEnumProc,
1434   LPCWSTR lpszReading, DWORD dwStyle,
1435   LPCWSTR lpszRegister, LPVOID lpData)
1436 {
1437     UINT ret = 0;
1438     LPSTR pszReadingA = NULL, pszRegisterA = NULL;
1439     ENUM_WORD_A2W EnumDataA2W;
1440     PIMEDPI pImeDpi;
1441 
1442     TRACE("(%p, %p, %s, 0x%lX, %s, %p)", hKL, lpfnEnumProc, debugstr_w(lpszReading),
1443           dwStyle, debugstr_w(lpszRegister), lpData);
1444 
1445     pImeDpi = ImmLockOrLoadImeDpi(hKL);
1446     if (!pImeDpi)
1447         return 0;
1448 
1449     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
1450     {
1451         ret = pImeDpi->ImeEnumRegisterWord(lpfnEnumProc, lpszReading, dwStyle,
1452                                            lpszRegister, lpData);
1453         ImmUnlockImeDpi(pImeDpi);
1454         return ret;
1455     }
1456 
1457     if (lpszReading)
1458     {
1459         pszReadingA = Imm32AnsiFromWide(lpszReading);
1460         if (pszReadingA == NULL)
1461             goto Quit;
1462     }
1463 
1464     if (lpszRegister)
1465     {
1466         pszRegisterA = Imm32AnsiFromWide(lpszRegister);
1467         if (pszRegisterA == NULL)
1468             goto Quit;
1469     }
1470 
1471     EnumDataA2W.lpfnEnumProc = lpfnEnumProc;
1472     EnumDataA2W.lpData = lpData;
1473     EnumDataA2W.ret = 0;
1474     pImeDpi->ImeEnumRegisterWord(Imm32EnumWordProcA2W, pszReadingA, dwStyle,
1475                                  pszRegisterA, &EnumDataA2W);
1476     ret = EnumDataA2W.ret;
1477 
1478 Quit:
1479     if (pszReadingA)
1480         HeapFree(g_hImm32Heap, 0, pszReadingA);
1481     if (pszRegisterA)
1482         HeapFree(g_hImm32Heap, 0, pszRegisterA);
1483     ImmUnlockImeDpi(pImeDpi);
1484     return ret;
1485 }
1486 
1487 static inline BOOL EscapeRequiresWA(UINT uEscape)
1488 {
1489         if (uEscape == IME_ESC_GET_EUDC_DICTIONARY ||
1490             uEscape == IME_ESC_SET_EUDC_DICTIONARY ||
1491             uEscape == IME_ESC_IME_NAME ||
1492             uEscape == IME_ESC_GETHELPFILENAME)
1493         return TRUE;
1494     return FALSE;
1495 }
1496 
1497 /***********************************************************************
1498  *		ImmEscapeA (IMM32.@)
1499  */
1500 LRESULT WINAPI ImmEscapeA(
1501   HKL hKL, HIMC hIMC,
1502   UINT uEscape, LPVOID lpData)
1503 {
1504     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
1505     TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData);
1506 
1507     if (immHkl->hIME && immHkl->pImeEscape)
1508     {
1509         if (!EscapeRequiresWA(uEscape) || !is_kbd_ime_unicode(immHkl))
1510             return immHkl->pImeEscape(hIMC,uEscape,lpData);
1511         else
1512         {
1513             WCHAR buffer[81]; /* largest required buffer should be 80 */
1514             LRESULT rc;
1515             if (uEscape == IME_ESC_SET_EUDC_DICTIONARY)
1516             {
1517                 MultiByteToWideChar(CP_ACP,0,lpData,-1,buffer,81);
1518                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
1519             }
1520             else
1521             {
1522                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
1523                 WideCharToMultiByte(CP_ACP,0,buffer,-1,lpData,80, NULL, NULL);
1524             }
1525             return rc;
1526         }
1527     }
1528     else
1529         return 0;
1530 }
1531 
1532 /***********************************************************************
1533  *		ImmEscapeW (IMM32.@)
1534  */
1535 LRESULT WINAPI ImmEscapeW(
1536   HKL hKL, HIMC hIMC,
1537   UINT uEscape, LPVOID lpData)
1538 {
1539     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
1540     TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData);
1541 
1542     if (immHkl->hIME && immHkl->pImeEscape)
1543     {
1544         if (!EscapeRequiresWA(uEscape) || is_kbd_ime_unicode(immHkl))
1545             return immHkl->pImeEscape(hIMC,uEscape,lpData);
1546         else
1547         {
1548             CHAR buffer[81]; /* largest required buffer should be 80 */
1549             LRESULT rc;
1550             if (uEscape == IME_ESC_SET_EUDC_DICTIONARY)
1551             {
1552                 WideCharToMultiByte(CP_ACP,0,lpData,-1,buffer,81, NULL, NULL);
1553                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
1554             }
1555             else
1556             {
1557                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
1558                 MultiByteToWideChar(CP_ACP,0,buffer,-1,lpData,80);
1559             }
1560             return rc;
1561         }
1562     }
1563     else
1564         return 0;
1565 }
1566 
1567 static PCLIENTIMC APIENTRY Imm32GetClientImcCache(void)
1568 {
1569     // FIXME: Do something properly here
1570     return NULL;
1571 }
1572 
1573 static DWORD APIENTRY Imm32AllocAndBuildHimcList(DWORD dwThreadId, HIMC **pphList)
1574 {
1575 #define INITIAL_COUNT 0x40
1576 #define MAX_RETRY 10
1577     NTSTATUS Status;
1578     DWORD dwCount = INITIAL_COUNT, cRetry = 0;
1579     HIMC *phNewList;
1580 
1581     phNewList = Imm32HeapAlloc(0, dwCount * sizeof(HIMC));
1582     if (phNewList == NULL)
1583         return 0;
1584 
1585     Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
1586     while (Status == STATUS_BUFFER_TOO_SMALL)
1587     {
1588         HeapFree(g_hImm32Heap, 0, phNewList);
1589         if (cRetry++ >= MAX_RETRY)
1590             return 0;
1591 
1592         phNewList = Imm32HeapAlloc(0, dwCount * sizeof(HIMC));
1593         if (phNewList == NULL)
1594             return 0;
1595 
1596         Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
1597     }
1598 
1599     if (NT_ERROR(Status) || !dwCount)
1600     {
1601         HeapFree(g_hImm32Heap, 0, phNewList);
1602         return 0;
1603     }
1604 
1605     *pphList = phNewList;
1606     return dwCount;
1607 #undef INITIAL_COUNT
1608 #undef MAX_RETRY
1609 }
1610 
1611 static BOOL APIENTRY Imm32ImeNonImeToggle(HIMC hIMC, HKL hKL, HWND hWnd, LANGID LangID)
1612 {
1613     LPINPUTCONTEXT pIC;
1614     BOOL fOpen;
1615 
1616     if (hWnd != NULL)
1617         return FALSE;
1618 
1619     if (!IS_IME_HKL(hKL) || LOWORD(hKL) != LangID)
1620     {
1621         FIXME("We have to do something here\n");
1622         return TRUE;
1623     }
1624 
1625     pIC = ImmLockIMC(hIMC);
1626     if (pIC == NULL)
1627         return TRUE;
1628 
1629     fOpen = pIC->fOpen;
1630     ImmUnlockIMC(hIMC);
1631 
1632     if (!fOpen)
1633     {
1634         ImmSetOpenStatus(hIMC, TRUE);
1635         return TRUE;
1636     }
1637 
1638     FIXME("We have to do something here\n");
1639     return TRUE;
1640 }
1641 
1642 static BOOL APIENTRY Imm32CShapeToggle(HIMC hIMC, HKL hKL, HWND hWnd)
1643 {
1644     LPINPUTCONTEXT pIC;
1645     BOOL fOpen;
1646     DWORD dwConversion, dwSentence;
1647 
1648     if (hWnd == NULL || !IS_IME_HKL(hKL))
1649         return FALSE;
1650 
1651     pIC = ImmLockIMC(hIMC);
1652     if (pIC == NULL)
1653         return TRUE;
1654 
1655     fOpen = pIC->fOpen;
1656     if (fOpen)
1657     {
1658         dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
1659         dwSentence = pIC->fdwSentence;
1660     }
1661 
1662     ImmUnlockIMC(hIMC);
1663 
1664     if (fOpen)
1665         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
1666     else
1667         ImmSetOpenStatus(hIMC, TRUE);
1668 
1669     return TRUE;
1670 }
1671 
1672 static BOOL APIENTRY Imm32CSymbolToggle(HIMC hIMC, HKL hKL, HWND hWnd)
1673 {
1674     LPINPUTCONTEXT pIC;
1675     BOOL fOpen;
1676     DWORD dwConversion, dwSentence;
1677 
1678     if (hWnd == NULL || !IS_IME_HKL(hKL))
1679         return FALSE;
1680 
1681     pIC = ImmLockIMC(hIMC);
1682     if (pIC == NULL)
1683         return TRUE;
1684 
1685     fOpen = pIC->fOpen;
1686     if (fOpen)
1687     {
1688         dwConversion = (pIC->fdwConversion ^ IME_CMODE_SYMBOL);
1689         dwSentence = pIC->fdwSentence;
1690     }
1691 
1692     ImmUnlockIMC(hIMC);
1693 
1694     if (fOpen)
1695         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
1696     else
1697         ImmSetOpenStatus(hIMC, TRUE);
1698 
1699     return TRUE;
1700 }
1701 
1702 static BOOL APIENTRY Imm32JCloseOpen(HIMC hIMC, HKL hKL, HWND hWnd)
1703 {
1704     BOOL fOpen;
1705 
1706     if (ImmIsIME(hKL) && LOWORD(hKL) == LANGID_JAPANESE)
1707     {
1708         fOpen = ImmGetOpenStatus(hIMC);
1709         ImmSetOpenStatus(hIMC, !fOpen);
1710         return TRUE;
1711     }
1712 
1713     FIXME("We have to do something here\n");
1714     return TRUE;
1715 }
1716 
1717 static BOOL APIENTRY Imm32KShapeToggle(HIMC hIMC)
1718 {
1719     LPINPUTCONTEXT pIC;
1720     DWORD dwConversion, dwSentence;
1721 
1722     pIC = ImmLockIMC(hIMC);
1723     if (pIC == NULL)
1724         return FALSE;
1725 
1726     dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
1727     dwSentence = pIC->fdwSentence;
1728     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
1729 
1730     if (pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE))
1731         ImmSetOpenStatus(hIMC, TRUE);
1732     else
1733         ImmSetOpenStatus(hIMC, FALSE);
1734 
1735     ImmUnlockIMC(hIMC);
1736     return TRUE;
1737 }
1738 
1739 static BOOL APIENTRY Imm32KHanjaConvert(HIMC hIMC)
1740 {
1741     LPINPUTCONTEXT pIC;
1742     DWORD dwConversion, dwSentence;
1743 
1744     pIC = ImmLockIMC(hIMC);
1745     if (!pIC)
1746         return FALSE;
1747 
1748     dwConversion = (pIC->fdwConversion ^ IME_CMODE_HANJACONVERT);
1749     dwSentence = pIC->fdwSentence;
1750     ImmUnlockIMC(hIMC);
1751 
1752     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
1753     return TRUE;
1754 }
1755 
1756 static BOOL APIENTRY Imm32KEnglish(HIMC hIMC)
1757 {
1758     LPINPUTCONTEXT pIC;
1759     DWORD dwConversion, dwSentence;
1760     BOOL fOpen;
1761 
1762     pIC = ImmLockIMC(hIMC);
1763     if (pIC == NULL)
1764         return FALSE;
1765 
1766     dwConversion = (pIC->fdwConversion ^ IME_CMODE_NATIVE);
1767     dwSentence = pIC->fdwSentence;
1768     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
1769 
1770     fOpen = ((pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE)) != 0);
1771     ImmSetOpenStatus(hIMC, fOpen);
1772 
1773     ImmUnlockIMC(hIMC);
1774     return TRUE;
1775 }
1776 
1777 static BOOL APIENTRY Imm32ProcessHotKey(HWND hWnd, HIMC hIMC, HKL hKL, DWORD dwHotKeyID)
1778 {
1779     PIMEDPI pImeDpi;
1780     BOOL ret;
1781 
1782     if (hIMC && Imm32IsCrossThreadAccess(hIMC))
1783         return FALSE;
1784 
1785     switch (dwHotKeyID)
1786     {
1787         case IME_CHOTKEY_IME_NONIME_TOGGLE:
1788             return Imm32ImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_SIMPLIFIED);
1789 
1790         case IME_CHOTKEY_SHAPE_TOGGLE:
1791             return Imm32CShapeToggle(hIMC, hKL, hWnd);
1792 
1793         case IME_CHOTKEY_SYMBOL_TOGGLE:
1794             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
1795 
1796         case IME_JHOTKEY_CLOSE_OPEN:
1797             return Imm32JCloseOpen(hIMC, hKL, hWnd);
1798 
1799         case IME_KHOTKEY_SHAPE_TOGGLE:
1800             return Imm32KShapeToggle(hIMC);
1801 
1802         case IME_KHOTKEY_HANJACONVERT:
1803             return Imm32KHanjaConvert(hIMC);
1804 
1805         case IME_KHOTKEY_ENGLISH:
1806             return Imm32KEnglish(hIMC);
1807 
1808         case IME_THOTKEY_IME_NONIME_TOGGLE:
1809             return Imm32ImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_TRADITIONAL);
1810 
1811         case IME_THOTKEY_SHAPE_TOGGLE:
1812             return Imm32CShapeToggle(hIMC, hKL, hWnd);
1813 
1814         case IME_THOTKEY_SYMBOL_TOGGLE:
1815             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
1816 
1817         default:
1818             break;
1819     }
1820 
1821     if (dwHotKeyID < IME_HOTKEY_PRIVATE_FIRST || IME_HOTKEY_PRIVATE_LAST < dwHotKeyID)
1822         return FALSE;
1823 
1824     pImeDpi = ImmLockImeDpi(hKL);
1825     if (pImeDpi == NULL)
1826         return FALSE;
1827 
1828     ret = (BOOL)pImeDpi->ImeEscape(hIMC, IME_ESC_PRIVATE_HOTKEY, &dwHotKeyID);
1829     ImmUnlockImeDpi(pImeDpi);
1830     return ret;
1831 }
1832 
1833 /***********************************************************************
1834  *		ImmLockClientImc (IMM32.@)
1835  */
1836 PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
1837 {
1838     PCLIENTIMC pClientImc;
1839 
1840     TRACE("(%p)\n", hImc);
1841 
1842     if (hImc == NULL)
1843         return NULL;
1844 
1845     pClientImc = Imm32GetClientImcCache();
1846     if (!pClientImc)
1847     {
1848         pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
1849         if (!pClientImc)
1850             return NULL;
1851 
1852         RtlInitializeCriticalSection(&pClientImc->cs);
1853 
1854         // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
1855         pClientImc->unknown = NtUserGetThreadState(13);
1856 
1857         if (!NtUserUpdateInputContext(hImc, 0, pClientImc))
1858         {
1859             HeapFree(g_hImm32Heap, 0, pClientImc);
1860             return NULL;
1861         }
1862 
1863         pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
1864     }
1865     else
1866     {
1867         if (pClientImc->dwFlags & CLIENTIMC_UNKNOWN1)
1868             return NULL;
1869     }
1870 
1871     InterlockedIncrement(&pClientImc->cLockObj);
1872     return pClientImc;
1873 }
1874 
1875 /***********************************************************************
1876  *		ImmUnlockClientImc (IMM32.@)
1877  */
1878 VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc)
1879 {
1880     LONG cLocks;
1881     HIMC hImc;
1882 
1883     TRACE("(%p)\n", pClientImc);
1884 
1885     cLocks = InterlockedDecrement(&pClientImc->cLockObj);
1886     if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_UNKNOWN1))
1887         return;
1888 
1889     hImc = pClientImc->hImc;
1890     if (hImc)
1891         LocalFree(hImc);
1892 
1893     RtlDeleteCriticalSection(&pClientImc->cs);
1894     HeapFree(g_hImm32Heap, 0, pClientImc);
1895 }
1896 
1897 static HIMC APIENTRY Imm32GetContextEx(HWND hWnd, DWORD dwContextFlags)
1898 {
1899     HIMC hIMC;
1900     PCLIENTIMC pClientImc;
1901     PWND pWnd;
1902 
1903     if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
1904         return NULL;
1905 
1906     if (!hWnd)
1907     {
1908         // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
1909         hIMC = (HIMC)NtUserGetThreadState(4);
1910         goto Quit;
1911     }
1912 
1913     pWnd = ValidateHwndNoErr(hWnd);
1914     if (!pWnd || Imm32IsCrossProcessAccess(hWnd))
1915         return NULL;
1916 
1917     hIMC = pWnd->hImc;
1918     if (!hIMC && (dwContextFlags & 1))
1919         hIMC = (HIMC)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_ICONTEXT);
1920 
1921 Quit:
1922     pClientImc = ImmLockClientImc(hIMC);
1923     if (pClientImc == NULL)
1924         return NULL;
1925     if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_UNKNOWN3))
1926         hIMC = NULL;
1927     ImmUnlockClientImc(pClientImc);
1928     return hIMC;
1929 }
1930 
1931 static DWORD APIENTRY
1932 CandidateListWideToAnsi(const CANDIDATELIST *pWideCL, LPCANDIDATELIST pAnsiCL, DWORD dwBufLen,
1933                         UINT uCodePage)
1934 {
1935     BOOL bUsedDefault;
1936     DWORD dwSize, dwIndex, cbGot, cbLeft;
1937     const BYTE *pbWide;
1938     LPBYTE pbAnsi;
1939     LPDWORD pibOffsets;
1940 
1941     /* calculate total ansi size */
1942     if (pWideCL->dwCount > 0)
1943     {
1944         dwSize = sizeof(CANDIDATELIST) + ((pWideCL->dwCount - 1) * sizeof(DWORD));
1945         for (dwIndex = 0; dwIndex < pWideCL->dwCount; ++dwIndex)
1946         {
1947             pbWide = (const BYTE *)pWideCL + pWideCL->dwOffset[dwIndex];
1948             cbGot = WideCharToMultiByte(uCodePage, 0, (LPCWSTR)pbWide, -1, NULL, 0,
1949                                         NULL, &bUsedDefault);
1950             dwSize += cbGot;
1951         }
1952     }
1953     else
1954     {
1955         dwSize = sizeof(CANDIDATELIST);
1956     }
1957 
1958     dwSize = ROUNDUP4(dwSize);
1959     if (dwBufLen == 0)
1960         return dwSize;
1961     if (dwBufLen < dwSize)
1962         return 0;
1963 
1964     /* store to ansi */
1965     pAnsiCL->dwSize = dwBufLen;
1966     pAnsiCL->dwStyle = pWideCL->dwStyle;
1967     pAnsiCL->dwCount = pWideCL->dwCount;
1968     pAnsiCL->dwSelection = pWideCL->dwSelection;
1969     pAnsiCL->dwPageStart = pWideCL->dwPageStart;
1970     pAnsiCL->dwPageSize = pWideCL->dwPageSize;
1971 
1972     pibOffsets = pAnsiCL->dwOffset;
1973     if (pWideCL->dwCount > 0)
1974     {
1975         pibOffsets[0] = sizeof(CANDIDATELIST) + ((pWideCL->dwCount - 1) * sizeof(DWORD));
1976         cbLeft = dwBufLen - pibOffsets[0];
1977 
1978         for (dwIndex = 0; dwIndex < pWideCL->dwCount; ++dwIndex)
1979         {
1980             pbWide = (const BYTE *)pWideCL + pWideCL->dwOffset[dwIndex];
1981             pbAnsi = (LPBYTE)pAnsiCL + pibOffsets[dwIndex];
1982 
1983             /* convert to ansi */
1984             cbGot = WideCharToMultiByte(uCodePage, 0, (LPCWSTR)pbWide, -1,
1985                                         (LPSTR)pbAnsi, cbLeft, NULL, &bUsedDefault);
1986             cbLeft -= cbGot;
1987 
1988             if (dwIndex < pWideCL->dwCount - 1)
1989                 pibOffsets[dwIndex + 1] = pibOffsets[dwIndex] + cbGot;
1990         }
1991     }
1992     else
1993     {
1994         pibOffsets[0] = sizeof(CANDIDATELIST);
1995     }
1996 
1997     return dwBufLen;
1998 }
1999 
2000 static DWORD APIENTRY
2001 CandidateListAnsiToWide(const CANDIDATELIST *pAnsiCL, LPCANDIDATELIST pWideCL, DWORD dwBufLen,
2002                         UINT uCodePage)
2003 {
2004     DWORD dwSize, dwIndex, cchGot, cbGot, cbLeft;
2005     const BYTE *pbAnsi;
2006     LPBYTE pbWide;
2007     LPDWORD pibOffsets;
2008 
2009     /* calculate total wide size */
2010     if (pAnsiCL->dwCount > 0)
2011     {
2012         dwSize = sizeof(CANDIDATELIST) + ((pAnsiCL->dwCount - 1) * sizeof(DWORD));
2013         for (dwIndex = 0; dwIndex < pAnsiCL->dwCount; ++dwIndex)
2014         {
2015             pbAnsi = (const BYTE *)pAnsiCL + pAnsiCL->dwOffset[dwIndex];
2016             cchGot = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, (LPCSTR)pbAnsi, -1, NULL, 0);
2017             dwSize += cchGot * sizeof(WCHAR);
2018         }
2019     }
2020     else
2021     {
2022         dwSize = sizeof(CANDIDATELIST);
2023     }
2024 
2025     dwSize = ROUNDUP4(dwSize);
2026     if (dwBufLen == 0)
2027         return dwSize;
2028     if (dwBufLen < dwSize)
2029         return 0;
2030 
2031     /* store to wide */
2032     pWideCL->dwSize = dwBufLen;
2033     pWideCL->dwStyle = pAnsiCL->dwStyle;
2034     pWideCL->dwCount = pAnsiCL->dwCount;
2035     pWideCL->dwSelection = pAnsiCL->dwSelection;
2036     pWideCL->dwPageStart = pAnsiCL->dwPageStart;
2037     pWideCL->dwPageSize = pAnsiCL->dwPageSize;
2038 
2039     pibOffsets = pWideCL->dwOffset;
2040     if (pAnsiCL->dwCount > 0)
2041     {
2042         pibOffsets[0] = sizeof(CANDIDATELIST) + ((pWideCL->dwCount - 1) * sizeof(DWORD));
2043         cbLeft = dwBufLen - pibOffsets[0];
2044 
2045         for (dwIndex = 0; dwIndex < pAnsiCL->dwCount; ++dwIndex)
2046         {
2047             pbAnsi = (const BYTE *)pAnsiCL + pAnsiCL->dwOffset[dwIndex];
2048             pbWide = (LPBYTE)pWideCL + pibOffsets[dwIndex];
2049 
2050             /* convert to wide */
2051             cchGot = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, (LPCSTR)pbAnsi, -1,
2052                                          (LPWSTR)pbWide, cbLeft / sizeof(WCHAR));
2053             cbGot = cchGot * sizeof(WCHAR);
2054             cbLeft -= cbGot;
2055 
2056             if (dwIndex + 1 < pAnsiCL->dwCount)
2057                 pibOffsets[dwIndex + 1] = pibOffsets[dwIndex] + cbGot;
2058         }
2059     }
2060     else
2061     {
2062         pibOffsets[0] = sizeof(CANDIDATELIST);
2063     }
2064 
2065     return dwBufLen;
2066 }
2067 
2068 static DWORD APIENTRY
2069 ImmGetCandidateListAW(HIMC hIMC, DWORD dwIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen,
2070                       BOOL bAnsi)
2071 {
2072     DWORD ret = 0;
2073     LPINPUTCONTEXT pIC;
2074     PCLIENTIMC pClientImc;
2075     LPCANDIDATEINFO pCI;
2076     LPCANDIDATELIST pCL;
2077     DWORD dwSize;
2078 
2079     pClientImc = ImmLockClientImc(hIMC);
2080     if (!pClientImc)
2081         return 0;
2082 
2083     pIC = ImmLockIMC(hIMC);
2084     if (pIC == NULL)
2085     {
2086         ImmUnlockClientImc(pClientImc);
2087         return 0;
2088     }
2089 
2090     pCI = ImmLockIMCC(pIC->hCandInfo);
2091     if (pCI == NULL)
2092     {
2093         ImmUnlockIMC(hIMC);
2094         ImmUnlockClientImc(pClientImc);
2095         return 0;
2096     }
2097 
2098     if (pCI->dwSize < sizeof(CANDIDATEINFO) || pCI->dwCount <= dwIndex)
2099         goto Quit;
2100 
2101     /* get required size */
2102     pCL = (LPCANDIDATELIST)((LPBYTE)pCI + pCI->dwOffset[dwIndex]);
2103     if (bAnsi)
2104     {
2105         if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2106             dwSize = CandidateListAnsiToWide(pCL, NULL, 0, CP_ACP);
2107         else
2108             dwSize = pCL->dwSize;
2109     }
2110     else
2111     {
2112         if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2113             dwSize = pCL->dwSize;
2114         else
2115             dwSize = CandidateListWideToAnsi(pCL, NULL, 0, CP_ACP);
2116     }
2117 
2118     if (dwBufLen != 0 && dwSize != 0)
2119     {
2120         if (lpCandList == NULL || dwBufLen < dwSize)
2121             goto Quit;
2122 
2123         /* store */
2124         if (bAnsi)
2125         {
2126             if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2127                 CandidateListAnsiToWide(pCL, lpCandList, dwSize, CP_ACP);
2128             else
2129                 RtlCopyMemory(lpCandList, pCL, dwSize);
2130         }
2131         else
2132         {
2133             if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2134                 RtlCopyMemory(lpCandList, pCL, dwSize);
2135             else
2136                 CandidateListWideToAnsi(pCL, lpCandList, dwSize, CP_ACP);
2137         }
2138     }
2139 
2140     ret = dwSize;
2141 
2142 Quit:
2143     ImmUnlockIMCC(pIC->hCandInfo);
2144     ImmUnlockIMC(hIMC);
2145     ImmUnlockClientImc(pClientImc);
2146     return ret;
2147 }
2148 
2149 DWORD APIENTRY ImmGetCandidateListCountAW(HIMC hIMC, LPDWORD lpdwListCount, BOOL bAnsi)
2150 {
2151     DWORD ret = 0, cbGot, dwIndex;
2152     PCLIENTIMC pClientImc;
2153     LPINPUTCONTEXT pIC;
2154     const CANDIDATEINFO *pCI;
2155     const BYTE *pb;
2156     const CANDIDATELIST *pCL;
2157     const DWORD *pdwOffsets;
2158 
2159     if (lpdwListCount == NULL)
2160         return 0;
2161 
2162     *lpdwListCount = 0;
2163 
2164     pClientImc = ImmLockClientImc(hIMC);
2165     if (pClientImc == NULL)
2166         return 0;
2167 
2168     pIC = ImmLockIMC(hIMC);
2169     if (pIC == NULL)
2170     {
2171         ImmUnlockClientImc(pClientImc);
2172         return 0;
2173     }
2174 
2175     pCI = ImmLockIMCC(pIC->hCandInfo);
2176     if (pCI == NULL)
2177     {
2178         ImmUnlockIMC(hIMC);
2179         ImmUnlockClientImc(pClientImc);
2180         return 0;
2181     }
2182 
2183     if (pCI->dwSize < sizeof(CANDIDATEINFO))
2184         goto Quit;
2185 
2186     *lpdwListCount = pCI->dwCount; /* the number of candidate lists */
2187 
2188     /* calculate total size of candidate lists */
2189     if (bAnsi)
2190     {
2191         if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2192         {
2193             ret = ROUNDUP4(pCI->dwPrivateSize);
2194             pdwOffsets = pCI->dwOffset;
2195             for (dwIndex = 0; dwIndex < pCI->dwCount; ++dwIndex)
2196             {
2197                 pb = (const BYTE *)pCI + pdwOffsets[dwIndex];
2198                 pCL = (const CANDIDATELIST *)pb;
2199                 cbGot = CandidateListWideToAnsi(pCL, NULL, 0, CP_ACP);
2200                 ret += cbGot;
2201             }
2202         }
2203         else
2204         {
2205             ret = pCI->dwSize;
2206         }
2207     }
2208     else
2209     {
2210         if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2211         {
2212             ret = pCI->dwSize;
2213         }
2214         else
2215         {
2216             ret = ROUNDUP4(pCI->dwPrivateSize);
2217             pdwOffsets = pCI->dwOffset;
2218             for (dwIndex = 0; dwIndex < pCI->dwCount; ++dwIndex)
2219             {
2220                 pb = (const BYTE *)pCI + pdwOffsets[dwIndex];
2221                 pCL = (const CANDIDATELIST *)pb;
2222                 cbGot = CandidateListAnsiToWide(pCL, NULL, 0, CP_ACP);
2223                 ret += cbGot;
2224             }
2225         }
2226     }
2227 
2228 Quit:
2229     ImmUnlockIMCC(pIC->hCandInfo);
2230     ImmUnlockIMC(hIMC);
2231     ImmUnlockClientImc(pClientImc);
2232     return ret;
2233 }
2234 
2235 /***********************************************************************
2236  *		ImmGetCandidateListA (IMM32.@)
2237  */
2238 DWORD WINAPI ImmGetCandidateListA(
2239   HIMC hIMC, DWORD dwIndex,
2240   LPCANDIDATELIST lpCandList, DWORD dwBufLen)
2241 {
2242     return ImmGetCandidateListAW(hIMC, dwIndex, lpCandList, dwBufLen, TRUE);
2243 }
2244 
2245 /***********************************************************************
2246  *		ImmGetCandidateListCountA (IMM32.@)
2247  */
2248 DWORD WINAPI ImmGetCandidateListCountA(
2249   HIMC hIMC, LPDWORD lpdwListCount)
2250 {
2251     return ImmGetCandidateListCountAW(hIMC, lpdwListCount, TRUE);
2252 }
2253 
2254 /***********************************************************************
2255  *		ImmGetCandidateListCountW (IMM32.@)
2256  */
2257 DWORD WINAPI ImmGetCandidateListCountW(
2258   HIMC hIMC, LPDWORD lpdwListCount)
2259 {
2260     return ImmGetCandidateListCountAW(hIMC, lpdwListCount, FALSE);
2261 }
2262 
2263 /***********************************************************************
2264  *		ImmGetCandidateListW (IMM32.@)
2265  */
2266 DWORD WINAPI ImmGetCandidateListW(
2267   HIMC hIMC, DWORD dwIndex,
2268   LPCANDIDATELIST lpCandList, DWORD dwBufLen)
2269 {
2270     return ImmGetCandidateListAW(hIMC, dwIndex, lpCandList, dwBufLen, FALSE);
2271 }
2272 
2273 /***********************************************************************
2274  *		ImmGetCandidateWindow (IMM32.@)
2275  */
2276 BOOL WINAPI ImmGetCandidateWindow(
2277   HIMC hIMC, DWORD dwIndex, LPCANDIDATEFORM lpCandidate)
2278 {
2279     BOOL ret = FALSE;
2280     LPINPUTCONTEXT pIC;
2281     LPCANDIDATEFORM pCF;
2282 
2283     TRACE("(%p, %lu, %p)\n", hIMC, dwIndex, lpCandidate);
2284 
2285     pIC = ImmLockIMC(hIMC);
2286     if (pIC  == NULL)
2287         return FALSE;
2288 
2289     pCF = &pIC->cfCandForm[dwIndex];
2290     if (pCF->dwIndex != IMM_INVALID_CANDFORM)
2291     {
2292         *lpCandidate = *pCF;
2293         ret = TRUE;
2294     }
2295 
2296     ImmUnlockIMC(hIMC);
2297     return ret;
2298 }
2299 
2300 static VOID APIENTRY LogFontAnsiToWide(const LOGFONTA *plfA, LPLOGFONTW plfW)
2301 {
2302     size_t cch;
2303     RtlCopyMemory(plfW, plfA, offsetof(LOGFONTA, lfFaceName));
2304     StringCchLengthA(plfA->lfFaceName, _countof(plfA->lfFaceName), &cch);
2305     cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plfA->lfFaceName, (INT)cch,
2306                               plfW->lfFaceName, _countof(plfW->lfFaceName));
2307     if (cch > _countof(plfW->lfFaceName) - 1)
2308         cch = _countof(plfW->lfFaceName) - 1;
2309     plfW->lfFaceName[cch] = 0;
2310 }
2311 
2312 static VOID APIENTRY LogFontWideToAnsi(const LOGFONTW *plfW, LPLOGFONTA plfA)
2313 {
2314     size_t cch;
2315     RtlCopyMemory(plfA, plfW, offsetof(LOGFONTW, lfFaceName));
2316     StringCchLengthW(plfW->lfFaceName, _countof(plfW->lfFaceName), &cch);
2317     cch = WideCharToMultiByte(CP_ACP, 0, plfW->lfFaceName, (INT)cch,
2318                               plfA->lfFaceName, _countof(plfA->lfFaceName), NULL, NULL);
2319     if (cch > _countof(plfA->lfFaceName) - 1)
2320         cch = _countof(plfA->lfFaceName) - 1;
2321     plfA->lfFaceName[cch] = 0;
2322 }
2323 
2324 /***********************************************************************
2325  *		ImmGetCompositionFontA (IMM32.@)
2326  */
2327 BOOL WINAPI ImmGetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
2328 {
2329     PCLIENTIMC pClientImc;
2330     BOOL ret = FALSE, bWide;
2331     LPINPUTCONTEXT pIC;
2332 
2333     TRACE("(%p, %p)\n", hIMC, lplf);
2334 
2335     pClientImc = ImmLockClientImc(hIMC);
2336     if (pClientImc == NULL)
2337         return FALSE;
2338 
2339     bWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
2340     ImmUnlockClientImc(pClientImc);
2341 
2342     pIC = ImmLockIMC(hIMC);
2343     if (pIC == NULL)
2344         return FALSE;
2345 
2346     if (pIC->fdwInit & INIT_LOGFONT)
2347     {
2348         if (bWide)
2349             LogFontWideToAnsi(&pIC->lfFont.W, lplf);
2350         else
2351             *lplf = pIC->lfFont.A;
2352 
2353         ret = TRUE;
2354     }
2355 
2356     ImmUnlockIMC(hIMC);
2357     return ret;
2358 }
2359 
2360 /***********************************************************************
2361  *		ImmGetCompositionFontW (IMM32.@)
2362  */
2363 BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
2364 {
2365     PCLIENTIMC pClientImc;
2366     BOOL bWide;
2367     LPINPUTCONTEXT pIC;
2368     BOOL ret = FALSE;
2369 
2370     TRACE("(%p, %p)\n", hIMC, lplf);
2371 
2372     pClientImc = ImmLockClientImc(hIMC);
2373     if (pClientImc == NULL)
2374         return FALSE;
2375 
2376     bWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
2377     ImmUnlockClientImc(pClientImc);
2378 
2379     pIC = ImmLockIMC(hIMC);
2380     if (pIC == NULL)
2381         return FALSE;
2382 
2383     if (pIC->fdwInit & INIT_LOGFONT)
2384     {
2385         if (bWide)
2386             *lplf = pIC->lfFont.W;
2387         else
2388             LogFontAnsiToWide(&pIC->lfFont.A, lplf);
2389 
2390         ret = TRUE;
2391     }
2392 
2393     ImmUnlockIMC(hIMC);
2394     return ret;
2395 }
2396 
2397 
2398 /* Helpers for the GetCompositionString functions */
2399 
2400 /* Source encoding is defined by context, source length is always given in respective characters. Destination buffer
2401    length is always in bytes. */
2402 static INT CopyCompStringIMEtoClient(const InputContextData *data, const void *src, INT src_len, void *dst,
2403         INT dst_len, BOOL unicode)
2404 {
2405     int char_size = unicode ? sizeof(WCHAR) : sizeof(char);
2406     INT ret;
2407 
2408     if (is_himc_ime_unicode(data) ^ unicode)
2409     {
2410         if (unicode)
2411             ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR));
2412         else
2413             ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL);
2414         ret *= char_size;
2415     }
2416     else
2417     {
2418         if (dst_len)
2419         {
2420             ret = min(src_len * char_size, dst_len);
2421             memcpy(dst, src, ret);
2422         }
2423         else
2424             ret = src_len * char_size;
2425     }
2426 
2427     return ret;
2428 }
2429 
2430 /* Composition string encoding is defined by context, returned attributes correspond to string, converted according to
2431    passed mode. String length is in characters, attributes are in byte arrays. */
2432 static INT CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src, INT src_len, const void *comp_string,
2433         INT str_len, BYTE *dst, INT dst_len, BOOL unicode)
2434 {
2435     union
2436     {
2437         const void *str;
2438         const WCHAR *strW;
2439         const char *strA;
2440     } string;
2441     INT rc;
2442 
2443     string.str = comp_string;
2444 
2445     if (is_himc_ime_unicode(data) && !unicode)
2446     {
2447         rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL);
2448         if (dst_len)
2449         {
2450             int i, j = 0, k = 0;
2451 
2452             if (rc < dst_len)
2453                 dst_len = rc;
2454             for (i = 0; i < str_len; ++i)
2455             {
2456                 int len;
2457 
2458                 len = WideCharToMultiByte(CP_ACP, 0, string.strW + i, 1, NULL, 0, NULL, NULL);
2459                 for (; len > 0; --len)
2460                 {
2461                     dst[j++] = src[k];
2462 
2463                     if (j >= dst_len)
2464                         goto end;
2465                 }
2466                 ++k;
2467             }
2468         end:
2469             rc = j;
2470         }
2471     }
2472     else if (!is_himc_ime_unicode(data) && unicode)
2473     {
2474         rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0);
2475         if (dst_len)
2476         {
2477             int i, j = 0;
2478 
2479             if (rc < dst_len)
2480                 dst_len = rc;
2481             for (i = 0; i < str_len; ++i)
2482             {
2483                 if (IsDBCSLeadByte(string.strA[i]))
2484                     continue;
2485 
2486                 dst[j++] = src[i];
2487 
2488                 if (j >= dst_len)
2489                     break;
2490             }
2491             rc = j;
2492         }
2493     }
2494     else
2495     {
2496         memcpy(dst, src, min(src_len, dst_len));
2497         rc = src_len;
2498     }
2499 
2500     return rc;
2501 }
2502 
2503 static INT CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource,
2504                                      LPBYTE target, INT tlen, BOOL unicode )
2505 {
2506     INT rc;
2507 
2508     if (is_himc_ime_unicode(data) && !unicode)
2509     {
2510         if (tlen)
2511         {
2512             int i;
2513 
2514             if (slen < tlen)
2515                 tlen = slen;
2516             tlen /= sizeof (DWORD);
2517             for (i = 0; i < tlen; ++i)
2518             {
2519                 ((DWORD *)target)[i] = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource,
2520                                                           ((DWORD *)source)[i],
2521                                                           NULL, 0,
2522                                                           NULL, NULL);
2523             }
2524             rc = sizeof (DWORD) * i;
2525         }
2526         else
2527             rc = slen;
2528     }
2529     else if (!is_himc_ime_unicode(data) && unicode)
2530     {
2531         if (tlen)
2532         {
2533             int i;
2534 
2535             if (slen < tlen)
2536                 tlen = slen;
2537             tlen /= sizeof (DWORD);
2538             for (i = 0; i < tlen; ++i)
2539             {
2540                 ((DWORD *)target)[i] = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource,
2541                                                           ((DWORD *)source)[i],
2542                                                           NULL, 0);
2543             }
2544             rc = sizeof (DWORD) * i;
2545         }
2546         else
2547             rc = slen;
2548     }
2549     else
2550     {
2551         memcpy( target, source, min(slen,tlen));
2552         rc = slen;
2553     }
2554 
2555     return rc;
2556 }
2557 
2558 static INT CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYTE ssource, BOOL unicode)
2559 {
2560     int rc;
2561 
2562     if (is_himc_ime_unicode(data) && !unicode)
2563     {
2564         rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL);
2565     }
2566     else if (!is_himc_ime_unicode(data) && unicode)
2567     {
2568         rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0);
2569     }
2570     else
2571         rc = offset;
2572 
2573     return rc;
2574 }
2575 
2576 static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf,
2577                                       DWORD dwBufLen, BOOL unicode)
2578 {
2579     LONG rc = 0;
2580     InputContextData *data = get_imc_data(hIMC);
2581     LPCOMPOSITIONSTRING compstr;
2582     LPBYTE compdata;
2583 
2584     TRACE("(%p, 0x%x, %p, %d)\n", hIMC, dwIndex, lpBuf, dwBufLen);
2585 
2586     if (!data)
2587        return FALSE;
2588 
2589     if (!data->IMC.hCompStr)
2590        return FALSE;
2591 
2592     compdata = ImmLockIMCC(data->IMC.hCompStr);
2593     compstr = (LPCOMPOSITIONSTRING)compdata;
2594 
2595     switch (dwIndex)
2596     {
2597     case GCS_RESULTSTR:
2598         TRACE("GCS_RESULTSTR\n");
2599         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode);
2600         break;
2601     case GCS_COMPSTR:
2602         TRACE("GCS_COMPSTR\n");
2603         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode);
2604         break;
2605     case GCS_COMPATTR:
2606         TRACE("GCS_COMPATTR\n");
2607         rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen,
2608                                      compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen,
2609                                      lpBuf, dwBufLen, unicode);
2610         break;
2611     case GCS_COMPCLAUSE:
2612         TRACE("GCS_COMPCLAUSE\n");
2613         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen,
2614                                        compdata + compstr->dwCompStrOffset,
2615                                        lpBuf, dwBufLen, unicode);
2616         break;
2617     case GCS_RESULTCLAUSE:
2618         TRACE("GCS_RESULTCLAUSE\n");
2619         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen,
2620                                        compdata + compstr->dwResultStrOffset,
2621                                        lpBuf, dwBufLen, unicode);
2622         break;
2623     case GCS_RESULTREADSTR:
2624         TRACE("GCS_RESULTREADSTR\n");
2625         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode);
2626         break;
2627     case GCS_RESULTREADCLAUSE:
2628         TRACE("GCS_RESULTREADCLAUSE\n");
2629         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen,
2630                                        compdata + compstr->dwResultStrOffset,
2631                                        lpBuf, dwBufLen, unicode);
2632         break;
2633     case GCS_COMPREADSTR:
2634         TRACE("GCS_COMPREADSTR\n");
2635         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode);
2636         break;
2637     case GCS_COMPREADATTR:
2638         TRACE("GCS_COMPREADATTR\n");
2639         rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen,
2640                                      compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen,
2641                                      lpBuf, dwBufLen, unicode);
2642         break;
2643     case GCS_COMPREADCLAUSE:
2644         TRACE("GCS_COMPREADCLAUSE\n");
2645         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen,
2646                                        compdata + compstr->dwCompStrOffset,
2647                                        lpBuf, dwBufLen, unicode);
2648         break;
2649     case GCS_CURSORPOS:
2650         TRACE("GCS_CURSORPOS\n");
2651         rc = CopyCompOffsetIMEtoClient(data, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode);
2652         break;
2653     case GCS_DELTASTART:
2654         TRACE("GCS_DELTASTART\n");
2655         rc = CopyCompOffsetIMEtoClient(data, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode);
2656         break;
2657     default:
2658         FIXME("Unhandled index 0x%x\n",dwIndex);
2659         break;
2660     }
2661 
2662     ImmUnlockIMCC(data->IMC.hCompStr);
2663 
2664     return rc;
2665 }
2666 
2667 /***********************************************************************
2668  *		ImmGetCompositionStringA (IMM32.@)
2669  */
2670 LONG WINAPI ImmGetCompositionStringA(
2671   HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
2672 {
2673     return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, FALSE);
2674 }
2675 
2676 
2677 /***********************************************************************
2678  *		ImmGetCompositionStringW (IMM32.@)
2679  */
2680 LONG WINAPI ImmGetCompositionStringW(
2681   HIMC hIMC, DWORD dwIndex,
2682   LPVOID lpBuf, DWORD dwBufLen)
2683 {
2684     return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, TRUE);
2685 }
2686 
2687 /***********************************************************************
2688  *		ImmGetCompositionWindow (IMM32.@)
2689  */
2690 BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
2691 {
2692     LPINPUTCONTEXT pIC;
2693     BOOL ret = FALSE;
2694 
2695     TRACE("(%p, %p)\n", hIMC, lpCompForm);
2696 
2697     pIC = ImmLockIMC(hIMC);
2698     if (!pIC)
2699         return FALSE;
2700 
2701     if (pIC->fdwInit & INIT_COMPFORM)
2702     {
2703         *lpCompForm = pIC->cfCompForm;
2704         ret = TRUE;
2705     }
2706 
2707     ImmUnlockIMC(hIMC);
2708     return ret;
2709 }
2710 
2711 /***********************************************************************
2712  *		ImmGetContext (IMM32.@)
2713  */
2714 HIMC WINAPI ImmGetContext(HWND hWnd)
2715 {
2716     TRACE("(%p)\n", hWnd);
2717     if (hWnd == NULL)
2718         return NULL;
2719     return Imm32GetContextEx(hWnd, 2);
2720 }
2721 
2722 /***********************************************************************
2723  *		ImmGetConversionListA (IMM32.@)
2724  */
2725 DWORD WINAPI ImmGetConversionListA(
2726   HKL hKL, HIMC hIMC,
2727   LPCSTR pSrc, LPCANDIDATELIST lpDst,
2728   DWORD dwBufLen, UINT uFlag)
2729 {
2730     DWORD ret = 0;
2731     UINT cb;
2732     LPWSTR pszSrcW = NULL;
2733     LPCANDIDATELIST pCL = NULL;
2734     PIMEDPI pImeDpi;
2735 
2736     TRACE("(%p, %p, %s, %p, %lu, 0x%lX)\n", hKL, hIMC, debugstr_a(pSrc),
2737           lpDst, dwBufLen, uFlag);
2738 
2739     pImeDpi = ImmLockOrLoadImeDpi(hKL);
2740     if (pImeDpi == NULL)
2741         return 0;
2742 
2743     if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE))
2744     {
2745         ret = pImeDpi->ImeConversionList(hIMC, pSrc, lpDst, dwBufLen, uFlag);
2746         ImmUnlockImeDpi(pImeDpi);
2747         return ret;
2748     }
2749 
2750     if (pSrc)
2751     {
2752         pszSrcW = Imm32WideFromAnsi(pSrc);
2753         if (pszSrcW == NULL)
2754             goto Quit;
2755     }
2756 
2757     cb = pImeDpi->ImeConversionList(hIMC, pszSrcW, NULL, 0, uFlag);
2758     if (cb == 0)
2759         goto Quit;
2760 
2761     pCL = Imm32HeapAlloc(0, cb);
2762     if (pCL == NULL)
2763         goto Quit;
2764 
2765     cb = pImeDpi->ImeConversionList(hIMC, pszSrcW, pCL, cb, uFlag);
2766     if (cb == 0)
2767         goto Quit;
2768 
2769     ret = CandidateListWideToAnsi(pCL, lpDst, dwBufLen, CP_ACP);
2770 
2771 Quit:
2772     if (pszSrcW)
2773         HeapFree(g_hImm32Heap, 0, pszSrcW);
2774     if (pCL)
2775         HeapFree(g_hImm32Heap, 0, pCL);
2776     ImmUnlockImeDpi(pImeDpi);
2777     return ret;
2778 }
2779 
2780 /***********************************************************************
2781  *		ImmGetConversionListW (IMM32.@)
2782  */
2783 DWORD WINAPI ImmGetConversionListW(
2784   HKL hKL, HIMC hIMC,
2785   LPCWSTR pSrc, LPCANDIDATELIST lpDst,
2786   DWORD dwBufLen, UINT uFlag)
2787 {
2788     DWORD ret = 0;
2789     INT cb;
2790     PIMEDPI pImeDpi;
2791     LPCANDIDATELIST pCL = NULL;
2792     LPSTR pszSrcA = NULL;
2793 
2794     TRACE("(%p, %p, %s, %p, %lu, 0x%lX)\n", hKL, hIMC, debugstr_w(pSrc),
2795           lpDst, dwBufLen, uFlag);
2796 
2797     pImeDpi = ImmLockOrLoadImeDpi(hKL);
2798     if (!pImeDpi)
2799         return 0;
2800 
2801     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
2802     {
2803         ret = pImeDpi->ImeConversionList(hIMC, pSrc, lpDst, dwBufLen, uFlag);
2804         ImmUnlockImeDpi(pImeDpi);
2805         return ret;
2806     }
2807 
2808     if (pSrc)
2809     {
2810         pszSrcA = Imm32AnsiFromWide(pSrc);
2811         if (pszSrcA == NULL)
2812             goto Quit;
2813     }
2814 
2815     cb = pImeDpi->ImeConversionList(hIMC, pszSrcA, NULL, 0, uFlag);
2816     if (cb == 0)
2817         goto Quit;
2818 
2819     pCL = Imm32HeapAlloc(0, cb);
2820     if (!pCL)
2821         goto Quit;
2822 
2823     cb = pImeDpi->ImeConversionList(hIMC, pszSrcA, pCL, cb, uFlag);
2824     if (!cb)
2825         goto Quit;
2826 
2827     ret = CandidateListAnsiToWide(pCL, lpDst, dwBufLen, CP_ACP);
2828 
2829 Quit:
2830     if (pszSrcA)
2831         HeapFree(g_hImm32Heap, 0, pszSrcA);
2832     if (pCL)
2833         HeapFree(g_hImm32Heap, 0, pCL);
2834     ImmUnlockImeDpi(pImeDpi);
2835     return ret;
2836 }
2837 
2838 /***********************************************************************
2839  *		ImmGetConversionStatus (IMM32.@)
2840  */
2841 BOOL WINAPI ImmGetConversionStatus(
2842   HIMC hIMC, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence)
2843 {
2844     LPINPUTCONTEXT pIC;
2845 
2846     TRACE("(%p %p %p)\n", hIMC, lpfdwConversion, lpfdwSentence);
2847 
2848     pIC = ImmLockIMC(hIMC);
2849     if (!pIC)
2850         return FALSE;
2851 
2852     if (lpfdwConversion)
2853         *lpfdwConversion = pIC->fdwConversion;
2854     if (lpfdwSentence)
2855         *lpfdwSentence = pIC->fdwSentence;
2856 
2857     ImmUnlockIMC(hIMC);
2858     return TRUE;
2859 }
2860 
2861 /***********************************************************************
2862  *		ImmGetDefaultIMEWnd (IMM32.@)
2863  */
2864 HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd)
2865 {
2866     if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
2867         return NULL;
2868 
2869     // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
2870     if (hWnd == NULL)
2871         return (HWND)NtUserGetThreadState(3);
2872 
2873     return (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_IME);
2874 }
2875 
2876 /***********************************************************************
2877  *		CtfImmIsCiceroEnabled (IMM32.@)
2878  */
2879 BOOL WINAPI CtfImmIsCiceroEnabled(VOID)
2880 {
2881     return (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED));
2882 }
2883 
2884 /***********************************************************************
2885  *		ImmGetDescriptionA (IMM32.@)
2886  */
2887 UINT WINAPI ImmGetDescriptionA(
2888   HKL hKL, LPSTR lpszDescription, UINT uBufLen)
2889 {
2890     IMEINFOEX info;
2891     size_t cch;
2892 
2893     TRACE("(%p,%p,%d)\n", hKL, lpszDescription, uBufLen);
2894 
2895     if (!ImmGetImeInfoEx(&info, ImeInfoExKeyboardLayout, &hKL) || !IS_IME_HKL(hKL))
2896         return 0;
2897 
2898     StringCchLengthW(info.wszImeDescription, _countof(info.wszImeDescription), &cch);
2899     cch = WideCharToMultiByte(CP_ACP, 0, info.wszImeDescription, (INT)cch,
2900                               lpszDescription, uBufLen, NULL, NULL);
2901     if (uBufLen)
2902         lpszDescription[cch] = 0;
2903     return (UINT)cch;
2904 }
2905 
2906 /***********************************************************************
2907  *		ImmGetDescriptionW (IMM32.@)
2908  */
2909 UINT WINAPI ImmGetDescriptionW(HKL hKL, LPWSTR lpszDescription, UINT uBufLen)
2910 {
2911     IMEINFOEX info;
2912     size_t cch;
2913 
2914     TRACE("(%p, %p, %d)\n", hKL, lpszDescription, uBufLen);
2915 
2916     if (!ImmGetImeInfoEx(&info, ImeInfoExKeyboardLayout, &hKL) || !IS_IME_HKL(hKL))
2917         return 0;
2918 
2919     if (uBufLen != 0)
2920         StringCchCopyW(lpszDescription, uBufLen, info.wszImeDescription);
2921 
2922     StringCchLengthW(info.wszImeDescription, _countof(info.wszImeDescription), &cch);
2923     return (UINT)cch;
2924 }
2925 
2926 static DWORD APIENTRY
2927 ImmGetGuideLineAW(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen, BOOL bAnsi)
2928 {
2929     PCLIENTIMC pClientImc;
2930     LPINPUTCONTEXT pIC;
2931     LPGUIDELINE pGuideLine;
2932     DWORD cb, ret = 0;
2933     LPVOID pvStr, pvPrivate;
2934     BOOL bUsedDefault;
2935 
2936     pClientImc = ImmLockClientImc(hIMC);
2937     if (!pClientImc)
2938         return 0;
2939 
2940     pIC = ImmLockIMC(hIMC);
2941     if (!pIC)
2942     {
2943         ImmUnlockClientImc(pClientImc);
2944         return 0;
2945     }
2946 
2947     pGuideLine = ImmLockIMCC(pIC->hGuideLine);
2948     if (!pGuideLine)
2949     {
2950         ImmUnlockIMC(hIMC);
2951         ImmUnlockClientImc(pClientImc);
2952         return 0;
2953     }
2954 
2955     if (dwIndex == GGL_LEVEL)
2956     {
2957         ret = pGuideLine->dwLevel;
2958         goto Quit;
2959     }
2960 
2961     if (dwIndex == GGL_INDEX)
2962     {
2963         ret = pGuideLine->dwIndex;
2964         goto Quit;
2965     }
2966 
2967     if (dwIndex == GGL_STRING)
2968     {
2969         pvStr = (LPBYTE)pGuideLine + pGuideLine->dwStrOffset;
2970 
2971         /* get size */
2972         if (bAnsi)
2973         {
2974             if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2975             {
2976                 cb = WideCharToMultiByte(CP_ACP, 0, pvStr, pGuideLine->dwStrLen,
2977                                          NULL, 0, NULL, &bUsedDefault);
2978             }
2979             else
2980             {
2981                 cb = pGuideLine->dwStrLen * sizeof(CHAR);
2982             }
2983         }
2984         else
2985         {
2986             if (pClientImc->dwFlags & CLIENTIMC_WIDE)
2987             {
2988                 cb = pGuideLine->dwStrLen * sizeof(WCHAR);
2989             }
2990             else
2991             {
2992                 cb = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pvStr, pGuideLine->dwStrLen,
2993                                          NULL, 0) * sizeof(WCHAR);
2994             }
2995         }
2996 
2997         if (dwBufLen == 0 || cb == 0 || lpBuf == NULL || dwBufLen < cb)
2998         {
2999             ret = cb;
3000             goto Quit;
3001         }
3002 
3003         /* store to buffer */
3004         if (bAnsi)
3005         {
3006             if (pClientImc->dwFlags & CLIENTIMC_WIDE)
3007             {
3008                 ret = WideCharToMultiByte(CP_ACP, 0, pvStr, pGuideLine->dwStrLen,
3009                                           lpBuf, dwBufLen, NULL, &bUsedDefault);
3010                 goto Quit;
3011             }
3012         }
3013         else
3014         {
3015             if (!(pClientImc->dwFlags & CLIENTIMC_WIDE))
3016             {
3017                 ret = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pvStr, pGuideLine->dwStrLen,
3018                                           lpBuf, dwBufLen) * sizeof(WCHAR);
3019                 goto Quit;
3020             }
3021         }
3022 
3023         RtlCopyMemory(lpBuf, pvStr, cb);
3024         ret = cb;
3025         goto Quit;
3026     }
3027 
3028     if (dwIndex == GGL_PRIVATE)
3029     {
3030         pvPrivate = (LPBYTE)pGuideLine + pGuideLine->dwPrivateOffset;
3031 
3032         /* get size */
3033         if (bAnsi)
3034         {
3035             if ((pClientImc->dwFlags & CLIENTIMC_WIDE) &&
3036                 pGuideLine->dwIndex == GL_ID_REVERSECONVERSION)
3037             {
3038                 cb = CandidateListWideToAnsi(pvPrivate, NULL, 0, CP_ACP);
3039             }
3040             else
3041             {
3042                 cb = pGuideLine->dwPrivateSize;
3043             }
3044         }
3045         else
3046         {
3047             if (!(pClientImc->dwFlags & CLIENTIMC_WIDE) &&
3048                 pGuideLine->dwIndex == GL_ID_REVERSECONVERSION)
3049             {
3050                 cb = CandidateListAnsiToWide(pvPrivate, NULL, 0, CP_ACP);
3051             }
3052             else
3053             {
3054                 cb = pGuideLine->dwPrivateSize;
3055             }
3056         }
3057 
3058         if (dwBufLen == 0 || cb == 0 || lpBuf == NULL || dwBufLen < cb)
3059         {
3060             ret = cb;
3061             goto Quit;
3062         }
3063 
3064         /* store to buffer */
3065         if (bAnsi)
3066         {
3067             if ((pClientImc->dwFlags & CLIENTIMC_WIDE) &&
3068                 pGuideLine->dwIndex == GL_ID_REVERSECONVERSION)
3069             {
3070                 ret = CandidateListWideToAnsi(pvPrivate, lpBuf, cb, CP_ACP);
3071                 goto Quit;
3072             }
3073         }
3074         else
3075         {
3076             if (!(pClientImc->dwFlags & CLIENTIMC_WIDE) &&
3077                 pGuideLine->dwIndex == GL_ID_REVERSECONVERSION)
3078             {
3079                 ret = CandidateListAnsiToWide(pvPrivate, lpBuf, cb, CP_ACP);
3080                 goto Quit;
3081             }
3082         }
3083 
3084         RtlCopyMemory(lpBuf, pvPrivate, cb);
3085         ret = cb;
3086         goto Quit;
3087     }
3088 
3089 Quit:
3090     ImmUnlockIMCC(pIC->hGuideLine);
3091     ImmUnlockIMC(hIMC);
3092     ImmUnlockClientImc(pClientImc);
3093     return ret;
3094 }
3095 
3096 /***********************************************************************
3097  *		ImmGetGuideLineA (IMM32.@)
3098  */
3099 DWORD WINAPI ImmGetGuideLineA(
3100   HIMC hIMC, DWORD dwIndex, LPSTR lpBuf, DWORD dwBufLen)
3101 {
3102     TRACE("(%p, %lu, %p, %lu)\n", hIMC, dwIndex, lpBuf, dwBufLen);
3103     return ImmGetGuideLineAW(hIMC, dwIndex, lpBuf, dwBufLen, TRUE);
3104 }
3105 
3106 /***********************************************************************
3107  *		ImmGetGuideLineW (IMM32.@)
3108  */
3109 DWORD WINAPI ImmGetGuideLineW(HIMC hIMC, DWORD dwIndex, LPWSTR lpBuf, DWORD dwBufLen)
3110 {
3111     TRACE("(%p, %lu, %p, %lu)\n", hIMC, dwIndex, lpBuf, dwBufLen);
3112     return ImmGetGuideLineAW(hIMC, dwIndex, lpBuf, dwBufLen, FALSE);
3113 }
3114 
3115 /***********************************************************************
3116  *		ImmGetIMEFileNameA (IMM32.@)
3117  */
3118 UINT WINAPI ImmGetIMEFileNameA( HKL hKL, LPSTR lpszFileName, UINT uBufLen)
3119 {
3120     BOOL bDefUsed;
3121     IMEINFOEX info;
3122     size_t cch;
3123 
3124     TRACE("(%p, %p, %u)\n", hKL, lpszFileName, uBufLen);
3125 
3126     if (!ImmGetImeInfoEx(&info, ImeInfoExKeyboardLayout, &hKL) || !IS_IME_HKL(hKL))
3127     {
3128         if (uBufLen > 0)
3129             lpszFileName[0] = 0;
3130         return 0;
3131     }
3132 
3133     StringCchLengthW(info.wszImeFile, _countof(info.wszImeFile), &cch);
3134 
3135     cch = WideCharToMultiByte(CP_ACP, 0, info.wszImeFile, (INT)cch,
3136                               lpszFileName, uBufLen, NULL, &bDefUsed);
3137     if (uBufLen == 0)
3138         return (UINT)cch;
3139 
3140     if (cch > uBufLen - 1)
3141         cch = uBufLen - 1;
3142 
3143     lpszFileName[cch] = 0;
3144     return (UINT)cch;
3145 }
3146 
3147 /***********************************************************************
3148  *		ImmGetIMEFileNameW (IMM32.@)
3149  */
3150 UINT WINAPI ImmGetIMEFileNameW(HKL hKL, LPWSTR lpszFileName, UINT uBufLen)
3151 {
3152     IMEINFOEX info;
3153     size_t cch;
3154 
3155     TRACE("(%p, %p, %u)\n", hKL, lpszFileName, uBufLen);
3156 
3157     if (!ImmGetImeInfoEx(&info, ImeInfoExKeyboardLayout, &hKL) || !IS_IME_HKL(hKL))
3158     {
3159         if (uBufLen > 0)
3160             lpszFileName[0] = 0;
3161         return 0;
3162     }
3163 
3164     StringCchLengthW(info.wszImeFile, _countof(info.wszImeFile), &cch);
3165     if (uBufLen == 0)
3166         return (UINT)cch;
3167 
3168     StringCchCopyNW(lpszFileName, uBufLen, info.wszImeFile, cch);
3169 
3170     if (cch > uBufLen - 1)
3171         cch = uBufLen - 1;
3172 
3173     lpszFileName[cch] = 0;
3174     return (UINT)cch;
3175 }
3176 
3177 /***********************************************************************
3178  *		ImmGetOpenStatus (IMM32.@)
3179  */
3180 BOOL WINAPI ImmGetOpenStatus(HIMC hIMC)
3181 {
3182     BOOL ret;
3183     LPINPUTCONTEXT pIC;
3184 
3185     TRACE("(%p)\n", hIMC);
3186 
3187     if (!hIMC)
3188         return FALSE;
3189 
3190     pIC = ImmLockIMC(hIMC);
3191     if (!pIC)
3192         return FALSE;
3193 
3194     ret = pIC->fOpen;
3195 
3196     ImmUnlockIMC(hIMC);
3197     return ret;
3198 }
3199 
3200 /***********************************************************************
3201  *		ImmGetProperty (IMM32.@)
3202  */
3203 DWORD WINAPI ImmGetProperty(HKL hKL, DWORD fdwIndex)
3204 {
3205     IMEINFOEX ImeInfoEx;
3206     LPIMEINFO pImeInfo;
3207     DWORD dwValue;
3208     PIMEDPI pImeDpi = NULL;
3209 
3210     TRACE("(%p, %lu)\n", hKL, fdwIndex);
3211 
3212     if (!ImmGetImeInfoEx(&ImeInfoEx, ImeInfoExKeyboardLayout, &hKL))
3213         return FALSE;
3214 
3215     if (fdwIndex == IGP_GETIMEVERSION)
3216         return ImeInfoEx.dwImeWinVersion;
3217 
3218     if (ImeInfoEx.fLoadFlag != 2)
3219     {
3220         pImeDpi = ImmLockOrLoadImeDpi(hKL);
3221         if (pImeDpi == NULL)
3222             return FALSE;
3223 
3224         pImeInfo = &pImeDpi->ImeInfo;
3225     }
3226     else
3227     {
3228         pImeInfo = &ImeInfoEx.ImeInfo;
3229     }
3230 
3231     switch (fdwIndex)
3232     {
3233         case IGP_PROPERTY:      dwValue = pImeInfo->fdwProperty; break;
3234         case IGP_CONVERSION:    dwValue = pImeInfo->fdwConversionCaps; break;
3235         case IGP_SENTENCE:      dwValue = pImeInfo->fdwSentenceCaps; break;
3236         case IGP_UI:            dwValue = pImeInfo->fdwUICaps; break;
3237         case IGP_SETCOMPSTR:    dwValue = pImeInfo->fdwSCSCaps; break;
3238         case IGP_SELECT:        dwValue = pImeInfo->fdwSelectCaps; break;
3239         default:                dwValue = 0; break;
3240     }
3241 
3242     if (pImeDpi)
3243         ImmUnlockImeDpi(pImeDpi);
3244     return dwValue;
3245 }
3246 
3247 /***********************************************************************
3248  *		ImmGetRegisterWordStyleA (IMM32.@)
3249  */
3250 UINT WINAPI ImmGetRegisterWordStyleA(
3251   HKL hKL, UINT nItem, LPSTYLEBUFA lpStyleBuf)
3252 {
3253     UINT iItem, ret = 0;
3254     PIMEDPI pImeDpi;
3255     LPSTYLEBUFA pDestA;
3256     LPSTYLEBUFW pSrcW, pNewStylesW = NULL;
3257     size_t cchW;
3258     INT cchA;
3259 
3260     TRACE("(%p, %u, %p)\n", hKL, nItem, lpStyleBuf);
3261 
3262     pImeDpi = ImmLockOrLoadImeDpi(hKL);
3263     if (!pImeDpi)
3264         return 0;
3265 
3266     if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE))
3267     {
3268         ret = pImeDpi->ImeGetRegisterWordStyle(nItem, lpStyleBuf);
3269         goto Quit;
3270     }
3271 
3272     if (nItem > 0)
3273     {
3274         pNewStylesW = Imm32HeapAlloc(0, nItem * sizeof(STYLEBUFW));
3275         if (!pNewStylesW)
3276             goto Quit;
3277     }
3278 
3279     ret = pImeDpi->ImeGetRegisterWordStyle(nItem, pNewStylesW);
3280 
3281     if (nItem > 0)
3282     {
3283         /* lpStyleBuf <-- pNewStylesW */
3284         for (iItem = 0; iItem < ret; ++iItem)
3285         {
3286             pSrcW = &pNewStylesW[iItem];
3287             pDestA = &lpStyleBuf[iItem];
3288             pDestA->dwStyle = pSrcW->dwStyle;
3289             StringCchLengthW(pSrcW->szDescription, _countof(pSrcW->szDescription), &cchW);
3290             cchA = WideCharToMultiByte(CP_ACP, MB_PRECOMPOSED,
3291                                        pSrcW->szDescription, (INT)cchW,
3292                                        pDestA->szDescription, _countof(pDestA->szDescription),
3293                                        NULL, NULL);
3294             if (cchA > _countof(pDestA->szDescription) - 1)
3295                 cchA = _countof(pDestA->szDescription) - 1;
3296             pDestA->szDescription[cchA] = 0;
3297         }
3298     }
3299 
3300 Quit:
3301     if (pNewStylesW)
3302         HeapFree(g_hImm32Heap, 0, pNewStylesW);
3303     ImmUnlockImeDpi(pImeDpi);
3304     return ret;
3305 }
3306 
3307 /***********************************************************************
3308  *		ImmGetRegisterWordStyleW (IMM32.@)
3309  */
3310 UINT WINAPI ImmGetRegisterWordStyleW(
3311   HKL hKL, UINT nItem, LPSTYLEBUFW lpStyleBuf)
3312 {
3313     UINT iItem, ret = 0;
3314     PIMEDPI pImeDpi;
3315     LPSTYLEBUFA pSrcA, pNewStylesA = NULL;
3316     LPSTYLEBUFW pDestW;
3317     size_t cchA;
3318     INT cchW;
3319 
3320     TRACE("(%p, %u, %p)\n", hKL, nItem, lpStyleBuf);
3321 
3322     pImeDpi = ImmLockOrLoadImeDpi(hKL);
3323     if (!pImeDpi)
3324         return 0;
3325 
3326     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
3327     {
3328         ret = pImeDpi->ImeGetRegisterWordStyle(nItem, lpStyleBuf);
3329         goto Quit;
3330     }
3331 
3332     if (nItem > 0)
3333     {
3334         pNewStylesA = Imm32HeapAlloc(0, nItem * sizeof(STYLEBUFA));
3335         if (!pNewStylesA)
3336             goto Quit;
3337     }
3338 
3339     ret = pImeDpi->ImeGetRegisterWordStyle(nItem, pNewStylesA);
3340 
3341     if (nItem > 0)
3342     {
3343         /* lpStyleBuf <-- pNewStylesA */
3344         for (iItem = 0; iItem < ret; ++iItem)
3345         {
3346             pSrcA = &pNewStylesA[iItem];
3347             pDestW = &lpStyleBuf[iItem];
3348             pDestW->dwStyle = pSrcA->dwStyle;
3349             StringCchLengthA(pSrcA->szDescription, _countof(pSrcA->szDescription), &cchA);
3350             cchW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
3351                                        pSrcA->szDescription, (INT)cchA,
3352                                        pDestW->szDescription, _countof(pDestW->szDescription));
3353             if (cchW > _countof(pDestW->szDescription) - 1)
3354                 cchW = _countof(pDestW->szDescription) - 1;
3355             pDestW->szDescription[cchW] = 0;
3356         }
3357     }
3358 
3359 Quit:
3360     if (pNewStylesA)
3361         HeapFree(g_hImm32Heap, 0, pNewStylesA);
3362     ImmUnlockImeDpi(pImeDpi);
3363     return ret;
3364 }
3365 
3366 /***********************************************************************
3367  *		ImmGetStatusWindowPos (IMM32.@)
3368  */
3369 BOOL WINAPI ImmGetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos)
3370 {
3371     LPINPUTCONTEXT pIC;
3372     BOOL ret;
3373 
3374     TRACE("(%p, %p)\n", hIMC, lpptPos);
3375 
3376     pIC = ImmLockIMC(hIMC);
3377     if (pIC == NULL)
3378         return FALSE;
3379 
3380     ret = !!(pIC->fdwInit & INIT_STATUSWNDPOS);
3381     if (ret)
3382         *lpptPos = pIC->ptStatusWndPos;
3383 
3384     ImmUnlockIMC(hIMC);
3385     return ret;
3386 }
3387 
3388 /***********************************************************************
3389  *		ImmGetVirtualKey (IMM32.@)
3390  */
3391 UINT WINAPI ImmGetVirtualKey(HWND hWnd)
3392 {
3393     HIMC hIMC;
3394     LPINPUTCONTEXTDX pIC;
3395     UINT ret = VK_PROCESSKEY;
3396 
3397     TRACE("(%p)\n", hWnd);
3398 
3399     hIMC = ImmGetContext(hWnd);
3400     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
3401     if (!pIC)
3402         return ret;
3403 
3404     if (pIC->bNeedsTrans)
3405         ret = pIC->nVKey;
3406 
3407     ImmUnlockIMC(hIMC);
3408     return ret;
3409 }
3410 
3411 /***********************************************************************
3412  *		ImmInstallIMEA (IMM32.@)
3413  */
3414 HKL WINAPI ImmInstallIMEA(
3415   LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText)
3416 {
3417     HKL hKL = NULL;
3418     LPWSTR pszFileNameW = NULL, pszLayoutTextW = NULL;
3419 
3420     TRACE("(%s, %s)\n", debugstr_a(lpszIMEFileName), debugstr_a(lpszLayoutText));
3421 
3422     pszFileNameW = Imm32WideFromAnsi(lpszIMEFileName);
3423     if (pszFileNameW == NULL)
3424         goto Quit;
3425 
3426     pszLayoutTextW = Imm32WideFromAnsi(lpszLayoutText);
3427     if (pszLayoutTextW == NULL)
3428         goto Quit;
3429 
3430     hKL = ImmInstallIMEW(pszFileNameW, pszLayoutTextW);
3431 
3432 Quit:
3433     if (pszFileNameW)
3434         HeapFree(g_hImm32Heap, 0, pszFileNameW);
3435     if (pszLayoutTextW)
3436         HeapFree(g_hImm32Heap, 0, pszLayoutTextW);
3437     return hKL;
3438 }
3439 
3440 /***********************************************************************
3441  *		ImmInstallIMEW (IMM32.@)
3442  */
3443 HKL WINAPI ImmInstallIMEW(
3444   LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText)
3445 {
3446     INT lcid = GetUserDefaultLCID();
3447     INT count;
3448     HKL hkl;
3449     DWORD rc;
3450     HKEY hkey;
3451     WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8];
3452 
3453     TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName),
3454                           debugstr_w(lpszLayoutText));
3455 
3456     /* Start with 2.  e001 will be blank and so default to the wine internal IME */
3457     count = 2;
3458 
3459     while (count < 0xfff)
3460     {
3461         DWORD disposition = 0;
3462 
3463         hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count );
3464         wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hkl);
3465 
3466         rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, regKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition);
3467         if (rc == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
3468             break;
3469         else if (rc == ERROR_SUCCESS)
3470             RegCloseKey(hkey);
3471 
3472         count++;
3473     }
3474 
3475     if (count == 0xfff)
3476     {
3477         WARN("Unable to find slot to install IME\n");
3478         return 0;
3479     }
3480 
3481     if (rc == ERROR_SUCCESS)
3482     {
3483         rc = RegSetValueExW(hkey, szImeFileW, 0, REG_SZ, (const BYTE*)lpszIMEFileName,
3484                             (lstrlenW(lpszIMEFileName) + 1) * sizeof(WCHAR));
3485         if (rc == ERROR_SUCCESS)
3486             rc = RegSetValueExW(hkey, szLayoutTextW, 0, REG_SZ, (const BYTE*)lpszLayoutText,
3487                                 (lstrlenW(lpszLayoutText) + 1) * sizeof(WCHAR));
3488         RegCloseKey(hkey);
3489         return hkl;
3490     }
3491     else
3492     {
3493         WARN("Unable to set IME registry values\n");
3494         return 0;
3495     }
3496 }
3497 
3498 /***********************************************************************
3499  *		ImmIsIME (IMM32.@)
3500  */
3501 BOOL WINAPI ImmIsIME(HKL hKL)
3502 {
3503     IMEINFOEX info;
3504     TRACE("(%p)\n", hKL);
3505     return !!ImmGetImeInfoEx(&info, ImeInfoExImeWindow, &hKL);
3506 }
3507 
3508 static BOOL APIENTRY
3509 ImmIsUIMessageAW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
3510 {
3511     switch (msg)
3512     {
3513         case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION:
3514         case WM_IME_COMPOSITION: case WM_IME_SETCONTEXT: case WM_IME_NOTIFY:
3515         case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: case WM_IME_SYSTEM:
3516             break;
3517         default:
3518             return FALSE;
3519     }
3520 
3521     if (!hWndIME)
3522         return TRUE;
3523 
3524     if (bAnsi)
3525         SendMessageA(hWndIME, msg, wParam, lParam);
3526     else
3527         SendMessageW(hWndIME, msg, wParam, lParam);
3528 
3529     return TRUE;
3530 }
3531 
3532 /***********************************************************************
3533  *		ImmIsUIMessageA (IMM32.@)
3534  */
3535 BOOL WINAPI ImmIsUIMessageA(
3536   HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
3537 {
3538     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
3539     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, TRUE);
3540 }
3541 
3542 /***********************************************************************
3543  *		ImmIsUIMessageW (IMM32.@)
3544  */
3545 BOOL WINAPI ImmIsUIMessageW(
3546   HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
3547 {
3548     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
3549     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, FALSE);
3550 }
3551 
3552 /***********************************************************************
3553  *		ImmNotifyIME (IMM32.@)
3554  */
3555 BOOL WINAPI ImmNotifyIME(
3556   HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
3557 {
3558     HKL hKL;
3559     PIMEDPI pImeDpi;
3560     BOOL ret;
3561 
3562     TRACE("(%p, %lu, %lu, %lu)\n", hIMC, dwAction, dwIndex, dwValue);
3563 
3564     if (hIMC && Imm32IsCrossThreadAccess(hIMC))
3565         return FALSE;
3566 
3567     hKL = GetKeyboardLayout(0);
3568     pImeDpi = ImmLockImeDpi(hKL);
3569     if (pImeDpi == NULL)
3570         return FALSE;
3571 
3572     ret = pImeDpi->NotifyIME(hIMC, dwAction, dwIndex, dwValue);
3573     ImmUnlockImeDpi(pImeDpi);
3574     return ret;
3575 }
3576 
3577 /***********************************************************************
3578  *		ImmRegisterWordA (IMM32.@)
3579  */
3580 BOOL WINAPI ImmRegisterWordA(
3581   HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszRegister)
3582 {
3583     BOOL ret = FALSE;
3584     PIMEDPI pImeDpi;
3585     LPWSTR pszReadingW = NULL, pszRegisterW = NULL;
3586 
3587     TRACE("(%p, %s, 0x%lX, %s)\n", hKL, debugstr_a(lpszReading), dwStyle,
3588           debugstr_a(lpszRegister));
3589 
3590     pImeDpi = ImmLockOrLoadImeDpi(hKL);
3591     if (!pImeDpi)
3592         return FALSE;
3593 
3594     if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE))
3595     {
3596         ret = pImeDpi->ImeRegisterWord(lpszReading, dwStyle, lpszRegister);
3597         ImmUnlockImeDpi(pImeDpi);
3598         return ret;
3599     }
3600 
3601     if (lpszReading)
3602     {
3603         pszReadingW = Imm32WideFromAnsi(lpszReading);
3604         if (pszReadingW == NULL)
3605             goto Quit;
3606     }
3607 
3608     if (lpszRegister)
3609     {
3610         pszRegisterW = Imm32WideFromAnsi(lpszRegister);
3611         if (pszRegisterW == NULL)
3612             goto Quit;
3613     }
3614 
3615     ret = pImeDpi->ImeRegisterWord(pszReadingW, dwStyle, pszRegisterW);
3616 
3617 Quit:
3618     if (pszReadingW)
3619         HeapFree(g_hImm32Heap, 0, pszReadingW);
3620     if (pszRegisterW)
3621         HeapFree(g_hImm32Heap, 0, pszRegisterW);
3622     ImmUnlockImeDpi(pImeDpi);
3623     return ret;
3624 }
3625 
3626 /***********************************************************************
3627  *		ImmRegisterWordW (IMM32.@)
3628  */
3629 BOOL WINAPI ImmRegisterWordW(
3630   HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister)
3631 {
3632     BOOL ret = FALSE;
3633     PIMEDPI pImeDpi;
3634     LPSTR pszReadingA = NULL, pszRegisterA = NULL;
3635 
3636     TRACE("(%p, %s, 0x%lX, %s)\n", hKL, debugstr_w(lpszReading), dwStyle,
3637           debugstr_w(lpszRegister));
3638 
3639     pImeDpi = ImmLockOrLoadImeDpi(hKL);
3640     if (!pImeDpi)
3641         return FALSE;
3642 
3643     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
3644     {
3645         ret = pImeDpi->ImeRegisterWord(lpszReading, dwStyle, lpszRegister);
3646         ImmUnlockImeDpi(pImeDpi);
3647         return ret;
3648     }
3649 
3650     if (lpszReading)
3651     {
3652         pszReadingA = Imm32AnsiFromWide(lpszReading);
3653         if (!pszReadingA)
3654             goto Quit;
3655     }
3656 
3657     if (lpszRegister)
3658     {
3659         pszRegisterA = Imm32AnsiFromWide(lpszRegister);
3660         if (!pszRegisterA)
3661             goto Quit;
3662     }
3663 
3664     ret = pImeDpi->ImeRegisterWord(pszReadingA, dwStyle, pszRegisterA);
3665 
3666 Quit:
3667     if (pszReadingA)
3668         HeapFree(g_hImm32Heap, 0, pszReadingA);
3669     if (pszRegisterA)
3670         HeapFree(g_hImm32Heap, 0, pszRegisterA);
3671     ImmUnlockImeDpi(pImeDpi);
3672     return ret;
3673 }
3674 
3675 /***********************************************************************
3676  *		ImmReleaseContext (IMM32.@)
3677  */
3678 BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
3679 {
3680     TRACE("(%p, %p)\n", hWnd, hIMC);
3681     UNREFERENCED_PARAMETER(hWnd);
3682     UNREFERENCED_PARAMETER(hIMC);
3683     return TRUE; // Do nothing. This is correct.
3684 }
3685 
3686 /***********************************************************************
3687 *              ImmRequestMessageA(IMM32.@)
3688 */
3689 LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
3690 {
3691     InputContextData *data = get_imc_data(hIMC);
3692 
3693     TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
3694 
3695     if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
3696 
3697     SetLastError(ERROR_INVALID_HANDLE);
3698     return 0;
3699 }
3700 
3701 /***********************************************************************
3702 *              ImmRequestMessageW(IMM32.@)
3703 */
3704 LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
3705 {
3706     InputContextData *data = get_imc_data(hIMC);
3707 
3708     TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
3709 
3710     if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
3711 
3712     SetLastError(ERROR_INVALID_HANDLE);
3713     return 0;
3714 }
3715 
3716 /***********************************************************************
3717  *		ImmSetCandidateWindow (IMM32.@)
3718  */
3719 BOOL WINAPI ImmSetCandidateWindow(
3720   HIMC hIMC, LPCANDIDATEFORM lpCandidate)
3721 {
3722     HWND hWnd;
3723     LPINPUTCONTEXT pIC;
3724 
3725     TRACE("(%p, %p)\n", hIMC, lpCandidate);
3726 
3727     if (lpCandidate->dwIndex >= MAX_CANDIDATEFORM)
3728         return FALSE;
3729 
3730     if (Imm32IsCrossThreadAccess(hIMC))
3731         return FALSE;
3732 
3733     pIC = ImmLockIMC(hIMC);
3734     if (pIC == NULL)
3735         return FALSE;
3736 
3737     hWnd = pIC->hWnd;
3738     pIC->cfCandForm[lpCandidate->dwIndex] = *lpCandidate;
3739 
3740     ImmUnlockIMC(hIMC);
3741 
3742     Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS,
3743                       IMN_SETCANDIDATEPOS, (1 << (BYTE)lpCandidate->dwIndex));
3744     return TRUE;
3745 }
3746 
3747 static VOID APIENTRY WideToAnsiLogFont(const LOGFONTW *plfW, LPLOGFONTA plfA)
3748 {
3749     BOOL bUsedDef;
3750     size_t cchW, cchA = _countof(plfA->lfFaceName);
3751     RtlCopyMemory(plfA, plfW, offsetof(LOGFONTA, lfFaceName));
3752     StringCchLengthW(plfW->lfFaceName, _countof(plfW->lfFaceName), &cchW);
3753     cchA = WideCharToMultiByte(CP_ACP, 0, plfW->lfFaceName, (INT)cchW,
3754                                plfA->lfFaceName, (INT)cchA, NULL, &bUsedDef);
3755     if (cchA > _countof(plfA->lfFaceName) - 1)
3756         cchA = _countof(plfA->lfFaceName) - 1;
3757     plfA->lfFaceName[cchA] = 0;
3758 }
3759 
3760 static VOID APIENTRY AnsiToWideLogFont(const LOGFONTA *plfA, LPLOGFONTW plfW)
3761 {
3762     size_t cchA, cchW = _countof(plfW->lfFaceName);
3763     RtlCopyMemory(plfW, plfA, offsetof(LOGFONTW, lfFaceName));
3764     StringCchLengthA(plfA->lfFaceName, _countof(plfA->lfFaceName), &cchA);
3765     cchW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plfA->lfFaceName, (INT)cchA,
3766                                plfW->lfFaceName, (INT)cchW);
3767     if (cchW > _countof(plfW->lfFaceName) - 1)
3768         cchW = _countof(plfW->lfFaceName) - 1;
3769     plfW->lfFaceName[cchW] = 0;
3770 }
3771 
3772 /***********************************************************************
3773  *		ImmSetCompositionFontA (IMM32.@)
3774  */
3775 BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
3776 {
3777     LOGFONTW lfW;
3778     PCLIENTIMC pClientImc;
3779     BOOL bWide;
3780     LPINPUTCONTEXTDX pIC;
3781     LCID lcid;
3782     HWND hWnd;
3783     PTEB pTeb;
3784 
3785     TRACE("(%p, %p)\n", hIMC, lplf);
3786 
3787     if (Imm32IsCrossThreadAccess(hIMC))
3788         return FALSE;
3789 
3790     pClientImc = ImmLockClientImc(hIMC);
3791     if (pClientImc == NULL)
3792         return FALSE;
3793 
3794     bWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
3795     ImmUnlockClientImc(pClientImc);
3796 
3797     if (bWide)
3798     {
3799         AnsiToWideLogFont(lplf, &lfW);
3800         return ImmSetCompositionFontW(hIMC, &lfW);
3801     }
3802 
3803     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
3804     if (pIC == NULL)
3805         return FALSE;
3806 
3807     pTeb = NtCurrentTeb();
3808     if (pTeb->Win32ClientInfo[2] < 0x400)
3809     {
3810         lcid = GetSystemDefaultLCID();
3811         if (PRIMARYLANGID(lcid) == LANG_JAPANESE && !(pIC->dwUIFlags & 2) &&
3812             pIC->cfCompForm.dwStyle != CFS_DEFAULT)
3813         {
3814             PostMessageA(pIC->hWnd, WM_IME_REPORT, IR_CHANGECONVERT, 0);
3815         }
3816     }
3817 
3818     pIC->lfFont.A = *lplf;
3819     pIC->fdwInit |= INIT_LOGFONT;
3820     hWnd = pIC->hWnd;
3821 
3822     ImmUnlockIMC(hIMC);
3823 
3824     Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT,
3825                       IMN_SETCOMPOSITIONFONT, 0);
3826     return TRUE;
3827 }
3828 
3829 /***********************************************************************
3830  *		ImmSetCompositionFontW (IMM32.@)
3831  */
3832 BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
3833 {
3834     LOGFONTA lfA;
3835     PCLIENTIMC pClientImc;
3836     BOOL bWide;
3837     HWND hWnd;
3838     LPINPUTCONTEXTDX pIC;
3839     PTEB pTeb;
3840     LCID lcid;
3841 
3842     TRACE("(%p, %p)\n", hIMC, lplf);
3843 
3844     if (Imm32IsCrossThreadAccess(hIMC))
3845         return FALSE;
3846 
3847     pClientImc = ImmLockClientImc(hIMC);
3848     if (pClientImc == NULL)
3849         return FALSE;
3850 
3851     bWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
3852     ImmUnlockClientImc(pClientImc);
3853 
3854     if (!bWide)
3855     {
3856         WideToAnsiLogFont(lplf, &lfA);
3857         return ImmSetCompositionFontA(hIMC, &lfA);
3858     }
3859 
3860     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
3861     if (pIC == NULL)
3862         return FALSE;
3863 
3864     pTeb = NtCurrentTeb();
3865     if (pTeb->Win32ClientInfo[2] < 0x400)
3866     {
3867         lcid = GetSystemDefaultLCID();
3868         if (PRIMARYLANGID(lcid) == LANG_JAPANESE &&
3869             !(pIC->dwUIFlags & 2) &&
3870             pIC->cfCompForm.dwStyle != CFS_DEFAULT)
3871         {
3872             PostMessageW(pIC->hWnd, WM_IME_REPORT, IR_CHANGECONVERT, 0);
3873         }
3874     }
3875 
3876     pIC->lfFont.W = *lplf;
3877     pIC->fdwInit |= INIT_LOGFONT;
3878     hWnd = pIC->hWnd;
3879 
3880     ImmUnlockIMC(hIMC);
3881 
3882     Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT,
3883                       IMN_SETCOMPOSITIONFONT, 0);
3884     return TRUE;
3885 }
3886 
3887 /***********************************************************************
3888  *		ImmSetCompositionStringA (IMM32.@)
3889  */
3890 BOOL WINAPI ImmSetCompositionStringA(
3891   HIMC hIMC, DWORD dwIndex,
3892   LPCVOID lpComp, DWORD dwCompLen,
3893   LPCVOID lpRead, DWORD dwReadLen)
3894 {
3895     DWORD comp_len;
3896     DWORD read_len;
3897     WCHAR *CompBuffer = NULL;
3898     WCHAR *ReadBuffer = NULL;
3899     BOOL rc;
3900     InputContextData *data = get_imc_data(hIMC);
3901 
3902     TRACE("(%p, %d, %p, %d, %p, %d):\n",
3903             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
3904 
3905     if (!data)
3906         return FALSE;
3907 
3908     if (!(dwIndex == SCS_SETSTR ||
3909           dwIndex == SCS_CHANGEATTR ||
3910           dwIndex == SCS_CHANGECLAUSE ||
3911           dwIndex == SCS_SETRECONVERTSTRING ||
3912           dwIndex == SCS_QUERYRECONVERTSTRING))
3913         return FALSE;
3914 
3915     if (!is_himc_ime_unicode(data))
3916         return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
3917                         dwCompLen, lpRead, dwReadLen);
3918 
3919     comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0);
3920     if (comp_len)
3921     {
3922         CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len * sizeof(WCHAR));
3923         MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len);
3924     }
3925 
3926     read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0);
3927     if (read_len)
3928     {
3929         ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len * sizeof(WCHAR));
3930         MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len);
3931     }
3932 
3933     rc =  ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len,
3934                                    ReadBuffer, read_len);
3935 
3936     HeapFree(GetProcessHeap(), 0, CompBuffer);
3937     HeapFree(GetProcessHeap(), 0, ReadBuffer);
3938 
3939     return rc;
3940 }
3941 
3942 /***********************************************************************
3943  *		ImmSetCompositionStringW (IMM32.@)
3944  */
3945 BOOL WINAPI ImmSetCompositionStringW(
3946 	HIMC hIMC, DWORD dwIndex,
3947 	LPCVOID lpComp, DWORD dwCompLen,
3948 	LPCVOID lpRead, DWORD dwReadLen)
3949 {
3950     DWORD comp_len;
3951     DWORD read_len;
3952     CHAR *CompBuffer = NULL;
3953     CHAR *ReadBuffer = NULL;
3954     BOOL rc;
3955     InputContextData *data = get_imc_data(hIMC);
3956 
3957     TRACE("(%p, %d, %p, %d, %p, %d):\n",
3958             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
3959 
3960     if (!data)
3961         return FALSE;
3962 
3963     if (!(dwIndex == SCS_SETSTR ||
3964           dwIndex == SCS_CHANGEATTR ||
3965           dwIndex == SCS_CHANGECLAUSE ||
3966           dwIndex == SCS_SETRECONVERTSTRING ||
3967           dwIndex == SCS_QUERYRECONVERTSTRING))
3968         return FALSE;
3969 
3970     if (is_himc_ime_unicode(data))
3971         return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
3972                         dwCompLen, lpRead, dwReadLen);
3973 
3974     comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL,
3975                                    NULL);
3976     if (comp_len)
3977     {
3978         CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len);
3979         WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len,
3980                             NULL, NULL);
3981     }
3982 
3983     read_len = WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, NULL, 0, NULL,
3984                                    NULL);
3985     if (read_len)
3986     {
3987         ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len);
3988         WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len,
3989                             NULL, NULL);
3990     }
3991 
3992     rc =  ImmSetCompositionStringA(hIMC, dwIndex, CompBuffer, comp_len,
3993                                    ReadBuffer, read_len);
3994 
3995     HeapFree(GetProcessHeap(), 0, CompBuffer);
3996     HeapFree(GetProcessHeap(), 0, ReadBuffer);
3997 
3998     return rc;
3999 }
4000 
4001 /***********************************************************************
4002  *		ImmSetCompositionWindow (IMM32.@)
4003  */
4004 BOOL WINAPI ImmSetCompositionWindow(
4005   HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
4006 {
4007     LPINPUTCONTEXT pIC;
4008     HWND hWnd;
4009 
4010     if (Imm32IsCrossThreadAccess(hIMC))
4011         return FALSE;
4012 
4013     pIC = ImmLockIMC(hIMC);
4014     if (pIC == NULL)
4015         return FALSE;
4016 
4017     pIC->cfCompForm = *lpCompForm;
4018     pIC->fdwInit |= INIT_COMPFORM;
4019 
4020     hWnd = pIC->hWnd;
4021 
4022     ImmUnlockIMC(hIMC);
4023 
4024     Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0,
4025                       IMC_SETCOMPOSITIONWINDOW, IMN_SETCOMPOSITIONWINDOW, 0);
4026     return TRUE;
4027 }
4028 
4029 /***********************************************************************
4030  *		ImmSetConversionStatus (IMM32.@)
4031  */
4032 BOOL WINAPI ImmSetConversionStatus(
4033   HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence)
4034 {
4035     HKL hKL;
4036     LPINPUTCONTEXT pIC;
4037     DWORD dwOldConversion, dwOldSentence;
4038     BOOL fConversionChange = FALSE, fSentenceChange = FALSE;
4039     HWND hWnd;
4040 
4041     TRACE("(%p, 0x%lX, 0x%lX)\n", hIMC, fdwConversion, fdwSentence);
4042 
4043     hKL = GetKeyboardLayout(0);
4044     if (!IS_IME_HKL(hKL))
4045     {
4046         if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
4047         {
4048             FIXME("Cicero\n");
4049             return FALSE;
4050         }
4051     }
4052 
4053     if (Imm32IsCrossThreadAccess(hIMC))
4054         return FALSE;
4055 
4056     pIC = ImmLockIMC(hIMC);
4057     if (pIC == NULL)
4058         return FALSE;
4059 
4060     if (pIC->fdwConversion != fdwConversion)
4061     {
4062         dwOldConversion = pIC->fdwConversion;
4063         pIC->fdwConversion = fdwConversion;
4064         fConversionChange = TRUE;
4065     }
4066 
4067     if (pIC->fdwSentence != fdwSentence)
4068     {
4069         dwOldSentence = pIC->fdwSentence;
4070         pIC->fdwSentence = fdwSentence;
4071         fSentenceChange = TRUE;
4072     }
4073 
4074     hWnd = pIC->hWnd;
4075     ImmUnlockIMC(hIMC);
4076 
4077     if (fConversionChange)
4078     {
4079         Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, dwOldConversion,
4080                           IMC_SETCONVERSIONMODE, IMN_SETCONVERSIONMODE, 0);
4081         NtUserNotifyIMEStatus(hWnd, hIMC, fdwConversion);
4082     }
4083 
4084     if (fSentenceChange)
4085     {
4086         Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, dwOldSentence,
4087                           IMC_SETSENTENCEMODE, IMN_SETSENTENCEMODE, 0);
4088     }
4089 
4090     return TRUE;
4091 }
4092 
4093 /***********************************************************************
4094  *		ImmLockImeDpi (IMM32.@)
4095  */
4096 PIMEDPI WINAPI ImmLockImeDpi(HKL hKL)
4097 {
4098     PIMEDPI pImeDpi = NULL;
4099 
4100     TRACE("(%p)\n", hKL);
4101 
4102     RtlEnterCriticalSection(&g_csImeDpi);
4103 
4104     /* Find by hKL */
4105     for (pImeDpi = g_pImeDpiList; pImeDpi; pImeDpi = pImeDpi->pNext)
4106     {
4107         if (pImeDpi->hKL == hKL) /* found */
4108         {
4109             /* lock if possible */
4110             if (pImeDpi->dwFlags & IMEDPI_FLAG_UNKNOWN)
4111                 pImeDpi = NULL;
4112             else
4113                 ++(pImeDpi->cLockObj);
4114             break;
4115         }
4116     }
4117 
4118     RtlLeaveCriticalSection(&g_csImeDpi);
4119     return pImeDpi;
4120 }
4121 
4122 /***********************************************************************
4123  *		ImmUnlockImeDpi (IMM32.@)
4124  */
4125 VOID WINAPI ImmUnlockImeDpi(PIMEDPI pImeDpi)
4126 {
4127     PIMEDPI *ppEntry;
4128 
4129     TRACE("(%p)\n", pImeDpi);
4130 
4131     if (pImeDpi == NULL)
4132         return;
4133 
4134     RtlEnterCriticalSection(&g_csImeDpi);
4135 
4136     /* unlock */
4137     --(pImeDpi->cLockObj);
4138     if (pImeDpi->cLockObj != 0)
4139     {
4140         RtlLeaveCriticalSection(&g_csImeDpi);
4141         return;
4142     }
4143 
4144     if ((pImeDpi->dwFlags & IMEDPI_FLAG_UNKNOWN) == 0)
4145     {
4146         if ((pImeDpi->dwFlags & IMEDPI_FLAG_LOCKED) == 0 ||
4147             (pImeDpi->ImeInfo.fdwProperty & IME_PROP_END_UNLOAD) == 0)
4148         {
4149             RtlLeaveCriticalSection(&g_csImeDpi);
4150             return;
4151         }
4152     }
4153 
4154     /* Remove from list */
4155     for (ppEntry = &g_pImeDpiList; *ppEntry; ppEntry = &((*ppEntry)->pNext))
4156     {
4157         if (*ppEntry == pImeDpi) /* found */
4158         {
4159             *ppEntry = pImeDpi->pNext;
4160             break;
4161         }
4162     }
4163 
4164     Imm32FreeImeDpi(pImeDpi, TRUE);
4165     HeapFree(g_hImm32Heap, 0, pImeDpi);
4166 
4167     RtlLeaveCriticalSection(&g_csImeDpi);
4168 }
4169 
4170 /***********************************************************************
4171  *		ImmSetOpenStatus (IMM32.@)
4172  */
4173 BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen)
4174 {
4175     DWORD dwConversion;
4176     LPINPUTCONTEXT pIC;
4177     HWND hWnd;
4178     BOOL bHasChange = FALSE;
4179 
4180     TRACE("(%p, %d)\n", hIMC, fOpen);
4181 
4182     if (Imm32IsCrossThreadAccess(hIMC))
4183         return FALSE;
4184 
4185     pIC = ImmLockIMC(hIMC);
4186     if (pIC == NULL)
4187         return FALSE;
4188 
4189     if (pIC->fOpen != fOpen)
4190     {
4191         pIC->fOpen = fOpen;
4192         hWnd = pIC->hWnd;
4193         dwConversion = pIC->fdwConversion;
4194         bHasChange = TRUE;
4195     }
4196 
4197     ImmUnlockIMC(hIMC);
4198 
4199     if (bHasChange)
4200     {
4201         Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0,
4202                           IMC_SETOPENSTATUS, IMN_SETOPENSTATUS, 0);
4203         NtUserNotifyIMEStatus(hWnd, hIMC, dwConversion);
4204     }
4205 
4206     return TRUE;
4207 }
4208 
4209 /***********************************************************************
4210  *		ImmSetStatusWindowPos (IMM32.@)
4211  */
4212 BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos)
4213 {
4214     LPINPUTCONTEXT pIC;
4215     HWND hWnd;
4216 
4217     TRACE("(%p, {%ld, %ld})\n", hIMC, lpptPos->x, lpptPos->y);
4218 
4219     if (Imm32IsCrossThreadAccess(hIMC))
4220         return FALSE;
4221 
4222     pIC = ImmLockIMC(hIMC);
4223     if (!pIC)
4224         return FALSE;
4225 
4226     hWnd = pIC->hWnd;
4227     pIC->ptStatusWndPos = *lpptPos;
4228     pIC->fdwInit |= INIT_STATUSWNDPOS;
4229 
4230     ImmUnlockIMC(hIMC);
4231 
4232     Imm32NotifyAction(hIMC, hWnd, NI_CONTEXTUPDATED, 0,
4233                       IMC_SETSTATUSWINDOWPOS, IMN_SETSTATUSWINDOWPOS, 0);
4234     return TRUE;
4235 }
4236 
4237 /***********************************************************************
4238  *              ImmCreateSoftKeyboard(IMM32.@)
4239  */
4240 HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
4241 {
4242     FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
4243     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4244     return 0;
4245 }
4246 
4247 /***********************************************************************
4248  *              ImmDestroySoftKeyboard(IMM32.@)
4249  */
4250 BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
4251 {
4252     TRACE("(%p)\n", hSoftWnd);
4253     return DestroyWindow(hSoftWnd);
4254 }
4255 
4256 /***********************************************************************
4257  *              ImmShowSoftKeyboard(IMM32.@)
4258  */
4259 BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
4260 {
4261     TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
4262     if (hSoftWnd)
4263         return ShowWindow(hSoftWnd, nCmdShow);
4264     return FALSE;
4265 }
4266 
4267 /***********************************************************************
4268  *		ImmSimulateHotKey (IMM32.@)
4269  */
4270 BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
4271 {
4272     HIMC hIMC;
4273     DWORD dwThreadId;
4274     HKL hKL;
4275     BOOL ret;
4276 
4277     TRACE("(%p, 0x%lX)\n", hWnd, dwHotKeyID);
4278 
4279     hIMC = ImmGetContext(hWnd);
4280     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
4281     hKL = GetKeyboardLayout(dwThreadId);
4282     ret = Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID);
4283     ImmReleaseContext(hWnd, hIMC);
4284     return ret;
4285 }
4286 
4287 /***********************************************************************
4288  *		ImmUnregisterWordA (IMM32.@)
4289  */
4290 BOOL WINAPI ImmUnregisterWordA(
4291   HKL hKL, LPCSTR lpszReading, DWORD dwStyle, LPCSTR lpszUnregister)
4292 {
4293     BOOL ret = FALSE;
4294     PIMEDPI pImeDpi;
4295     LPWSTR pszReadingW = NULL, pszUnregisterW = NULL;
4296 
4297     TRACE("(%p, %s, 0x%lX, %s)\n", hKL, debugstr_a(lpszReading), dwStyle,
4298           debugstr_a(lpszUnregister));
4299 
4300     pImeDpi = ImmLockOrLoadImeDpi(hKL);
4301     if (pImeDpi == NULL)
4302         return FALSE;
4303 
4304     if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE))
4305     {
4306         ret = pImeDpi->ImeUnregisterWord(lpszReading, dwStyle, lpszUnregister);
4307         ImmUnlockImeDpi(pImeDpi);
4308         return ret;
4309     }
4310 
4311     if (lpszReading)
4312     {
4313         pszReadingW = Imm32WideFromAnsi(lpszReading);
4314         if (pszReadingW == NULL)
4315             goto Quit;
4316     }
4317 
4318     if (lpszUnregister)
4319     {
4320         pszUnregisterW = Imm32WideFromAnsi(lpszUnregister);
4321         if (pszUnregisterW == NULL)
4322             goto Quit;
4323     }
4324 
4325     ret = pImeDpi->ImeUnregisterWord(pszReadingW, dwStyle, pszUnregisterW);
4326 
4327 Quit:
4328     if (pszReadingW)
4329         HeapFree(g_hImm32Heap, 0, pszReadingW);
4330     if (pszUnregisterW)
4331         HeapFree(g_hImm32Heap, 0, pszUnregisterW);
4332     ImmUnlockImeDpi(pImeDpi);
4333     return ret;
4334 }
4335 
4336 /***********************************************************************
4337  *		ImmUnregisterWordW (IMM32.@)
4338  */
4339 BOOL WINAPI ImmUnregisterWordW(
4340   HKL hKL, LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister)
4341 {
4342     BOOL ret = FALSE;
4343     PIMEDPI pImeDpi;
4344     LPSTR pszReadingA = NULL, pszUnregisterA = NULL;
4345 
4346     TRACE("(%p, %s, 0x%lX, %s)\n", hKL, debugstr_w(lpszReading), dwStyle,
4347           debugstr_w(lpszUnregister));
4348 
4349     pImeDpi = ImmLockOrLoadImeDpi(hKL);
4350     if (!pImeDpi)
4351         return FALSE;
4352 
4353     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
4354     {
4355         ret = pImeDpi->ImeUnregisterWord(lpszReading, dwStyle, lpszUnregister);
4356         ImmUnlockImeDpi(pImeDpi);
4357         return ret;
4358     }
4359 
4360     if (lpszReading)
4361     {
4362         pszReadingA = Imm32AnsiFromWide(lpszReading);
4363         if (!pszReadingA)
4364             goto Quit;
4365     }
4366 
4367     if (lpszUnregister)
4368     {
4369         pszUnregisterA = Imm32AnsiFromWide(lpszUnregister);
4370         if (!pszUnregisterA)
4371             goto Quit;
4372     }
4373 
4374     ret = pImeDpi->ImeUnregisterWord(pszReadingA, dwStyle, pszUnregisterA);
4375 
4376 Quit:
4377     if (pszReadingA)
4378         HeapFree(g_hImm32Heap, 0, pszReadingA);
4379     if (pszUnregisterA)
4380         HeapFree(g_hImm32Heap, 0, pszUnregisterA);
4381     ImmUnlockImeDpi(pImeDpi);
4382     return ret;
4383 }
4384 
4385 /***********************************************************************
4386  *		ImmGetImeMenuItemsA (IMM32.@)
4387  */
4388 DWORD WINAPI ImmGetImeMenuItemsA( HIMC hIMC, DWORD dwFlags, DWORD dwType,
4389    LPIMEMENUITEMINFOA lpImeParentMenu, LPIMEMENUITEMINFOA lpImeMenu,
4390     DWORD dwSize)
4391 {
4392     InputContextData *data = get_imc_data(hIMC);
4393     TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
4394         lpImeParentMenu, lpImeMenu, dwSize);
4395 
4396     if (!data)
4397     {
4398         SetLastError(ERROR_INVALID_HANDLE);
4399         return 0;
4400     }
4401 
4402     if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
4403     {
4404         if (!is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
4405             return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
4406                                 (IMEMENUITEMINFOW*)lpImeParentMenu,
4407                                 (IMEMENUITEMINFOW*)lpImeMenu, dwSize);
4408         else
4409         {
4410             IMEMENUITEMINFOW lpImeParentMenuW;
4411             IMEMENUITEMINFOW *lpImeMenuW, *parent = NULL;
4412             DWORD rc;
4413 
4414             if (lpImeParentMenu)
4415                 parent = &lpImeParentMenuW;
4416             if (lpImeMenu)
4417             {
4418                 int count = dwSize / sizeof(LPIMEMENUITEMINFOA);
4419                 dwSize = count * sizeof(IMEMENUITEMINFOW);
4420                 lpImeMenuW = HeapAlloc(GetProcessHeap(), 0, dwSize);
4421             }
4422             else
4423                 lpImeMenuW = NULL;
4424 
4425             rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
4426                                 parent, lpImeMenuW, dwSize);
4427 
4428             if (lpImeParentMenu)
4429             {
4430                 memcpy(lpImeParentMenu,&lpImeParentMenuW,sizeof(IMEMENUITEMINFOA));
4431                 lpImeParentMenu->hbmpItem = lpImeParentMenuW.hbmpItem;
4432                 WideCharToMultiByte(CP_ACP, 0, lpImeParentMenuW.szString,
4433                     -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE,
4434                     NULL, NULL);
4435             }
4436             if (lpImeMenu && rc)
4437             {
4438                 unsigned int i;
4439                 for (i = 0; i < rc; i++)
4440                 {
4441                     memcpy(&lpImeMenu[i],&lpImeMenuW[1],sizeof(IMEMENUITEMINFOA));
4442                     lpImeMenu[i].hbmpItem = lpImeMenuW[i].hbmpItem;
4443                     WideCharToMultiByte(CP_ACP, 0, lpImeMenuW[i].szString,
4444                         -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE,
4445                         NULL, NULL);
4446                 }
4447             }
4448             HeapFree(GetProcessHeap(),0,lpImeMenuW);
4449             return rc;
4450         }
4451     }
4452     else
4453         return 0;
4454 }
4455 
4456 /***********************************************************************
4457 *		ImmGetImeMenuItemsW (IMM32.@)
4458 */
4459 DWORD WINAPI ImmGetImeMenuItemsW( HIMC hIMC, DWORD dwFlags, DWORD dwType,
4460    LPIMEMENUITEMINFOW lpImeParentMenu, LPIMEMENUITEMINFOW lpImeMenu,
4461    DWORD dwSize)
4462 {
4463     InputContextData *data = get_imc_data(hIMC);
4464     TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
4465         lpImeParentMenu, lpImeMenu, dwSize);
4466 
4467     if (!data)
4468     {
4469         SetLastError(ERROR_INVALID_HANDLE);
4470         return 0;
4471     }
4472 
4473     if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
4474     {
4475         if (is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
4476             return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
4477                                 lpImeParentMenu, lpImeMenu, dwSize);
4478         else
4479         {
4480             IMEMENUITEMINFOA lpImeParentMenuA;
4481             IMEMENUITEMINFOA *lpImeMenuA, *parent = NULL;
4482             DWORD rc;
4483 
4484             if (lpImeParentMenu)
4485                 parent = &lpImeParentMenuA;
4486             if (lpImeMenu)
4487             {
4488                 int count = dwSize / sizeof(LPIMEMENUITEMINFOW);
4489                 dwSize = count * sizeof(IMEMENUITEMINFOA);
4490                 lpImeMenuA = HeapAlloc(GetProcessHeap(), 0, dwSize);
4491             }
4492             else
4493                 lpImeMenuA = NULL;
4494 
4495             rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
4496                                 (IMEMENUITEMINFOW*)parent,
4497                                 (IMEMENUITEMINFOW*)lpImeMenuA, dwSize);
4498 
4499             if (lpImeParentMenu)
4500             {
4501                 memcpy(lpImeParentMenu,&lpImeParentMenuA,sizeof(IMEMENUITEMINFOA));
4502                 lpImeParentMenu->hbmpItem = lpImeParentMenuA.hbmpItem;
4503                 MultiByteToWideChar(CP_ACP, 0, lpImeParentMenuA.szString,
4504                     -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE);
4505             }
4506             if (lpImeMenu && rc)
4507             {
4508                 unsigned int i;
4509                 for (i = 0; i < rc; i++)
4510                 {
4511                     memcpy(&lpImeMenu[i],&lpImeMenuA[1],sizeof(IMEMENUITEMINFOA));
4512                     lpImeMenu[i].hbmpItem = lpImeMenuA[i].hbmpItem;
4513                     MultiByteToWideChar(CP_ACP, 0, lpImeMenuA[i].szString,
4514                         -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE);
4515                 }
4516             }
4517             HeapFree(GetProcessHeap(),0,lpImeMenuA);
4518             return rc;
4519         }
4520     }
4521     else
4522         return 0;
4523 }
4524 
4525 /***********************************************************************
4526 *		ImmLockIMC(IMM32.@)
4527 */
4528 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
4529 {
4530     InputContextData *data = get_imc_data(hIMC);
4531 
4532     if (!data)
4533         return NULL;
4534     data->dwLock++;
4535     return &data->IMC;
4536 }
4537 
4538 /***********************************************************************
4539 *		ImmUnlockIMC(IMM32.@)
4540 */
4541 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
4542 {
4543     PCLIENTIMC pClientImc;
4544     HIMC hClientImc;
4545 
4546     pClientImc = ImmLockClientImc(hIMC);
4547     if (pClientImc == NULL)
4548         return FALSE;
4549 
4550     hClientImc = pClientImc->hImc;
4551     if (hClientImc)
4552         LocalUnlock(hClientImc);
4553 
4554     InterlockedDecrement(&pClientImc->cLockObj);
4555     ImmUnlockClientImc(pClientImc);
4556     return TRUE;
4557 }
4558 
4559 /***********************************************************************
4560 *		ImmGetIMCLockCount(IMM32.@)
4561 */
4562 DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC)
4563 {
4564     DWORD ret;
4565     HIMC hClientImc;
4566     PCLIENTIMC pClientImc;
4567 
4568     pClientImc = ImmLockClientImc(hIMC);
4569     if (pClientImc == NULL)
4570         return 0;
4571 
4572     ret = 0;
4573     hClientImc = pClientImc->hImc;
4574     if (hClientImc)
4575         ret = (LocalFlags(hClientImc) & LMEM_LOCKCOUNT);
4576 
4577     ImmUnlockClientImc(pClientImc);
4578     return ret;
4579 }
4580 
4581 /***********************************************************************
4582 *		ImmCreateIMCC(IMM32.@)
4583 */
4584 HIMCC  WINAPI ImmCreateIMCC(DWORD size)
4585 {
4586     if (size < 4)
4587         size = 4;
4588     return LocalAlloc(LHND, size);
4589 }
4590 
4591 /***********************************************************************
4592 *       ImmDestroyIMCC(IMM32.@)
4593 */
4594 HIMCC WINAPI ImmDestroyIMCC(HIMCC block)
4595 {
4596     if (block)
4597         return LocalFree(block);
4598     return NULL;
4599 }
4600 
4601 /***********************************************************************
4602 *		ImmLockIMCC(IMM32.@)
4603 */
4604 LPVOID WINAPI ImmLockIMCC(HIMCC imcc)
4605 {
4606     if (imcc)
4607         return LocalLock(imcc);
4608     return NULL;
4609 }
4610 
4611 /***********************************************************************
4612 *		ImmUnlockIMCC(IMM32.@)
4613 */
4614 BOOL WINAPI ImmUnlockIMCC(HIMCC imcc)
4615 {
4616     if (imcc)
4617         return LocalUnlock(imcc);
4618     return FALSE;
4619 }
4620 
4621 /***********************************************************************
4622 *		ImmGetIMCCLockCount(IMM32.@)
4623 */
4624 DWORD WINAPI ImmGetIMCCLockCount(HIMCC imcc)
4625 {
4626     return LocalFlags(imcc) & LMEM_LOCKCOUNT;
4627 }
4628 
4629 /***********************************************************************
4630 *		ImmReSizeIMCC(IMM32.@)
4631 */
4632 HIMCC  WINAPI ImmReSizeIMCC(HIMCC imcc, DWORD size)
4633 {
4634     if (!imcc)
4635         return NULL;
4636     return LocalReAlloc(imcc, size, LHND);
4637 }
4638 
4639 /***********************************************************************
4640 *		ImmGetIMCCSize(IMM32.@)
4641 */
4642 DWORD WINAPI ImmGetIMCCSize(HIMCC imcc)
4643 {
4644     if (imcc)
4645         return LocalSize(imcc);
4646     return 0;
4647 }
4648 
4649 /***********************************************************************
4650 *		ImmGenerateMessage(IMM32.@)
4651 */
4652 BOOL WINAPI ImmGenerateMessage(HIMC hIMC)
4653 {
4654     PCLIENTIMC pClientImc;
4655     LPINPUTCONTEXT pIC;
4656     LPTRANSMSG pMsgs, pTrans = NULL, pItem;
4657     HWND hWnd;
4658     DWORD dwIndex, dwCount, cbTrans;
4659     HIMCC hMsgBuf = NULL;
4660     BOOL bAnsi;
4661 
4662     TRACE("(%p)\n", hIMC);
4663 
4664     if (Imm32IsCrossThreadAccess(hIMC))
4665         return FALSE;
4666 
4667     pClientImc = ImmLockClientImc(hIMC);
4668     if (pClientImc == NULL)
4669         return FALSE;
4670 
4671     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
4672     ImmUnlockClientImc(pClientImc);
4673 
4674     pIC = ImmLockIMC(hIMC);
4675     if (pIC == NULL)
4676         return FALSE;
4677 
4678     dwCount = pIC->dwNumMsgBuf;
4679     if (dwCount == 0)
4680         goto Quit;
4681 
4682     hMsgBuf = pIC->hMsgBuf;
4683     pMsgs = ImmLockIMCC(hMsgBuf);
4684     if (pMsgs == NULL)
4685         goto Quit;
4686 
4687     cbTrans = dwCount * sizeof(TRANSMSG);
4688     pTrans = Imm32HeapAlloc(0, cbTrans);
4689     if (pTrans == NULL)
4690         goto Quit;
4691 
4692     RtlCopyMemory(pTrans, pMsgs, cbTrans);
4693 
4694 #ifdef IMP_SUPPORT
4695     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
4696     {
4697         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
4698         WORD wLang = PRIMARYLANGID(LangID);
4699 
4700         /* translate the messages if Japanese or Korean */
4701         if (wLang == LANG_JAPANESE ||
4702             (wLang == LANG_KOREAN && NtUserGetAppImeLevel(pIC->hWnd) == 3))
4703         {
4704             dwCount = ImpTrans(dwCount, pTrans, hIMC, bAnsi, wLang);
4705         }
4706     }
4707 #endif
4708 
4709     /* send them */
4710     hWnd = pIC->hWnd;
4711     pItem = pTrans;
4712     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
4713     {
4714         if (bAnsi)
4715             SendMessageA(hWnd, pItem->message, pItem->wParam, pItem->lParam);
4716         else
4717             SendMessageW(hWnd, pItem->message, pItem->wParam, pItem->lParam);
4718     }
4719 
4720 Quit:
4721     if (pTrans)
4722         HeapFree(g_hImm32Heap, 0, pTrans);
4723     if (hMsgBuf)
4724         ImmUnlockIMCC(hMsgBuf);
4725     pIC->dwNumMsgBuf = 0; /* done */
4726     ImmUnlockIMC(hIMC);
4727     return TRUE;
4728 }
4729 
4730 static VOID APIENTRY
4731 Imm32PostMessages(HWND hwnd, HIMC hIMC, DWORD dwCount, LPTRANSMSG lpTransMsg)
4732 {
4733     DWORD dwIndex;
4734     PCLIENTIMC pClientImc;
4735     LPTRANSMSG pNewTransMsg = lpTransMsg, pItem;
4736     BOOL bAnsi;
4737 
4738     pClientImc = ImmLockClientImc(hIMC);
4739     if (pClientImc == NULL)
4740         return;
4741 
4742     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
4743     ImmUnlockClientImc(pClientImc);
4744 
4745 #ifdef IMP_SUPPORT
4746     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
4747     {
4748         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
4749         WORD Lang = PRIMARYLANGID(LangID);
4750 
4751         /* translate the messages if Japanese or Korean */
4752         if (Lang == LANG_JAPANESE ||
4753             (Lang == LANG_KOREAN && NtUserGetAppImeLevel(hwnd) == 3))
4754         {
4755             DWORD cbTransMsg = dwCount * sizeof(TRANSMSG);
4756             pNewTransMsg = Imm32HeapAlloc(0, cbTransMsg);
4757             if (pNewTransMsg)
4758             {
4759                 RtlCopyMemory(pNewTransMsg, lpTransMsg, cbTransMsg);
4760                 dwCount = ImpTrans(dwCount, pNewTransMsg, hIMC, bAnsi, Lang);
4761             }
4762             else
4763             {
4764                 pNewTransMsg = lpTransMsg;
4765             }
4766         }
4767     }
4768 #endif
4769 
4770     /* post them */
4771     pItem = pNewTransMsg;
4772     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
4773     {
4774         if (bAnsi)
4775             PostMessageA(hwnd, pItem->message, pItem->wParam, pItem->lParam);
4776         else
4777             PostMessageW(hwnd, pItem->message, pItem->wParam, pItem->lParam);
4778     }
4779 
4780 #ifdef IMP_SUPPORT
4781     if (pNewTransMsg && pNewTransMsg != lpTransMsg)
4782         HeapFree(g_hImm32Heap, 0, pNewTransMsg);
4783 #endif
4784 }
4785 
4786 /***********************************************************************
4787 *       ImmTranslateMessage(IMM32.@)
4788 *       ( Undocumented, call internally and from user32.dll )
4789 */
4790 BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData)
4791 {
4792 #define MSG_COUNT 0x100
4793     BOOL ret = FALSE;
4794     INT kret;
4795     LPINPUTCONTEXTDX pIC;
4796     PIMEDPI pImeDpi = NULL;
4797     LPTRANSMSGLIST pList = NULL;
4798     LPTRANSMSG pTransMsg;
4799     BYTE abKeyState[256];
4800     HIMC hIMC;
4801     HKL hKL;
4802     UINT vk;
4803     DWORD dwThreadId, dwCount, cbList;
4804     WCHAR wch;
4805     WORD wChar;
4806 
4807     TRACE("(%p, 0x%X, %p, %p)\n", hwnd, msg, wParam, lKeyData);
4808 
4809     /* filter the message */
4810     switch (msg)
4811     {
4812         case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP:
4813             break;
4814         default:
4815             return FALSE;
4816     }
4817 
4818     hIMC = ImmGetContext(hwnd);
4819     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
4820     if (pIC == NULL)
4821     {
4822         ImmReleaseContext(hwnd, hIMC);
4823         return FALSE;
4824     }
4825 
4826     if (!pIC->bNeedsTrans) /* is translation needed? */
4827     {
4828         /* directly post them */
4829         dwCount = pIC->dwNumMsgBuf;
4830         if (dwCount == 0)
4831             goto Quit;
4832 
4833         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
4834         if (pTransMsg)
4835         {
4836             Imm32PostMessages(hwnd, hIMC, dwCount, pTransMsg);
4837             ImmUnlockIMCC(pIC->hMsgBuf);
4838             ret = TRUE;
4839         }
4840         pIC->dwNumMsgBuf = 0; /* done */
4841         goto Quit;
4842     }
4843     pIC->bNeedsTrans = FALSE; /* clear the flag */
4844 
4845     dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
4846     hKL = GetKeyboardLayout(dwThreadId);
4847     pImeDpi = ImmLockImeDpi(hKL);
4848     if (pImeDpi == NULL)
4849         goto Quit;
4850 
4851     if (!GetKeyboardState(abKeyState)) /* get keyboard ON/OFF status */
4852         goto Quit;
4853 
4854     /* convert a virtual key if IME_PROP_KBD_CHAR_FIRST */
4855     vk = pIC->nVKey;
4856     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST)
4857     {
4858         if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
4859         {
4860             wch = 0;
4861             kret = ToUnicode(vk, HIWORD(lKeyData), abKeyState, &wch, 1, 0);
4862             if (kret == 1)
4863                 vk = MAKELONG(LOBYTE(vk), wch);
4864         }
4865         else
4866         {
4867             wChar = 0;
4868             kret = ToAsciiEx(vk, HIWORD(lKeyData), abKeyState, &wChar, 0, hKL);
4869             if (kret > 0)
4870                 vk = MAKEWORD(vk, wChar);
4871         }
4872     }
4873 
4874     /* allocate a list */
4875     cbList = offsetof(TRANSMSGLIST, TransMsg) + MSG_COUNT * sizeof(TRANSMSG);
4876     pList = Imm32HeapAlloc(0, cbList);
4877     if (!pList)
4878         goto Quit;
4879 
4880     /* use IME conversion engine and convert the list */
4881     pList->uMsgCount = MSG_COUNT;
4882     kret = pImeDpi->ImeToAsciiEx(vk, HIWORD(lKeyData), abKeyState, pList, 0, hIMC);
4883     if (kret <= 0)
4884         goto Quit;
4885 
4886     /* post them */
4887     if (kret <= MSG_COUNT)
4888     {
4889         Imm32PostMessages(hwnd, hIMC, kret, pList->TransMsg);
4890         ret = TRUE;
4891     }
4892     else
4893     {
4894         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
4895         if (pTransMsg == NULL)
4896             goto Quit;
4897         Imm32PostMessages(hwnd, hIMC, kret, pTransMsg);
4898         ImmUnlockIMCC(pIC->hMsgBuf);
4899     }
4900 
4901 Quit:
4902     if (pList)
4903         HeapFree(g_hImm32Heap, 0, pList);
4904     ImmUnlockImeDpi(pImeDpi);
4905     ImmUnlockIMC(hIMC);
4906     ImmReleaseContext(hwnd, hIMC);
4907     return ret;
4908 #undef MSG_COUNT
4909 }
4910 
4911 /***********************************************************************
4912 *		ImmProcessKey(IMM32.@)
4913 *       ( Undocumented, called from user32.dll )
4914 */
4915 DWORD WINAPI ImmProcessKey(HWND hWnd, HKL hKL, UINT vKey, LPARAM lParam, DWORD dwHotKeyID)
4916 {
4917     DWORD ret = 0;
4918     HIMC hIMC;
4919     PIMEDPI pImeDpi;
4920     LPINPUTCONTEXTDX pIC;
4921     BYTE KeyState[256];
4922     UINT vk;
4923     BOOL bUseIme = TRUE, bSkipThisKey = FALSE, bLowWordOnly = FALSE;
4924 
4925     TRACE("(%p, %p, 0x%X, %p, 0x%lX)\n", hWnd, hKL, vKey, lParam, dwHotKeyID);
4926 
4927     hIMC = ImmGetContext(hWnd);
4928     pImeDpi = ImmLockImeDpi(hKL);
4929     if (pImeDpi)
4930     {
4931         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
4932         if (pIC)
4933         {
4934             if (LOBYTE(vKey) == VK_PACKET &&
4935                 !(pImeDpi->ImeInfo.fdwProperty & IME_PROP_ACCEPT_WIDE_VKEY))
4936             {
4937                 if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)
4938                 {
4939                     bLowWordOnly = TRUE;
4940                 }
4941                 else
4942                 {
4943                     bUseIme = FALSE;
4944                     if (pIC->fOpen)
4945                         bSkipThisKey = TRUE;
4946                 }
4947             }
4948 
4949             if (bUseIme)
4950             {
4951                 if (GetKeyboardState(KeyState))
4952                 {
4953                     vk = (bLowWordOnly ? LOWORD(vKey) : vKey);
4954                     if (pImeDpi->ImeProcessKey(hIMC, vk, lParam, KeyState))
4955                     {
4956                         pIC->bNeedsTrans = TRUE;
4957                         pIC->nVKey = vKey;
4958                         ret |= IPHK_PROCESSBYIME;
4959                     }
4960                 }
4961             }
4962             else if (bSkipThisKey)
4963             {
4964                 ret |= IPHK_SKIPTHISKEY;
4965             }
4966 
4967             ImmUnlockIMC(hIMC);
4968         }
4969 
4970         ImmUnlockImeDpi(pImeDpi);
4971     }
4972 
4973     if (dwHotKeyID != INVALID_HOTKEY_ID)
4974     {
4975         if (Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID))
4976         {
4977             if (vKey != VK_KANJI || dwHotKeyID != IME_JHOTKEY_CLOSE_OPEN)
4978                 ret |= IPHK_HOTKEY;
4979         }
4980     }
4981 
4982     if (ret & IPHK_PROCESSBYIME)
4983     {
4984         FIXME("TODO: We have to do something here.\n");
4985     }
4986 
4987     ImmReleaseContext(hWnd, hIMC);
4988     return ret;
4989 }
4990 
4991 /***********************************************************************
4992 *		ImmDisableTextFrameService(IMM32.@)
4993 */
4994 BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
4995 {
4996     FIXME("Stub\n");
4997     return FALSE;
4998 }
4999 
5000 /***********************************************************************
5001  *              ImmEnumInputContext(IMM32.@)
5002  */
5003 BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
5004 {
5005     HIMC *phList;
5006     DWORD dwIndex, dwCount;
5007     BOOL ret = TRUE;
5008     HIMC hIMC;
5009 
5010     TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
5011 
5012     dwCount = Imm32AllocAndBuildHimcList(dwThreadId, &phList);
5013     if (!dwCount)
5014         return FALSE;
5015 
5016     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
5017     {
5018         hIMC = phList[dwIndex];
5019         ret = (*lpfn)(hIMC, lParam);
5020         if (!ret)
5021             break;
5022     }
5023 
5024     HeapFree(g_hImm32Heap, 0, phList);
5025     return ret;
5026 }
5027 
5028 /***********************************************************************
5029  *              ImmGetHotKey(IMM32.@)
5030  */
5031 BOOL WINAPI
5032 ImmGetHotKey(IN DWORD dwHotKey,
5033              OUT LPUINT lpuModifiers,
5034              OUT LPUINT lpuVKey,
5035              OUT LPHKL lphKL)
5036 {
5037     TRACE("(0x%lX, %p, %p, %p)\n", dwHotKey, lpuModifiers, lpuVKey, lphKL);
5038     if (lpuModifiers && lpuVKey)
5039         return NtUserGetImeHotKey(dwHotKey, lpuModifiers, lpuVKey, lphKL);
5040     return FALSE;
5041 }
5042 
5043 /***********************************************************************
5044  *              ImmDisableLegacyIME(IMM32.@)
5045  */
5046 BOOL WINAPI ImmDisableLegacyIME(void)
5047 {
5048     FIXME("stub\n");
5049     return TRUE;
5050 }
5051 
5052 /***********************************************************************
5053  *              ImmSetActiveContext(IMM32.@)
5054  */
5055 BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC hIMC, BOOL fFlag)
5056 {
5057     FIXME("(%p, %p, %d): stub\n", hwnd, hIMC, fFlag);
5058     return FALSE;
5059 }
5060 
5061 /***********************************************************************
5062  *              ImmSetActiveContextConsoleIME(IMM32.@)
5063  */
5064 BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
5065 {
5066     HIMC hIMC;
5067     TRACE("(%p, %d)\n", hwnd, fFlag);
5068 
5069     hIMC = ImmGetContext(hwnd);
5070     if (hIMC)
5071         return ImmSetActiveContext(hwnd, hIMC, fFlag);
5072     return FALSE;
5073 }
5074 
5075 /***********************************************************************
5076 *		ImmRegisterClient(IMM32.@)
5077 *       ( Undocumented, called from user32.dll )
5078 */
5079 BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod)
5080 {
5081     g_SharedInfo = *ptr;
5082     g_psi = g_SharedInfo.psi;
5083     return Imm32InitInstance(hMod);
5084 }
5085 
5086 /***********************************************************************
5087  *		CtfImmIsTextFrameServiceDisabled(IMM32.@)
5088  */
5089 BOOL WINAPI CtfImmIsTextFrameServiceDisabled(VOID)
5090 {
5091     PTEB pTeb = NtCurrentTeb();
5092     if (((PW32CLIENTINFO)pTeb->Win32ClientInfo)->CI_flags & CI_TFSDISABLED)
5093         return TRUE;
5094     return FALSE;
5095 }
5096 
5097 /***********************************************************************
5098  *              ImmGetImeInfoEx (IMM32.@)
5099  */
5100 BOOL WINAPI
5101 ImmGetImeInfoEx(PIMEINFOEX pImeInfoEx,
5102                 IMEINFOEXCLASS SearchType,
5103                 PVOID pvSearchKey)
5104 {
5105     BOOL bDisabled = FALSE;
5106     HKL hKL;
5107     PTEB pTeb;
5108 
5109     switch (SearchType)
5110     {
5111         case ImeInfoExKeyboardLayout:
5112             break;
5113 
5114         case ImeInfoExImeWindow:
5115             bDisabled = CtfImmIsTextFrameServiceDisabled();
5116             SearchType = ImeInfoExKeyboardLayout;
5117             break;
5118 
5119         case ImeInfoExImeFileName:
5120             StringCchCopyW(pImeInfoEx->wszImeFile, _countof(pImeInfoEx->wszImeFile),
5121                            pvSearchKey);
5122             goto Quit;
5123     }
5124 
5125     hKL = *(HKL*)pvSearchKey;
5126     pImeInfoEx->hkl = hKL;
5127 
5128     if (!IS_IME_HKL(hKL))
5129     {
5130         if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
5131         {
5132             pTeb = NtCurrentTeb();
5133             if (((PW32CLIENTINFO)pTeb->Win32ClientInfo)->W32ClientInfo[0] & 2)
5134                 return FALSE;
5135             if (!bDisabled)
5136                 goto Quit;
5137         }
5138         return FALSE;
5139     }
5140 
5141 Quit:
5142     return NtUserGetImeInfoEx(pImeInfoEx, SearchType);
5143 }
5144 
5145 /***********************************************************************
5146  *              ImmWINNLSGetIMEHotkey (IMM32.@)
5147  */
5148 UINT WINAPI ImmWINNLSGetIMEHotkey(HWND hwndIme)
5149 {
5150     TRACE("(%p)\n", hwndIme);
5151     UNREFERENCED_PARAMETER(hwndIme);
5152     return 0; /* This is correct. This function of Windows just returns zero. */
5153 }
5154 
5155 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
5156 
5157 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
5158 {
5159     HKL hKL;
5160     HIMC hIMC;
5161     PTEB pTeb;
5162 
5163     TRACE("(%p, 0x%X, %p)\n", hinstDLL, fdwReason, lpReserved);
5164 
5165     switch (fdwReason)
5166     {
5167         case DLL_PROCESS_ATTACH:
5168             //Imm32GenerateRandomSeed(hinstDLL, 1, lpReserved); // Non-sense
5169             if (!Imm32InitInstance(hinstDLL))
5170             {
5171                 ERR("Imm32InitInstance failed\n");
5172                 return FALSE;
5173             }
5174             if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
5175             {
5176                 ERR("User32InitializeImmEntryTable failed\n");
5177                 return FALSE;
5178             }
5179             break;
5180 
5181         case DLL_THREAD_ATTACH:
5182             break;
5183 
5184         case DLL_THREAD_DETACH:
5185             if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
5186                 return TRUE;
5187 
5188             pTeb = NtCurrentTeb();
5189             if (pTeb->Win32ThreadInfo == NULL)
5190                 return TRUE;
5191 
5192             hKL = GetKeyboardLayout(0);
5193             // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
5194             hIMC = (HIMC)NtUserGetThreadState(4);
5195             Imm32CleanupContext(hIMC, hKL, TRUE);
5196             break;
5197 
5198         case DLL_PROCESS_DETACH:
5199             RtlDeleteCriticalSection(&g_csImeDpi);
5200             TRACE("imm32.dll is unloaded\n");
5201             break;
5202     }
5203 
5204     return TRUE;
5205 }
5206