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