xref: /reactos/ntoskrnl/ex/locale.c (revision de972e2b)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ex/locale.c
5  * PURPOSE:         Locale (Language) Support for the Executive
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Eric Kohl
8  *                  Thomas Weidenmueller (w3seek@reactos.org
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* GLOBALS *******************************************************************/
18 
19 /* System IDs: EN_US */
20 LCID PsDefaultSystemLocaleId = 0x00000409;
21 LANGID PsInstallUILanguageId = LANGIDFROMLCID(0x00000409);
22 
23 /* UI/Thread IDs: Same as system */
24 LCID PsDefaultThreadLocaleId = 0x00000409;
25 LANGID PsDefaultUILanguageId = LANGIDFROMLCID(0x00000409);
26 
27 /* PRIVATE FUNCTIONS *********************************************************/
28 
29 NTSTATUS
30 NTAPI
31 ExpGetCurrentUserUILanguage(IN PCWSTR MuiName,
32                             OUT LANGID* LanguageId)
33 {
34     UCHAR ValueBuffer[256];
35     PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
36     OBJECT_ATTRIBUTES ObjectAttributes;
37     UNICODE_STRING KeyName =
38         RTL_CONSTANT_STRING(L"Control Panel\\Desktop");
39     UNICODE_STRING ValueName;
40     UNICODE_STRING ValueString;
41     ULONG ValueLength;
42     ULONG Value;
43     HANDLE UserKey;
44     HANDLE KeyHandle;
45     NTSTATUS Status;
46     PAGED_CODE();
47 
48     /* Setup the key name */
49     RtlInitUnicodeString(&ValueName, MuiName);
50 
51     /* Open the use key */
52     Status = RtlOpenCurrentUser(KEY_READ, &UserKey);
53     if (!NT_SUCCESS(Status)) return Status;
54 
55     /* Initialize the attributes and open the key */
56     InitializeObjectAttributes(&ObjectAttributes,
57                                &KeyName,
58                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
59                                UserKey,
60                                NULL);
61     Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE,&ObjectAttributes);
62     if (NT_SUCCESS(Status))
63     {
64         /* Set buffer and query the current value */
65         ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
66         Status = ZwQueryValueKey(KeyHandle,
67                                  &ValueName,
68                                  KeyValuePartialInformation,
69                                  ValueBuffer,
70                                  sizeof(ValueBuffer),
71                                  &ValueLength);
72         if (NT_SUCCESS(Status))
73         {
74             /* Success, is the value the right type? */
75             if (ValueInfo->Type == REG_SZ)
76             {
77                 /* It is. Initialize the data and convert it */
78                 RtlInitUnicodeString(&ValueString, (PWSTR)ValueInfo->Data);
79                 Status = RtlUnicodeStringToInteger(&ValueString, 16, &Value);
80                 if (NT_SUCCESS(Status))
81                 {
82                     /* Return the language */
83                     *LanguageId = (USHORT)Value;
84                 }
85             }
86             else
87             {
88                 /* Fail */
89                 Status = STATUS_UNSUCCESSFUL;
90             }
91         }
92 
93         /* Close the key */
94         ZwClose(KeyHandle);
95     }
96 
97     /* Close the user key and return */
98     ZwClose(UserKey);
99     return Status;
100 }
101 
102 NTSTATUS
103 NTAPI
104 ExpSetCurrentUserUILanguage(IN PCWSTR MuiName,
105                             IN LANGID LanguageId)
106 {
107     OBJECT_ATTRIBUTES ObjectAttributes;
108     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"Control Panel\\Desktop");
109     UNICODE_STRING ValueName;
110     WCHAR ValueBuffer[8];
111     ULONG ValueLength;
112     HANDLE UserHandle;
113     HANDLE KeyHandle;
114     NTSTATUS Status;
115     PAGED_CODE();
116 
117     /* Setup the key name */
118     RtlInitUnicodeString(&ValueName, MuiName);
119 
120     /* Open the use key */
121     Status = RtlOpenCurrentUser(KEY_WRITE, &UserHandle);
122     if (!NT_SUCCESS(Status)) return Status;
123 
124     /* Initialize the attributes */
125     InitializeObjectAttributes(&ObjectAttributes,
126                                &KeyName,
127                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
128                                UserHandle,
129                                NULL);
130 
131     /* Open the key */
132     Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &ObjectAttributes);
133     if (NT_SUCCESS(Status))
134     {
135         /* Setup the value name */
136         ValueLength = swprintf(ValueBuffer,
137                                L"%04lX",
138                                (ULONG)LanguageId);
139 
140         /* Set the length for the call and set the value */
141         ValueLength = (ValueLength + 1) * sizeof(WCHAR);
142         Status = ZwSetValueKey(KeyHandle,
143                                &ValueName,
144                                0,
145                                REG_SZ,
146                                ValueBuffer,
147                                ValueLength);
148 
149         /* Close the handle for this key */
150         ZwClose(KeyHandle);
151     }
152 
153     /* Close the user key and return status */
154     ZwClose(UserHandle);
155     return Status;
156 }
157 
158 /* PUBLIC FUNCTIONS **********************************************************/
159 
160 NTSTATUS
161 NTAPI
162 NtQueryDefaultLocale(IN BOOLEAN UserProfile,
163                      OUT PLCID DefaultLocaleId)
164 {
165     NTSTATUS Status = STATUS_SUCCESS;
166     PAGED_CODE();
167 
168     /* Enter SEH for probing */
169     _SEH2_TRY
170     {
171         /* Check if we came from user mode */
172         if (KeGetPreviousMode() != KernelMode)
173         {
174             /* Probe the language ID */
175             ProbeForWriteLangId(DefaultLocaleId);
176         }
177 
178         /* Check if we have a user profile */
179         if (UserProfile)
180         {
181             /* Return session wide thread locale */
182             *DefaultLocaleId = MmGetSessionLocaleId();
183         }
184         else
185         {
186             /* Return system locale */
187             *DefaultLocaleId = PsDefaultSystemLocaleId;
188         }
189     }
190     _SEH2_EXCEPT(ExSystemExceptionFilter())
191     {
192         /* Get exception code */
193         Status = _SEH2_GetExceptionCode();
194     }
195     _SEH2_END;
196 
197     /* Return status */
198     return Status;
199 }
200 
201 NTSTATUS
202 NTAPI
203 NtSetDefaultLocale(IN BOOLEAN UserProfile,
204                    IN LCID DefaultLocaleId)
205 {
206     OBJECT_ATTRIBUTES ObjectAttributes;
207     UNICODE_STRING KeyName;
208     UNICODE_STRING ValueName;
209     UNICODE_STRING LocaleString;
210     HANDLE KeyHandle;
211     ULONG ValueLength;
212     WCHAR ValueBuffer[20];
213     HANDLE UserKey;
214     NTSTATUS Status;
215     UCHAR KeyValueBuffer[256];
216     PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
217     PAGED_CODE();
218 
219     /* Check if we have a profile */
220     if (UserProfile)
221     {
222         /* Open the user's key */
223         Status = RtlOpenCurrentUser(KEY_WRITE, &UserKey);
224         if (!NT_SUCCESS(Status)) return Status;
225 
226         /* Initialize the registry location */
227         RtlInitUnicodeString(&KeyName, L"Control Panel\\International");
228         RtlInitUnicodeString(&ValueName, L"Locale");
229     }
230     else
231     {
232         /* Initialize the system registry location */
233         RtlInitUnicodeString(&KeyName,
234                              L"\\Registry\\Machine\\System\\CurrentControlSet"
235                              L"\\Control\\Nls\\Language");
236         RtlInitUnicodeString(&ValueName, L"Default");
237         UserKey = NULL;
238     }
239 
240     /* Initialize the object attributes */
241     InitializeObjectAttributes(&ObjectAttributes,
242                                &KeyName,
243                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
244                                UserKey,
245                                NULL);
246 
247     /* Check if we don't have a default locale yet */
248     if (!DefaultLocaleId)
249     {
250         /* Open the key for reading */
251         Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
252         if (!NT_SUCCESS(Status))
253         {
254             KeyHandle = NULL;
255             goto Cleanup;
256         }
257 
258         /* Query the key value */
259         KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
260         Status = ZwQueryValueKey(KeyHandle,
261                                  &ValueName,
262                                  KeyValuePartialInformation,
263                                  KeyValueInformation,
264                                  sizeof(KeyValueBuffer),
265                                  &ValueLength);
266         if (!NT_SUCCESS(Status))
267         {
268             goto Cleanup;
269         }
270 
271         /* Check if this is a REG_DWORD */
272         if ((KeyValueInformation->Type == REG_DWORD) &&
273             (KeyValueInformation->DataLength == sizeof(ULONG)))
274         {
275             /* It contains the LCID as a DWORD */
276             DefaultLocaleId = *((ULONG*)KeyValueInformation->Data);
277         }
278         /* Otherwise check for a REG_SZ */
279         else if (KeyValueInformation->Type == REG_SZ)
280         {
281             /* Initialize a unicode string from the value data */
282             LocaleString.Buffer = (PWCHAR)KeyValueInformation->Data;
283             LocaleString.Length = (USHORT)KeyValueInformation->DataLength;
284             LocaleString.MaximumLength = LocaleString.Length;
285 
286             /* Convert the hex string to a number */
287             RtlUnicodeStringToInteger(&LocaleString, 16, &DefaultLocaleId);
288         }
289         else
290         {
291             Status = STATUS_UNSUCCESSFUL;
292         }
293     }
294     else
295     {
296         /* Otherwise, open the key */
297         Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &ObjectAttributes);
298         if (NT_SUCCESS(Status))
299         {
300             /* Check if we had a profile */
301             if (UserProfile)
302             {
303                 /* Fill in the buffer */
304                 ValueLength = swprintf(ValueBuffer,
305                                        L"%08lx",
306                                        (ULONG)DefaultLocaleId);
307             }
308             else
309             {
310                 /* Fill in the buffer */
311                 ValueLength = swprintf(ValueBuffer,
312                                        L"%04lx",
313                                        (ULONG)DefaultLocaleId & 0xFFFF);
314             }
315 
316             /* Set the length for the registry call */
317             ValueLength = (ValueLength + 1) * sizeof(WCHAR);
318 
319             /* Now write the actual value */
320             Status = ZwSetValueKey(KeyHandle,
321                                    &ValueName,
322                                    0,
323                                    REG_SZ,
324                                    ValueBuffer,
325                                    ValueLength);
326         }
327     }
328 
329 Cleanup:
330 
331     /* Close the locale key */
332     if (KeyHandle)
333     {
334         ObCloseHandle(KeyHandle, KernelMode);
335     }
336 
337     /* Close the user key */
338     if (UserKey)
339     {
340         ObCloseHandle(UserKey, KernelMode);
341     }
342 
343     /* Check for success */
344     if (NT_SUCCESS(Status))
345     {
346         /* Check if it was for a user */
347         if (UserProfile)
348         {
349             /* Set the session wide thread locale */
350             MmSetSessionLocaleId(DefaultLocaleId);
351         }
352         else
353         {
354             /* Set system locale */
355             PsDefaultSystemLocaleId = DefaultLocaleId;
356         }
357     }
358 
359     /* Return status */
360     return Status;
361 }
362 
363 /*
364  * @implemented
365  */
366 NTSTATUS
367 NTAPI
368 NtQueryInstallUILanguage(OUT LANGID* LanguageId)
369 {
370     NTSTATUS Status = STATUS_SUCCESS;
371     PAGED_CODE();
372 
373     /* Enter SEH for probing */
374     _SEH2_TRY
375     {
376         /* Check if we came from user mode */
377         if (KeGetPreviousMode() != KernelMode)
378         {
379             /* Probe the Language ID */
380             ProbeForWriteLangId(LanguageId);
381         }
382 
383         /* Return it */
384         *LanguageId = PsInstallUILanguageId;
385     }
386     _SEH2_EXCEPT(ExSystemExceptionFilter())
387     {
388         /* Get exception code */
389         Status = _SEH2_GetExceptionCode();
390     }
391     _SEH2_END;
392 
393     /* Return status */
394     return Status;
395 }
396 
397 /*
398  * @implemented
399  */
400 NTSTATUS
401 NTAPI
402 NtQueryDefaultUILanguage(OUT LANGID* LanguageId)
403 {
404     NTSTATUS Status;
405     LANGID SafeLanguageId;
406     PAGED_CODE();
407 
408     /* Call the executive helper routine */
409     Status = ExpGetCurrentUserUILanguage(L"MultiUILanguageId", &SafeLanguageId);
410 
411     /* Enter SEH for probing */
412     _SEH2_TRY
413     {
414         /* Check if we came from user mode */
415         if (KeGetPreviousMode() != KernelMode)
416         {
417             /* Probe the Language ID */
418             ProbeForWriteLangId(LanguageId);
419         }
420 
421         if (NT_SUCCESS(Status))
422         {
423             /* Success, return the language */
424             *LanguageId = SafeLanguageId;
425         }
426         else
427         {
428             /* Failed, use fallback value */
429             // NOTE: Windows doesn't use PsDefaultUILanguageId.
430             *LanguageId = PsInstallUILanguageId;
431         }
432     }
433     _SEH2_EXCEPT(ExSystemExceptionFilter())
434     {
435         /* Return exception code */
436         _SEH2_YIELD(return _SEH2_GetExceptionCode());
437     }
438     _SEH2_END;
439 
440     /* Return success */
441     return STATUS_SUCCESS;
442 }
443 
444 /*
445  * @implemented
446  */
447 NTSTATUS
448 NTAPI
449 NtSetDefaultUILanguage(IN LANGID LanguageId)
450 {
451     NTSTATUS Status;
452     PAGED_CODE();
453 
454     /* Check if the caller specified a language id */
455     if (LanguageId)
456     {
457         /* Set the pending MUI language id */
458         Status = ExpSetCurrentUserUILanguage(L"MUILanguagePending", LanguageId);
459     }
460     else
461     {
462         /* Otherwise get the pending MUI language id */
463         Status = ExpGetCurrentUserUILanguage(L"MUILanguagePending", &LanguageId);
464         if (!NT_SUCCESS(Status))
465         {
466             return Status;
467         }
468 
469         /* And apply it as actual */
470         Status = ExpSetCurrentUserUILanguage(L"MultiUILanguageId", LanguageId);
471     }
472 
473     return Status;
474 }
475 
476 /* EOF */
477