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 */ 10 11 #include <win32k.h> 12 13 #include <winnls.h> 14 15 DBG_DEFAULT_CHANNEL(UserKbdLayout); 16 17 PKL gspklBaseLayout = NULL; 18 PKBDFILE gpkfList = NULL; 19 DWORD gSystemFS = 0; 20 UINT gSystemCPCharSet = 0; 21 22 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID); 23 24 25 /* PRIVATE FUNCTIONS ******************************************************/ 26 27 /* 28 * UserLoadKbdDll 29 * 30 * Loads keyboard layout DLL and gets address to KbdTables 31 */ 32 static BOOL 33 UserLoadKbdDll(WCHAR *pwszLayoutPath, 34 HANDLE *phModule, 35 PKBDTABLES *pKbdTables) 36 { 37 PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor; 38 39 /* Load keyboard layout DLL */ 40 TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath); 41 *phModule = EngLoadImage(pwszLayoutPath); 42 if (!(*phModule)) 43 { 44 ERR("Failed to load dll %ws\n", pwszLayoutPath); 45 return FALSE; 46 } 47 48 /* Find KbdLayerDescriptor function and get layout tables */ 49 TRACE("Loaded %ws\n", pwszLayoutPath); 50 pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor"); 51 52 /* FIXME: Windows reads file instead of executing! 53 It's not safe to kbdlayout DLL in kernel mode! */ 54 55 if (pfnKbdLayerDescriptor) 56 *pKbdTables = pfnKbdLayerDescriptor(); 57 else 58 ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath); 59 60 if (!pfnKbdLayerDescriptor || !*pKbdTables) 61 { 62 ERR("Failed to load the keyboard layout.\n"); 63 EngUnloadImage(*phModule); 64 return FALSE; 65 } 66 67 #if 0 /* Dump keyboard layout */ 68 { 69 unsigned i; 70 PVK_TO_BIT pVkToBit = (*pKbdTables)->pCharModifiers->pVkToBit; 71 PVK_TO_WCHAR_TABLE pVkToWchTbl = (*pKbdTables)->pVkToWcharTable; 72 PVSC_VK pVscVk = (*pKbdTables)->pVSCtoVK_E0; 73 DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n", (*pKbdTables)->fLocaleFlags, (*pKbdTables)->bMaxVSCtoVK); 74 DbgPrint("wMaxModBits %x\n", (*pKbdTables)->pCharModifiers->wMaxModBits); 75 while (pVkToBit->Vk) 76 { 77 DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits); 78 ++pVkToBit; 79 } 80 for (i = 0; i <= (*pKbdTables)->pCharModifiers->wMaxModBits; ++i) 81 DbgPrint("ModNumber %x -> %x\n", i, (*pKbdTables)->pCharModifiers->ModNumber[i]); 82 while (pVkToWchTbl->pVkToWchars) 83 { 84 PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars; 85 DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", pVkToWchTbl->nModifications, pVkToWchTbl->cbSize); 86 while (pVkToWch->VirtualKey) 87 { 88 DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ", pVkToWch->VirtualKey, pVkToWch->Attributes); 89 for (i = 0; i < pVkToWchTbl->nModifications; ++i) 90 DbgPrint("%x ", pVkToWch->wch[i]); 91 DbgPrint("}\n"); 92 pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize); 93 } 94 ++pVkToWchTbl; 95 } 96 DbgPrint("pusVSCtoVK: { "); 97 for (i = 0; i < (*pKbdTables)->bMaxVSCtoVK; ++i) 98 DbgPrint("%x -> %x, ", i, (*pKbdTables)->pusVSCtoVK[i]); 99 DbgPrint("}\n"); 100 DbgPrint("pVSCtoVK_E0: { "); 101 while (pVscVk->Vsc) 102 { 103 DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); 104 ++pVscVk; 105 } 106 DbgPrint("}\n"); 107 pVscVk = (*pKbdTables)->pVSCtoVK_E1; 108 DbgPrint("pVSCtoVK_E1: { "); 109 while (pVscVk->Vsc) 110 { 111 DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); 112 ++pVscVk; 113 } 114 DbgPrint("}\n"); 115 DbgBreakPoint(); 116 } 117 #endif 118 119 return TRUE; 120 } 121 122 /* 123 * UserLoadKbdFile 124 * 125 * Loads keyboard layout DLL and creates KBDFILE object 126 */ 127 static PKBDFILE 128 UserLoadKbdFile(PUNICODE_STRING pwszKLID) 129 { 130 PKBDFILE pkf, pRet = NULL; 131 NTSTATUS Status; 132 ULONG cbSize; 133 HKEY hKey = NULL; 134 WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\"; 135 WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\" 136 L"Control\\Keyboard Layouts\\"; 137 138 /* Create keyboard layout file object */ 139 pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE)); 140 if (!pkf) 141 { 142 ERR("Failed to create object!\n"); 143 return NULL; 144 } 145 146 /* Set keyboard layout name */ 147 swprintf(pkf->awchKF, L"%wZ", pwszKLID); 148 149 /* Open layout registry key */ 150 RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF); 151 Status = RegOpenKey(wszLayoutRegKey, &hKey); 152 if (!NT_SUCCESS(Status)) 153 { 154 ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status); 155 goto cleanup; 156 } 157 158 /* Read filename of layout DLL */ 159 cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR)); 160 Status = RegQueryValue(hKey, 161 L"Layout File", 162 REG_SZ, 163 wszLayoutPath + wcslen(wszLayoutPath), 164 &cbSize); 165 166 if (!NT_SUCCESS(Status)) 167 { 168 ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status); 169 goto cleanup; 170 } 171 172 /* Load keyboard file now */ 173 if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl)) 174 { 175 ERR("Failed to load %ws dll!\n", wszLayoutPath); 176 goto cleanup; 177 } 178 179 /* Update next field */ 180 pkf->pkfNext = gpkfList; 181 gpkfList = pkf; 182 183 /* Return keyboard file */ 184 pRet = pkf; 185 186 cleanup: 187 if (hKey) 188 ZwClose(hKey); 189 if (pkf) 190 UserDereferenceObject(pkf); // we dont need ptr anymore 191 if (!pRet) 192 { 193 /* We have failed - destroy created object */ 194 if (pkf) 195 UserDeleteObject(pkf->head.h, TYPE_KBDFILE); 196 } 197 198 return pRet; 199 } 200 201 /* 202 * UserLoadKbdLayout 203 * 204 * Loads keyboard layout and creates KL object 205 */ 206 static PKL 207 UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL) 208 { 209 LCID lCid; 210 CHARSETINFO cs; 211 PKL pKl; 212 213 /* Create keyboard layout object */ 214 pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL)); 215 if (!pKl) 216 { 217 ERR("Failed to create object!\n"); 218 return NULL; 219 } 220 221 pKl->hkl = hKL; 222 pKl->spkf = UserLoadKbdFile(pustrKLID); 223 224 /* Dereference keyboard layout */ 225 UserDereferenceObject(pKl); 226 227 /* If we failed, remove KL object */ 228 if (!pKl->spkf) 229 { 230 ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID); 231 UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); 232 return NULL; 233 } 234 235 // Up to Language Identifiers.. 236 if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid))) 237 { 238 ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID); 239 UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); 240 return NULL; 241 } 242 243 TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid); 244 if (co_IntGetCharsetInfo(lCid, &cs)) 245 { 246 pKl->iBaseCharset = cs.ciCharset; 247 pKl->dwFontSigs = cs.fs.fsCsb[0]; 248 pKl->CodePage = (USHORT)cs.ciACP; 249 TRACE("Charset %u Font Sig %lu CodePage %u\n", 250 pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage); 251 } 252 else 253 { 254 pKl->iBaseCharset = ANSI_CHARSET; 255 pKl->dwFontSigs = FS_LATIN1; 256 pKl->CodePage = CP_ACP; 257 } 258 259 // Set initial system character set and font signature. 260 if (gSystemFS == 0) 261 { 262 gSystemCPCharSet = pKl->iBaseCharset; 263 gSystemFS = pKl->dwFontSigs; 264 } 265 266 return pKl; 267 } 268 269 /* 270 * UnloadKbdFile 271 * 272 * Destroys specified Keyboard File object 273 */ 274 static 275 VOID 276 UnloadKbdFile(_In_ PKBDFILE pkf) 277 { 278 PKBDFILE *ppkfLink = &gpkfList; 279 NT_ASSERT(pkf != NULL); 280 281 /* Find previous object */ 282 while (*ppkfLink) 283 { 284 if (*ppkfLink == pkf) 285 break; 286 287 ppkfLink = &(*ppkfLink)->pkfNext; 288 } 289 290 if (*ppkfLink == pkf) 291 *ppkfLink = pkf->pkfNext; 292 293 EngUnloadImage(pkf->hBase); 294 UserDeleteObject(pkf->head.h, TYPE_KBDFILE); 295 } 296 297 /* 298 * UserUnloadKbl 299 * 300 * Unloads specified Keyboard Layout if possible 301 */ 302 BOOL 303 UserUnloadKbl(PKL pKl) 304 { 305 /* According to msdn, UnloadKeyboardLayout can fail 306 if the keyboard layout identifier was preloaded. */ 307 if (pKl == gspklBaseLayout) 308 { 309 if (pKl->pklNext == pKl->pklPrev) 310 { 311 /* There is only one layout */ 312 return FALSE; 313 } 314 315 /* Set next layout as default */ 316 gspklBaseLayout = pKl->pklNext; 317 } 318 319 if (pKl->head.cLockObj > 1) 320 { 321 /* Layout is used by other threads */ 322 pKl->dwKL_Flags |= KLF_UNLOAD; 323 return FALSE; 324 } 325 326 /* Unload the layout */ 327 pKl->pklPrev->pklNext = pKl->pklNext; 328 pKl->pklNext->pklPrev = pKl->pklPrev; 329 UnloadKbdFile(pKl->spkf); 330 UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); 331 return TRUE; 332 } 333 334 /* 335 * W32kGetDefaultKeyLayout 336 * 337 * Returns default layout for new threads 338 */ 339 PKL 340 W32kGetDefaultKeyLayout(VOID) 341 { 342 PKL pKl = gspklBaseLayout; 343 344 if (!pKl) 345 return NULL; 346 347 /* Return not unloaded layout */ 348 do 349 { 350 if (!(pKl->dwKL_Flags & KLF_UNLOAD)) 351 return pKl; 352 353 pKl = pKl->pklPrev; /* Confirmed on Win2k */ 354 } while(pKl != gspklBaseLayout); 355 356 /* We have not found proper KL */ 357 return NULL; 358 } 359 360 /* 361 * UserHklToKbl 362 * 363 * Gets KL object from hkl value 364 */ 365 PKL 366 NTAPI 367 UserHklToKbl(HKL hKl) 368 { 369 PKL pKl = gspklBaseLayout; 370 371 if (!gspklBaseLayout) 372 return NULL; 373 374 do 375 { 376 if (pKl->hkl == hKl) 377 return pKl; 378 379 pKl = pKl->pklNext; 380 } while (pKl != gspklBaseLayout); 381 382 return NULL; 383 } 384 385 /* 386 * UserSetDefaultInputLang 387 * 388 * Sets default kyboard layout for system. Called from UserSystemParametersInfo. 389 */ 390 BOOL 391 NTAPI 392 UserSetDefaultInputLang(HKL hKl) 393 { 394 PKL pKl; 395 396 pKl = UserHklToKbl(hKl); 397 if (!pKl) 398 return FALSE; 399 400 gspklBaseLayout = pKl; 401 return TRUE; 402 } 403 404 /* 405 * co_UserActivateKbl 406 * 407 * Activates given layout in specified thread 408 */ 409 static PKL 410 co_UserActivateKbl(PTHREADINFO pti, PKL pKl, UINT Flags) 411 { 412 PKL pklPrev; 413 PWND pWnd; 414 415 pklPrev = pti->KeyboardLayout; 416 if (pklPrev) 417 UserDereferenceObject(pklPrev); 418 419 pti->KeyboardLayout = pKl; 420 pti->pClientInfo->hKL = pKl->hkl; 421 UserReferenceObject(pKl); 422 423 if (Flags & KLF_SETFORPROCESS) 424 { 425 // FIXME 426 } 427 428 if (!(pWnd = pti->MessageQueue->spwndFocus)) 429 { 430 pWnd = pti->MessageQueue->spwndActive; 431 } 432 433 // Send WM_INPUTLANGCHANGE to thread's focus window 434 co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0, 435 WM_INPUTLANGCHANGE, 436 (WPARAM)pKl->iBaseCharset, // FIXME: How to set it? 437 (LPARAM)pKl->hkl); // hkl 438 439 return pklPrev; 440 } 441 442 /* EXPORTS *******************************************************************/ 443 444 /* 445 * UserGetKeyboardLayout 446 * 447 * Returns hkl of given thread keyboard layout 448 */ 449 HKL FASTCALL 450 UserGetKeyboardLayout( 451 DWORD dwThreadId) 452 { 453 PTHREADINFO pti; 454 PLIST_ENTRY ListEntry; 455 PKL pKl; 456 457 pti = PsGetCurrentThreadWin32Thread(); 458 459 if (!dwThreadId) 460 { 461 pKl = pti->KeyboardLayout; 462 return pKl ? pKl->hkl : NULL; 463 } 464 465 ListEntry = pti->rpdesk->PtiList.Flink; 466 467 // 468 // Search the Desktop Thread list for related Desktop active Threads. 469 // 470 while(ListEntry != &pti->rpdesk->PtiList) 471 { 472 pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink); 473 474 if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId)) 475 { 476 pKl = pti->KeyboardLayout; 477 return pKl ? pKl->hkl : NULL; 478 } 479 480 ListEntry = ListEntry->Flink; 481 } 482 483 return NULL; 484 } 485 486 /* 487 * NtUserGetKeyboardLayoutList 488 * 489 * Returns list of loaded keyboard layouts in system 490 */ 491 UINT 492 APIENTRY 493 NtUserGetKeyboardLayoutList( 494 ULONG nBuff, 495 HKL *pHklBuff) 496 { 497 UINT uRet = 0; 498 PKL pKl; 499 500 if (!pHklBuff) 501 nBuff = 0; 502 503 UserEnterShared(); 504 505 if (!gspklBaseLayout) 506 { 507 UserLeave(); 508 return 0; 509 } 510 pKl = gspklBaseLayout; 511 512 if (nBuff == 0) 513 { 514 do 515 { 516 uRet++; 517 pKl = pKl->pklNext; 518 } while (pKl != gspklBaseLayout); 519 } 520 else 521 { 522 _SEH2_TRY 523 { 524 ProbeForWrite(pHklBuff, nBuff*sizeof(HKL), 4); 525 526 while (uRet < nBuff) 527 { 528 pHklBuff[uRet] = pKl->hkl; 529 uRet++; 530 pKl = pKl->pklNext; 531 if (pKl == gspklBaseLayout) 532 break; 533 } 534 } 535 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 536 { 537 SetLastNtError(_SEH2_GetExceptionCode()); 538 uRet = 0; 539 } 540 _SEH2_END; 541 } 542 543 UserLeave(); 544 return uRet; 545 } 546 547 /* 548 * NtUserGetKeyboardLayoutName 549 * 550 * Returns KLID of current thread keyboard layout 551 */ 552 BOOL 553 APIENTRY 554 NtUserGetKeyboardLayoutName( 555 LPWSTR pwszName) 556 { 557 BOOL bRet = FALSE; 558 PKL pKl; 559 PTHREADINFO pti; 560 561 UserEnterShared(); 562 563 pti = PsGetCurrentThreadWin32Thread(); 564 pKl = pti->KeyboardLayout; 565 566 if (!pKl) 567 goto cleanup; 568 569 _SEH2_TRY 570 { 571 ProbeForWrite(pwszName, KL_NAMELENGTH*sizeof(WCHAR), 1); 572 wcscpy(pwszName, pKl->spkf->awchKF); 573 bRet = TRUE; 574 } 575 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 576 { 577 SetLastNtError(_SEH2_GetExceptionCode()); 578 } 579 _SEH2_END; 580 581 cleanup: 582 UserLeave(); 583 return bRet; 584 } 585 586 /* 587 * NtUserLoadKeyboardLayoutEx 588 * 589 * Loads keyboard layout with given locale id 590 */ 591 HKL 592 APIENTRY 593 NtUserLoadKeyboardLayoutEx( 594 IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c) 595 IN DWORD offTable, // Offset to KbdTables 596 IN PUNICODE_STRING puszKeyboardName, // Not used? 597 IN HKL hklUnload, 598 IN PUNICODE_STRING pustrKLID, 599 IN DWORD hkl, 600 IN UINT Flags) 601 { 602 HKL hklRet = NULL; 603 PKL pKl = NULL, pklLast; 604 WCHAR Buffer[9]; 605 UNICODE_STRING ustrSafeKLID; 606 607 if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG| 608 KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS| 609 KLF_RESET|KLF_SHIFTLOCK)) 610 { 611 ERR("Invalid flags: %x\n", Flags); 612 EngSetLastError(ERROR_INVALID_FLAGS); 613 return NULL; 614 } 615 616 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */ 617 618 RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer)); 619 _SEH2_TRY 620 { 621 ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1); 622 ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1); 623 RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID); 624 } 625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 626 { 627 SetLastNtError(_SEH2_GetExceptionCode()); 628 _SEH2_YIELD(return NULL); 629 } 630 _SEH2_END; 631 632 UserEnterExclusive(); 633 634 /* If hklUnload is specified, unload it and load new layput as default */ 635 if (hklUnload && (hklUnload != UlongToHandle(hkl))) 636 { 637 pKl = UserHklToKbl(hklUnload); 638 if (pKl) 639 UserUnloadKbl(pKl); 640 } 641 642 /* Let's see if layout was already loaded. */ 643 pKl = UserHklToKbl(UlongToHandle(hkl)); 644 if (!pKl) 645 { 646 /* It wasn't, so load it. */ 647 pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl)); 648 if (!pKl) 649 goto cleanup; 650 651 if (gspklBaseLayout) 652 { 653 /* Find last not unloaded layout */ 654 pklLast = gspklBaseLayout->pklPrev; 655 while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD) 656 pklLast = pklLast->pklPrev; 657 658 /* Add new layout to the list */ 659 pKl->pklNext = pklLast->pklNext; 660 pKl->pklPrev = pklLast; 661 pKl->pklNext->pklPrev = pKl; 662 pKl->pklPrev->pklNext = pKl; 663 } 664 else 665 { 666 /* This is the first layout */ 667 pKl->pklNext = pKl; 668 pKl->pklPrev = pKl; 669 gspklBaseLayout = pKl; 670 } 671 } 672 673 /* If this layout was prepared to unload, undo it */ 674 pKl->dwKL_Flags &= ~KLF_UNLOAD; 675 676 /* Activate this layout in current thread */ 677 if (Flags & KLF_ACTIVATE) 678 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags); 679 680 /* Send shell message */ 681 if (!(Flags & KLF_NOTELLSHELL)) 682 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl); 683 684 /* Return hkl on success */ 685 hklRet = UlongToHandle(hkl); 686 687 /* FIXME: KLF_REPLACELANG 688 KLF_REORDER */ 689 690 cleanup: 691 UserLeave(); 692 return hklRet; 693 } 694 695 /* 696 * NtUserActivateKeyboardLayout 697 * 698 * Activates specified layout for thread or process 699 */ 700 HKL 701 APIENTRY 702 NtUserActivateKeyboardLayout( 703 HKL hKl, 704 ULONG Flags) 705 { 706 PKL pKl = NULL; 707 HKL hkl = NULL; 708 PTHREADINFO pti; 709 710 UserEnterExclusive(); 711 712 pti = PsGetCurrentThreadWin32Thread(); 713 714 /* hKl can have special value HKL_NEXT or HKL_PREV */ 715 if (hKl == (HKL)HKL_NEXT) 716 { 717 /* Get next keyboard layout starting with current */ 718 if (pti->KeyboardLayout) 719 pKl = pti->KeyboardLayout->pklNext; 720 } 721 else if (hKl == (HKL)HKL_PREV) 722 { 723 /* Get previous keyboard layout starting with current */ 724 if (pti->KeyboardLayout) 725 pKl = pti->KeyboardLayout->pklPrev; 726 } 727 else 728 pKl = UserHklToKbl(hKl); 729 730 if (!pKl) 731 { 732 ERR("Invalid HKL %p!\n", hKl); 733 goto cleanup; 734 } 735 736 hkl = pKl->hkl; 737 738 /* FIXME: KLF_RESET 739 KLF_SHIFTLOCK */ 740 741 if (Flags & KLF_REORDER) 742 gspklBaseLayout = pKl; 743 744 if (pKl != pti->KeyboardLayout) 745 { 746 /* Activate layout for current thread */ 747 pKl = co_UserActivateKbl(pti, pKl, Flags); 748 749 /* Send shell message */ 750 if (!(Flags & KLF_NOTELLSHELL)) 751 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl); 752 } 753 754 cleanup: 755 UserLeave(); 756 return hkl; 757 } 758 759 /* 760 * NtUserUnloadKeyboardLayout 761 * 762 * Unloads keyboard layout with specified hkl value 763 */ 764 BOOL 765 APIENTRY 766 NtUserUnloadKeyboardLayout( 767 HKL hKl) 768 { 769 PKL pKl; 770 BOOL bRet = FALSE; 771 772 UserEnterExclusive(); 773 774 pKl = UserHklToKbl(hKl); 775 if (pKl) 776 bRet = UserUnloadKbl(pKl); 777 else 778 ERR("Invalid HKL %p!\n", hKl); 779 780 UserLeave(); 781 return bRet; 782 } 783 784 /* EOF */ 785