1 /* 2 * PROJECT: ReactOS Win32k subsystem 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: win32ss/user/ntuser/kbdlayout.c 5 * PURPOSE: Keyboard layout management 6 * COPYRIGHT: Copyright 2007 Saveliy Tretiakov 7 * Copyright 2008 Colin Finck 8 * Copyright 2011 Rafal Harabien 9 * Copyright 2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 10 */ 11 12 #include <win32k.h> 13 #include <immdev.h> 14 15 // Was included only because of CP_ACP and required the 16 // definition of SYSTEMTIME in ndk\rtltypes.h 17 //#include <winnls.h> 18 #define CP_ACP 0 19 20 DBG_DEFAULT_CHANNEL(UserKbdLayout); 21 22 PKL gspklBaseLayout = NULL; /* FIXME: Please move this to pWinSta->spklList */ 23 PKBDFILE gpkfList = NULL; 24 DWORD gSystemFS = 0; 25 UINT gSystemCPCharSet = 0; 26 HKL ghKLSentToShell = NULL; 27 28 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID); 29 30 /* PRIVATE FUNCTIONS ******************************************************/ 31 32 /* 33 * Retrieves a PKL by an input locale identifier (HKL). 34 * @implemented 35 */ 36 PKL FASTCALL IntHKLtoPKL(_Inout_ PTHREADINFO pti, _In_ HKL hKL) 37 { 38 PKL pFirstKL, pKL; 39 40 pFirstKL = pti->KeyboardLayout; 41 if (!pFirstKL) 42 return NULL; 43 44 pKL = pFirstKL; 45 46 /* hKL can have special value HKL_NEXT or HKL_PREV */ 47 if (hKL == UlongToHandle(HKL_NEXT)) /* Looking forward */ 48 { 49 do 50 { 51 pKL = pKL->pklNext; 52 if (!(pKL->dwKL_Flags & KL_UNLOAD)) 53 return pKL; 54 } while (pKL != pFirstKL); 55 } 56 else if (hKL == UlongToHandle(HKL_PREV)) /* Looking backward */ 57 { 58 do 59 { 60 pKL = pKL->pklPrev; 61 if (!(pKL->dwKL_Flags & KL_UNLOAD)) 62 return pKL; 63 } while (pKL != pFirstKL); 64 } 65 else if (HIWORD(hKL)) /* hKL is a full input locale identifier */ 66 { 67 /* No KL_UNLOAD check */ 68 do 69 { 70 if (pKL->hkl == hKL) 71 return pKL; 72 73 pKL = pKL->pklNext; 74 } while (pKL != pFirstKL); 75 } 76 else /* Language only specified */ 77 { 78 /* No KL_UNLOAD check */ 79 do 80 { 81 if (LOWORD(pKL->hkl) == LOWORD(hKL)) /* Low word is language ID */ 82 return pKL; 83 84 pKL = pKL->pklNext; 85 } while (pKL != pFirstKL); 86 } 87 88 return NULL; 89 } 90 91 /* 92 * A helper function for NtUserGetKeyboardLayoutList. 93 * @implemented 94 */ 95 static UINT APIENTRY 96 IntGetKeyboardLayoutList( 97 _Inout_ PWINSTATION_OBJECT pWinSta, 98 _In_ ULONG nBuff, 99 _Out_ HKL *pHklBuff) 100 { 101 UINT ret = 0; 102 PKL pKL, pFirstKL; 103 104 pFirstKL = gspklBaseLayout; /* FIXME: Use pWinSta->spklList instead */ 105 if (!pWinSta || !pFirstKL) 106 return 0; 107 108 pKL = pFirstKL; 109 110 if (nBuff == 0) 111 { 112 /* Count the effective PKLs */ 113 do 114 { 115 if (!(pKL->dwKL_Flags & KL_UNLOAD)) 116 ++ret; 117 pKL = pKL->pklNext; 118 } while (pKL != pFirstKL); 119 } 120 else 121 { 122 /* Copy the effective HKLs to pHklBuff */ 123 do 124 { 125 if (!(pKL->dwKL_Flags & KL_UNLOAD)) 126 { 127 *pHklBuff = pKL->hkl; 128 ++pHklBuff; 129 ++ret; 130 --nBuff; 131 132 if (nBuff == 0) 133 break; 134 } 135 pKL = pKL->pklNext; 136 } while (pKL != pFirstKL); 137 } 138 139 return ret; 140 } 141 142 #if 0 && DBG 143 144 static VOID 145 DumpKbdLayout( 146 IN PKBDTABLES pKbdTbl) 147 { 148 PVK_TO_BIT pVkToBit; 149 PVK_TO_WCHAR_TABLE pVkToWchTbl; 150 PVSC_VK pVscVk; 151 ULONG i; 152 153 DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n", 154 pKbdTbl->fLocaleFlags, pKbdTbl->bMaxVSCtoVK); 155 DbgPrint("wMaxModBits %x\n", 156 pKbdTbl->pCharModifiers ? pKbdTbl->pCharModifiers->wMaxModBits 157 : 0); 158 159 if (pKbdTbl->pCharModifiers) 160 { 161 pVkToBit = pKbdTbl->pCharModifiers->pVkToBit; 162 if (pVkToBit) 163 { 164 for (; pVkToBit->Vk; ++pVkToBit) 165 { 166 DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits); 167 } 168 } 169 170 for (i = 0; i <= pKbdTbl->pCharModifiers->wMaxModBits; ++i) 171 { 172 DbgPrint("ModNumber %x -> %x\n", i, pKbdTbl->pCharModifiers->ModNumber[i]); 173 } 174 } 175 176 pVkToWchTbl = pKbdTbl->pVkToWcharTable; 177 if (pVkToWchTbl) 178 { 179 for (; pVkToWchTbl->pVkToWchars; ++pVkToWchTbl) 180 { 181 PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars; 182 183 DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", 184 pVkToWchTbl->nModifications, pVkToWchTbl->cbSize); 185 if (pVkToWch) 186 { 187 while (pVkToWch->VirtualKey) 188 { 189 DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ", 190 pVkToWch->VirtualKey, pVkToWch->Attributes); 191 for (i = 0; i < pVkToWchTbl->nModifications; ++i) 192 { 193 DbgPrint("%x ", pVkToWch->wch[i]); 194 } 195 DbgPrint("}\n"); 196 pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize); 197 } 198 } 199 } 200 } 201 202 // TODO: DeadKeys, KeyNames, KeyNamesExt, KeyNamesDead 203 204 DbgPrint("pusVSCtoVK: { "); 205 if (pKbdTbl->pusVSCtoVK) 206 { 207 for (i = 0; i < pKbdTbl->bMaxVSCtoVK; ++i) 208 { 209 DbgPrint("%x -> %x, ", i, pKbdTbl->pusVSCtoVK[i]); 210 } 211 } 212 DbgPrint("}\n"); 213 214 DbgPrint("pVSCtoVK_E0: { "); 215 pVscVk = pKbdTbl->pVSCtoVK_E0; 216 if (pVscVk) 217 { 218 for (; pVscVk->Vsc; ++pVscVk) 219 { 220 DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); 221 } 222 } 223 DbgPrint("}\n"); 224 225 DbgPrint("pVSCtoVK_E1: { "); 226 pVscVk = pKbdTbl->pVSCtoVK_E1; 227 if (pVscVk) 228 { 229 for (; pVscVk->Vsc; ++pVscVk) 230 { 231 DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); 232 } 233 } 234 DbgPrint("}\n"); 235 236 // TODO: Ligatures 237 } 238 239 #endif // DBG 240 241 242 /* 243 * UserLoadKbdDll 244 * 245 * Loads keyboard layout DLL and gets address to KbdTables 246 */ 247 static BOOL 248 UserLoadKbdDll(WCHAR *pwszLayoutPath, 249 HANDLE *phModule, 250 PKBDTABLES *pKbdTables) 251 { 252 PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor; 253 254 /* Load keyboard layout DLL */ 255 TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath); 256 *phModule = EngLoadImage(pwszLayoutPath); 257 if (!(*phModule)) 258 { 259 ERR("Failed to load dll %ws\n", pwszLayoutPath); 260 return FALSE; 261 } 262 263 /* Find KbdLayerDescriptor function and get layout tables */ 264 TRACE("Loaded %ws\n", pwszLayoutPath); 265 pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor"); 266 267 /* FIXME: Windows reads file instead of executing! 268 It's not safe to kbdlayout DLL in kernel mode! */ 269 270 if (pfnKbdLayerDescriptor) 271 *pKbdTables = pfnKbdLayerDescriptor(); 272 else 273 ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath); 274 275 if (!pfnKbdLayerDescriptor || !*pKbdTables) 276 { 277 ERR("Failed to load the keyboard layout.\n"); 278 EngUnloadImage(*phModule); 279 return FALSE; 280 } 281 282 #if 0 && DBG 283 /* Dump keyboard layout */ 284 DumpKbdLayout(*pKbdTables); 285 #endif 286 287 return TRUE; 288 } 289 290 /* 291 * UserLoadKbdFile 292 * 293 * Loads keyboard layout DLL and creates KBDFILE object 294 */ 295 static PKBDFILE 296 UserLoadKbdFile(PUNICODE_STRING pwszKLID) 297 { 298 PKBDFILE pkf, pRet = NULL; 299 NTSTATUS Status; 300 ULONG cbSize; 301 HKEY hKey = NULL; 302 WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\"; 303 WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\" 304 L"Control\\Keyboard Layouts\\"; 305 306 /* Create keyboard layout file object */ 307 pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE)); 308 if (!pkf) 309 { 310 ERR("Failed to create object!\n"); 311 return NULL; 312 } 313 314 /* Set keyboard layout name */ 315 swprintf(pkf->awchKF, L"%wZ", pwszKLID); 316 317 /* Open layout registry key */ 318 RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF); 319 Status = RegOpenKey(wszLayoutRegKey, &hKey); 320 if (!NT_SUCCESS(Status)) 321 { 322 ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status); 323 goto cleanup; 324 } 325 326 /* Read filename of layout DLL */ 327 cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR)); 328 Status = RegQueryValue(hKey, 329 L"Layout File", 330 REG_SZ, 331 wszLayoutPath + wcslen(wszLayoutPath), 332 &cbSize); 333 334 if (!NT_SUCCESS(Status)) 335 { 336 ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status); 337 goto cleanup; 338 } 339 340 /* Load keyboard file now */ 341 if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl)) 342 { 343 ERR("Failed to load %ws dll!\n", wszLayoutPath); 344 goto cleanup; 345 } 346 347 /* Update next field */ 348 pkf->pkfNext = gpkfList; 349 gpkfList = pkf; 350 351 /* Return keyboard file */ 352 pRet = pkf; 353 354 cleanup: 355 if (hKey) 356 ZwClose(hKey); 357 if (pkf) 358 UserDereferenceObject(pkf); // we dont need ptr anymore 359 if (!pRet) 360 { 361 /* We have failed - destroy created object */ 362 if (pkf) 363 UserDeleteObject(UserHMGetHandle(pkf), TYPE_KBDFILE); 364 } 365 366 return pRet; 367 } 368 369 /* 370 * co_UserLoadKbdLayout 371 * 372 * Loads keyboard layout and creates KL object 373 */ 374 static PKL 375 co_UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL) 376 { 377 LCID lCid; 378 CHARSETINFO cs; 379 PKL pKl; 380 381 /* Create keyboard layout object */ 382 pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL)); 383 if (!pKl) 384 { 385 ERR("Failed to create object!\n"); 386 return NULL; 387 } 388 389 pKl->hkl = hKL; 390 pKl->spkf = UserLoadKbdFile(pustrKLID); 391 392 /* Dereference keyboard layout */ 393 UserDereferenceObject(pKl); 394 395 /* If we failed, remove KL object */ 396 if (!pKl->spkf) 397 { 398 ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID); 399 UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT); 400 return NULL; 401 } 402 403 // Up to Language Identifiers.. 404 if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid))) 405 { 406 ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID); 407 UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT); 408 return NULL; 409 } 410 411 TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid); 412 if (co_IntGetCharsetInfo(lCid, &cs)) 413 { 414 pKl->iBaseCharset = cs.ciCharset; 415 pKl->dwFontSigs = cs.fs.fsCsb[0]; 416 pKl->CodePage = (USHORT)cs.ciACP; 417 TRACE("Charset %u Font Sig %lu CodePage %u\n", 418 pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage); 419 } 420 else 421 { 422 pKl->iBaseCharset = ANSI_CHARSET; 423 pKl->dwFontSigs = FS_LATIN1; 424 pKl->CodePage = CP_ACP; 425 } 426 427 // Set initial system character set and font signature. 428 if (gSystemFS == 0) 429 { 430 gSystemCPCharSet = pKl->iBaseCharset; 431 gSystemFS = pKl->dwFontSigs; 432 } 433 434 return pKl; 435 } 436 437 /* 438 * UnloadKbdFile 439 * 440 * Destroys specified Keyboard File object 441 */ 442 static 443 VOID 444 UnloadKbdFile(_In_ PKBDFILE pkf) 445 { 446 PKBDFILE *ppkfLink = &gpkfList; 447 NT_ASSERT(pkf != NULL); 448 449 /* Find previous object */ 450 while (*ppkfLink) 451 { 452 if (*ppkfLink == pkf) 453 break; 454 455 ppkfLink = &(*ppkfLink)->pkfNext; 456 } 457 458 if (*ppkfLink == pkf) 459 *ppkfLink = pkf->pkfNext; 460 461 EngUnloadImage(pkf->hBase); 462 UserDeleteObject(UserHMGetHandle(pkf), TYPE_KBDFILE); 463 } 464 465 /* 466 * UserUnloadKbl 467 * 468 * Unloads specified Keyboard Layout if possible 469 */ 470 BOOL 471 UserUnloadKbl(PKL pKl) 472 { 473 /* According to msdn, UnloadKeyboardLayout can fail 474 if the keyboard layout identifier was preloaded. */ 475 if (pKl == gspklBaseLayout) 476 { 477 if (pKl->pklNext == pKl->pklPrev) 478 { 479 /* There is only one layout */ 480 return FALSE; 481 } 482 483 /* Set next layout as default */ 484 gspklBaseLayout = pKl->pklNext; 485 } 486 487 if (pKl->head.cLockObj > 1) 488 { 489 /* Layout is used by other threads */ 490 pKl->dwKL_Flags |= KL_UNLOAD; 491 return FALSE; 492 } 493 494 /* Unload the layout */ 495 pKl->pklPrev->pklNext = pKl->pklNext; 496 pKl->pklNext->pklPrev = pKl->pklPrev; 497 UnloadKbdFile(pKl->spkf); 498 if (pKl->piiex) 499 { 500 ExFreePoolWithTag(pKl->piiex, USERTAG_IME); 501 } 502 UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT); 503 return TRUE; 504 } 505 506 /* 507 * W32kGetDefaultKeyLayout 508 * 509 * Returns default layout for new threads 510 */ 511 PKL 512 W32kGetDefaultKeyLayout(VOID) 513 { 514 PKL pKl = gspklBaseLayout; 515 516 if (!pKl) 517 return NULL; 518 519 /* Return not unloaded layout */ 520 do 521 { 522 if (!(pKl->dwKL_Flags & KL_UNLOAD)) 523 return pKl; 524 525 pKl = pKl->pklPrev; /* Confirmed on Win2k */ 526 } while(pKl != gspklBaseLayout); 527 528 /* We have not found proper KL */ 529 return NULL; 530 } 531 532 /* 533 * UserHklToKbl 534 * 535 * Gets KL object from hkl value 536 */ 537 PKL 538 NTAPI 539 UserHklToKbl(HKL hKl) 540 { 541 PKL pKl = gspklBaseLayout; 542 543 if (!gspklBaseLayout) 544 return NULL; 545 546 do 547 { 548 if (pKl->hkl == hKl) 549 return pKl; 550 551 pKl = pKl->pklNext; 552 } while (pKl != gspklBaseLayout); 553 554 return NULL; 555 } 556 557 VOID FASTCALL 558 IntReorderKeyboardLayouts( 559 _Inout_ PWINSTATION_OBJECT pWinSta, 560 _Inout_ PKL pNewKL) 561 { 562 PKL pOldKL = gspklBaseLayout; 563 564 if ((pWinSta->Flags & WSS_NOIO) || pNewKL == pOldKL) 565 return; 566 567 pNewKL->pklPrev->pklNext = pNewKL->pklNext; 568 pNewKL->pklNext->pklPrev = pNewKL->pklPrev; 569 pNewKL->pklNext = pOldKL; 570 pNewKL->pklPrev = pOldKL->pklPrev; 571 pOldKL->pklPrev->pklNext = pNewKL; 572 pOldKL->pklPrev = pNewKL; 573 gspklBaseLayout = pNewKL; /* Should we use UserAssignmentLock? */ 574 } 575 576 /* 577 * UserSetDefaultInputLang 578 * 579 * Sets default keyboard layout for system. Called from UserSystemParametersInfo. 580 */ 581 BOOL 582 NTAPI 583 UserSetDefaultInputLang(HKL hKl) 584 { 585 PKL pKl; 586 587 pKl = UserHklToKbl(hKl); 588 if (!pKl) 589 return FALSE; 590 591 IntReorderKeyboardLayouts(IntGetProcessWindowStation(NULL), pKl); 592 return TRUE; 593 } 594 595 VOID APIENTRY 596 IntImmActivateLayout( 597 _Inout_ PTHREADINFO pti, 598 _Inout_ PKL pKL) 599 { 600 PWND pImeWnd; 601 HWND hImeWnd; 602 USER_REFERENCE_ENTRY Ref; 603 604 if (pti->KeyboardLayout == pKL) 605 return; 606 607 pImeWnd = pti->spwndDefaultIme; 608 if (pImeWnd) 609 { 610 UserRefObjectCo(pImeWnd, &Ref); 611 hImeWnd = UserHMGetHandle(pImeWnd); 612 co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)pKL->hkl); 613 UserDerefObjectCo(pImeWnd); 614 } 615 else 616 { 617 /* Remember old keyboard layout to switch back for Chinese IMEs */ 618 pti->hklPrev = pti->KeyboardLayout->hkl; 619 620 if (pti->spDefaultImc) 621 { 622 /* IME Activation is needed */ 623 pti->pClientInfo->CI_flags |= CI_IMMACTIVATE; 624 } 625 } 626 627 UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKL); 628 pti->pClientInfo->hKL = pKL->hkl; 629 pti->pClientInfo->CodePage = pKL->CodePage; 630 } 631 632 static VOID co_IntSetKeyboardLayoutForProcess(PPROCESSINFO ppi, PKL pKL) 633 { 634 PTHREADINFO ptiNode, ptiNext; 635 PCLIENTINFO pClientInfo; 636 BOOL bImmMode = IS_IMM_MODE(); 637 638 for (ptiNode = ppi->ptiList; ptiNode; ptiNode = ptiNext) 639 { 640 IntReferenceThreadInfo(ptiNode); 641 ptiNext = ptiNode->ptiSibling; 642 643 /* Skip this thread if its keyboard layout is already the correct one, or if it's dying */ 644 if (ptiNode->KeyboardLayout == pKL || (ptiNode->TIF_flags & TIF_INCLEANUP)) 645 { 646 IntDereferenceThreadInfo(ptiNode); 647 continue; 648 } 649 650 if (bImmMode) 651 { 652 IntImmActivateLayout(ptiNode, pKL); 653 } 654 else 655 { 656 UserAssignmentLock((PVOID*)&ptiNode->KeyboardLayout, pKL); 657 pClientInfo = ptiNode->pClientInfo; 658 pClientInfo->CodePage = pKL->CodePage; 659 pClientInfo->hKL = pKL->hkl; 660 } 661 662 IntDereferenceThreadInfo(ptiNode); 663 } 664 } 665 666 HKL APIENTRY 667 co_UserActivateKeyboardLayout( 668 _Inout_ PKL pKL, 669 _In_ ULONG uFlags, 670 _In_opt_ PWND pWnd) 671 { 672 HKL hOldKL = NULL; 673 PKL pOldKL; 674 PTHREADINFO pti = GetW32ThreadInfo(); 675 PWND pTargetWnd, pImeWnd; 676 HWND hTargetWnd, hImeWnd; 677 USER_REFERENCE_ENTRY Ref1, Ref2; 678 PCLIENTINFO ClientInfo; 679 BOOL bSetForProcess = !!(uFlags & KLF_SETFORPROCESS); 680 681 IntReferenceThreadInfo(pti); 682 ClientInfo = pti->pClientInfo; 683 684 pOldKL = pti->KeyboardLayout; 685 if (pOldKL) 686 hOldKL = pOldKL->hkl; 687 688 if (uFlags & KLF_RESET) 689 { 690 FIXME("KLF_RESET\n"); 691 } 692 693 if (!bSetForProcess && pKL == pti->KeyboardLayout) 694 { 695 IntDereferenceThreadInfo(pti); 696 return hOldKL; 697 } 698 699 pKL->wchDiacritic = UNICODE_NULL; 700 701 if (pOldKL) 702 UserRefObjectCo(pOldKL, &Ref1); 703 704 if (pti->TIF_flags & TIF_CSRSSTHREAD) 705 { 706 UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL); 707 ClientInfo->CodePage = pKL->CodePage; 708 ClientInfo->hKL = pKL->hkl; 709 } 710 else if (bSetForProcess) 711 { 712 co_IntSetKeyboardLayoutForProcess(pti->ppi, pKL); 713 } 714 else 715 { 716 if (IS_IMM_MODE()) 717 IntImmActivateLayout(pti, pKL); 718 else 719 UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL); 720 721 ClientInfo->CodePage = pKL->CodePage; 722 ClientInfo->hKL = pKL->hkl; 723 } 724 725 /* Send shell message if necessary */ 726 if (gptiForeground && (gptiForeground->ppi == pti->ppi) && ISITHOOKED(WH_SHELL)) 727 { 728 /* Send the HKL if needed and remember it */ 729 if (ghKLSentToShell != pKL->hkl) 730 { 731 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)pKL->hkl); 732 ghKLSentToShell = pKL->hkl; 733 } 734 } 735 736 if (pti->MessageQueue) 737 { 738 /* Determine the target window */ 739 pTargetWnd = pti->MessageQueue->spwndFocus; 740 if (!pTargetWnd) 741 { 742 pTargetWnd = pti->MessageQueue->spwndActive; 743 if (!pTargetWnd) 744 pTargetWnd = pWnd; 745 } 746 747 /* Send WM_INPUTLANGCHANGE message */ 748 if (pTargetWnd) 749 { 750 UserRefObjectCo(pTargetWnd, &Ref2); 751 hTargetWnd = UserHMGetHandle(pTargetWnd); 752 co_IntSendMessage(hTargetWnd, WM_INPUTLANGCHANGE, pKL->iBaseCharset, (LPARAM)pKL->hkl); 753 UserDerefObjectCo(pTargetWnd); 754 } 755 } 756 757 // Refresh IME UI via WM_IME_SYSTEM:IMS_SENDNOTIFICATION messaging 758 if (!(pti->TIF_flags & TIF_CSRSSTHREAD)) 759 { 760 if (IS_IME_HKL(pKL->hkl) || (IS_CICERO_MODE() && !IS_16BIT_MODE())) 761 { 762 pImeWnd = pti->spwndDefaultIme; 763 if (pImeWnd) 764 { 765 bSetForProcess &= !IS_16BIT_MODE(); 766 UserRefObjectCo(pImeWnd, &Ref2); 767 hImeWnd = UserHMGetHandle(pImeWnd); 768 co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, bSetForProcess); 769 UserDerefObjectCo(pImeWnd); 770 } 771 } 772 } 773 774 if (pOldKL) 775 UserDerefObjectCo(pOldKL); 776 777 IntDereferenceThreadInfo(pti); 778 return hOldKL; 779 } 780 781 HKL APIENTRY 782 co_IntActivateKeyboardLayout( 783 _Inout_ PWINSTATION_OBJECT pWinSta, 784 _In_ HKL hKL, 785 _In_ ULONG uFlags, 786 _In_opt_ PWND pWnd) 787 { 788 PKL pKL; 789 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 790 791 pKL = IntHKLtoPKL(pti, hKL); 792 if (!pKL) 793 { 794 ERR("Invalid HKL %p!\n", hKL); 795 return NULL; 796 } 797 798 if (uFlags & KLF_REORDER) 799 IntReorderKeyboardLayouts(pWinSta, pKL); 800 801 return co_UserActivateKeyboardLayout(pKL, uFlags, pWnd); 802 } 803 804 static BOOL APIENTRY 805 co_IntUnloadKeyboardLayoutEx( 806 _Inout_ PWINSTATION_OBJECT pWinSta, 807 _Inout_ PKL pKL, 808 _In_ DWORD dwFlags) 809 { 810 PKL pNextKL; 811 USER_REFERENCE_ENTRY Ref1, Ref2; 812 PTHREADINFO pti = gptiCurrent; 813 814 if (pKL == gspklBaseLayout && !(dwFlags & UKL_NOACTIVATENEXT)) 815 return FALSE; 816 817 UserRefObjectCo(pKL, &Ref1); /* Add reference */ 818 819 /* Regard as unloaded */ 820 UserMarkObjectDestroy(pKL); 821 pKL->dwKL_Flags |= KL_UNLOAD; 822 823 if (!(dwFlags & UKL_NOACTIVATENEXT) && pti->KeyboardLayout == pKL) 824 { 825 pNextKL = IntHKLtoPKL(pti, UlongToHandle(HKL_NEXT)); 826 if (pNextKL) 827 { 828 UserRefObjectCo(pNextKL, &Ref2); /* Add reference */ 829 co_UserActivateKeyboardLayout(pNextKL, dwFlags, NULL); 830 UserDerefObjectCo(pNextKL); /* Release reference */ 831 } 832 } 833 834 if (gspklBaseLayout == pKL && pKL != pKL->pklNext) 835 { 836 /* Set next layout as default (FIXME: Use UserAssignmentLock?) */ 837 gspklBaseLayout = pKL->pklNext; 838 } 839 840 UserDerefObjectCo(pKL); /* Release reference */ 841 842 if (ISITHOOKED(WH_SHELL)) 843 { 844 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, 0); 845 ghKLSentToShell = NULL; 846 } 847 848 return TRUE; 849 } 850 851 static BOOL APIENTRY 852 IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL) 853 { 854 PKL pKL = IntHKLtoPKL(gptiCurrent, hKL); 855 if (!pKL) 856 { 857 ERR("Invalid HKL %p!\n", hKL); 858 return FALSE; 859 } 860 return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0); 861 } 862 863 /// Invokes imm32!ImmLoadLayout and returns PIMEINFOEX 864 PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL) 865 { 866 PIMEINFOEX piiex; 867 868 if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE()) 869 return NULL; 870 871 piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME); 872 if (!piiex) 873 return NULL; 874 875 if (!co_ClientImmLoadLayout(hKL, piiex)) 876 { 877 ExFreePoolWithTag(piiex, USERTAG_IME); 878 return NULL; 879 } 880 881 return piiex; 882 } 883 884 HKL APIENTRY 885 co_IntLoadKeyboardLayoutEx( 886 IN OUT PWINSTATION_OBJECT pWinSta, 887 IN HANDLE hSafeFile, 888 IN HKL hOldKL, 889 IN PUNICODE_STRING puszSafeKLID, 890 IN HKL hNewKL, 891 IN UINT Flags) 892 { 893 PKL pOldKL, pNewKL; 894 895 UNREFERENCED_PARAMETER(hSafeFile); 896 897 if (hNewKL == NULL || (pWinSta->Flags & WSS_NOIO)) 898 return NULL; 899 900 /* If hOldKL is specified, unload it and load new layput as default */ 901 if (hOldKL && hOldKL != hNewKL) 902 { 903 pOldKL = UserHklToKbl(hOldKL); 904 if (pOldKL) 905 UserUnloadKbl(pOldKL); 906 } 907 908 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */ 909 910 /* Let's see if layout was already loaded. */ 911 pNewKL = UserHklToKbl(hNewKL); 912 if (!pNewKL) 913 { 914 /* It wasn't, so load it. */ 915 pNewKL = co_UserLoadKbdLayout(puszSafeKLID, hNewKL); 916 if (!pNewKL) 917 return NULL; 918 919 if (gspklBaseLayout) 920 { 921 /* Find last not unloaded layout */ 922 PKL pLastKL = gspklBaseLayout->pklPrev; 923 while (pLastKL != gspklBaseLayout && (pLastKL->dwKL_Flags & KL_UNLOAD)) 924 pLastKL = pLastKL->pklPrev; 925 926 /* Add new layout to the list */ 927 pNewKL->pklNext = pLastKL->pklNext; 928 pNewKL->pklPrev = pLastKL; 929 pNewKL->pklNext->pklPrev = pNewKL; 930 pNewKL->pklPrev->pklNext = pNewKL; 931 } 932 else 933 { 934 /* This is the first layout */ 935 pNewKL->pklNext = pNewKL; 936 pNewKL->pklPrev = pNewKL; 937 gspklBaseLayout = pNewKL; 938 } 939 940 pNewKL->piiex = co_UserImmLoadLayout(hNewKL); 941 } 942 943 /* If this layout was prepared to unload, undo it */ 944 pNewKL->dwKL_Flags &= ~KL_UNLOAD; 945 946 /* Reorder if necessary */ 947 if (Flags & KLF_REORDER) 948 IntReorderKeyboardLayouts(pWinSta, pNewKL); 949 950 /* Activate this layout in current thread */ 951 if (Flags & KLF_ACTIVATE) 952 co_UserActivateKeyboardLayout(pNewKL, Flags, NULL); 953 954 /* Send shell message */ 955 if (!(Flags & KLF_NOTELLSHELL)) 956 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hNewKL); 957 958 /* FIXME: KLF_REPLACELANG */ 959 960 return hNewKL; 961 } 962 963 HANDLE FASTCALL IntVerifyKeyboardFileHandle(HANDLE hFile) 964 { 965 PFILE_OBJECT FileObject; 966 NTSTATUS Status; 967 968 if (hFile == INVALID_HANDLE_VALUE) 969 return NULL; 970 971 Status = ObReferenceObjectByHandle(hFile, FILE_READ_DATA, NULL, UserMode, 972 (PVOID*)&FileObject, NULL); 973 if (!NT_SUCCESS(Status)) 974 { 975 ERR("0x%08X\n", Status); 976 return NULL; 977 } 978 979 /* FIXME: Is the file in the system directory? */ 980 981 if (FileObject) 982 ObDereferenceObject(FileObject); 983 984 return hFile; 985 } 986 987 /* EXPORTS *******************************************************************/ 988 989 /* 990 * UserGetKeyboardLayout 991 * 992 * Returns hkl of given thread keyboard layout 993 */ 994 HKL FASTCALL 995 UserGetKeyboardLayout( 996 DWORD dwThreadId) 997 { 998 PTHREADINFO pti; 999 PLIST_ENTRY ListEntry; 1000 PKL pKl; 1001 1002 pti = PsGetCurrentThreadWin32Thread(); 1003 1004 if (!dwThreadId) 1005 { 1006 pKl = pti->KeyboardLayout; 1007 return pKl ? pKl->hkl : NULL; 1008 } 1009 1010 ListEntry = pti->rpdesk->PtiList.Flink; 1011 1012 // 1013 // Search the Desktop Thread list for related Desktop active Threads. 1014 // 1015 while(ListEntry != &pti->rpdesk->PtiList) 1016 { 1017 pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink); 1018 1019 if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId)) 1020 { 1021 pKl = pti->KeyboardLayout; 1022 return pKl ? pKl->hkl : NULL; 1023 } 1024 1025 ListEntry = ListEntry->Flink; 1026 } 1027 1028 return NULL; 1029 } 1030 1031 /* 1032 * NtUserGetKeyboardLayoutList 1033 * 1034 * Returns list of loaded keyboard layouts in system 1035 */ 1036 UINT 1037 APIENTRY 1038 NtUserGetKeyboardLayoutList( 1039 ULONG nBuff, 1040 HKL *pHklBuff) 1041 { 1042 UINT ret = 0; 1043 PWINSTATION_OBJECT pWinSta; 1044 1045 if (!pHklBuff) 1046 nBuff = 0; 1047 1048 UserEnterShared(); 1049 1050 if (nBuff > MAXULONG / sizeof(HKL)) 1051 { 1052 SetLastNtError(ERROR_INVALID_PARAMETER); 1053 goto Quit; 1054 } 1055 1056 _SEH2_TRY 1057 { 1058 ProbeForWrite(pHklBuff, nBuff * sizeof(HKL), 1); 1059 } 1060 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1061 { 1062 SetLastNtError(_SEH2_GetExceptionCode()); 1063 goto Quit; 1064 } 1065 _SEH2_END; 1066 1067 pWinSta = IntGetProcessWindowStation(NULL); 1068 1069 _SEH2_TRY 1070 { 1071 ret = IntGetKeyboardLayoutList(pWinSta, nBuff, pHklBuff); 1072 } 1073 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1074 { 1075 SetLastNtError(_SEH2_GetExceptionCode()); 1076 goto Quit; 1077 } 1078 _SEH2_END; 1079 1080 Quit: 1081 UserLeave(); 1082 return ret; 1083 } 1084 1085 /* 1086 * NtUserGetKeyboardLayoutName 1087 * 1088 * Returns KLID of current thread keyboard layout 1089 */ 1090 BOOL 1091 APIENTRY 1092 NtUserGetKeyboardLayoutName( 1093 _Inout_ PUNICODE_STRING pustrName) 1094 { 1095 BOOL bRet = FALSE; 1096 PKL pKl; 1097 PTHREADINFO pti; 1098 UNICODE_STRING ustrNameSafe; 1099 NTSTATUS Status; 1100 1101 UserEnterShared(); 1102 1103 pti = PsGetCurrentThreadWin32Thread(); 1104 pKl = pti->KeyboardLayout; 1105 1106 if (!pKl) 1107 goto cleanup; 1108 1109 _SEH2_TRY 1110 { 1111 ProbeForWriteUnicodeString(pustrName); 1112 ustrNameSafe = *pustrName; 1113 1114 ProbeForWrite(ustrNameSafe.Buffer, ustrNameSafe.MaximumLength, 1); 1115 1116 if (IS_IME_HKL(pKl->hkl)) 1117 { 1118 Status = RtlIntegerToUnicodeString(HandleToUlong(pKl->hkl), 16, &ustrNameSafe); 1119 } 1120 else 1121 { 1122 if (ustrNameSafe.MaximumLength < KL_NAMELENGTH * sizeof(WCHAR)) 1123 { 1124 EngSetLastError(ERROR_INVALID_PARAMETER); 1125 goto cleanup; 1126 } 1127 1128 /* FIXME: Do not use awchKF */ 1129 ustrNameSafe.Length = 0; 1130 Status = RtlAppendUnicodeToString(&ustrNameSafe, pKl->spkf->awchKF); 1131 } 1132 1133 if (NT_SUCCESS(Status)) 1134 { 1135 *pustrName = ustrNameSafe; 1136 bRet = TRUE; 1137 } 1138 } 1139 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1140 { 1141 SetLastNtError(_SEH2_GetExceptionCode()); 1142 } 1143 _SEH2_END; 1144 1145 cleanup: 1146 UserLeave(); 1147 return bRet; 1148 } 1149 1150 /* 1151 * NtUserLoadKeyboardLayoutEx 1152 * 1153 * Loads keyboard layout with given locale id 1154 * 1155 * NOTE: We adopt a different design from Microsoft's one due to security reason. 1156 * We don't use the 3rd parameter of NtUserLoadKeyboardLayoutEx. 1157 * See https://bugtraq.securityfocus.com/detail/50056B96.6040306 1158 */ 1159 HKL 1160 NTAPI 1161 NtUserLoadKeyboardLayoutEx( 1162 IN HANDLE hFile, 1163 IN DWORD offTable, 1164 IN PVOID pTables, 1165 IN HKL hOldKL, 1166 IN PUNICODE_STRING puszKLID, 1167 IN DWORD dwNewKL, 1168 IN UINT Flags) 1169 { 1170 HKL hRetKL; 1171 WCHAR Buffer[KL_NAMELENGTH]; 1172 UNICODE_STRING uszSafeKLID; 1173 PWINSTATION_OBJECT pWinSta; 1174 HANDLE hSafeFile; 1175 1176 UNREFERENCED_PARAMETER(offTable); 1177 UNREFERENCED_PARAMETER(pTables); 1178 1179 if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG| 1180 KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS| 1181 KLF_RESET|KLF_SHIFTLOCK)) 1182 { 1183 ERR("Invalid flags: %x\n", Flags); 1184 EngSetLastError(ERROR_INVALID_FLAGS); 1185 return NULL; 1186 } 1187 1188 RtlInitEmptyUnicodeString(&uszSafeKLID, Buffer, sizeof(Buffer)); 1189 _SEH2_TRY 1190 { 1191 ProbeForRead(puszKLID, sizeof(*puszKLID), 1); 1192 ProbeForRead(puszKLID->Buffer, sizeof(puszKLID->Length), 1); 1193 RtlCopyUnicodeString(&uszSafeKLID, puszKLID); 1194 } 1195 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1196 { 1197 SetLastNtError(_SEH2_GetExceptionCode()); 1198 _SEH2_YIELD(return NULL); 1199 } 1200 _SEH2_END; 1201 1202 UserEnterExclusive(); 1203 1204 hSafeFile = (hFile ? IntVerifyKeyboardFileHandle(hFile) : NULL); 1205 pWinSta = IntGetProcessWindowStation(NULL); 1206 hRetKL = co_IntLoadKeyboardLayoutEx(pWinSta, 1207 hSafeFile, 1208 hOldKL, 1209 &uszSafeKLID, 1210 UlongToHandle(dwNewKL), 1211 Flags); 1212 if (hSafeFile) 1213 ZwClose(hSafeFile); 1214 1215 UserLeave(); 1216 return hRetKL; 1217 } 1218 1219 /* 1220 * NtUserActivateKeyboardLayout 1221 * 1222 * Activates specified layout for thread or process 1223 */ 1224 HKL 1225 NTAPI 1226 NtUserActivateKeyboardLayout( 1227 HKL hKL, 1228 ULONG Flags) 1229 { 1230 PWINSTATION_OBJECT pWinSta; 1231 HKL hOldKL; 1232 1233 UserEnterExclusive(); 1234 1235 /* FIXME */ 1236 1237 pWinSta = IntGetProcessWindowStation(NULL); 1238 hOldKL = co_IntActivateKeyboardLayout(pWinSta, hKL, Flags, NULL); 1239 UserLeave(); 1240 1241 return hOldKL; 1242 } 1243 1244 /* 1245 * NtUserUnloadKeyboardLayout 1246 * 1247 * Unloads keyboard layout with specified hkl value 1248 */ 1249 BOOL 1250 APIENTRY 1251 NtUserUnloadKeyboardLayout( 1252 HKL hKl) 1253 { 1254 BOOL ret; 1255 PWINSTATION_OBJECT pWinSta; 1256 1257 UserEnterExclusive(); 1258 1259 pWinSta = IntGetProcessWindowStation(NULL); 1260 ret = IntUnloadKeyboardLayout(pWinSta, hKl); 1261 1262 UserLeave(); 1263 return ret; 1264 } 1265 1266 /* EOF */ 1267