xref: /reactos/dll/win32/imm32/imm.c (revision 53221834)
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 "precomp.h"
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(imm);
16 
17 HMODULE g_hImm32Inst = NULL;
18 PSERVERINFO g_psi = NULL;
19 SHAREDINFO g_SharedInfo = { NULL };
20 BYTE g_bClientRegd = FALSE;
21 
22 static BOOL APIENTRY Imm32InitInstance(HMODULE hMod)
23 {
24     NTSTATUS status;
25 
26     if (hMod)
27         g_hImm32Inst = hMod;
28 
29     if (g_bClientRegd)
30         return TRUE;
31 
32     status = RtlInitializeCriticalSection(&g_csImeDpi);
33     if (NT_ERROR(status))
34         return FALSE;
35 
36     g_bClientRegd = TRUE;
37     return TRUE;
38 }
39 
40 /***********************************************************************
41  *		ImmRegisterClient(IMM32.@)
42  *       ( Undocumented, called from user32.dll )
43  */
44 BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod)
45 {
46     g_SharedInfo = *ptr;
47     g_psi = g_SharedInfo.psi;
48     return Imm32InitInstance(hMod);
49 }
50 
51 /***********************************************************************
52  *		ImmLoadLayout (IMM32.@)
53  */
54 HKL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
55 {
56     DWORD cbData;
57     UNICODE_STRING UnicodeString;
58     HKEY hLayoutKey = NULL, hLayoutsKey = NULL;
59     LONG error;
60     NTSTATUS Status;
61     WCHAR szLayout[MAX_PATH];
62 
63     TRACE("(%p, %p)\n", hKL, pImeInfoEx);
64 
65     if (IS_IME_HKL(hKL) ||
66         !g_psi || !(g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED) ||
67         ((PW32CLIENTINFO)NtCurrentTeb()->Win32ClientInfo)->W32ClientInfo[0] & 2)
68     {
69         UnicodeString.Buffer = szLayout;
70         UnicodeString.MaximumLength = sizeof(szLayout);
71         Status = RtlIntegerToUnicodeString((DWORD_PTR)hKL, 16, &UnicodeString);
72         if (!NT_SUCCESS(Status))
73             return NULL;
74 
75         error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hLayoutsKey);
76         if (error)
77             return NULL;
78 
79         error = RegOpenKeyW(hLayoutsKey, szLayout, &hLayoutKey);
80     }
81     else
82     {
83         error = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_IMM, &hLayoutKey);
84     }
85 
86     if (error)
87     {
88         ERR("RegOpenKeyW error: 0x%08lX\n", error);
89         hKL = NULL;
90     }
91     else
92     {
93         cbData = sizeof(pImeInfoEx->wszImeFile);
94         error = RegQueryValueExW(hLayoutKey, L"Ime File", 0, 0,
95                                  (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
96         if (error)
97             hKL = NULL;
98     }
99 
100     RegCloseKey(hLayoutKey);
101     if (hLayoutsKey)
102         RegCloseKey(hLayoutsKey);
103     return hKL;
104 }
105 
106 typedef struct _tagImmHkl
107 {
108     struct list entry;
109     HKL         hkl;
110     HMODULE     hIME;
111     IMEINFO     imeInfo;
112     WCHAR       imeClassName[17]; /* 16 character max */
113     ULONG       uSelected;
114     HWND        UIWnd;
115 
116     /* Function Pointers */
117     BOOL (WINAPI *pImeInquire)(IMEINFO *, WCHAR *, const WCHAR *);
118     BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *);
119     BOOL (WINAPI *pImeDestroy)(UINT);
120     LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *);
121     BOOL (WINAPI *pImeSelect)(HIMC, BOOL);
122     BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL);
123     UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, DWORD *, UINT, HIMC);
124     BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD);
125     BOOL (WINAPI *pImeRegisterWord)(const WCHAR *, DWORD, const WCHAR *);
126     BOOL (WINAPI *pImeUnregisterWord)(const WCHAR *, DWORD, const WCHAR *);
127     UINT (WINAPI *pImeEnumRegisterWord)(REGISTERWORDENUMPROCW, const WCHAR *, DWORD, const WCHAR *, void *);
128     BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void *, DWORD, const void *, DWORD);
129     DWORD (WINAPI *pImeConversionList)(HIMC, const WCHAR *, CANDIDATELIST *, DWORD, UINT);
130     BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *);
131     UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *);
132     DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD);
133 } ImmHkl;
134 
135 typedef struct tagInputContextData
136 {
137     DWORD           dwLock;
138     INPUTCONTEXT    IMC;
139     DWORD           threadID;
140 
141     ImmHkl          *immKbd;
142     UINT            lastVK;
143     BOOL            threadDefault;
144     DWORD           magic;
145 } InputContextData;
146 
147 #define WINE_IMC_VALID_MAGIC 0x56434D49
148 
149 typedef struct _tagIMMThreadData
150 {
151     struct list entry;
152     DWORD threadID;
153     HIMC defaultContext;
154     HWND hwndDefault;
155     BOOL disableIME;
156     DWORD windowRefs;
157 } IMMThreadData;
158 
159 static struct list ImmHklList = LIST_INIT(ImmHklList);
160 static struct list ImmThreadDataList = LIST_INIT(ImmThreadDataList);
161 
162 static const WCHAR szwWineIMCProperty[] = {'W','i','n','e','I','m','m','H','I','M','C','P','r','o','p','e','r','t','y',0};
163 
164 static const WCHAR szImeFileW[] = {'I','m','e',' ','F','i','l','e',0};
165 static const WCHAR szLayoutTextW[] = {'L','a','y','o','u','t',' ','T','e','x','t',0};
166 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};
167 
168 static inline BOOL is_himc_ime_unicode(const InputContextData *data)
169 {
170     return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE);
171 }
172 
173 static inline BOOL is_kbd_ime_unicode(const ImmHkl *hkl)
174 {
175     return !!(hkl->imeInfo.fdwProperty & IME_PROP_UNICODE);
176 }
177 
178 static InputContextData* get_imc_data(HIMC hIMC);
179 
180 static inline WCHAR *strdupAtoW( const char *str )
181 {
182     WCHAR *ret = NULL;
183     if (str)
184     {
185         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
186         if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
187             MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
188     }
189     return ret;
190 }
191 
192 static inline CHAR *strdupWtoA( const WCHAR *str )
193 {
194     CHAR *ret = NULL;
195     if (str)
196     {
197         DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
198         if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
199             WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
200     }
201     return ret;
202 }
203 
204 static HMODULE load_graphics_driver(void)
205 {
206     static const WCHAR display_device_guid_propW[] = {
207         '_','_','w','i','n','e','_','d','i','s','p','l','a','y','_',
208         'd','e','v','i','c','e','_','g','u','i','d',0 };
209     static const WCHAR key_pathW[] = {
210         'S','y','s','t','e','m','\\',
211         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
212         'C','o','n','t','r','o','l','\\',
213         'V','i','d','e','o','\\','{',0};
214     static const WCHAR displayW[] = {'}','\\','0','0','0','0',0};
215     static const WCHAR driverW[] = {'G','r','a','p','h','i','c','s','D','r','i','v','e','r',0};
216 
217     HMODULE ret = 0;
218     HKEY hkey;
219     DWORD size;
220     WCHAR path[MAX_PATH];
221     WCHAR key[ARRAY_SIZE( key_pathW ) + ARRAY_SIZE( displayW ) + 40];
222     UINT guid_atom = HandleToULong( GetPropW( GetDesktopWindow(), display_device_guid_propW ));
223 
224     if (!guid_atom) return 0;
225     memcpy( key, key_pathW, sizeof(key_pathW) );
226     if (!GlobalGetAtomNameW( guid_atom, key + lstrlenW(key), 40 )) return 0;
227     lstrcatW( key, displayW );
228     if (RegOpenKeyW( HKEY_LOCAL_MACHINE, key, &hkey )) return 0;
229     size = sizeof(path);
230     if (!RegQueryValueExW( hkey, driverW, NULL, NULL, (BYTE *)path, &size )) ret = LoadLibraryW( path );
231     RegCloseKey( hkey );
232     TRACE( "%s %p\n", debugstr_w(path), ret );
233     return ret;
234 }
235 
236 /* ImmHkl loading and freeing */
237 #define LOAD_FUNCPTR(f) if((ptr->p##f = (LPVOID)GetProcAddress(ptr->hIME, #f)) == NULL){WARN("Can't find function %s in ime\n", #f);}
238 static ImmHkl *IMM_GetImmHkl(HKL hkl)
239 {
240     ImmHkl *ptr;
241     WCHAR filename[MAX_PATH];
242 
243     TRACE("Seeking ime for keyboard %p\n",hkl);
244 
245     LIST_FOR_EACH_ENTRY(ptr, &ImmHklList, ImmHkl, entry)
246     {
247         if (ptr->hkl == hkl)
248             return ptr;
249     }
250     /* not found... create it */
251 
252     ptr = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ImmHkl));
253 
254     ptr->hkl = hkl;
255     if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename);
256     if (!ptr->hIME) ptr->hIME = load_graphics_driver();
257     if (ptr->hIME)
258     {
259         LOAD_FUNCPTR(ImeInquire);
260         if (!ptr->pImeInquire || !ptr->pImeInquire(&ptr->imeInfo, ptr->imeClassName, NULL))
261         {
262             FreeLibrary(ptr->hIME);
263             ptr->hIME = NULL;
264         }
265         else
266         {
267             LOAD_FUNCPTR(ImeDestroy);
268             LOAD_FUNCPTR(ImeSelect);
269             if (!ptr->pImeSelect || !ptr->pImeDestroy)
270             {
271                 FreeLibrary(ptr->hIME);
272                 ptr->hIME = NULL;
273             }
274             else
275             {
276                 LOAD_FUNCPTR(ImeConfigure);
277                 LOAD_FUNCPTR(ImeEscape);
278                 LOAD_FUNCPTR(ImeSetActiveContext);
279                 LOAD_FUNCPTR(ImeToAsciiEx);
280                 LOAD_FUNCPTR(NotifyIME);
281                 LOAD_FUNCPTR(ImeRegisterWord);
282                 LOAD_FUNCPTR(ImeUnregisterWord);
283                 LOAD_FUNCPTR(ImeEnumRegisterWord);
284                 LOAD_FUNCPTR(ImeSetCompositionString);
285                 LOAD_FUNCPTR(ImeConversionList);
286                 LOAD_FUNCPTR(ImeProcessKey);
287                 LOAD_FUNCPTR(ImeGetRegisterWordStyle);
288                 LOAD_FUNCPTR(ImeGetImeMenuItems);
289                 /* make sure our classname is WCHAR */
290                 if (!is_kbd_ime_unicode(ptr))
291                 {
292                     WCHAR bufW[17];
293                     MultiByteToWideChar(CP_ACP, 0, (LPSTR)ptr->imeClassName,
294                                         -1, bufW, 17);
295                     lstrcpyW(ptr->imeClassName, bufW);
296                 }
297             }
298         }
299     }
300     list_add_head(&ImmHklList,&ptr->entry);
301 
302     return ptr;
303 }
304 #undef LOAD_FUNCPTR
305 
306 static InputContextData* get_imc_data(HIMC hIMC)
307 {
308     InputContextData *data = (InputContextData *)hIMC;
309 
310     if (hIMC == NULL)
311         return NULL;
312 
313     if(IsBadReadPtr(data, sizeof(InputContextData)) || data->magic != WINE_IMC_VALID_MAGIC)
314     {
315         SetLastError(ERROR_INVALID_HANDLE);
316         return NULL;
317     }
318     return data;
319 }
320 
321 static HIMC get_default_context( HWND hwnd )
322 {
323     FIXME("Don't use this function\n");
324     return FALSE;
325 }
326 
327 static BOOL IMM_IsCrossThreadAccess(HWND hWnd,  HIMC hIMC)
328 {
329     InputContextData *data;
330 
331     if (hWnd)
332     {
333         DWORD thread = GetWindowThreadProcessId(hWnd, NULL);
334         if (thread != GetCurrentThreadId()) return TRUE;
335     }
336     data = get_imc_data(hIMC);
337     if (data && data->threadID != GetCurrentThreadId())
338         return TRUE;
339 
340     return FALSE;
341 }
342 
343 /***********************************************************************
344  *		ImmAssociateContext (IMM32.@)
345  */
346 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
347 {
348     HIMC old = NULL;
349     InputContextData *data = get_imc_data(hIMC);
350 
351     TRACE("(%p, %p):\n", hWnd, hIMC);
352 
353     if(hIMC && !data)
354         return NULL;
355 
356     /*
357      * If already associated just return
358      */
359     if (hIMC && data->IMC.hWnd == hWnd)
360         return hIMC;
361 
362     if (hIMC && IMM_IsCrossThreadAccess(hWnd, hIMC))
363         return NULL;
364 
365     if (hWnd)
366     {
367         HIMC defaultContext = get_default_context( hWnd );
368         old = RemovePropW(hWnd,szwWineIMCProperty);
369 
370         if (old == NULL)
371             old = defaultContext;
372         else if (old == (HIMC)-1)
373             old = NULL;
374 
375         if (hIMC != defaultContext)
376         {
377             if (hIMC == NULL) /* Meaning disable imm for that window*/
378                 SetPropW(hWnd,szwWineIMCProperty,(HANDLE)-1);
379             else
380                 SetPropW(hWnd,szwWineIMCProperty,hIMC);
381         }
382 
383         if (old)
384         {
385             InputContextData *old_data = (InputContextData *)old;
386             if (old_data->IMC.hWnd == hWnd)
387                 old_data->IMC.hWnd = NULL;
388         }
389     }
390 
391     if (!hIMC)
392         return old;
393 
394     if(GetActiveWindow() == data->IMC.hWnd)
395     {
396         SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, FALSE, ISC_SHOWUIALL);
397         data->IMC.hWnd = hWnd;
398         SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, TRUE, ISC_SHOWUIALL);
399     }
400 
401     return old;
402 }
403 
404 /*
405  * Helper function for ImmAssociateContextEx
406  */
407 static BOOL CALLBACK _ImmAssociateContextExEnumProc(HWND hwnd, LPARAM lParam)
408 {
409     HIMC hImc = (HIMC)lParam;
410     ImmAssociateContext(hwnd,hImc);
411     return TRUE;
412 }
413 
414 /***********************************************************************
415  *              ImmAssociateContextEx (IMM32.@)
416  */
417 BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
418 {
419     TRACE("(%p, %p, 0x%x):\n", hWnd, hIMC, dwFlags);
420 
421     if (!hWnd)
422         return FALSE;
423 
424     switch (dwFlags)
425     {
426     case 0:
427         ImmAssociateContext(hWnd,hIMC);
428         return TRUE;
429     case IACE_DEFAULT:
430     {
431         HIMC defaultContext = get_default_context( hWnd );
432         if (!defaultContext) return FALSE;
433         ImmAssociateContext(hWnd,defaultContext);
434         return TRUE;
435     }
436     case IACE_IGNORENOCONTEXT:
437         if (GetPropW(hWnd,szwWineIMCProperty))
438             ImmAssociateContext(hWnd,hIMC);
439         return TRUE;
440     case IACE_CHILDREN:
441         EnumChildWindows(hWnd,_ImmAssociateContextExEnumProc,(LPARAM)hIMC);
442         return TRUE;
443     default:
444         FIXME("Unknown dwFlags 0x%x\n",dwFlags);
445         return FALSE;
446     }
447 }
448 
449 /***********************************************************************
450  *		ImmConfigureIMEA (IMM32.@)
451  */
452 BOOL WINAPI ImmConfigureIMEA(HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
453 {
454     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
455 
456     TRACE("(%p, %p, %d, %p):\n", hKL, hWnd, dwMode, lpData);
457 
458     if (dwMode == IME_CONFIG_REGISTERWORD && !lpData)
459         return FALSE;
460 
461     if (immHkl->hIME && immHkl->pImeConfigure)
462     {
463         if (dwMode != IME_CONFIG_REGISTERWORD || !is_kbd_ime_unicode(immHkl))
464             return immHkl->pImeConfigure(hKL,hWnd,dwMode,lpData);
465         else
466         {
467             REGISTERWORDW rww;
468             REGISTERWORDA *rwa = lpData;
469             BOOL rc;
470 
471             rww.lpReading = strdupAtoW(rwa->lpReading);
472             rww.lpWord = strdupAtoW(rwa->lpWord);
473             rc = immHkl->pImeConfigure(hKL,hWnd,dwMode,&rww);
474             HeapFree(GetProcessHeap(),0,rww.lpReading);
475             HeapFree(GetProcessHeap(),0,rww.lpWord);
476             return rc;
477         }
478     }
479     else
480         return FALSE;
481 }
482 
483 /***********************************************************************
484  *		ImmConfigureIMEW (IMM32.@)
485  */
486 BOOL WINAPI ImmConfigureIMEW(HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData)
487 {
488     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
489 
490     TRACE("(%p, %p, %d, %p):\n", hKL, hWnd, dwMode, lpData);
491 
492     if (dwMode == IME_CONFIG_REGISTERWORD && !lpData)
493         return FALSE;
494 
495     if (immHkl->hIME && immHkl->pImeConfigure)
496     {
497         if (dwMode != IME_CONFIG_REGISTERWORD || is_kbd_ime_unicode(immHkl))
498             return immHkl->pImeConfigure(hKL,hWnd,dwMode,lpData);
499         else
500         {
501             REGISTERWORDW *rww = lpData;
502             REGISTERWORDA rwa;
503             BOOL rc;
504 
505             rwa.lpReading = strdupWtoA(rww->lpReading);
506             rwa.lpWord = strdupWtoA(rww->lpWord);
507             rc = immHkl->pImeConfigure(hKL,hWnd,dwMode,&rwa);
508             HeapFree(GetProcessHeap(),0,rwa.lpReading);
509             HeapFree(GetProcessHeap(),0,rwa.lpWord);
510             return rc;
511         }
512     }
513     else
514         return FALSE;
515 }
516 
517 /***********************************************************************
518  *		ImmCreateContext (IMM32.@)
519  */
520 HIMC WINAPI ImmCreateContext(void)
521 {
522     PCLIENTIMC pClientImc;
523     HIMC hIMC;
524 
525     TRACE("()\n");
526 
527     if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
528         return NULL;
529 
530     pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
531     if (pClientImc == NULL)
532         return NULL;
533 
534     hIMC = NtUserCreateInputContext(pClientImc);
535     if (hIMC == NULL)
536     {
537         HeapFree(g_hImm32Heap, 0, pClientImc);
538         return NULL;
539     }
540 
541     RtlInitializeCriticalSection(&pClientImc->cs);
542 
543     // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
544     pClientImc->unknown = NtUserGetThreadState(13);
545 
546     return hIMC;
547 }
548 
549 static VOID APIENTRY Imm32CleanupContextExtra(LPINPUTCONTEXT pIC)
550 {
551     FIXME("We have to do something do here");
552 }
553 
554 static PCLIENTIMC APIENTRY Imm32FindClientImc(HIMC hIMC)
555 {
556     // FIXME
557     return NULL;
558 }
559 
560 BOOL APIENTRY Imm32CleanupContext(HIMC hIMC, HKL hKL, BOOL bKeep)
561 {
562     PIMEDPI pImeDpi;
563     LPINPUTCONTEXT pIC;
564     PCLIENTIMC pClientImc;
565 
566     if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32) || hIMC == NULL)
567         return FALSE;
568 
569     FIXME("We have do something to do here\n");
570     pClientImc = Imm32FindClientImc(hIMC);
571     if (!pClientImc)
572         return FALSE;
573 
574     if (pClientImc->hImc == NULL)
575     {
576         pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
577         ImmUnlockClientImc(pClientImc);
578         if (!bKeep)
579             return NtUserDestroyInputContext(hIMC);
580         return TRUE;
581     }
582 
583     pIC = ImmLockIMC(hIMC);
584     if (pIC == NULL)
585     {
586         ImmUnlockClientImc(pClientImc);
587         return FALSE;
588     }
589 
590     FIXME("We have do something to do here\n");
591 
592     if (pClientImc->hKL == hKL)
593     {
594         pImeDpi = ImmLockImeDpi(hKL);
595         if (pImeDpi != NULL)
596         {
597             if (IS_IME_HKL(hKL))
598             {
599                 pImeDpi->ImeSelect(hIMC, FALSE);
600             }
601             else if (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
602             {
603                 FIXME("We have do something to do here\n");
604             }
605             ImmUnlockImeDpi(pImeDpi);
606         }
607         pClientImc->hKL = NULL;
608     }
609 
610     ImmDestroyIMCC(pIC->hPrivate);
611     ImmDestroyIMCC(pIC->hMsgBuf);
612     ImmDestroyIMCC(pIC->hGuideLine);
613     ImmDestroyIMCC(pIC->hCandInfo);
614     ImmDestroyIMCC(pIC->hCompStr);
615 
616     Imm32CleanupContextExtra(pIC);
617 
618     ImmUnlockIMC(hIMC);
619 
620     pClientImc->dwFlags |= CLIENTIMC_UNKNOWN1;
621     ImmUnlockClientImc(pClientImc);
622 
623     if (!bKeep)
624         return NtUserDestroyInputContext(hIMC);
625 
626     return TRUE;
627 }
628 
629 /***********************************************************************
630  *		ImmDestroyContext (IMM32.@)
631  */
632 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
633 {
634     HKL hKL;
635 
636     TRACE("(%p)\n", hIMC);
637 
638     if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
639         return FALSE;
640 
641     if (Imm32IsCrossThreadAccess(hIMC))
642         return FALSE;
643 
644     hKL = GetKeyboardLayout(0);
645     return Imm32CleanupContext(hIMC, hKL, FALSE);
646 }
647 
648 static inline BOOL EscapeRequiresWA(UINT uEscape)
649 {
650     if (uEscape == IME_ESC_GET_EUDC_DICTIONARY ||
651         uEscape == IME_ESC_SET_EUDC_DICTIONARY ||
652         uEscape == IME_ESC_IME_NAME ||
653         uEscape == IME_ESC_GETHELPFILENAME)
654         return TRUE;
655     return FALSE;
656 }
657 
658 /***********************************************************************
659  *		ImmEscapeA (IMM32.@)
660  */
661 LRESULT WINAPI ImmEscapeA(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID lpData)
662 {
663     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
664     TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData);
665 
666     if (immHkl->hIME && immHkl->pImeEscape)
667     {
668         if (!EscapeRequiresWA(uEscape) || !is_kbd_ime_unicode(immHkl))
669             return immHkl->pImeEscape(hIMC,uEscape,lpData);
670         else
671         {
672             WCHAR buffer[81]; /* largest required buffer should be 80 */
673             LRESULT rc;
674             if (uEscape == IME_ESC_SET_EUDC_DICTIONARY)
675             {
676                 MultiByteToWideChar(CP_ACP,0,lpData,-1,buffer,81);
677                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
678             }
679             else
680             {
681                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
682                 WideCharToMultiByte(CP_ACP,0,buffer,-1,lpData,80, NULL, NULL);
683             }
684             return rc;
685         }
686     }
687     else
688         return 0;
689 }
690 
691 /***********************************************************************
692  *		ImmEscapeW (IMM32.@)
693  */
694 LRESULT WINAPI ImmEscapeW(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID lpData)
695 {
696     ImmHkl *immHkl = IMM_GetImmHkl(hKL);
697     TRACE("(%p, %p, %d, %p):\n", hKL, hIMC, uEscape, lpData);
698 
699     if (immHkl->hIME && immHkl->pImeEscape)
700     {
701         if (!EscapeRequiresWA(uEscape) || is_kbd_ime_unicode(immHkl))
702             return immHkl->pImeEscape(hIMC,uEscape,lpData);
703         else
704         {
705             CHAR buffer[81]; /* largest required buffer should be 80 */
706             LRESULT rc;
707             if (uEscape == IME_ESC_SET_EUDC_DICTIONARY)
708             {
709                 WideCharToMultiByte(CP_ACP,0,lpData,-1,buffer,81, NULL, NULL);
710                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
711             }
712             else
713             {
714                 rc = immHkl->pImeEscape(hIMC,uEscape,buffer);
715                 MultiByteToWideChar(CP_ACP,0,buffer,-1,lpData,80);
716             }
717             return rc;
718         }
719     }
720     else
721         return 0;
722 }
723 
724 static PCLIENTIMC APIENTRY Imm32GetClientImcCache(void)
725 {
726     // FIXME: Do something properly here
727     return NULL;
728 }
729 
730 /***********************************************************************
731  *		ImmLockClientImc (IMM32.@)
732  */
733 PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
734 {
735     PCLIENTIMC pClientImc;
736 
737     TRACE("(%p)\n", hImc);
738 
739     if (hImc == NULL)
740         return NULL;
741 
742     pClientImc = Imm32GetClientImcCache();
743     if (!pClientImc)
744     {
745         pClientImc = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
746         if (!pClientImc)
747             return NULL;
748 
749         RtlInitializeCriticalSection(&pClientImc->cs);
750 
751         // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
752         pClientImc->unknown = NtUserGetThreadState(13);
753 
754         if (!NtUserUpdateInputContext(hImc, 0, pClientImc))
755         {
756             HeapFree(g_hImm32Heap, 0, pClientImc);
757             return NULL;
758         }
759 
760         pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
761     }
762     else
763     {
764         if (pClientImc->dwFlags & CLIENTIMC_UNKNOWN1)
765             return NULL;
766     }
767 
768     InterlockedIncrement(&pClientImc->cLockObj);
769     return pClientImc;
770 }
771 
772 /***********************************************************************
773  *		ImmUnlockClientImc (IMM32.@)
774  */
775 VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc)
776 {
777     LONG cLocks;
778     HIMC hImc;
779 
780     TRACE("(%p)\n", pClientImc);
781 
782     cLocks = InterlockedDecrement(&pClientImc->cLockObj);
783     if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_UNKNOWN1))
784         return;
785 
786     hImc = pClientImc->hImc;
787     if (hImc)
788         LocalFree(hImc);
789 
790     RtlDeleteCriticalSection(&pClientImc->cs);
791     HeapFree(g_hImm32Heap, 0, pClientImc);
792 }
793 
794 static HIMC APIENTRY Imm32GetContextEx(HWND hWnd, DWORD dwContextFlags)
795 {
796     HIMC hIMC;
797     PCLIENTIMC pClientImc;
798     PWND pWnd;
799 
800     if (!g_psi || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
801         return NULL;
802 
803     if (!hWnd)
804     {
805         // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
806         hIMC = (HIMC)NtUserGetThreadState(4);
807         goto Quit;
808     }
809 
810     pWnd = ValidateHwndNoErr(hWnd);
811     if (!pWnd || Imm32IsCrossProcessAccess(hWnd))
812         return NULL;
813 
814     hIMC = pWnd->hImc;
815     if (!hIMC && (dwContextFlags & 1))
816         hIMC = (HIMC)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_ICONTEXT);
817 
818 Quit:
819     pClientImc = ImmLockClientImc(hIMC);
820     if (pClientImc == NULL)
821         return NULL;
822     if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_UNKNOWN3))
823         hIMC = NULL;
824     ImmUnlockClientImc(pClientImc);
825     return hIMC;
826 }
827 
828 
829 /* Helpers for the GetCompositionString functions */
830 
831 /* Source encoding is defined by context, source length is always given in respective characters. Destination buffer
832    length is always in bytes. */
833 static INT
834 CopyCompStringIMEtoClient(const InputContextData *data, const void *src, INT src_len, void *dst,
835                           INT dst_len, BOOL unicode)
836 {
837     int char_size = unicode ? sizeof(WCHAR) : sizeof(char);
838     INT ret;
839 
840     if (is_himc_ime_unicode(data) ^ unicode)
841     {
842         if (unicode)
843             ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR));
844         else
845             ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL);
846         ret *= char_size;
847     }
848     else
849     {
850         if (dst_len)
851         {
852             ret = min(src_len * char_size, dst_len);
853             memcpy(dst, src, ret);
854         }
855         else
856             ret = src_len * char_size;
857     }
858 
859     return ret;
860 }
861 
862 /* Composition string encoding is defined by context, returned attributes correspond to string, converted according to
863    passed mode. String length is in characters, attributes are in byte arrays. */
864 static INT
865 CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src, INT src_len, const void *comp_string,
866                         INT str_len, BYTE *dst, INT dst_len, BOOL unicode)
867 {
868     union
869     {
870         const void *str;
871         const WCHAR *strW;
872         const char *strA;
873     } string;
874     INT rc;
875 
876     string.str = comp_string;
877 
878     if (is_himc_ime_unicode(data) && !unicode)
879     {
880         rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL);
881         if (dst_len)
882         {
883             int i, j = 0, k = 0;
884 
885             if (rc < dst_len)
886                 dst_len = rc;
887             for (i = 0; i < str_len; ++i)
888             {
889                 int len;
890 
891                 len = WideCharToMultiByte(CP_ACP, 0, string.strW + i, 1, NULL, 0, NULL, NULL);
892                 for (; len > 0; --len)
893                 {
894                     dst[j++] = src[k];
895 
896                     if (j >= dst_len)
897                         goto end;
898                 }
899                 ++k;
900             }
901         end:
902             rc = j;
903         }
904     }
905     else if (!is_himc_ime_unicode(data) && unicode)
906     {
907         rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0);
908         if (dst_len)
909         {
910             int i, j = 0;
911 
912             if (rc < dst_len)
913                 dst_len = rc;
914             for (i = 0; i < str_len; ++i)
915             {
916                 if (IsDBCSLeadByte(string.strA[i]))
917                     continue;
918 
919                 dst[j++] = src[i];
920 
921                 if (j >= dst_len)
922                     break;
923             }
924             rc = j;
925         }
926     }
927     else
928     {
929         memcpy(dst, src, min(src_len, dst_len));
930         rc = src_len;
931     }
932 
933     return rc;
934 }
935 
936 static INT
937 CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource,
938                           LPBYTE target, INT tlen, BOOL unicode )
939 {
940     INT rc;
941 
942     if (is_himc_ime_unicode(data) && !unicode)
943     {
944         if (tlen)
945         {
946             int i;
947 
948             if (slen < tlen)
949                 tlen = slen;
950             tlen /= sizeof (DWORD);
951             for (i = 0; i < tlen; ++i)
952             {
953                 ((DWORD *)target)[i] = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource,
954                                                           ((DWORD *)source)[i],
955                                                           NULL, 0,
956                                                           NULL, NULL);
957             }
958             rc = sizeof (DWORD) * i;
959         }
960         else
961             rc = slen;
962     }
963     else if (!is_himc_ime_unicode(data) && unicode)
964     {
965         if (tlen)
966         {
967             int i;
968 
969             if (slen < tlen)
970                 tlen = slen;
971             tlen /= sizeof (DWORD);
972             for (i = 0; i < tlen; ++i)
973             {
974                 ((DWORD *)target)[i] = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource,
975                                                           ((DWORD *)source)[i],
976                                                           NULL, 0);
977             }
978             rc = sizeof (DWORD) * i;
979         }
980         else
981             rc = slen;
982     }
983     else
984     {
985         memcpy( target, source, min(slen,tlen));
986         rc = slen;
987     }
988 
989     return rc;
990 }
991 
992 static INT
993 CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYTE ssource, BOOL unicode)
994 {
995     int rc;
996 
997     if (is_himc_ime_unicode(data) && !unicode)
998     {
999         rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL);
1000     }
1001     else if (!is_himc_ime_unicode(data) && unicode)
1002     {
1003         rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0);
1004     }
1005     else
1006         rc = offset;
1007 
1008     return rc;
1009 }
1010 
1011 static LONG
1012 ImmGetCompositionStringT(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf,
1013                          DWORD dwBufLen, BOOL unicode)
1014 {
1015     LONG rc = 0;
1016     InputContextData *data = get_imc_data(hIMC);
1017     LPCOMPOSITIONSTRING compstr;
1018     LPBYTE compdata;
1019 
1020     TRACE("(%p, 0x%x, %p, %d)\n", hIMC, dwIndex, lpBuf, dwBufLen);
1021 
1022     if (!data)
1023        return FALSE;
1024 
1025     if (!data->IMC.hCompStr)
1026        return FALSE;
1027 
1028     compdata = ImmLockIMCC(data->IMC.hCompStr);
1029     compstr = (LPCOMPOSITIONSTRING)compdata;
1030 
1031     switch (dwIndex)
1032     {
1033     case GCS_RESULTSTR:
1034         TRACE("GCS_RESULTSTR\n");
1035         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode);
1036         break;
1037     case GCS_COMPSTR:
1038         TRACE("GCS_COMPSTR\n");
1039         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode);
1040         break;
1041     case GCS_COMPATTR:
1042         TRACE("GCS_COMPATTR\n");
1043         rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen,
1044                                      compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen,
1045                                      lpBuf, dwBufLen, unicode);
1046         break;
1047     case GCS_COMPCLAUSE:
1048         TRACE("GCS_COMPCLAUSE\n");
1049         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen,
1050                                        compdata + compstr->dwCompStrOffset,
1051                                        lpBuf, dwBufLen, unicode);
1052         break;
1053     case GCS_RESULTCLAUSE:
1054         TRACE("GCS_RESULTCLAUSE\n");
1055         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen,
1056                                        compdata + compstr->dwResultStrOffset,
1057                                        lpBuf, dwBufLen, unicode);
1058         break;
1059     case GCS_RESULTREADSTR:
1060         TRACE("GCS_RESULTREADSTR\n");
1061         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode);
1062         break;
1063     case GCS_RESULTREADCLAUSE:
1064         TRACE("GCS_RESULTREADCLAUSE\n");
1065         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen,
1066                                        compdata + compstr->dwResultStrOffset,
1067                                        lpBuf, dwBufLen, unicode);
1068         break;
1069     case GCS_COMPREADSTR:
1070         TRACE("GCS_COMPREADSTR\n");
1071         rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode);
1072         break;
1073     case GCS_COMPREADATTR:
1074         TRACE("GCS_COMPREADATTR\n");
1075         rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen,
1076                                      compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen,
1077                                      lpBuf, dwBufLen, unicode);
1078         break;
1079     case GCS_COMPREADCLAUSE:
1080         TRACE("GCS_COMPREADCLAUSE\n");
1081         rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen,
1082                                        compdata + compstr->dwCompStrOffset,
1083                                        lpBuf, dwBufLen, unicode);
1084         break;
1085     case GCS_CURSORPOS:
1086         TRACE("GCS_CURSORPOS\n");
1087         rc = CopyCompOffsetIMEtoClient(data, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode);
1088         break;
1089     case GCS_DELTASTART:
1090         TRACE("GCS_DELTASTART\n");
1091         rc = CopyCompOffsetIMEtoClient(data, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode);
1092         break;
1093     default:
1094         FIXME("Unhandled index 0x%x\n",dwIndex);
1095         break;
1096     }
1097 
1098     ImmUnlockIMCC(data->IMC.hCompStr);
1099 
1100     return rc;
1101 }
1102 
1103 /***********************************************************************
1104  *		ImmGetCompositionStringA (IMM32.@)
1105  */
1106 LONG WINAPI ImmGetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
1107 {
1108     return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, FALSE);
1109 }
1110 
1111 /***********************************************************************
1112  *		ImmGetCompositionStringW (IMM32.@)
1113  */
1114 LONG WINAPI ImmGetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
1115 {
1116     return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, TRUE);
1117 }
1118 
1119 /***********************************************************************
1120  *		ImmGetContext (IMM32.@)
1121  */
1122 HIMC WINAPI ImmGetContext(HWND hWnd)
1123 {
1124     TRACE("(%p)\n", hWnd);
1125     if (hWnd == NULL)
1126         return NULL;
1127     return Imm32GetContextEx(hWnd, 2);
1128 }
1129 
1130 /***********************************************************************
1131  *		CtfImmIsCiceroEnabled (IMM32.@)
1132  */
1133 BOOL WINAPI CtfImmIsCiceroEnabled(VOID)
1134 {
1135     return (g_psi && (g_psi->dwSRVIFlags & SRVINFO_CICERO_ENABLED));
1136 }
1137 
1138 /***********************************************************************
1139  *		ImmInstallIMEA (IMM32.@)
1140  */
1141 HKL WINAPI ImmInstallIMEA(LPCSTR lpszIMEFileName, LPCSTR lpszLayoutText)
1142 {
1143     HKL hKL = NULL;
1144     LPWSTR pszFileNameW = NULL, pszLayoutTextW = NULL;
1145 
1146     TRACE("(%s, %s)\n", debugstr_a(lpszIMEFileName), debugstr_a(lpszLayoutText));
1147 
1148     pszFileNameW = Imm32WideFromAnsi(lpszIMEFileName);
1149     if (pszFileNameW == NULL)
1150         goto Quit;
1151 
1152     pszLayoutTextW = Imm32WideFromAnsi(lpszLayoutText);
1153     if (pszLayoutTextW == NULL)
1154         goto Quit;
1155 
1156     hKL = ImmInstallIMEW(pszFileNameW, pszLayoutTextW);
1157 
1158 Quit:
1159     if (pszFileNameW)
1160         HeapFree(g_hImm32Heap, 0, pszFileNameW);
1161     if (pszLayoutTextW)
1162         HeapFree(g_hImm32Heap, 0, pszLayoutTextW);
1163     return hKL;
1164 }
1165 
1166 /***********************************************************************
1167  *		ImmInstallIMEW (IMM32.@)
1168  */
1169 HKL WINAPI ImmInstallIMEW(LPCWSTR lpszIMEFileName, LPCWSTR lpszLayoutText)
1170 {
1171     INT lcid = GetUserDefaultLCID();
1172     INT count;
1173     HKL hkl;
1174     DWORD rc;
1175     HKEY hkey;
1176     WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8];
1177 
1178     TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName),
1179                           debugstr_w(lpszLayoutText));
1180 
1181     /* Start with 2.  e001 will be blank and so default to the wine internal IME */
1182     count = 2;
1183 
1184     while (count < 0xfff)
1185     {
1186         DWORD disposition = 0;
1187 
1188         hkl = (HKL)MAKELPARAM( lcid, 0xe000 | count );
1189         wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hkl);
1190 
1191         rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, regKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, &disposition);
1192         if (rc == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
1193             break;
1194         else if (rc == ERROR_SUCCESS)
1195             RegCloseKey(hkey);
1196 
1197         count++;
1198     }
1199 
1200     if (count == 0xfff)
1201     {
1202         WARN("Unable to find slot to install IME\n");
1203         return 0;
1204     }
1205 
1206     if (rc == ERROR_SUCCESS)
1207     {
1208         rc = RegSetValueExW(hkey, szImeFileW, 0, REG_SZ, (const BYTE*)lpszIMEFileName,
1209                             (lstrlenW(lpszIMEFileName) + 1) * sizeof(WCHAR));
1210         if (rc == ERROR_SUCCESS)
1211             rc = RegSetValueExW(hkey, szLayoutTextW, 0, REG_SZ, (const BYTE*)lpszLayoutText,
1212                                 (lstrlenW(lpszLayoutText) + 1) * sizeof(WCHAR));
1213         RegCloseKey(hkey);
1214         return hkl;
1215     }
1216     else
1217     {
1218         WARN("Unable to set IME registry values\n");
1219         return 0;
1220     }
1221 }
1222 
1223 /***********************************************************************
1224 *		ImmLockIMC(IMM32.@)
1225 */
1226 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
1227 {
1228     InputContextData *data = get_imc_data(hIMC);
1229 
1230     if (!data)
1231         return NULL;
1232     data->dwLock++;
1233     return &data->IMC;
1234 }
1235 
1236 /***********************************************************************
1237 *		ImmUnlockIMC(IMM32.@)
1238 */
1239 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
1240 {
1241     PCLIENTIMC pClientImc;
1242     HIMC hClientImc;
1243 
1244     pClientImc = ImmLockClientImc(hIMC);
1245     if (pClientImc == NULL)
1246         return FALSE;
1247 
1248     hClientImc = pClientImc->hImc;
1249     if (hClientImc)
1250         LocalUnlock(hClientImc);
1251 
1252     InterlockedDecrement(&pClientImc->cLockObj);
1253     ImmUnlockClientImc(pClientImc);
1254     return TRUE;
1255 }
1256 
1257 /***********************************************************************
1258  *              ImmRequestMessageA(IMM32.@)
1259  */
1260 LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1261 {
1262     InputContextData *data = get_imc_data(hIMC);
1263 
1264     TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
1265 
1266     if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
1267 
1268     SetLastError(ERROR_INVALID_HANDLE);
1269     return 0;
1270 }
1271 
1272 /***********************************************************************
1273  *              ImmRequestMessageW(IMM32.@)
1274  */
1275 LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1276 {
1277     InputContextData *data = get_imc_data(hIMC);
1278 
1279     TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
1280 
1281     if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
1282 
1283     SetLastError(ERROR_INVALID_HANDLE);
1284     return 0;
1285 }
1286 
1287 /***********************************************************************
1288  *		ImmReleaseContext (IMM32.@)
1289  */
1290 BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
1291 {
1292     TRACE("(%p, %p)\n", hWnd, hIMC);
1293     UNREFERENCED_PARAMETER(hWnd);
1294     UNREFERENCED_PARAMETER(hIMC);
1295     return TRUE; // Do nothing. This is correct.
1296 }
1297 
1298 /***********************************************************************
1299  *		ImmSetCompositionStringA (IMM32.@)
1300  */
1301 BOOL WINAPI
1302 ImmSetCompositionStringA(HIMC hIMC, DWORD dwIndex,
1303                          LPCVOID lpComp, DWORD dwCompLen,
1304                          LPCVOID lpRead, DWORD dwReadLen)
1305 {
1306     DWORD comp_len;
1307     DWORD read_len;
1308     WCHAR *CompBuffer = NULL;
1309     WCHAR *ReadBuffer = NULL;
1310     BOOL rc;
1311     InputContextData *data = get_imc_data(hIMC);
1312 
1313     TRACE("(%p, %d, %p, %d, %p, %d):\n",
1314             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
1315 
1316     if (!data)
1317         return FALSE;
1318 
1319     if (!(dwIndex == SCS_SETSTR ||
1320           dwIndex == SCS_CHANGEATTR ||
1321           dwIndex == SCS_CHANGECLAUSE ||
1322           dwIndex == SCS_SETRECONVERTSTRING ||
1323           dwIndex == SCS_QUERYRECONVERTSTRING))
1324         return FALSE;
1325 
1326     if (!is_himc_ime_unicode(data))
1327         return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
1328                         dwCompLen, lpRead, dwReadLen);
1329 
1330     comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0);
1331     if (comp_len)
1332     {
1333         CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len * sizeof(WCHAR));
1334         MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len);
1335     }
1336 
1337     read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0);
1338     if (read_len)
1339     {
1340         ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len * sizeof(WCHAR));
1341         MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len);
1342     }
1343 
1344     rc =  ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len,
1345                                    ReadBuffer, read_len);
1346 
1347     HeapFree(GetProcessHeap(), 0, CompBuffer);
1348     HeapFree(GetProcessHeap(), 0, ReadBuffer);
1349 
1350     return rc;
1351 }
1352 
1353 /***********************************************************************
1354  *		ImmSetCompositionStringW (IMM32.@)
1355  */
1356 BOOL WINAPI
1357 ImmSetCompositionStringW(HIMC hIMC, DWORD dwIndex,
1358                          LPCVOID lpComp, DWORD dwCompLen,
1359                          LPCVOID lpRead, DWORD dwReadLen)
1360 {
1361     DWORD comp_len;
1362     DWORD read_len;
1363     CHAR *CompBuffer = NULL;
1364     CHAR *ReadBuffer = NULL;
1365     BOOL rc;
1366     InputContextData *data = get_imc_data(hIMC);
1367 
1368     TRACE("(%p, %d, %p, %d, %p, %d):\n",
1369             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
1370 
1371     if (!data)
1372         return FALSE;
1373 
1374     if (!(dwIndex == SCS_SETSTR ||
1375           dwIndex == SCS_CHANGEATTR ||
1376           dwIndex == SCS_CHANGECLAUSE ||
1377           dwIndex == SCS_SETRECONVERTSTRING ||
1378           dwIndex == SCS_QUERYRECONVERTSTRING))
1379         return FALSE;
1380 
1381     if (is_himc_ime_unicode(data))
1382         return data->immKbd->pImeSetCompositionString(hIMC, dwIndex, lpComp,
1383                         dwCompLen, lpRead, dwReadLen);
1384 
1385     comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL,
1386                                    NULL);
1387     if (comp_len)
1388     {
1389         CompBuffer = HeapAlloc(GetProcessHeap(),0,comp_len);
1390         WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len,
1391                             NULL, NULL);
1392     }
1393 
1394     read_len = WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, NULL, 0, NULL,
1395                                    NULL);
1396     if (read_len)
1397     {
1398         ReadBuffer = HeapAlloc(GetProcessHeap(),0,read_len);
1399         WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len,
1400                             NULL, NULL);
1401     }
1402 
1403     rc =  ImmSetCompositionStringA(hIMC, dwIndex, CompBuffer, comp_len,
1404                                    ReadBuffer, read_len);
1405 
1406     HeapFree(GetProcessHeap(), 0, CompBuffer);
1407     HeapFree(GetProcessHeap(), 0, ReadBuffer);
1408 
1409     return rc;
1410 }
1411 
1412 /***********************************************************************
1413  *              ImmCreateSoftKeyboard(IMM32.@)
1414  */
1415 HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
1416 {
1417     FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
1418     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1419     return 0;
1420 }
1421 
1422 /***********************************************************************
1423  *              ImmDestroySoftKeyboard(IMM32.@)
1424  */
1425 BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
1426 {
1427     TRACE("(%p)\n", hSoftWnd);
1428     return DestroyWindow(hSoftWnd);
1429 }
1430 
1431 /***********************************************************************
1432  *              ImmShowSoftKeyboard(IMM32.@)
1433  */
1434 BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
1435 {
1436     TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
1437     if (hSoftWnd)
1438         return ShowWindow(hSoftWnd, nCmdShow);
1439     return FALSE;
1440 }
1441 
1442 /***********************************************************************
1443 *		ImmDisableTextFrameService(IMM32.@)
1444 */
1445 BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
1446 {
1447     FIXME("Stub\n");
1448     return FALSE;
1449 }
1450 
1451 /***********************************************************************
1452  *              ImmEnumInputContext(IMM32.@)
1453  */
1454 BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
1455 {
1456     HIMC *phList;
1457     DWORD dwIndex, dwCount;
1458     BOOL ret = TRUE;
1459     HIMC hIMC;
1460 
1461     TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
1462 
1463     dwCount = Imm32AllocAndBuildHimcList(dwThreadId, &phList);
1464     if (!dwCount)
1465         return FALSE;
1466 
1467     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
1468     {
1469         hIMC = phList[dwIndex];
1470         ret = (*lpfn)(hIMC, lParam);
1471         if (!ret)
1472             break;
1473     }
1474 
1475     HeapFree(g_hImm32Heap, 0, phList);
1476     return ret;
1477 }
1478 
1479 /***********************************************************************
1480  *              ImmSetActiveContext(IMM32.@)
1481  */
1482 BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC hIMC, BOOL fFlag)
1483 {
1484     FIXME("(%p, %p, %d): stub\n", hwnd, hIMC, fFlag);
1485     return FALSE;
1486 }
1487 
1488 /***********************************************************************
1489  *              ImmSetActiveContextConsoleIME(IMM32.@)
1490  */
1491 BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
1492 {
1493     HIMC hIMC;
1494     TRACE("(%p, %d)\n", hwnd, fFlag);
1495 
1496     hIMC = ImmGetContext(hwnd);
1497     if (hIMC)
1498         return ImmSetActiveContext(hwnd, hIMC, fFlag);
1499     return FALSE;
1500 }
1501 
1502 /***********************************************************************
1503  *		ImmGetImeMenuItemsA (IMM32.@)
1504  */
1505 DWORD WINAPI
1506 ImmGetImeMenuItemsA(HIMC hIMC, DWORD dwFlags, DWORD dwType,
1507                     LPIMEMENUITEMINFOA lpImeParentMenu,
1508                     LPIMEMENUITEMINFOA lpImeMenu, DWORD dwSize)
1509 {
1510     InputContextData *data = get_imc_data(hIMC);
1511     TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
1512         lpImeParentMenu, lpImeMenu, dwSize);
1513 
1514     if (!data)
1515     {
1516         SetLastError(ERROR_INVALID_HANDLE);
1517         return 0;
1518     }
1519 
1520     if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
1521     {
1522         if (!is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
1523             return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1524                                 (IMEMENUITEMINFOW*)lpImeParentMenu,
1525                                 (IMEMENUITEMINFOW*)lpImeMenu, dwSize);
1526         else
1527         {
1528             IMEMENUITEMINFOW lpImeParentMenuW;
1529             IMEMENUITEMINFOW *lpImeMenuW, *parent = NULL;
1530             DWORD rc;
1531 
1532             if (lpImeParentMenu)
1533                 parent = &lpImeParentMenuW;
1534             if (lpImeMenu)
1535             {
1536                 int count = dwSize / sizeof(LPIMEMENUITEMINFOA);
1537                 dwSize = count * sizeof(IMEMENUITEMINFOW);
1538                 lpImeMenuW = HeapAlloc(GetProcessHeap(), 0, dwSize);
1539             }
1540             else
1541                 lpImeMenuW = NULL;
1542 
1543             rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1544                                 parent, lpImeMenuW, dwSize);
1545 
1546             if (lpImeParentMenu)
1547             {
1548                 memcpy(lpImeParentMenu,&lpImeParentMenuW,sizeof(IMEMENUITEMINFOA));
1549                 lpImeParentMenu->hbmpItem = lpImeParentMenuW.hbmpItem;
1550                 WideCharToMultiByte(CP_ACP, 0, lpImeParentMenuW.szString,
1551                     -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE,
1552                     NULL, NULL);
1553             }
1554             if (lpImeMenu && rc)
1555             {
1556                 unsigned int i;
1557                 for (i = 0; i < rc; i++)
1558                 {
1559                     memcpy(&lpImeMenu[i],&lpImeMenuW[1],sizeof(IMEMENUITEMINFOA));
1560                     lpImeMenu[i].hbmpItem = lpImeMenuW[i].hbmpItem;
1561                     WideCharToMultiByte(CP_ACP, 0, lpImeMenuW[i].szString,
1562                         -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE,
1563                         NULL, NULL);
1564                 }
1565             }
1566             HeapFree(GetProcessHeap(),0,lpImeMenuW);
1567             return rc;
1568         }
1569     }
1570     else
1571         return 0;
1572 }
1573 
1574 /***********************************************************************
1575  *		ImmGetImeMenuItemsW (IMM32.@)
1576  */
1577 DWORD WINAPI
1578 ImmGetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType,
1579                     LPIMEMENUITEMINFOW lpImeParentMenu,
1580                     LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize)
1581 {
1582     InputContextData *data = get_imc_data(hIMC);
1583     TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
1584         lpImeParentMenu, lpImeMenu, dwSize);
1585 
1586     if (!data)
1587     {
1588         SetLastError(ERROR_INVALID_HANDLE);
1589         return 0;
1590     }
1591 
1592     if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
1593     {
1594         if (is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
1595             return data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1596                                 lpImeParentMenu, lpImeMenu, dwSize);
1597         else
1598         {
1599             IMEMENUITEMINFOA lpImeParentMenuA;
1600             IMEMENUITEMINFOA *lpImeMenuA, *parent = NULL;
1601             DWORD rc;
1602 
1603             if (lpImeParentMenu)
1604                 parent = &lpImeParentMenuA;
1605             if (lpImeMenu)
1606             {
1607                 int count = dwSize / sizeof(LPIMEMENUITEMINFOW);
1608                 dwSize = count * sizeof(IMEMENUITEMINFOA);
1609                 lpImeMenuA = HeapAlloc(GetProcessHeap(), 0, dwSize);
1610             }
1611             else
1612                 lpImeMenuA = NULL;
1613 
1614             rc = data->immKbd->pImeGetImeMenuItems(hIMC, dwFlags, dwType,
1615                                 (IMEMENUITEMINFOW*)parent,
1616                                 (IMEMENUITEMINFOW*)lpImeMenuA, dwSize);
1617 
1618             if (lpImeParentMenu)
1619             {
1620                 memcpy(lpImeParentMenu,&lpImeParentMenuA,sizeof(IMEMENUITEMINFOA));
1621                 lpImeParentMenu->hbmpItem = lpImeParentMenuA.hbmpItem;
1622                 MultiByteToWideChar(CP_ACP, 0, lpImeParentMenuA.szString,
1623                     -1, lpImeParentMenu->szString, IMEMENUITEM_STRING_SIZE);
1624             }
1625             if (lpImeMenu && rc)
1626             {
1627                 unsigned int i;
1628                 for (i = 0; i < rc; i++)
1629                 {
1630                     memcpy(&lpImeMenu[i],&lpImeMenuA[1],sizeof(IMEMENUITEMINFOA));
1631                     lpImeMenu[i].hbmpItem = lpImeMenuA[i].hbmpItem;
1632                     MultiByteToWideChar(CP_ACP, 0, lpImeMenuA[i].szString,
1633                         -1, lpImeMenu[i].szString, IMEMENUITEM_STRING_SIZE);
1634                 }
1635             }
1636             HeapFree(GetProcessHeap(),0,lpImeMenuA);
1637             return rc;
1638         }
1639     }
1640     else
1641         return 0;
1642 }
1643 
1644 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
1645 
1646 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
1647 {
1648     HKL hKL;
1649     HIMC hIMC;
1650     PTEB pTeb;
1651 
1652     TRACE("(%p, 0x%X, %p)\n", hinstDLL, fdwReason, lpReserved);
1653 
1654     switch (fdwReason)
1655     {
1656         case DLL_PROCESS_ATTACH:
1657             //Imm32GenerateRandomSeed(hinstDLL, 1, lpReserved); // Non-sense
1658             if (!Imm32InitInstance(hinstDLL))
1659             {
1660                 ERR("Imm32InitInstance failed\n");
1661                 return FALSE;
1662             }
1663             if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
1664             {
1665                 ERR("User32InitializeImmEntryTable failed\n");
1666                 return FALSE;
1667             }
1668             break;
1669 
1670         case DLL_THREAD_ATTACH:
1671             break;
1672 
1673         case DLL_THREAD_DETACH:
1674             if (g_psi == NULL || !(g_psi->dwSRVIFlags & SRVINFO_IMM32))
1675                 return TRUE;
1676 
1677             pTeb = NtCurrentTeb();
1678             if (pTeb->Win32ThreadInfo == NULL)
1679                 return TRUE;
1680 
1681             hKL = GetKeyboardLayout(0);
1682             // FIXME: NtUserGetThreadState and enum ThreadStateRoutines are broken.
1683             hIMC = (HIMC)NtUserGetThreadState(4);
1684             Imm32CleanupContext(hIMC, hKL, TRUE);
1685             break;
1686 
1687         case DLL_PROCESS_DETACH:
1688             RtlDeleteCriticalSection(&g_csImeDpi);
1689             TRACE("imm32.dll is unloaded\n");
1690             break;
1691     }
1692 
1693     return TRUE;
1694 }
1695