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