/* * PROJECT: ReactOS Win32k subsystem * LICENSE: GPL - See COPYING in the top level directory * FILE: win32ss/user/ntuser/kbdlayout.c * PURPOSE: Keyboard layout management * COPYRIGHT: Copyright 2007 Saveliy Tretiakov * Copyright 2008 Colin Finck * Copyright 2011 Rafal Harabien * Copyright 2022 Katayama Hirofumi MZ */ #include #include // Was included only because of CP_ACP and required the // definition of SYSTEMTIME in ndk\rtltypes.h //#include #define CP_ACP 0 DBG_DEFAULT_CHANNEL(UserKbdLayout); PKL gspklBaseLayout = NULL; /* FIXME: Please move this to pWinSta->spklList */ PKBDFILE gpkfList = NULL; DWORD gSystemFS = 0; UINT gSystemCPCharSet = 0; DWORD gLCIDSentToShell = 0; typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID); /* PRIVATE FUNCTIONS ******************************************************/ /* * Retrieves a PKL by an input locale identifier (HKL). * @implemented * Win: HKLtoPKL */ PKL FASTCALL IntHKLtoPKL(_Inout_ PTHREADINFO pti, _In_ HKL hKL) { PKL pFirstKL, pKL; pFirstKL = pti->KeyboardLayout; if (!pFirstKL) return NULL; pKL = pFirstKL; /* hKL can have special value HKL_NEXT or HKL_PREV */ if (hKL == (HKL)(ULONG_PTR)HKL_NEXT) /* Looking forward */ { do { pKL = pKL->pklNext; if (!(pKL->dwKL_Flags & KLF_UNLOAD)) return pKL; } while (pKL != pFirstKL); } else if (hKL == (HKL)(ULONG_PTR)HKL_PREV) /* Looking backward */ { do { pKL = pKL->pklPrev; if (!(pKL->dwKL_Flags & KLF_UNLOAD)) return pKL; } while (pKL != pFirstKL); } else if (HIWORD(hKL)) /* hKL is a full input locale identifier */ { /* No KLF_UNLOAD check */ do { if (pKL->hkl == hKL) return pKL; pKL = pKL->pklNext; } while (pKL != pFirstKL); } else /* Language only specified */ { /* No KLF_UNLOAD check */ do { if (LOWORD(pKL->hkl) == LOWORD(hKL)) /* Low word is language ID */ return pKL; pKL = pKL->pklNext; } while (pKL != pFirstKL); } return NULL; } /* * A helper function for NtUserGetKeyboardLayoutList. * @implemented * Win: _GetKeyboardLayoutList */ static UINT APIENTRY IntGetKeyboardLayoutList( _Inout_ PWINSTATION_OBJECT pWinSta, _In_ ULONG nBuff, _Out_ HKL *pHklBuff) { UINT ret = 0; PKL pKL, pFirstKL; pFirstKL = gspklBaseLayout; /* FIXME: Use pWinSta->spklList instead */ if (!pWinSta || !pFirstKL) return 0; pKL = pFirstKL; if (nBuff == 0) { /* Count the effective PKLs */ do { if (!(pKL->dwKL_Flags & KLF_UNLOAD)) ++ret; pKL = pKL->pklNext; } while (pKL != pFirstKL); } else { /* Copy the effective HKLs to pHklBuff */ do { if (!(pKL->dwKL_Flags & KLF_UNLOAD)) { *pHklBuff = pKL->hkl; ++pHklBuff; ++ret; --nBuff; if (nBuff == 0) break; } pKL = pKL->pklNext; } while (pKL != pFirstKL); } return ret; } #if 0 && DBG static VOID DumpKbdLayout( IN PKBDTABLES pKbdTbl) { PVK_TO_BIT pVkToBit; PVK_TO_WCHAR_TABLE pVkToWchTbl; PVSC_VK pVscVk; ULONG i; DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n", pKbdTbl->fLocaleFlags, pKbdTbl->bMaxVSCtoVK); DbgPrint("wMaxModBits %x\n", pKbdTbl->pCharModifiers ? pKbdTbl->pCharModifiers->wMaxModBits : 0); if (pKbdTbl->pCharModifiers) { pVkToBit = pKbdTbl->pCharModifiers->pVkToBit; if (pVkToBit) { for (; pVkToBit->Vk; ++pVkToBit) { DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits); } } for (i = 0; i <= pKbdTbl->pCharModifiers->wMaxModBits; ++i) { DbgPrint("ModNumber %x -> %x\n", i, pKbdTbl->pCharModifiers->ModNumber[i]); } } pVkToWchTbl = pKbdTbl->pVkToWcharTable; if (pVkToWchTbl) { for (; pVkToWchTbl->pVkToWchars; ++pVkToWchTbl) { PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars; DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", pVkToWchTbl->nModifications, pVkToWchTbl->cbSize); if (pVkToWch) { while (pVkToWch->VirtualKey) { DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ", pVkToWch->VirtualKey, pVkToWch->Attributes); for (i = 0; i < pVkToWchTbl->nModifications; ++i) { DbgPrint("%x ", pVkToWch->wch[i]); } DbgPrint("}\n"); pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize); } } } } // TODO: DeadKeys, KeyNames, KeyNamesExt, KeyNamesDead DbgPrint("pusVSCtoVK: { "); if (pKbdTbl->pusVSCtoVK) { for (i = 0; i < pKbdTbl->bMaxVSCtoVK; ++i) { DbgPrint("%x -> %x, ", i, pKbdTbl->pusVSCtoVK[i]); } } DbgPrint("}\n"); DbgPrint("pVSCtoVK_E0: { "); pVscVk = pKbdTbl->pVSCtoVK_E0; if (pVscVk) { for (; pVscVk->Vsc; ++pVscVk) { DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); } } DbgPrint("}\n"); DbgPrint("pVSCtoVK_E1: { "); pVscVk = pKbdTbl->pVSCtoVK_E1; if (pVscVk) { for (; pVscVk->Vsc; ++pVscVk) { DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk); } } DbgPrint("}\n"); // TODO: Ligatures } #endif // DBG /* * UserLoadKbdDll * * Loads keyboard layout DLL and gets address to KbdTables */ static BOOL UserLoadKbdDll(WCHAR *pwszLayoutPath, HANDLE *phModule, PKBDTABLES *pKbdTables) { PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor; /* Load keyboard layout DLL */ TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath); *phModule = EngLoadImage(pwszLayoutPath); if (!(*phModule)) { ERR("Failed to load dll %ws\n", pwszLayoutPath); return FALSE; } /* Find KbdLayerDescriptor function and get layout tables */ TRACE("Loaded %ws\n", pwszLayoutPath); pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor"); /* FIXME: Windows reads file instead of executing! It's not safe to kbdlayout DLL in kernel mode! */ if (pfnKbdLayerDescriptor) *pKbdTables = pfnKbdLayerDescriptor(); else ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath); if (!pfnKbdLayerDescriptor || !*pKbdTables) { ERR("Failed to load the keyboard layout.\n"); EngUnloadImage(*phModule); return FALSE; } #if 0 && DBG /* Dump keyboard layout */ DumpKbdLayout(*pKbdTables); #endif return TRUE; } /* * UserLoadKbdFile * * Loads keyboard layout DLL and creates KBDFILE object */ static PKBDFILE UserLoadKbdFile(PUNICODE_STRING pwszKLID) { PKBDFILE pkf, pRet = NULL; NTSTATUS Status; ULONG cbSize; HKEY hKey = NULL; WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\"; WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\" L"Control\\Keyboard Layouts\\"; /* Create keyboard layout file object */ pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE)); if (!pkf) { ERR("Failed to create object!\n"); return NULL; } /* Set keyboard layout name */ swprintf(pkf->awchKF, L"%wZ", pwszKLID); /* Open layout registry key */ RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF); Status = RegOpenKey(wszLayoutRegKey, &hKey); if (!NT_SUCCESS(Status)) { ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status); goto cleanup; } /* Read filename of layout DLL */ cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR)); Status = RegQueryValue(hKey, L"Layout File", REG_SZ, wszLayoutPath + wcslen(wszLayoutPath), &cbSize); if (!NT_SUCCESS(Status)) { ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status); goto cleanup; } /* Load keyboard file now */ if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl)) { ERR("Failed to load %ws dll!\n", wszLayoutPath); goto cleanup; } /* Update next field */ pkf->pkfNext = gpkfList; gpkfList = pkf; /* Return keyboard file */ pRet = pkf; cleanup: if (hKey) ZwClose(hKey); if (pkf) UserDereferenceObject(pkf); // we dont need ptr anymore if (!pRet) { /* We have failed - destroy created object */ if (pkf) UserDeleteObject(pkf->head.h, TYPE_KBDFILE); } return pRet; } /* * UserLoadKbdLayout * * Loads keyboard layout and creates KL object */ static PKL UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL) { LCID lCid; CHARSETINFO cs; PKL pKl; /* Create keyboard layout object */ pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL)); if (!pKl) { ERR("Failed to create object!\n"); return NULL; } pKl->hkl = hKL; pKl->spkf = UserLoadKbdFile(pustrKLID); /* Dereference keyboard layout */ UserDereferenceObject(pKl); /* If we failed, remove KL object */ if (!pKl->spkf) { ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID); UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); return NULL; } // Up to Language Identifiers.. if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid))) { ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID); UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); return NULL; } TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid); if (co_IntGetCharsetInfo(lCid, &cs)) { pKl->iBaseCharset = cs.ciCharset; pKl->dwFontSigs = cs.fs.fsCsb[0]; pKl->CodePage = (USHORT)cs.ciACP; TRACE("Charset %u Font Sig %lu CodePage %u\n", pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage); } else { pKl->iBaseCharset = ANSI_CHARSET; pKl->dwFontSigs = FS_LATIN1; pKl->CodePage = CP_ACP; } // Set initial system character set and font signature. if (gSystemFS == 0) { gSystemCPCharSet = pKl->iBaseCharset; gSystemFS = pKl->dwFontSigs; } return pKl; } /* * UnloadKbdFile * * Destroys specified Keyboard File object */ static VOID UnloadKbdFile(_In_ PKBDFILE pkf) { PKBDFILE *ppkfLink = &gpkfList; NT_ASSERT(pkf != NULL); /* Find previous object */ while (*ppkfLink) { if (*ppkfLink == pkf) break; ppkfLink = &(*ppkfLink)->pkfNext; } if (*ppkfLink == pkf) *ppkfLink = pkf->pkfNext; EngUnloadImage(pkf->hBase); UserDeleteObject(pkf->head.h, TYPE_KBDFILE); } /* * UserUnloadKbl * * Unloads specified Keyboard Layout if possible */ BOOL UserUnloadKbl(PKL pKl) { /* According to msdn, UnloadKeyboardLayout can fail if the keyboard layout identifier was preloaded. */ if (pKl == gspklBaseLayout) { if (pKl->pklNext == pKl->pklPrev) { /* There is only one layout */ return FALSE; } /* Set next layout as default */ gspklBaseLayout = pKl->pklNext; } if (pKl->head.cLockObj > 1) { /* Layout is used by other threads */ pKl->dwKL_Flags |= KLF_UNLOAD; return FALSE; } /* Unload the layout */ pKl->pklPrev->pklNext = pKl->pklNext; pKl->pklNext->pklPrev = pKl->pklPrev; UnloadKbdFile(pKl->spkf); UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); return TRUE; } /* * W32kGetDefaultKeyLayout * * Returns default layout for new threads */ PKL W32kGetDefaultKeyLayout(VOID) { PKL pKl = gspklBaseLayout; if (!pKl) return NULL; /* Return not unloaded layout */ do { if (!(pKl->dwKL_Flags & KLF_UNLOAD)) return pKl; pKl = pKl->pklPrev; /* Confirmed on Win2k */ } while(pKl != gspklBaseLayout); /* We have not found proper KL */ return NULL; } /* * UserHklToKbl * * Gets KL object from hkl value */ PKL NTAPI UserHklToKbl(HKL hKl) { PKL pKl = gspklBaseLayout; if (!gspklBaseLayout) return NULL; do { if (pKl->hkl == hKl) return pKl; pKl = pKl->pklNext; } while (pKl != gspklBaseLayout); return NULL; } /* * UserSetDefaultInputLang * * Sets default kyboard layout for system. Called from UserSystemParametersInfo. */ BOOL NTAPI UserSetDefaultInputLang(HKL hKl) { PKL pKl; pKl = UserHklToKbl(hKl); if (!pKl) return FALSE; gspklBaseLayout = pKl; return TRUE; } /* * co_UserActivateKbl * * Activates given layout in specified thread */ static PKL co_UserActivateKbl(PTHREADINFO pti, PKL pKl, UINT Flags) { PKL pklPrev; PWND pWnd; pklPrev = pti->KeyboardLayout; UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKl); pti->pClientInfo->hKL = pKl->hkl; if (Flags & KLF_SETFORPROCESS) { FIXME("KLF_SETFORPROCESS\n"); } if (!(pWnd = pti->MessageQueue->spwndFocus)) { pWnd = pti->MessageQueue->spwndActive; } // Send WM_INPUTLANGCHANGE to thread's focus window co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0, WM_INPUTLANGCHANGE, (WPARAM)pKl->iBaseCharset, // FIXME: How to set it? (LPARAM)pKl->hkl); // hkl return pklPrev; } // Win: xxxImmActivateLayout VOID APIENTRY IntImmActivateLayout( _Inout_ PTHREADINFO pti, _Inout_ PKL pKL) { PWND pImeWnd; HWND hImeWnd; USER_REFERENCE_ENTRY Ref; if (pti->KeyboardLayout == pKL) return; pImeWnd = pti->spwndDefaultIme; if (pImeWnd) { UserRefObjectCo(pImeWnd, &Ref); hImeWnd = UserHMGetHandle(pImeWnd); co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)pKL->hkl); UserDerefObjectCo(pImeWnd); } UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKL); pti->pClientInfo->hKL = pKL->hkl; } /* Win: xxxInternalActivateKeyboardLayout */ HKL APIENTRY co_UserActivateKeyboardLayout( _Inout_ PKL pKL, _In_ ULONG uFlags, _Inout_ PWND pWnd) { HKL hOldKL = NULL; PKL pOldKL = NULL; PTHREADINFO pti = GetW32ThreadInfo(); PWND pTargetWnd, pImeWnd; HWND hTargetWnd, hImeWnd; USER_REFERENCE_ENTRY Ref1, Ref2; PCLIENTINFO ClientInfo = pti->pClientInfo; if (pti->KeyboardLayout) { pOldKL = pti->KeyboardLayout; if (pOldKL) hOldKL = pOldKL->hkl; } if (uFlags & KLF_RESET) { FIXME("KLF_RESET\n"); } if (!(uFlags & KLF_SETFORPROCESS) && pKL == pti->KeyboardLayout) return hOldKL; pKL->wchDiacritic = 0; if (pOldKL) UserRefObjectCo(pOldKL, &Ref1); if (pti->TIF_flags & TIF_CSRSSTHREAD) { UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL); ClientInfo->CodePage = pKL->CodePage; ClientInfo->hKL = pKL->hkl; } else if (uFlags & KLF_SETFORPROCESS) { FIXME("KLF_SETFORPROCESS\n"); } else { if (IS_IMM_MODE()) IntImmActivateLayout(pti, pKL); else UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL); ClientInfo->CodePage = pKL->CodePage; ClientInfo->hKL = pKL->hkl; } if (gptiForeground && (gptiForeground->ppi == pti->ppi)) { /* Send shell message */ co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)pKL->hkl); } if (pti->MessageQueue) { /* Determine the target window */ pTargetWnd = pti->MessageQueue->spwndFocus; if (!pTargetWnd) { pTargetWnd = pti->MessageQueue->spwndActive; if (!pTargetWnd) pTargetWnd = pWnd; } /* Send WM_INPUTLANGCHANGE message */ if (pTargetWnd) { UserRefObjectCo(pTargetWnd, &Ref2); hTargetWnd = UserHMGetHandle(pTargetWnd); co_IntSendMessage(hTargetWnd, WM_INPUTLANGCHANGE, pKL->iBaseCharset, (LPARAM)pKL->hkl); UserDerefObjectCo(pTargetWnd); } } /* Send WM_IME_SYSTEM:IMS_SENDNOTIFICATION message if necessary */ if (pti && !(pti->TIF_flags & TIF_CSRSSTHREAD)) { if (IS_IME_HKL(pKL->hkl) || (gpsi->dwSRVIFlags & SRVINFO_CICERO_ENABLED)) { pImeWnd = pti->spwndDefaultIme; if (pImeWnd) { UserRefObjectCo(pImeWnd, &Ref2); BOOL bProcess = !!(pti->TIF_flags & KLF_SETFORPROCESS); hImeWnd = UserHMGetHandle(pImeWnd); co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, bProcess); UserDerefObjectCo(pImeWnd); } } } if (pOldKL) UserDerefObjectCo(pOldKL); return hOldKL; } // Win: ReorderKeyboardLayouts VOID FASTCALL IntReorderKeyboardLayouts( _Inout_ PWINSTATION_OBJECT pWinSta, _Inout_ PKL pKL) { /* FIXME */ gspklBaseLayout = pKL; } /* Win: xxxActivateKeyboardLayout */ HKL APIENTRY co_IntActivateKeyboardLayout( _Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL, _In_ ULONG uFlags, _Inout_ PWND pWnd) { PKL pKL; PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); pKL = IntHKLtoPKL(pti, hKL); if (!pKL) { ERR("Invalid HKL %p!\n", hKL); return NULL; } if (uFlags & KLF_REORDER) IntReorderKeyboardLayouts(pWinSta, pKL); return co_UserActivateKeyboardLayout(pKL, uFlags, pWnd); } // Win: xxxInternalUnloadKeyboardLayout static BOOL APIENTRY co_IntUnloadKeyboardLayoutEx( _Inout_ PWINSTATION_OBJECT pWinSta, _Inout_ PKL pKL, _In_ DWORD dwFlags) { PKL pNextKL; USER_REFERENCE_ENTRY Ref1, Ref2; PTHREADINFO pti = gptiCurrent; if (pKL == gspklBaseLayout && !(dwFlags & 0x80000000)) return FALSE; UserRefObjectCo(pKL, &Ref1); /* Add reference */ /* Regard as unloaded */ UserMarkObjectDestroy(pKL); pKL->dwKL_Flags |= KLF_UNLOAD; if (!(dwFlags & 0x80000000) && pti->KeyboardLayout == pKL) { pNextKL = IntHKLtoPKL(pti, (HKL)(ULONG_PTR)HKL_NEXT); if (pNextKL) { UserRefObjectCo(pNextKL, &Ref2); /* Add reference */ co_UserActivateKeyboardLayout(pNextKL, dwFlags, NULL); UserDerefObjectCo(pNextKL); /* Release reference */ } } if (gspklBaseLayout == pKL && pKL != pKL->pklNext) { /* Set next layout as default (FIXME: Use UserAssignmentLock?) */ gspklBaseLayout = pKL->pklNext; } UserDerefObjectCo(pKL); /* Release reference */ if (pti->pDeskInfo->fsHooks) { co_IntShellHookNotify(HSHELL_LANGUAGE, 0, 0); gLCIDSentToShell = 0; } return TRUE; } // Win: xxxUnloadKeyboardLayout static BOOL APIENTRY IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL) { PKL pKL = IntHKLtoPKL(gptiCurrent, hKL); if (!pKL) { ERR("Invalid HKL %p!\n", hKL); return FALSE; } return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0); } /* EXPORTS *******************************************************************/ /* * UserGetKeyboardLayout * * Returns hkl of given thread keyboard layout */ HKL FASTCALL UserGetKeyboardLayout( DWORD dwThreadId) { PTHREADINFO pti; PLIST_ENTRY ListEntry; PKL pKl; pti = PsGetCurrentThreadWin32Thread(); if (!dwThreadId) { pKl = pti->KeyboardLayout; return pKl ? pKl->hkl : NULL; } ListEntry = pti->rpdesk->PtiList.Flink; // // Search the Desktop Thread list for related Desktop active Threads. // while(ListEntry != &pti->rpdesk->PtiList) { pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink); if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId)) { pKl = pti->KeyboardLayout; return pKl ? pKl->hkl : NULL; } ListEntry = ListEntry->Flink; } return NULL; } /* * NtUserGetKeyboardLayoutList * * Returns list of loaded keyboard layouts in system */ UINT APIENTRY NtUserGetKeyboardLayoutList( ULONG nBuff, HKL *pHklBuff) { UINT ret = 0; PWINSTATION_OBJECT pWinSta; if (!pHklBuff) nBuff = 0; UserEnterShared(); if (nBuff > MAXULONG / sizeof(HKL)) { SetLastNtError(ERROR_INVALID_PARAMETER); goto Quit; } _SEH2_TRY { ProbeForWrite(pHklBuff, nBuff * sizeof(HKL), 1); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); goto Quit; } _SEH2_END; pWinSta = IntGetProcessWindowStation(NULL); _SEH2_TRY { ret = IntGetKeyboardLayoutList(pWinSta, nBuff, pHklBuff); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); goto Quit; } _SEH2_END; Quit: UserLeave(); return ret; } /* * NtUserGetKeyboardLayoutName * * Returns KLID of current thread keyboard layout */ BOOL APIENTRY NtUserGetKeyboardLayoutName( _Inout_ PUNICODE_STRING pustrName) { BOOL bRet = FALSE; PKL pKl; PTHREADINFO pti; UNICODE_STRING ustrNameSafe; NTSTATUS Status; UserEnterShared(); pti = PsGetCurrentThreadWin32Thread(); pKl = pti->KeyboardLayout; if (!pKl) goto cleanup; _SEH2_TRY { ProbeForWriteUnicodeString(pustrName); ustrNameSafe = *pustrName; ProbeForWrite(ustrNameSafe.Buffer, ustrNameSafe.MaximumLength, 1); if (IS_IME_HKL(pKl->hkl)) { Status = RtlIntegerToUnicodeString((ULONG)(ULONG_PTR)pKl->hkl, 16, &ustrNameSafe); } else { if (ustrNameSafe.MaximumLength < KL_NAMELENGTH * sizeof(WCHAR)) { EngSetLastError(ERROR_INVALID_PARAMETER); goto cleanup; } /* FIXME: Do not use awchKF */ ustrNameSafe.Length = 0; Status = RtlAppendUnicodeToString(&ustrNameSafe, pKl->spkf->awchKF); } if (NT_SUCCESS(Status)) { *pustrName = ustrNameSafe; bRet = TRUE; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); } _SEH2_END; cleanup: UserLeave(); return bRet; } /* * NtUserLoadKeyboardLayoutEx * * Loads keyboard layout with given locale id */ HKL APIENTRY NtUserLoadKeyboardLayoutEx( IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c) IN DWORD offTable, // Offset to KbdTables IN PUNICODE_STRING puszKeyboardName, // Not used? IN HKL hklUnload, IN PUNICODE_STRING pustrKLID, IN DWORD hkl, IN UINT Flags) { HKL hklRet = NULL; PKL pKl = NULL, pklLast; WCHAR Buffer[9]; UNICODE_STRING ustrSafeKLID; if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG| KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS| KLF_RESET|KLF_SHIFTLOCK)) { ERR("Invalid flags: %x\n", Flags); EngSetLastError(ERROR_INVALID_FLAGS); return NULL; } /* FIXME: It seems KLF_RESET is only supported for WINLOGON */ RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer)); _SEH2_TRY { ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1); ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1); RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); _SEH2_YIELD(return NULL); } _SEH2_END; UserEnterExclusive(); /* If hklUnload is specified, unload it and load new layput as default */ if (hklUnload && (hklUnload != UlongToHandle(hkl))) { pKl = UserHklToKbl(hklUnload); if (pKl) UserUnloadKbl(pKl); } /* Let's see if layout was already loaded. */ pKl = UserHklToKbl(UlongToHandle(hkl)); if (!pKl) { /* It wasn't, so load it. */ pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl)); if (!pKl) goto cleanup; if (gspklBaseLayout) { /* Find last not unloaded layout */ pklLast = gspklBaseLayout->pklPrev; while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD) pklLast = pklLast->pklPrev; /* Add new layout to the list */ pKl->pklNext = pklLast->pklNext; pKl->pklPrev = pklLast; pKl->pklNext->pklPrev = pKl; pKl->pklPrev->pklNext = pKl; } else { /* This is the first layout */ pKl->pklNext = pKl; pKl->pklPrev = pKl; gspklBaseLayout = pKl; } } /* If this layout was prepared to unload, undo it */ pKl->dwKL_Flags &= ~KLF_UNLOAD; /* Activate this layout in current thread */ if (Flags & KLF_ACTIVATE) co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags); /* Send shell message */ if (!(Flags & KLF_NOTELLSHELL)) co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl); /* Return hkl on success */ hklRet = UlongToHandle(hkl); /* FIXME: KLF_REPLACELANG KLF_REORDER */ cleanup: UserLeave(); return hklRet; } /* * NtUserActivateKeyboardLayout * * Activates specified layout for thread or process */ HKL NTAPI NtUserActivateKeyboardLayout( HKL hKL, ULONG Flags) { PWINSTATION_OBJECT pWinSta; HKL hOldKL; UserEnterExclusive(); /* FIXME */ pWinSta = IntGetProcessWindowStation(NULL); hOldKL = co_IntActivateKeyboardLayout(pWinSta, hKL, Flags, NULL); UserLeave(); return hOldKL; } /* * NtUserUnloadKeyboardLayout * * Unloads keyboard layout with specified hkl value */ BOOL APIENTRY NtUserUnloadKeyboardLayout( HKL hKl) { BOOL ret; PWINSTATION_OBJECT pWinSta; UserEnterExclusive(); pWinSta = IntGetProcessWindowStation(NULL); ret = IntUnloadKeyboardLayout(pWinSta, hKl); UserLeave(); return ret; } /* EOF */