1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Test for Keyboard Layout ID (KLID), HKL, and registry
5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 */
7
8 #include "precomp.h"
9
10 #include <stdlib.h>
11 #include <imm32_undoc.h>
12 #include <strsafe.h>
13
14 typedef enum tagHKL_TYPE
15 {
16 HKL_TYPE_PURE = 0,
17 HKL_TYPE_SPECIAL = 1,
18 HKL_TYPE_IME = 2,
19 HKL_TYPE_CHIMERA = 3,
20 } HKL_TYPE;
21
GetHKLType(HKL hKL)22 static HKL_TYPE GetHKLType(HKL hKL)
23 {
24 /* 0xEXXXYYYY: An IME HKL. EXXX is an IME keyboard. YYYY is a language */
25 if (IS_IME_HKL(hKL))
26 return HKL_TYPE_IME;
27
28 /* 0xFXXXYYYY: A special HKL. XXX is a special ID. YYYY is a language */
29 if (IS_SPECIAL_HKL(hKL))
30 return HKL_TYPE_SPECIAL;
31
32 /* 0xXXXXXXXX: The keyboard layout and language is the same value */
33 if (LOWORD(hKL) == HIWORD(hKL))
34 return HKL_TYPE_PURE;
35
36 /* 0xXXXXYYYY: XXXX is a keyboard. YYYY is a language */
37 return HKL_TYPE_CHIMERA;
38 }
39
OpenKeyboardLayouts(void)40 static HKEY OpenKeyboardLayouts(void)
41 {
42 HKEY hKey = NULL;
43 RegOpenKeyExW(HKEY_LOCAL_MACHINE,
44 L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts",
45 0, KEY_READ, &hKey);
46 return hKey;
47 }
48
KLIDFromSpecialHKL(HKL hKL)49 static DWORD KLIDFromSpecialHKL(HKL hKL)
50 {
51 WCHAR szName[16], szLayoutId[16];
52 HKEY hkeyLayouts, hkeyKLID;
53 LSTATUS error;
54 DWORD dwSpecialId, dwLayoutId, cbValue, dwKLID = 0;
55
56 hkeyLayouts = OpenKeyboardLayouts();
57 ok(hkeyLayouts != NULL, "hkeyLayouts was NULL\n");
58
59 dwSpecialId = SPECIALIDFROMHKL(hKL);
60
61 /* Search from "Keyboard Layouts" registry key */
62 for (DWORD dwIndex = 0; dwIndex < 1000; ++dwIndex)
63 {
64 error = RegEnumKeyW(hkeyLayouts, dwIndex, szName, _countof(szName));
65 szName[_countof(szName) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
66 if (error != ERROR_SUCCESS)
67 break;
68
69 error = RegOpenKeyExW(hkeyLayouts, szName, 0, KEY_READ, &hkeyKLID);
70 if (error != ERROR_SUCCESS)
71 break;
72
73 cbValue = sizeof(szLayoutId);
74 error = RegQueryValueExW(hkeyKLID, L"Layout Id", NULL, NULL, (LPBYTE)szLayoutId, &cbValue);
75 szLayoutId[_countof(szLayoutId) - 1] = UNICODE_NULL; /* Avoid buffer overrun */
76 if (error != ERROR_SUCCESS)
77 {
78 RegCloseKey(hkeyKLID);
79 continue;
80 }
81
82 dwLayoutId = wcstoul(szLayoutId, NULL, 16);
83 RegCloseKey(hkeyKLID);
84 if (dwLayoutId == dwSpecialId) /* Found */
85 {
86 dwKLID = wcstoul(szName, NULL, 16);
87 break;
88 }
89 }
90
91 RegCloseKey(hkeyLayouts);
92 return dwKLID;
93 }
94
KLIDFromHKL(HKL hKL)95 static DWORD KLIDFromHKL(HKL hKL)
96 {
97 HKL_TYPE type = GetHKLType(hKL);
98
99 trace("type: %d\n", type);
100 switch (type)
101 {
102 case HKL_TYPE_PURE:
103 case HKL_TYPE_CHIMERA:
104 return HIWORD(hKL);
105
106 case HKL_TYPE_SPECIAL:
107 return KLIDFromSpecialHKL(hKL);
108
109 case HKL_TYPE_IME:
110 return HandleToUlong(hKL);
111 }
112
113 return 0;
114 }
115
Test_KLID(DWORD dwKLID,HKL hKL)116 static void Test_KLID(DWORD dwKLID, HKL hKL)
117 {
118 WCHAR szKLID[16], szValue[MAX_PATH];
119 LSTATUS error;
120 DWORD dwValue, cbValue;
121 HKEY hkeyKLID, hkeyLayouts;
122 HKL_TYPE type;
123
124 hkeyLayouts = OpenKeyboardLayouts();
125 ok(hkeyLayouts != NULL, "hkeyLayouts was NULL\n");
126
127 StringCchPrintfW(szKLID, _countof(szKLID), L"%08lX", dwKLID);
128 RegOpenKeyExW(hkeyLayouts, szKLID, 0, KEY_READ, &hkeyKLID);
129 ok(hkeyKLID != NULL, "hkeyKLID was NULL\n");
130
131 error = RegQueryValueExW(hkeyKLID, L"Layout File", NULL, NULL, NULL, NULL);
132 ok_long(error, ERROR_SUCCESS);
133
134 type = GetHKLType(hKL);
135
136 if (type == HKL_TYPE_IME)
137 {
138 ok_long(dwKLID, HandleToUlong(hKL));
139 error = RegQueryValueExW(hkeyKLID, L"IME File", NULL, NULL, NULL, NULL);
140 ok_long(error, ERROR_SUCCESS);
141 }
142
143 if (type == HKL_TYPE_SPECIAL)
144 {
145 cbValue = sizeof(szValue);
146 error = RegQueryValueExW(hkeyKLID, L"Layout Id", NULL, NULL, (LPBYTE)&szValue, &cbValue);
147 ok_long(error, ERROR_SUCCESS);
148
149 dwValue = wcstoul(szValue, NULL, 16);
150 ok_long(dwValue, SPECIALIDFROMHKL(hKL));
151 }
152
153 RegCloseKey(hkeyKLID);
154 RegCloseKey(hkeyLayouts);
155 }
156
Test_HKL(HKL hKL)157 static void Test_HKL(HKL hKL)
158 {
159 DWORD dwKLID;
160
161 ok(hKL != NULL, "hKL was NULL\n");
162
163 dwKLID = KLIDFromHKL(hKL);
164 trace("dwKLID 0x%08lX, hKL %p\n", dwKLID, hKL);
165
166 Test_KLID(dwKLID, hKL);
167 }
168
START_TEST(KLID)169 START_TEST(KLID)
170 {
171 HKL *phKLs;
172 INT iKL, cKLs;
173
174 cKLs = GetKeyboardLayoutList(0, NULL);
175 trace("cKLs: %d\n", cKLs);
176 if (!cKLs)
177 {
178 skip("cKLs was zero\n");
179 return;
180 }
181
182 phKLs = calloc(cKLs, sizeof(HKL));
183 if (!phKLs)
184 {
185 skip("!phKLs\n");
186 return;
187 }
188
189 ok_int(GetKeyboardLayoutList(cKLs, phKLs), cKLs);
190
191 for (iKL = 0; iKL < cKLs; ++iKL)
192 {
193 trace("---\n");
194 trace("phKLs[%d]: %p\n", iKL, phKLs[iKL]);
195 Test_HKL(phKLs[iKL]);
196 }
197
198 free(phKLs);
199 }
200