xref: /reactos/modules/rostests/apitests/imm32/KLID.c (revision d6eebaa4)
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 
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 
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 
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 
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 
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 
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 
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