2006-07-18 01:58:10 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
|
|
* FILE: ntoskrnl/ex/locale.c
|
|
|
|
* PURPOSE: Locale (Language) Support for the Executive
|
|
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
|
|
* Eric Kohl
|
|
|
|
* Thomas Weidenmueller (w3seek@reactos.org
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
2008-08-30 16:31:06 +00:00
|
|
|
#include <debug.h>
|
2006-07-18 01:58:10 +00:00
|
|
|
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
|
|
|
|
/* System IDs: EN_US */
|
|
|
|
LCID PsDefaultSystemLocaleId = 0x00000409;
|
|
|
|
LANGID PsInstallUILanguageId = LANGIDFROMLCID(0x00000409);
|
|
|
|
|
|
|
|
/* UI/Thread IDs: Same as system */
|
2022-01-21 16:09:53 +00:00
|
|
|
LCID PsDefaultThreadLocaleId = 0x00000409;
|
|
|
|
LANGID PsDefaultUILanguageId = LANGIDFROMLCID(0x00000409);
|
2006-07-18 01:58:10 +00:00
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/* DEFINES *******************************************************************/
|
|
|
|
|
|
|
|
#define BOGUS_LOCALE_ID 0xFFFF0000
|
|
|
|
|
2006-07-18 01:58:10 +00:00
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Validates the registry data of a NLS locale.
|
|
|
|
*
|
|
|
|
* @param[in] LocaleData
|
|
|
|
* A pointer to partial information that contains
|
|
|
|
* the NLS locale data.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Returns TRUE if the following conditions are met,
|
|
|
|
* otherwise FALSE is returned.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
__inline
|
|
|
|
BOOLEAN
|
|
|
|
ExpValidateNlsLocaleData(
|
|
|
|
_In_ PKEY_VALUE_PARTIAL_INFORMATION LocaleData)
|
|
|
|
{
|
|
|
|
PWCHAR Data;
|
|
|
|
|
|
|
|
/* Is this a null-terminated string type? */
|
|
|
|
if (LocaleData->Type != REG_SZ)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Does it have a consistent length? */
|
|
|
|
if (LocaleData->DataLength < sizeof(WCHAR))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Is the locale set and null-terminated? */
|
|
|
|
Data = (PWSTR)LocaleData->Data;
|
|
|
|
if (Data[0] != L'1' || Data[1] != UNICODE_NULL)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* All of the conditions above are met */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Validates a NLS locale. Whether a locale is valid
|
|
|
|
* or not depends on the following conditions:
|
|
|
|
*
|
|
|
|
* - The locale must exist in the Locale key, otherwise
|
|
|
|
* in the Alternate Sorts key;
|
|
|
|
*
|
|
|
|
* - The locale must exist in the Language Groups key, and
|
|
|
|
* the queried value must be readable;
|
|
|
|
*
|
|
|
|
* - The locale registry data must be of REG_SIZE type,
|
|
|
|
* has a consistent length and the locale belongs to
|
|
|
|
* a supported language group that is set.
|
|
|
|
*
|
|
|
|
* @param[in] LocaleId
|
|
|
|
* A locale identifier that corresponds to a specific
|
|
|
|
* locale to be validated.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Returns STATUS_SUCCESS if the function has successfully
|
|
|
|
* validated the locale and it is valid. STATUS_OBJECT_NAME_NOT_FOUND
|
|
|
|
* is returned if the following locale does not exist on the system.
|
|
|
|
* A failure NTSTATUS code is returned otherwise.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
NTSTATUS
|
|
|
|
ExpValidateNlsLocaleId(
|
|
|
|
_In_ LCID LocaleId)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
HANDLE NlsLocaleKey = NULL, AltSortKey = NULL, LangGroupKey = NULL;
|
|
|
|
OBJECT_ATTRIBUTES NlsLocalKeyAttrs, AltSortKeyAttrs, LangGroupKeyAttrs;
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION BufferKey;
|
|
|
|
WCHAR ValueBuffer[20], LocaleIdBuffer[20];
|
|
|
|
ULONG ReturnedLength;
|
|
|
|
UNICODE_STRING LocaleIdString;
|
|
|
|
static UNICODE_STRING NlsLocaleKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Locale");
|
|
|
|
static UNICODE_STRING AltSortKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Locale\\Alternate Sorts");
|
|
|
|
static UNICODE_STRING LangGroupPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Nls\\Language Groups");
|
|
|
|
|
|
|
|
/* Initialize the registry path attributes */
|
|
|
|
InitializeObjectAttributes(&NlsLocalKeyAttrs,
|
|
|
|
&NlsLocaleKeyPath,
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
InitializeObjectAttributes(&AltSortKeyAttrs,
|
|
|
|
&AltSortKeyPath,
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
InitializeObjectAttributes(&LangGroupKeyAttrs,
|
|
|
|
&LangGroupPath,
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* Copy the locale ID into a buffer */
|
|
|
|
swprintf(LocaleIdBuffer,
|
|
|
|
L"%08lx",
|
|
|
|
(ULONG)LocaleId);
|
|
|
|
|
|
|
|
/* And build the LCID string */
|
|
|
|
RtlInitUnicodeString(&LocaleIdString, LocaleIdBuffer);
|
|
|
|
|
|
|
|
/* Open the NLS locale key */
|
|
|
|
Status = ZwOpenKey(&NlsLocaleKey,
|
|
|
|
KEY_QUERY_VALUE,
|
|
|
|
&NlsLocalKeyAttrs);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("Failed to open %wZ (Status 0x%lx)\n", NlsLocaleKeyPath, Status);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the NLS alternate sort locales key */
|
|
|
|
Status = ZwOpenKey(&AltSortKey,
|
|
|
|
KEY_QUERY_VALUE,
|
|
|
|
&AltSortKeyAttrs);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("Failed to open %wZ (Status 0x%lx)\n", AltSortKeyPath, Status);
|
|
|
|
goto Quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the NLS language groups key */
|
|
|
|
Status = ZwOpenKey(&LangGroupKey,
|
|
|
|
KEY_QUERY_VALUE,
|
|
|
|
&LangGroupKeyAttrs);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("Failed to open %wZ (Status 0x%lx)\n", LangGroupPath, Status);
|
|
|
|
goto Quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the captured locale ID exists in the list of other locales */
|
|
|
|
BufferKey = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
|
|
|
|
Status = ZwQueryValueKey(NlsLocaleKey,
|
|
|
|
&LocaleIdString,
|
|
|
|
KeyValuePartialInformation,
|
|
|
|
BufferKey,
|
|
|
|
sizeof(ValueBuffer),
|
|
|
|
&ReturnedLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* We failed, retry by looking at the alternate sorts locales */
|
|
|
|
Status = ZwQueryValueKey(AltSortKey,
|
|
|
|
&LocaleIdString,
|
|
|
|
KeyValuePartialInformation,
|
|
|
|
BufferKey,
|
|
|
|
sizeof(ValueBuffer),
|
|
|
|
&ReturnedLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("Failed to query value from Alternate Sorts key (Status 0x%lx)\n", Status);
|
|
|
|
goto Quit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure the queried locale is of the right key type with a sane length */
|
|
|
|
if (BufferKey->Type != REG_SZ ||
|
|
|
|
BufferKey->DataLength < sizeof(WCHAR))
|
|
|
|
{
|
|
|
|
DPRINT1("The queried locale is of bad value type or length (Type %lu, DataLength %lu)\n",
|
|
|
|
BufferKey->Type, BufferKey->DataLength);
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
goto Quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We got what we need, now query the locale from the language groups */
|
|
|
|
RtlInitUnicodeString(&LocaleIdString, (PWSTR)BufferKey->Data);
|
|
|
|
Status = ZwQueryValueKey(LangGroupKey,
|
|
|
|
&LocaleIdString,
|
|
|
|
KeyValuePartialInformation,
|
|
|
|
BufferKey,
|
|
|
|
sizeof(ValueBuffer),
|
|
|
|
&ReturnedLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
DPRINT1("Failed to query value from Language Groups key (Status 0x%lx)\n", Status);
|
|
|
|
goto Quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have queried the locale with its data. However we are not finished here yet,
|
|
|
|
* because the locale data could be malformed or the locale itself was not set
|
|
|
|
* so ensure all of these conditions are met.
|
|
|
|
*/
|
|
|
|
if (!ExpValidateNlsLocaleData(BufferKey))
|
|
|
|
{
|
|
|
|
DPRINT1("The locale data is not valid!\n");
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
Quit:
|
|
|
|
if (LangGroupKey != NULL)
|
|
|
|
{
|
|
|
|
ZwClose(LangGroupKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AltSortKey != NULL)
|
|
|
|
{
|
|
|
|
ZwClose(AltSortKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NlsLocaleKey != NULL)
|
|
|
|
{
|
|
|
|
ZwClose(NlsLocaleKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
2006-07-18 01:58:10 +00:00
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2022-01-21 16:09:53 +00:00
|
|
|
ExpGetCurrentUserUILanguage(IN PCWSTR MuiName,
|
2008-11-21 21:05:33 +00:00
|
|
|
OUT LANGID* LanguageId)
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
UCHAR ValueBuffer[256];
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
UNICODE_STRING KeyName =
|
2015-05-16 09:22:10 +00:00
|
|
|
RTL_CONSTANT_STRING(L"Control Panel\\Desktop");
|
2006-07-18 01:58:10 +00:00
|
|
|
UNICODE_STRING ValueName;
|
|
|
|
UNICODE_STRING ValueString;
|
|
|
|
ULONG ValueLength;
|
|
|
|
ULONG Value;
|
|
|
|
HANDLE UserKey;
|
|
|
|
HANDLE KeyHandle;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
/* Setup the key name */
|
|
|
|
RtlInitUnicodeString(&ValueName, MuiName);
|
|
|
|
|
|
|
|
/* Open the use key */
|
|
|
|
Status = RtlOpenCurrentUser(KEY_READ, &UserKey);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
|
|
|
|
/* Initialize the attributes and open the key */
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
|
|
&KeyName,
|
2015-10-16 13:28:40 +00:00
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
2006-07-18 01:58:10 +00:00
|
|
|
UserKey,
|
|
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE,&ObjectAttributes);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Set buffer and query the current value */
|
|
|
|
ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
|
|
|
|
Status = ZwQueryValueKey(KeyHandle,
|
|
|
|
&ValueName,
|
|
|
|
KeyValuePartialInformation,
|
|
|
|
ValueBuffer,
|
|
|
|
sizeof(ValueBuffer),
|
|
|
|
&ValueLength);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Success, is the value the right type? */
|
|
|
|
if (ValueInfo->Type == REG_SZ)
|
|
|
|
{
|
2008-08-22 14:29:01 +00:00
|
|
|
/* It is. Initialize the data and convert it */
|
2006-07-18 01:58:10 +00:00
|
|
|
RtlInitUnicodeString(&ValueString, (PWSTR)ValueInfo->Data);
|
|
|
|
Status = RtlUnicodeStringToInteger(&ValueString, 16, &Value);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Return the language */
|
2006-10-26 01:49:51 +00:00
|
|
|
*LanguageId = (USHORT)Value;
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Fail */
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
}
|
2015-10-16 13:28:40 +00:00
|
|
|
|
|
|
|
/* Close the key */
|
|
|
|
ZwClose(KeyHandle);
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Close the user key and return */
|
|
|
|
ZwClose(UserKey);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2022-01-21 16:09:53 +00:00
|
|
|
ExpSetCurrentUserUILanguage(IN PCWSTR MuiName,
|
2006-07-18 01:58:10 +00:00
|
|
|
IN LANGID LanguageId)
|
|
|
|
{
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"Control Panel\\Desktop");
|
|
|
|
UNICODE_STRING ValueName;
|
|
|
|
WCHAR ValueBuffer[8];
|
|
|
|
ULONG ValueLength;
|
|
|
|
HANDLE UserHandle;
|
|
|
|
HANDLE KeyHandle;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Check that the passed language ID is not bogus */
|
|
|
|
if (LanguageId & BOGUS_LOCALE_ID)
|
|
|
|
{
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
2006-07-18 01:58:10 +00:00
|
|
|
/* Setup the key name */
|
|
|
|
RtlInitUnicodeString(&ValueName, MuiName);
|
|
|
|
|
|
|
|
/* Open the use key */
|
|
|
|
Status = RtlOpenCurrentUser(KEY_WRITE, &UserHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
|
|
|
|
/* Initialize the attributes */
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
|
|
&KeyName,
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
UserHandle,
|
|
|
|
NULL);
|
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Validate the language ID */
|
|
|
|
Status = ExpValidateNlsLocaleId(MAKELCID(LanguageId, SORT_DEFAULT));
|
2006-07-18 01:58:10 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Open the key */
|
|
|
|
Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &ObjectAttributes);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Setup the value name */
|
|
|
|
ValueLength = swprintf(ValueBuffer,
|
|
|
|
L"%04lX",
|
|
|
|
(ULONG)LanguageId);
|
|
|
|
|
|
|
|
/* Set the length for the call and set the value */
|
|
|
|
ValueLength = (ValueLength + 1) * sizeof(WCHAR);
|
|
|
|
Status = ZwSetValueKey(KeyHandle,
|
|
|
|
&ValueName,
|
|
|
|
0,
|
|
|
|
REG_SZ,
|
|
|
|
ValueBuffer,
|
|
|
|
ValueLength);
|
|
|
|
|
|
|
|
/* Close the handle for this key */
|
|
|
|
ZwClose(KeyHandle);
|
|
|
|
}
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Close the user key and return status */
|
|
|
|
ZwClose(UserHandle);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtQueryDefaultLocale(IN BOOLEAN UserProfile,
|
|
|
|
OUT PLCID DefaultLocaleId)
|
|
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
/* Enter SEH for probing */
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_TRY
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
/* Check if we came from user mode */
|
|
|
|
if (KeGetPreviousMode() != KernelMode)
|
|
|
|
{
|
|
|
|
/* Probe the language ID */
|
2022-01-21 15:45:26 +00:00
|
|
|
ProbeForWriteLangId(DefaultLocaleId);
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we have a user profile */
|
|
|
|
if (UserProfile)
|
|
|
|
{
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Return session wide thread locale */
|
|
|
|
*DefaultLocaleId = MmGetSessionLocaleId();
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Return system locale */
|
|
|
|
*DefaultLocaleId = PsDefaultSystemLocaleId;
|
|
|
|
}
|
|
|
|
}
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
/* Get exception code */
|
2008-11-24 13:40:26 +00:00
|
|
|
Status = _SEH2_GetExceptionCode();
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_END;
|
2006-07-18 01:58:10 +00:00
|
|
|
|
|
|
|
/* Return status */
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtSetDefaultLocale(IN BOOLEAN UserProfile,
|
|
|
|
IN LCID DefaultLocaleId)
|
|
|
|
{
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
UNICODE_STRING KeyName;
|
|
|
|
UNICODE_STRING ValueName;
|
2013-11-22 15:23:18 +00:00
|
|
|
UNICODE_STRING LocaleString;
|
2023-11-12 10:10:27 +00:00
|
|
|
HANDLE KeyHandle = NULL;
|
2006-07-18 01:58:10 +00:00
|
|
|
ULONG ValueLength;
|
|
|
|
WCHAR ValueBuffer[20];
|
2023-11-12 10:10:27 +00:00
|
|
|
HANDLE UserKey = NULL;
|
2006-07-18 01:58:10 +00:00
|
|
|
NTSTATUS Status;
|
2013-11-22 15:23:18 +00:00
|
|
|
UCHAR KeyValueBuffer[256];
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
|
2006-07-18 01:58:10 +00:00
|
|
|
PAGED_CODE();
|
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Check that the passed locale ID is not bogus */
|
|
|
|
if (DefaultLocaleId & BOGUS_LOCALE_ID)
|
|
|
|
{
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
2006-07-18 01:58:10 +00:00
|
|
|
/* Check if we have a profile */
|
|
|
|
if (UserProfile)
|
|
|
|
{
|
|
|
|
/* Open the user's key */
|
|
|
|
Status = RtlOpenCurrentUser(KEY_WRITE, &UserKey);
|
2016-09-18 23:28:32 +00:00
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
2006-07-18 01:58:10 +00:00
|
|
|
|
|
|
|
/* Initialize the registry location */
|
|
|
|
RtlInitUnicodeString(&KeyName, L"Control Panel\\International");
|
|
|
|
RtlInitUnicodeString(&ValueName, L"Locale");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Initialize the system registry location */
|
|
|
|
RtlInitUnicodeString(&KeyName,
|
|
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet"
|
2006-09-07 05:07:34 +00:00
|
|
|
L"\\Control\\Nls\\Language");
|
2006-07-18 01:58:10 +00:00
|
|
|
RtlInitUnicodeString(&ValueName, L"Default");
|
|
|
|
}
|
|
|
|
|
2015-04-24 19:40:38 +00:00
|
|
|
/* Initialize the object attributes */
|
2006-07-18 01:58:10 +00:00
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
2016-09-18 23:28:32 +00:00
|
|
|
&KeyName,
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
UserKey,
|
|
|
|
NULL);
|
2006-07-18 01:58:10 +00:00
|
|
|
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Check if we don't have a default locale yet */
|
2006-07-18 01:58:10 +00:00
|
|
|
if (!DefaultLocaleId)
|
|
|
|
{
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Open the key for reading */
|
|
|
|
Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Query the key value */
|
|
|
|
KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
|
|
Status = ZwQueryValueKey(KeyHandle,
|
|
|
|
&ValueName,
|
|
|
|
KeyValuePartialInformation,
|
|
|
|
KeyValueInformation,
|
|
|
|
sizeof(KeyValueBuffer),
|
|
|
|
&ValueLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this is a REG_DWORD */
|
|
|
|
if ((KeyValueInformation->Type == REG_DWORD) &&
|
|
|
|
(KeyValueInformation->DataLength == sizeof(ULONG)))
|
|
|
|
{
|
|
|
|
/* It contains the LCID as a DWORD */
|
|
|
|
DefaultLocaleId = *((ULONG*)KeyValueInformation->Data);
|
|
|
|
}
|
|
|
|
/* Otherwise check for a REG_SZ */
|
|
|
|
else if (KeyValueInformation->Type == REG_SZ)
|
|
|
|
{
|
|
|
|
/* Initialize a unicode string from the value data */
|
|
|
|
LocaleString.Buffer = (PWCHAR)KeyValueInformation->Data;
|
|
|
|
LocaleString.Length = (USHORT)KeyValueInformation->DataLength;
|
|
|
|
LocaleString.MaximumLength = LocaleString.Length;
|
|
|
|
|
|
|
|
/* Convert the hex string to a number */
|
|
|
|
RtlUnicodeStringToInteger(&LocaleString, 16, &DefaultLocaleId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-11-12 10:10:27 +00:00
|
|
|
/* We have a locale, validate it */
|
|
|
|
Status = ExpValidateNlsLocaleId(DefaultLocaleId);
|
2006-07-18 01:58:10 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Open the key now */
|
|
|
|
Status = ZwOpenKey(&KeyHandle, KEY_SET_VALUE, &ObjectAttributes);
|
|
|
|
if (NT_SUCCESS(Status))
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Check if we had a profile */
|
|
|
|
if (UserProfile)
|
|
|
|
{
|
|
|
|
/* Fill in the buffer */
|
|
|
|
ValueLength = swprintf(ValueBuffer,
|
|
|
|
L"%08lx",
|
|
|
|
(ULONG)DefaultLocaleId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Fill in the buffer */
|
|
|
|
ValueLength = swprintf(ValueBuffer,
|
|
|
|
L"%04lx",
|
|
|
|
(ULONG)DefaultLocaleId & 0xFFFF);
|
|
|
|
}
|
2006-07-18 01:58:10 +00:00
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Set the length for the registry call */
|
|
|
|
ValueLength = (ValueLength + 1) * sizeof(WCHAR);
|
2006-07-18 01:58:10 +00:00
|
|
|
|
2023-11-12 10:10:27 +00:00
|
|
|
/* Now write the actual value */
|
|
|
|
Status = ZwSetValueKey(KeyHandle,
|
|
|
|
&ValueName,
|
|
|
|
0,
|
|
|
|
REG_SZ,
|
|
|
|
ValueBuffer,
|
|
|
|
ValueLength);
|
|
|
|
}
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-22 15:23:18 +00:00
|
|
|
Cleanup:
|
|
|
|
|
|
|
|
/* Close the locale key */
|
|
|
|
if (KeyHandle)
|
|
|
|
{
|
|
|
|
ObCloseHandle(KeyHandle, KernelMode);
|
|
|
|
}
|
|
|
|
|
2006-07-18 01:58:10 +00:00
|
|
|
/* Close the user key */
|
2012-12-19 23:49:13 +00:00
|
|
|
if (UserKey)
|
|
|
|
{
|
|
|
|
ObCloseHandle(UserKey, KernelMode);
|
|
|
|
}
|
2006-07-18 01:58:10 +00:00
|
|
|
|
|
|
|
/* Check for success */
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Check if it was for a user */
|
|
|
|
if (UserProfile)
|
|
|
|
{
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Set the session wide thread locale */
|
|
|
|
MmSetSessionLocaleId(DefaultLocaleId);
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set system locale */
|
|
|
|
PsDefaultSystemLocaleId = DefaultLocaleId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return status */
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2008-11-21 21:05:33 +00:00
|
|
|
NtQueryInstallUILanguage(OUT LANGID* LanguageId)
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
/* Enter SEH for probing */
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_TRY
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
/* Check if we came from user mode */
|
|
|
|
if (KeGetPreviousMode() != KernelMode)
|
|
|
|
{
|
|
|
|
/* Probe the Language ID */
|
2022-01-21 15:45:26 +00:00
|
|
|
ProbeForWriteLangId(LanguageId);
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return it */
|
|
|
|
*LanguageId = PsInstallUILanguageId;
|
|
|
|
}
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
/* Get exception code */
|
2008-11-24 13:40:26 +00:00
|
|
|
Status = _SEH2_GetExceptionCode();
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_END;
|
2006-07-18 01:58:10 +00:00
|
|
|
|
|
|
|
/* Return status */
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2008-11-21 21:05:33 +00:00
|
|
|
NtQueryDefaultUILanguage(OUT LANGID* LanguageId)
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
2012-09-29 13:22:31 +00:00
|
|
|
NTSTATUS Status;
|
|
|
|
LANGID SafeLanguageId;
|
2006-07-18 01:58:10 +00:00
|
|
|
PAGED_CODE();
|
|
|
|
|
2012-09-29 13:22:31 +00:00
|
|
|
/* Call the executive helper routine */
|
|
|
|
Status = ExpGetCurrentUserUILanguage(L"MultiUILanguageId", &SafeLanguageId);
|
|
|
|
|
2006-07-18 01:58:10 +00:00
|
|
|
/* Enter SEH for probing */
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_TRY
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
|
|
|
/* Check if we came from user mode */
|
|
|
|
if (KeGetPreviousMode() != KernelMode)
|
|
|
|
{
|
|
|
|
/* Probe the Language ID */
|
2022-01-21 15:45:26 +00:00
|
|
|
ProbeForWriteLangId(LanguageId);
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Success, return the language */
|
2012-09-29 13:22:31 +00:00
|
|
|
*LanguageId = SafeLanguageId;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Failed, use fallback value */
|
2022-01-21 16:09:53 +00:00
|
|
|
// NOTE: Windows doesn't use PsDefaultUILanguageId.
|
2006-07-18 01:58:10 +00:00
|
|
|
*LanguageId = PsInstallUILanguageId;
|
|
|
|
}
|
|
|
|
}
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Return exception code */
|
2016-09-07 18:52:09 +00:00
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
2008-11-24 13:40:26 +00:00
|
|
|
_SEH2_END;
|
2006-07-18 01:58:10 +00:00
|
|
|
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Return success */
|
|
|
|
return STATUS_SUCCESS;
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtSetDefaultUILanguage(IN LANGID LanguageId)
|
|
|
|
{
|
2013-11-22 15:23:18 +00:00
|
|
|
NTSTATUS Status;
|
2006-07-18 01:58:10 +00:00
|
|
|
PAGED_CODE();
|
|
|
|
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Check if the caller specified a language id */
|
|
|
|
if (LanguageId)
|
2006-07-18 01:58:10 +00:00
|
|
|
{
|
2013-11-22 15:23:18 +00:00
|
|
|
/* Set the pending MUI language id */
|
|
|
|
Status = ExpSetCurrentUserUILanguage(L"MUILanguagePending", LanguageId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Otherwise get the pending MUI language id */
|
|
|
|
Status = ExpGetCurrentUserUILanguage(L"MUILanguagePending", &LanguageId);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And apply it as actual */
|
|
|
|
Status = ExpSetCurrentUserUILanguage(L"MultiUILanguageId", LanguageId);
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2013-11-22 15:23:18 +00:00
|
|
|
return Status;
|
2006-07-18 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|