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 */
IntHKLtoPKL(_Inout_ PTHREADINFO pti,_In_ HKL hKL)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
IntGetKeyboardLayoutList(_Inout_ PWINSTATION_OBJECT pWinSta,_In_ ULONG nBuff,_Out_ HKL * pHklBuff)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
UserLoadKbdDll(WCHAR * pwszLayoutPath,HANDLE * phModule,PKBDTABLES * pKbdTables)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
UserLoadKbdFile(PUNICODE_STRING pwszKLID)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
co_UserLoadKbdLayout(PUNICODE_STRING pustrKLID,HKL hKL)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
UnloadKbdFile(_In_ PKBDFILE pkf)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
UserUnloadKbl(PKL pKl)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
W32kGetDefaultKeyLayout(VOID)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
UserHklToKbl(HKL hKl)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
IntReorderKeyboardLayouts(_Inout_ PWINSTATION_OBJECT pWinSta,_Inout_ PKL pNewKL)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
UserSetDefaultInputLang(HKL hKl)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
IntImmActivateLayout(_Inout_ PTHREADINFO pti,_Inout_ PKL pKL)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
co_IntSetKeyboardLayoutForProcess(PPROCESSINFO ppi,PKL pKL)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
co_UserActivateKeyboardLayout(_Inout_ PKL pKL,_In_ ULONG uFlags,_In_opt_ PWND pWnd)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
co_IntActivateKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta,_In_ HKL hKL,_In_ ULONG uFlags,_In_opt_ PWND pWnd)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
co_IntUnloadKeyboardLayoutEx(_Inout_ PWINSTATION_OBJECT pWinSta,_Inout_ PKL pKL,_In_ DWORD dwFlags)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
IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta,_In_ HKL hKL)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
co_UserImmLoadLayout(_In_ HKL hKL)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
co_IntLoadKeyboardLayoutEx(IN OUT PWINSTATION_OBJECT pWinSta,IN HANDLE hSafeFile,IN HKL hOldKL,IN PUNICODE_STRING puszSafeKLID,IN HKL hNewKL,IN UINT Flags)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
IntVerifyKeyboardFileHandle(HANDLE hFile)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
UserGetKeyboardLayout(DWORD dwThreadId)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
NtUserGetKeyboardLayoutList(ULONG nBuff,HKL * pHklBuff)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
NtUserGetKeyboardLayoutName(_Inout_ PUNICODE_STRING pustrName)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
NtUserLoadKeyboardLayoutEx(IN HANDLE hFile,IN DWORD offTable,IN PVOID pTables,IN HKL hOldKL,IN PUNICODE_STRING puszKLID,IN DWORD dwNewKL,IN UINT Flags)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
NtUserActivateKeyboardLayout(HKL hKL,ULONG Flags)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
NtUserUnloadKeyboardLayout(HKL hKl)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