xref: /reactos/dll/win32/imm32/utils.c (revision 990ba545)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing IMM32 helper functions
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 HANDLE g_hImm32Heap = NULL;
18 
19 HRESULT APIENTRY
20 Imm32StrToUInt(LPCWSTR pszText, LPDWORD pdwValue, ULONG nBase)
21 {
22     NTSTATUS Status;
23     UNICODE_STRING UnicodeString;
24     RtlInitUnicodeString(&UnicodeString, pszText);
25     Status = RtlUnicodeStringToInteger(&UnicodeString, nBase, pdwValue);
26     if (!NT_SUCCESS(Status))
27         return E_FAIL;
28     return S_OK;
29 }
30 
31 HRESULT APIENTRY
32 Imm32UIntToStr(DWORD dwValue, ULONG nBase, LPWSTR pszBuff, USHORT cchBuff)
33 {
34     NTSTATUS Status;
35     UNICODE_STRING UnicodeString;
36     UnicodeString.Buffer = pszBuff;
37     UnicodeString.MaximumLength = cchBuff * sizeof(WCHAR);
38     Status = RtlIntegerToUnicodeString(dwValue, nBase, &UnicodeString);
39     if (!NT_SUCCESS(Status))
40         return E_FAIL;
41     return S_OK;
42 }
43 
44 BOOL WINAPI Imm32IsImcAnsi(HIMC hIMC)
45 {
46     BOOL ret;
47     PCLIENTIMC pClientImc = ImmLockClientImc(hIMC);
48     if (!pClientImc)
49         return -1;
50     ret = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
51     ImmUnlockClientImc(pClientImc);
52     return ret;
53 }
54 
55 LPWSTR APIENTRY Imm32WideFromAnsi(LPCSTR pszA)
56 {
57     INT cch = lstrlenA(pszA);
58     LPWSTR pszW = Imm32HeapAlloc(0, (cch + 1) * sizeof(WCHAR));
59     if (pszW == NULL)
60         return NULL;
61     cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszA, cch, pszW, cch + 1);
62     pszW[cch] = 0;
63     return pszW;
64 }
65 
66 LPSTR APIENTRY Imm32AnsiFromWide(LPCWSTR pszW)
67 {
68     INT cchW = lstrlenW(pszW);
69     INT cchA = (cchW + 1) * sizeof(WCHAR);
70     LPSTR pszA = Imm32HeapAlloc(0, cchA);
71     if (!pszA)
72         return NULL;
73     cchA = WideCharToMultiByte(CP_ACP, 0, pszW, cchW, pszA, cchA, NULL, NULL);
74     pszA[cchA] = 0;
75     return pszA;
76 }
77 
78 /* Converts the character index */
79 LONG APIENTRY IchWideFromAnsi(LONG cchAnsi, LPCSTR pchAnsi, UINT uCodePage)
80 {
81     LONG cchWide;
82     for (cchWide = 0; cchAnsi > 0; ++cchWide)
83     {
84         if (IsDBCSLeadByteEx(uCodePage, *pchAnsi) && pchAnsi[1])
85         {
86             cchAnsi -= 2;
87             pchAnsi += 2;
88         }
89         else
90         {
91             --cchAnsi;
92             ++pchAnsi;
93         }
94     }
95     return cchWide;
96 }
97 
98 /* Converts the character index */
99 LONG APIENTRY IchAnsiFromWide(LONG cchWide, LPCWSTR pchWide, UINT uCodePage)
100 {
101     LONG cb, cchAnsi;
102     for (cchAnsi = 0; cchWide > 0; ++cchAnsi, ++pchWide, --cchWide)
103     {
104         cb = WideCharToMultiByte(uCodePage, 0, pchWide, 1, NULL, 0, NULL, NULL);
105         if (cb > 1)
106             ++cchAnsi;
107     }
108     return cchAnsi;
109 }
110 
111 BOOL Imm32GetSystemLibraryPath(LPWSTR pszPath, DWORD cchPath, LPCWSTR pszFileName)
112 {
113     if (!pszFileName[0] || !GetSystemDirectoryW(pszPath, cchPath))
114         return FALSE;
115     StringCchCatW(pszPath, cchPath, L"\\");
116     StringCchCatW(pszPath, cchPath, pszFileName);
117     return TRUE;
118 }
119 
120 VOID APIENTRY LogFontAnsiToWide(const LOGFONTA *plfA, LPLOGFONTW plfW)
121 {
122     size_t cch;
123     RtlCopyMemory(plfW, plfA, offsetof(LOGFONTA, lfFaceName));
124     StringCchLengthA(plfA->lfFaceName, _countof(plfA->lfFaceName), &cch);
125     cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plfA->lfFaceName, (INT)cch,
126                               plfW->lfFaceName, _countof(plfW->lfFaceName));
127     if (cch > _countof(plfW->lfFaceName) - 1)
128         cch = _countof(plfW->lfFaceName) - 1;
129     plfW->lfFaceName[cch] = 0;
130 }
131 
132 VOID APIENTRY LogFontWideToAnsi(const LOGFONTW *plfW, LPLOGFONTA plfA)
133 {
134     size_t cch;
135     RtlCopyMemory(plfA, plfW, offsetof(LOGFONTW, lfFaceName));
136     StringCchLengthW(plfW->lfFaceName, _countof(plfW->lfFaceName), &cch);
137     cch = WideCharToMultiByte(CP_ACP, 0, plfW->lfFaceName, (INT)cch,
138                               plfA->lfFaceName, _countof(plfA->lfFaceName), NULL, NULL);
139     if (cch > _countof(plfA->lfFaceName) - 1)
140         cch = _countof(plfA->lfFaceName) - 1;
141     plfA->lfFaceName[cch] = 0;
142 }
143 
144 LPVOID FASTCALL ValidateHandleNoErr(HANDLE hObject, UINT uType)
145 {
146     INT index;
147     PUSER_HANDLE_TABLE ht;
148     PUSER_HANDLE_ENTRY he;
149     WORD generation;
150 
151     if (!NtUserValidateHandleSecure(hObject))
152         return NULL;
153 
154     ht = g_SharedInfo.aheList; /* handle table */
155     ASSERT(ht);
156     /* ReactOS-Specific! */
157     ASSERT(g_SharedInfo.ulSharedDelta != 0);
158     he = (PUSER_HANDLE_ENTRY)((ULONG_PTR)ht->handles - g_SharedInfo.ulSharedDelta);
159 
160     index = (LOWORD(hObject) - FIRST_USER_HANDLE) >> 1;
161     if (index < 0 || ht->nb_handles <= index || he[index].type != uType)
162         return NULL;
163 
164     generation = HIWORD(hObject);
165     if (generation != he[index].generation && generation && generation != 0xFFFF)
166         return NULL;
167 
168     return &he[index];
169 }
170 
171 PWND FASTCALL ValidateHwndNoErr(HWND hwnd)
172 {
173     /* See if the window is cached */
174     PCLIENTINFO ClientInfo = GetWin32ClientInfo();
175     if (hwnd == ClientInfo->CallbackWnd.hWnd)
176         return ClientInfo->CallbackWnd.pWnd;
177 
178     return ValidateHandleNoErr(hwnd, TYPE_WINDOW);
179 }
180 
181 BOOL APIENTRY Imm32CheckImcProcess(PIMC pIMC)
182 {
183     HIMC hIMC;
184     DWORD dwProcessID;
185     if (pIMC->head.pti == NtCurrentTeb()->Win32ThreadInfo)
186         return TRUE;
187 
188     hIMC = pIMC->head.h;
189     dwProcessID = NtUserQueryInputContext(hIMC, 0);
190     return dwProcessID == (DWORD_PTR)NtCurrentTeb()->ClientId.UniqueProcess;
191 }
192 
193 LPVOID APIENTRY Imm32HeapAlloc(DWORD dwFlags, DWORD dwBytes)
194 {
195     if (!g_hImm32Heap)
196     {
197         g_hImm32Heap = RtlGetProcessHeap();
198         if (g_hImm32Heap == NULL)
199             return NULL;
200     }
201     return HeapAlloc(g_hImm32Heap, dwFlags, dwBytes);
202 }
203 
204 BOOL APIENTRY
205 Imm32NotifyAction(HIMC hIMC, HWND hwnd, DWORD dwAction, DWORD_PTR dwIndex, DWORD_PTR dwValue,
206                   DWORD_PTR dwCommand, DWORD_PTR dwData)
207 {
208     DWORD dwThreadId;
209     HKL hKL;
210     PIMEDPI pImeDpi;
211 
212     if (dwAction)
213     {
214         dwThreadId = NtUserQueryInputContext(hIMC, 1);
215         if (dwThreadId)
216         {
217             /* find keyboard layout and lock it */
218             hKL = GetKeyboardLayout(dwThreadId);
219             pImeDpi = ImmLockImeDpi(hKL);
220             if (pImeDpi)
221             {
222                 /* do notify */
223                 pImeDpi->NotifyIME(hIMC, dwAction, dwIndex, dwValue);
224 
225                 ImmUnlockImeDpi(pImeDpi); /* unlock */
226             }
227         }
228     }
229 
230     if (hwnd && dwCommand)
231         SendMessageW(hwnd, WM_IME_NOTIFY, dwCommand, dwData);
232 
233     return TRUE;
234 }
235 
236 DWORD APIENTRY Imm32AllocAndBuildHimcList(DWORD dwThreadId, HIMC **pphList)
237 {
238 #define INITIAL_COUNT 0x40
239 #define MAX_RETRY 10
240     NTSTATUS Status;
241     DWORD dwCount = INITIAL_COUNT, cRetry = 0;
242     HIMC *phNewList;
243 
244     phNewList = Imm32HeapAlloc(0, dwCount * sizeof(HIMC));
245     if (phNewList == NULL)
246         return 0;
247 
248     Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
249     while (Status == STATUS_BUFFER_TOO_SMALL)
250     {
251         Imm32HeapFree(phNewList);
252         if (cRetry++ >= MAX_RETRY)
253             return 0;
254 
255         phNewList = Imm32HeapAlloc(0, dwCount * sizeof(HIMC));
256         if (phNewList == NULL)
257             return 0;
258 
259         Status = NtUserBuildHimcList(dwThreadId, dwCount, phNewList, &dwCount);
260     }
261 
262     if (NT_ERROR(Status) || !dwCount)
263     {
264         Imm32HeapFree(phNewList);
265         return 0;
266     }
267 
268     *pphList = phNewList;
269     return dwCount;
270 #undef INITIAL_COUNT
271 #undef MAX_RETRY
272 }
273 
274 INT APIENTRY
275 Imm32ImeMenuAnsiToWide(const IMEMENUITEMINFOA *pItemA, LPIMEMENUITEMINFOW pItemW,
276                        UINT uCodePage, BOOL bBitmap)
277 {
278     INT ret;
279     pItemW->cbSize = pItemA->cbSize;
280     pItemW->fType = pItemA->fType;
281     pItemW->fState = pItemA->fState;
282     pItemW->wID = pItemA->wID;
283     if (bBitmap)
284     {
285         pItemW->hbmpChecked = pItemA->hbmpChecked;
286         pItemW->hbmpUnchecked = pItemA->hbmpUnchecked;
287         pItemW->hbmpItem = pItemA->hbmpItem;
288     }
289     pItemW->dwItemData = pItemA->dwItemData;
290     ret = MultiByteToWideChar(uCodePage, 0, pItemA->szString, -1,
291                               pItemW->szString, _countof(pItemW->szString));
292     if (ret >= _countof(pItemW->szString))
293     {
294         ret = 0;
295         pItemW->szString[0] = 0;
296     }
297     return ret;
298 }
299 
300 INT APIENTRY
301 Imm32ImeMenuWideToAnsi(const IMEMENUITEMINFOW *pItemW, LPIMEMENUITEMINFOA pItemA,
302                        UINT uCodePage)
303 {
304     INT ret;
305     pItemA->cbSize = pItemW->cbSize;
306     pItemA->fType = pItemW->fType;
307     pItemA->fState = pItemW->fState;
308     pItemA->wID = pItemW->wID;
309     pItemA->hbmpChecked = pItemW->hbmpChecked;
310     pItemA->hbmpUnchecked = pItemW->hbmpUnchecked;
311     pItemA->dwItemData = pItemW->dwItemData;
312     pItemA->hbmpItem = pItemW->hbmpItem;
313     ret = WideCharToMultiByte(uCodePage, 0, pItemW->szString, -1,
314                               pItemA->szString, _countof(pItemA->szString), NULL, NULL);
315     if (ret >= _countof(pItemA->szString))
316     {
317         ret = 0;
318         pItemA->szString[0] = 0;
319     }
320     return ret;
321 }
322 
323 PIME_STATE APIENTRY
324 Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL)
325 {
326     PIME_STATE pState;
327     WORD Lang = PRIMARYLANGID(LOWORD(hKL));
328     for (pState = pIC->pState; pState; pState = pState->pNext)
329     {
330         if (pState->wLang == Lang)
331             break;
332     }
333     if (!pState)
334     {
335         pState = Imm32HeapAlloc(HEAP_ZERO_MEMORY, sizeof(IME_STATE));
336         if (pState)
337         {
338             pState->wLang = Lang;
339             pState->pNext = pIC->pState;
340             pIC->pState = pState;
341         }
342     }
343     return pState;
344 }
345 
346 PIME_SUBSTATE APIENTRY
347 Imm32FetchImeSubState(PIME_STATE pState, HKL hKL)
348 {
349     PIME_SUBSTATE pSubState;
350     for (pSubState = pState->pSubState; pSubState; pSubState = pSubState->pNext)
351     {
352         if (pSubState->hKL == hKL)
353             return pSubState;
354     }
355     pSubState = Imm32HeapAlloc(0, sizeof(IME_SUBSTATE));
356     if (!pSubState)
357         return NULL;
358     pSubState->dwValue = 0;
359     pSubState->hKL = hKL;
360     pSubState->pNext = pState->pSubState;
361     pState->pSubState = pSubState;
362     return pSubState;
363 }
364 
365 BOOL APIENTRY
366 Imm32LoadImeStateSentence(LPINPUTCONTEXTDX pIC, PIME_STATE pState, HKL hKL)
367 {
368     PIME_SUBSTATE pSubState = Imm32FetchImeSubState(pState, hKL);
369     if (pSubState)
370     {
371         pIC->fdwSentence |= pSubState->dwValue;
372         return TRUE;
373     }
374     return FALSE;
375 }
376 
377 BOOL APIENTRY
378 Imm32SaveImeStateSentence(LPINPUTCONTEXTDX pIC, PIME_STATE pState, HKL hKL)
379 {
380     PIME_SUBSTATE pSubState = Imm32FetchImeSubState(pState, hKL);
381     if (pSubState)
382     {
383         pSubState->dwValue = (pIC->fdwSentence & 0xffff0000);
384         return TRUE;
385     }
386     return FALSE;
387 }
388 
389 /*
390  * See RECONVERTSTRING structure:
391  * https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/RECONVERTSTRING.html
392  *
393  * The dwCompStrOffset and dwTargetOffset members are the relative position of dwStrOffset.
394  * dwStrLen, dwCompStrLen, and dwTargetStrLen are the TCHAR count. dwStrOffset,
395  * dwCompStrOffset, and dwTargetStrOffset are the byte offset.
396  */
397 
398 DWORD APIENTRY
399 Imm32ReconvertWideFromAnsi(LPRECONVERTSTRING pDest, const RECONVERTSTRING *pSrc, UINT uCodePage)
400 {
401     DWORD cch0, cchDest, cbDest;
402     LPCSTR pchSrc = (LPCSTR)pSrc + pSrc->dwStrOffset;
403     LPWSTR pchDest;
404 
405     if (pSrc->dwVersion != 0)
406         return 0;
407 
408     cchDest = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, pchSrc, pSrc->dwStrLen,
409                                   NULL, 0);
410     cbDest = sizeof(RECONVERTSTRING) + (cchDest + 1) * sizeof(WCHAR);
411     if (!pDest)
412         return cbDest;
413 
414     if (pDest->dwSize < cbDest)
415         return 0;
416 
417     /* dwSize */
418     pDest->dwSize = cbDest;
419 
420     /* dwVersion */
421     pDest->dwVersion = 0;
422 
423     /* dwStrOffset */
424     pDest->dwStrOffset = sizeof(RECONVERTSTRING);
425 
426     /* dwCompStrOffset */
427     cch0 = IchWideFromAnsi(pSrc->dwCompStrOffset, pchSrc, uCodePage);
428     pDest->dwCompStrOffset = cch0 * sizeof(WCHAR);
429 
430     /* dwCompStrLen */
431     cch0 = IchWideFromAnsi(pSrc->dwCompStrOffset + pSrc->dwCompStrLen, pchSrc, uCodePage);
432     pDest->dwCompStrLen = (cch0 * sizeof(WCHAR) - pDest->dwCompStrOffset) / sizeof(WCHAR);
433 
434     /* dwTargetStrOffset */
435     cch0 = IchWideFromAnsi(pSrc->dwTargetStrOffset, pchSrc, uCodePage);
436     pDest->dwTargetStrOffset = cch0 * sizeof(WCHAR);
437 
438     /* dwTargetStrLen */
439     cch0 = IchWideFromAnsi(pSrc->dwTargetStrOffset + pSrc->dwTargetStrLen, pchSrc, uCodePage);
440     pDest->dwTargetStrLen = (cch0 * sizeof(WCHAR) - pSrc->dwTargetStrOffset) / sizeof(WCHAR);
441 
442     /* dwStrLen */
443     pDest->dwStrLen = cchDest;
444 
445     /* the string */
446     pchDest = (LPWSTR)((LPBYTE)pDest + pDest->dwStrOffset);
447     cchDest = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, pchSrc, pSrc->dwStrLen,
448                                   pchDest, cchDest);
449     pchDest[cchDest] = 0;
450 
451     return cbDest;
452 }
453 
454 DWORD APIENTRY
455 Imm32ReconvertAnsiFromWide(LPRECONVERTSTRING pDest, const RECONVERTSTRING *pSrc, UINT uCodePage)
456 {
457     DWORD cch0, cch1, cchDest, cbDest;
458     LPCWSTR pchSrc = (LPCWSTR)((LPCSTR)pSrc + pSrc->dwStrOffset);
459     LPSTR pchDest;
460 
461     if (pSrc->dwVersion != 0)
462         return 0;
463 
464     cchDest = WideCharToMultiByte(uCodePage, 0, pchSrc, pSrc->dwStrLen,
465                                   NULL, 0, NULL, NULL);
466     cbDest = sizeof(RECONVERTSTRING) + (cchDest + 1) * sizeof(CHAR);
467     if (!pDest)
468         return cbDest;
469 
470     if (pDest->dwSize < cbDest)
471         return 0;
472 
473     /* dwSize */
474     pDest->dwSize = cbDest;
475 
476     /* dwVersion */
477     pDest->dwVersion = 0;
478 
479     /* dwStrOffset */
480     pDest->dwStrOffset = sizeof(RECONVERTSTRING);
481 
482     /* dwCompStrOffset */
483     cch1 = pSrc->dwCompStrOffset / sizeof(WCHAR);
484     cch0 = IchAnsiFromWide(cch1, pchSrc, uCodePage);
485     pDest->dwCompStrOffset = cch0 * sizeof(CHAR);
486 
487     /* dwCompStrLen */
488     cch0 = IchAnsiFromWide(cch1 + pSrc->dwCompStrLen, pchSrc, uCodePage);
489     pDest->dwCompStrLen = cch0 * sizeof(CHAR) - pDest->dwCompStrOffset;
490 
491     /* dwTargetStrOffset */
492     cch1 = pSrc->dwTargetStrOffset / sizeof(WCHAR);
493     cch0 = IchAnsiFromWide(cch1, pchSrc, uCodePage);
494     pDest->dwTargetStrOffset = cch0 * sizeof(CHAR);
495 
496     /* dwTargetStrLen */
497     cch0 = IchAnsiFromWide(cch1 + pSrc->dwTargetStrLen, pchSrc, uCodePage);
498     pDest->dwTargetStrLen = cch0 * sizeof(CHAR) - pDest->dwTargetStrOffset;
499 
500     /* dwStrLen */
501     pDest->dwStrLen = cchDest;
502 
503     /* the string */
504     pchDest = (LPSTR)pDest + pDest->dwStrOffset;
505     cchDest = WideCharToMultiByte(uCodePage, 0, pchSrc, pSrc->dwStrLen,
506                                   pchDest, cchDest, NULL, NULL);
507     pchDest[cchDest] = 0;
508 
509     return cbDest;
510 }
511 
512 typedef BOOL (WINAPI *FN_GetFileVersionInfoW)(LPCWSTR, DWORD, DWORD, LPVOID);
513 typedef DWORD (WINAPI *FN_GetFileVersionInfoSizeW)(LPCWSTR, LPDWORD);
514 typedef BOOL (WINAPI *FN_VerQueryValueW)(LPCVOID, LPCWSTR, LPVOID*, PUINT);
515 
516 static FN_GetFileVersionInfoW s_fnGetFileVersionInfoW = NULL;
517 static FN_GetFileVersionInfoSizeW s_fnGetFileVersionInfoSizeW = NULL;
518 static FN_VerQueryValueW s_fnVerQueryValueW = NULL;
519 
520 static BOOL APIENTRY Imm32LoadImeFixedInfo(PIMEINFOEX pInfoEx, LPCVOID pVerInfo)
521 {
522     UINT cbFixed = 0;
523     VS_FIXEDFILEINFO *pFixed;
524     if (!s_fnVerQueryValueW(pVerInfo, L"\\", (LPVOID*)&pFixed, &cbFixed) || !cbFixed)
525         return FALSE;
526 
527     /* NOTE: The IME module must contain a version info of input method driver. */
528     if (pFixed->dwFileType != VFT_DRV || pFixed->dwFileSubtype != VFT2_DRV_INPUTMETHOD)
529         return FALSE;
530 
531     pInfoEx->dwProdVersion = pFixed->dwProductVersionMS;
532     pInfoEx->dwImeWinVersion = 0x40000;
533     return TRUE;
534 }
535 
536 static LPWSTR APIENTRY
537 Imm32GetVerInfoValue(LPCVOID pVerInfo, LPWSTR pszKey, DWORD cchKey, LPCWSTR pszName)
538 {
539     size_t cchExtra;
540     LPWSTR pszValue;
541     UINT cbValue = 0;
542 
543     StringCchLengthW(pszKey, cchKey, &cchExtra);
544 
545     StringCchCatW(pszKey, cchKey, pszName);
546     s_fnVerQueryValueW(pVerInfo, pszKey, (LPVOID*)&pszValue, &cbValue);
547     pszKey[cchExtra] = 0;
548 
549     return (cbValue ? pszValue : NULL);
550 }
551 
552 BOOL APIENTRY Imm32LoadImeLangAndDesc(PIMEINFOEX pInfoEx, LPCVOID pVerInfo)
553 {
554     BOOL ret;
555     WCHAR szKey[80];
556     LPWSTR pszDesc;
557     LPWORD pw;
558     UINT cbData;
559     LANGID LangID;
560 
561     /* Getting the version info. See VerQueryValue */
562     ret = s_fnVerQueryValueW(pVerInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&pw, &cbData);
563     if (!ret || !cbData)
564         return FALSE;
565 
566     if (pInfoEx->hkl == NULL)
567         pInfoEx->hkl = (HKL)(DWORD_PTR)*pw; /* This is an invalid HKL */
568 
569     /* Try the current language and the Unicode codepage (0x04B0) */
570     LangID = LANGIDFROMLCID(GetThreadLocale());
571     StringCchPrintfW(szKey, _countof(szKey), L"\\StringFileInfo\\%04X04B0\\", LangID);
572     pszDesc = Imm32GetVerInfoValue(pVerInfo, szKey, _countof(szKey), L"FileDescription");
573     if (!pszDesc)
574     {
575         /* Retry the language and codepage of the IME module */
576         StringCchPrintfW(szKey, _countof(szKey), L"\\StringFileInfo\\%04X%04X\\", pw[0], pw[1]);
577         pszDesc = Imm32GetVerInfoValue(pVerInfo, szKey, _countof(szKey), L"FileDescription");
578     }
579 
580     /* The description */
581     if (pszDesc)
582         StringCchCopyW(pInfoEx->wszImeDescription, _countof(pInfoEx->wszImeDescription), pszDesc);
583     else
584         pInfoEx->wszImeDescription[0] = 0;
585 
586     return TRUE;
587 }
588 
589 BOOL APIENTRY Imm32LoadImeVerInfo(PIMEINFOEX pImeInfoEx)
590 {
591     HINSTANCE hinstVersion;
592     BOOL ret = FALSE, bLoaded = FALSE;
593     WCHAR szPath[MAX_PATH];
594     LPVOID pVerInfo;
595     DWORD cbVerInfo, dwHandle;
596 
597     /* Load version.dll to use the version info API */
598     Imm32GetSystemLibraryPath(szPath, _countof(szPath), L"version.dll");
599     hinstVersion = GetModuleHandleW(szPath);
600     if (!hinstVersion)
601     {
602         hinstVersion = LoadLibraryW(szPath);
603         if (!hinstVersion)
604             return FALSE;
605         bLoaded = TRUE;
606     }
607 
608 #define GET_FN(name) do { \
609     s_fn##name = (FN_##name)GetProcAddress(hinstVersion, #name); \
610     if (!s_fn##name) goto Quit; \
611 } while (0)
612     GET_FN(GetFileVersionInfoW);
613     GET_FN(GetFileVersionInfoSizeW);
614     GET_FN(VerQueryValueW);
615 #undef GET_FN
616 
617     /* The path of the IME module */
618     Imm32GetSystemLibraryPath(szPath, _countof(szPath), pImeInfoEx->wszImeFile);
619 
620     cbVerInfo = s_fnGetFileVersionInfoSizeW(szPath, &dwHandle);
621     if (!cbVerInfo)
622         goto Quit;
623 
624     pVerInfo = Imm32HeapAlloc(0, cbVerInfo);
625     if (!pVerInfo)
626         goto Quit;
627 
628     /* Load the version info of the IME module */
629     if (s_fnGetFileVersionInfoW(szPath, dwHandle, cbVerInfo, pVerInfo) &&
630         Imm32LoadImeFixedInfo(pImeInfoEx, pVerInfo))
631     {
632         ret = Imm32LoadImeLangAndDesc(pImeInfoEx, pVerInfo);
633     }
634 
635     Imm32HeapFree(pVerInfo);
636 
637 Quit:
638     if (bLoaded)
639         FreeLibrary(hinstVersion);
640     return ret;
641 }
642 
643 HKL APIENTRY Imm32GetNextHKL(UINT cKLs, const REG_IME *pLayouts, WORD wLangID)
644 {
645     UINT iKL, wID, wLow = 0xE0FF, wHigh = 0xE01F, wNextID = 0;
646 
647     for (iKL = 0; iKL < cKLs; ++iKL)
648     {
649         wHigh = max(wHigh, HIWORD(pLayouts[iKL].hKL));
650         wLow = min(wLow, HIWORD(pLayouts[iKL].hKL));
651     }
652 
653     if (wHigh < 0xE0FF)
654     {
655         wNextID = wHigh + 1;
656     }
657     else if (wLow > 0xE001)
658     {
659         wNextID = wLow - 1;
660     }
661     else
662     {
663         for (wID = 0xE020; wID <= 0xE0FF; ++wID)
664         {
665             for (iKL = 0; iKL < cKLs; ++iKL)
666             {
667                 if (LOWORD(pLayouts[iKL].hKL) == wLangID &&
668                     HIWORD(pLayouts[iKL].hKL) == wID)
669                 {
670                     break;
671                 }
672             }
673 
674             if (iKL >= cKLs)
675                 break;
676         }
677 
678         if (wID <= 0xE0FF)
679             wNextID = wID;
680     }
681 
682     if (!wNextID)
683         return NULL;
684 
685     return (HKL)(DWORD_PTR)MAKELONG(wLangID, wNextID);
686 }
687 
688 UINT APIENTRY Imm32GetRegImes(PREG_IME pLayouts, UINT cLayouts)
689 {
690     HKEY hkeyLayouts, hkeyIME;
691     WCHAR szImeFileName[80], szImeKey[20];
692     UINT iKey, nCount;
693     DWORD cbData;
694     LONG lError;
695     ULONG Value;
696     HKL hKL;
697 
698     /* Open the registry keyboard layouts */
699     lError = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hkeyLayouts);
700     if (lError != ERROR_SUCCESS)
701         return 0;
702 
703     for (iKey = nCount = 0; ; ++iKey)
704     {
705         /* Get the key name */
706         lError = RegEnumKeyW(hkeyLayouts, iKey, szImeKey, _countof(szImeKey));
707         if (lError != ERROR_SUCCESS)
708             break;
709 
710         if (szImeKey[0] != L'E' && szImeKey[0] != L'e')
711             continue; /* Not an IME layout */
712 
713         if (pLayouts == NULL) /* for counting only */
714         {
715             ++nCount;
716             continue;
717         }
718 
719         if (cLayouts <= nCount)
720             break;
721 
722         lError = RegOpenKeyW(hkeyLayouts, szImeKey, &hkeyIME); /* Open the IME key */
723         if (lError != ERROR_SUCCESS)
724             break;
725 
726         /* Load the "Ime File" value */
727         szImeFileName[0] = 0;
728         cbData = sizeof(szImeFileName);
729         RegQueryValueExW(hkeyIME, L"Ime File", NULL, NULL, (LPBYTE)szImeFileName, &cbData);
730         szImeFileName[_countof(szImeFileName) - 1] = 0;
731 
732         RegCloseKey(hkeyIME);
733 
734         if (!szImeFileName[0])
735             break;
736 
737         Imm32StrToUInt(szImeKey, &Value, 16);
738         hKL = (HKL)(DWORD_PTR)Value;
739         if (!IS_IME_HKL(hKL))
740             break;
741 
742         /* Store the IME key and the IME filename */
743         pLayouts[nCount].hKL = hKL;
744         StringCchCopyW(pLayouts[nCount].szImeKey, _countof(pLayouts[nCount].szImeKey), szImeKey);
745         CharUpperW(szImeFileName);
746         StringCchCopyW(pLayouts[nCount].szFileName, _countof(pLayouts[nCount].szFileName),
747                        szImeFileName);
748         ++nCount;
749     }
750 
751     RegCloseKey(hkeyLayouts);
752     return nCount;
753 }
754 
755 BOOL APIENTRY Imm32WriteRegIme(HKL hKL, LPCWSTR pchFilePart, LPCWSTR pszLayout)
756 {
757     UINT iPreload;
758     HKEY hkeyLayouts, hkeyIME, hkeyPreload;
759     WCHAR szImeKey[20], szPreloadNumber[20], szPreloadKey[20], szImeFileName[80];
760     DWORD cbData;
761     LANGID LangID;
762     LONG lError;
763     LPCWSTR pszLayoutFile;
764 
765     /* Open the registry keyboard layouts */
766     lError = RegOpenKeyW(HKEY_LOCAL_MACHINE, REGKEY_KEYBOARD_LAYOUTS, &hkeyLayouts);
767     if (lError != ERROR_SUCCESS)
768         return FALSE;
769 
770     /* Get the IME key from hKL */
771     Imm32UIntToStr((DWORD)(DWORD_PTR)hKL, 16, szImeKey, _countof(szImeKey));
772 
773     /* Create a registry IME key */
774     lError = RegCreateKeyW(hkeyLayouts, szImeKey, &hkeyIME);
775     if (lError != ERROR_SUCCESS)
776         goto Failure;
777 
778     /* Write "Ime File" */
779     cbData = (wcslen(pchFilePart) + 1) * sizeof(WCHAR);
780     lError = RegSetValueExW(hkeyIME, L"Ime File", 0, REG_SZ, (LPBYTE)pchFilePart, cbData);
781     if (lError != ERROR_SUCCESS)
782         goto Failure;
783 
784     /* Write "Layout Text" */
785     cbData = (wcslen(pszLayout) + 1) * sizeof(WCHAR);
786     lError = RegSetValueExW(hkeyIME, L"Layout Text", 0, REG_SZ, (LPBYTE)pszLayout, cbData);
787     if (lError != ERROR_SUCCESS)
788         goto Failure;
789 
790     /* Choose "Layout File" from hKL */
791     LangID = LOWORD(hKL);
792     switch (LOBYTE(LangID))
793     {
794         case LANG_JAPANESE: pszLayoutFile = L"kbdjpn.dll"; break;
795         case LANG_KOREAN:   pszLayoutFile = L"kbdkor.dll"; break;
796         default:            pszLayoutFile = L"kbdus.dll"; break;
797     }
798     StringCchCopyW(szImeFileName, _countof(szImeFileName), pszLayoutFile);
799 
800     /* Write "Layout File" */
801     cbData = (wcslen(szImeFileName) + 1) * sizeof(WCHAR);
802     lError = RegSetValueExW(hkeyIME, L"Layout File", 0, REG_SZ, (LPBYTE)szImeFileName, cbData);
803     if (lError != ERROR_SUCCESS)
804         goto Failure;
805 
806     RegCloseKey(hkeyIME);
807     RegCloseKey(hkeyLayouts);
808 
809     /* Create "Preload" key */
810     RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", &hkeyPreload);
811 
812 #define MAX_PRELOAD 0x400
813     for (iPreload = 1; iPreload < MAX_PRELOAD; ++iPreload)
814     {
815         Imm32UIntToStr(iPreload, 10, szPreloadNumber, _countof(szPreloadNumber));
816 
817         /* Load the key of the preload number */
818         cbData = sizeof(szPreloadKey);
819         lError = RegQueryValueExW(hkeyPreload, szPreloadNumber, NULL, NULL,
820                                   (LPBYTE)szPreloadKey, &cbData);
821         szPreloadKey[_countof(szPreloadKey) - 1] = 0;
822 
823         if (lError != ERROR_SUCCESS || lstrcmpiW(szImeKey, szPreloadKey) == 0)
824             break; /* Found an empty room or the same key */
825     }
826 
827     if (iPreload >= MAX_PRELOAD) /* Not found */
828     {
829         RegCloseKey(hkeyPreload);
830         return FALSE;
831     }
832 #undef MAX_PRELOAD
833 
834     /* Write the IME key to the preload number */
835     cbData = (wcslen(szImeKey) + 1) * sizeof(WCHAR);
836     lError = RegSetValueExW(hkeyPreload, szPreloadNumber, 0, REG_SZ, (LPBYTE)szImeKey, cbData);
837     RegCloseKey(hkeyPreload);
838     return lError == ERROR_SUCCESS;
839 
840 Failure:
841     RegCloseKey(hkeyIME);
842     RegDeleteKeyW(hkeyLayouts, szImeKey);
843     RegCloseKey(hkeyLayouts);
844     return FALSE;
845 }
846 
847 typedef INT (WINAPI *FN_LZOpenFileW)(LPWSTR, LPOFSTRUCT, WORD);
848 typedef LONG (WINAPI *FN_LZCopy)(INT, INT);
849 typedef VOID (WINAPI *FN_LZClose)(INT);
850 
851 BOOL APIENTRY Imm32CopyFile(LPWSTR pszOldFile, LPCWSTR pszNewFile)
852 {
853     BOOL ret = FALSE, bLoaded = FALSE;
854     HMODULE hinstLZ32;
855     WCHAR szLZ32Path[MAX_PATH];
856     CHAR szDestA[MAX_PATH];
857     OFSTRUCT OFStruct;
858     FN_LZOpenFileW fnLZOpenFileW;
859     FN_LZCopy fnLZCopy;
860     FN_LZClose fnLZClose;
861     HFILE hfDest, hfSrc;
862 
863     /* Load LZ32.dll for copying/decompressing file */
864     Imm32GetSystemLibraryPath(szLZ32Path, _countof(szLZ32Path), L"LZ32");
865     hinstLZ32 = GetModuleHandleW(szLZ32Path);
866     if (!hinstLZ32)
867     {
868         hinstLZ32 = LoadLibraryW(szLZ32Path);
869         if (!hinstLZ32)
870             return FALSE;
871         bLoaded = TRUE;
872     }
873 
874 #define GET_FN(name) do { \
875     fn##name = (FN_##name)GetProcAddress(hinstLZ32, #name); \
876     if (!fn##name) goto Quit; \
877 } while (0)
878     GET_FN(LZOpenFileW);
879     GET_FN(LZCopy);
880     GET_FN(LZClose);
881 #undef GET_FN
882 
883     if (!WideCharToMultiByte(CP_ACP, 0, pszNewFile, -1, szDestA, _countof(szDestA), NULL, NULL))
884         goto Quit;
885     szDestA[_countof(szDestA) - 1] = 0;
886 
887     hfSrc = fnLZOpenFileW(pszOldFile, &OFStruct, OF_READ);
888     if (hfSrc < 0)
889         goto Quit;
890 
891     hfDest = OpenFile(szDestA, &OFStruct, OF_CREATE);
892     if (hfDest != HFILE_ERROR)
893     {
894         ret = (fnLZCopy(hfSrc, hfDest) >= 0);
895         _lclose(hfDest);
896     }
897 
898     fnLZClose(hfSrc);
899 
900 Quit:
901     if (bLoaded)
902         FreeLibrary(hinstLZ32);
903     return ret;
904 }
905 
906 /***********************************************************************
907  *		CtfImmIsTextFrameServiceDisabled(IMM32.@)
908  */
909 BOOL WINAPI CtfImmIsTextFrameServiceDisabled(VOID)
910 {
911     return !!(GetWin32ClientInfo()->CI_flags & CI_TFSDISABLED);
912 }
913 
914 /***********************************************************************
915  *		ImmCreateIMCC(IMM32.@)
916  */
917 HIMCC WINAPI ImmCreateIMCC(DWORD size)
918 {
919     if (size < sizeof(DWORD))
920         size = sizeof(DWORD);
921     return LocalAlloc(LHND, size);
922 }
923 
924 /***********************************************************************
925  *       ImmDestroyIMCC(IMM32.@)
926  */
927 HIMCC WINAPI ImmDestroyIMCC(HIMCC block)
928 {
929     if (block)
930         return LocalFree(block);
931     return NULL;
932 }
933 
934 /***********************************************************************
935  *		ImmLockIMCC(IMM32.@)
936  */
937 LPVOID WINAPI ImmLockIMCC(HIMCC imcc)
938 {
939     if (imcc)
940         return LocalLock(imcc);
941     return NULL;
942 }
943 
944 /***********************************************************************
945  *		ImmUnlockIMCC(IMM32.@)
946  */
947 BOOL WINAPI ImmUnlockIMCC(HIMCC imcc)
948 {
949     if (imcc)
950         return LocalUnlock(imcc);
951     return FALSE;
952 }
953 
954 /***********************************************************************
955  *		ImmGetIMCCLockCount(IMM32.@)
956  */
957 DWORD WINAPI ImmGetIMCCLockCount(HIMCC imcc)
958 {
959     return LocalFlags(imcc) & LMEM_LOCKCOUNT;
960 }
961 
962 /***********************************************************************
963  *		ImmReSizeIMCC(IMM32.@)
964  */
965 HIMCC WINAPI ImmReSizeIMCC(HIMCC imcc, DWORD size)
966 {
967     if (!imcc)
968         return NULL;
969     return LocalReAlloc(imcc, size, LHND);
970 }
971 
972 /***********************************************************************
973  *		ImmGetIMCCSize(IMM32.@)
974  */
975 DWORD WINAPI ImmGetIMCCSize(HIMCC imcc)
976 {
977     if (imcc)
978         return LocalSize(imcc);
979     return 0;
980 }
981 
982 /***********************************************************************
983  *		ImmGetIMCLockCount(IMM32.@)
984  */
985 DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC)
986 {
987     DWORD ret;
988     HANDLE hInputContext;
989     PCLIENTIMC pClientImc;
990 
991     pClientImc = ImmLockClientImc(hIMC);
992     if (pClientImc == NULL)
993         return 0;
994 
995     ret = 0;
996     hInputContext = pClientImc->hInputContext;
997     if (hInputContext)
998         ret = (LocalFlags(hInputContext) & LMEM_LOCKCOUNT);
999 
1000     ImmUnlockClientImc(pClientImc);
1001     return ret;
1002 }
1003 
1004 /***********************************************************************
1005  *		ImmIMPGetIMEA(IMM32.@)
1006  */
1007 BOOL WINAPI ImmIMPGetIMEA(HWND hWnd, LPIMEPROA pImePro)
1008 {
1009     FIXME("(%p, %p)\n", hWnd, pImePro);
1010     return FALSE;
1011 }
1012 
1013 /***********************************************************************
1014  *		ImmIMPGetIMEW(IMM32.@)
1015  */
1016 BOOL WINAPI ImmIMPGetIMEW(HWND hWnd, LPIMEPROW pImePro)
1017 {
1018     FIXME("(%p, %p)\n", hWnd, pImePro);
1019     return FALSE;
1020 }
1021 
1022 /***********************************************************************
1023  *		ImmIMPQueryIMEA(IMM32.@)
1024  */
1025 BOOL WINAPI ImmIMPQueryIMEA(LPIMEPROA pImePro)
1026 {
1027     FIXME("(%p)\n", pImePro);
1028     return FALSE;
1029 }
1030 
1031 /***********************************************************************
1032  *		ImmIMPQueryIMEW(IMM32.@)
1033  */
1034 BOOL WINAPI ImmIMPQueryIMEW(LPIMEPROW pImePro)
1035 {
1036     FIXME("(%p)\n", pImePro);
1037     return FALSE;
1038 }
1039 
1040 /***********************************************************************
1041  *		ImmIMPSetIMEA(IMM32.@)
1042  */
1043 BOOL WINAPI ImmIMPSetIMEA(HWND hWnd, LPIMEPROA pImePro)
1044 {
1045     FIXME("(%p, %p)\n", hWnd, pImePro);
1046     return FALSE;
1047 }
1048 
1049 /***********************************************************************
1050  *		ImmIMPSetIMEW(IMM32.@)
1051  */
1052 BOOL WINAPI ImmIMPSetIMEW(HWND hWnd, LPIMEPROW pImePro)
1053 {
1054     FIXME("(%p, %p)\n", hWnd, pImePro);
1055     return FALSE;
1056 }
1057