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