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