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