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