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