From 2548a667b4d201b70248368072c9a3aa37ddac0a Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 22 Jul 2021 10:29:20 -0700 Subject: [PATCH 1/2] Change reading time zone display naames --- .../Interop/Windows/Kernel32/Interop.MUI.cs | 16 -- .../System.Private.CoreLib.Shared.projitems | 3 - .../src/System/TimeZoneInfo.Win32.cs | 160 +++--------------- 3 files changed, 27 insertions(+), 152 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MUI.cs diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MUI.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MUI.cs deleted file mode 100644 index 779d26d322295..0000000000000 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MUI.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Kernel32 - { - internal const uint MUI_USER_PREFERRED_UI_LANGUAGES = 0x10; - internal const uint MUI_USE_INSTALLED_LANGUAGES = 0x20; - - [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] - internal static extern unsafe bool GetFileMUIPath(uint dwFlags, string pcwszFilePath, char* pwszLanguage, ref uint pcchLanguage, char* pwszFileMUIPath, ref uint pcchFileMUIPath, ref ulong pululEnumerator); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 2c6783465d5ae..df824ab16bab3 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1603,9 +1603,6 @@ Common\Interop\Windows\Kernel32\Interop.MEMORYSTATUSEX.cs - - Common\Interop\Windows\Kernel32\Interop.MUI.cs - Common\Interop\Windows\Kernel32\Interop.MultiByteToWideChar.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index 30862efd02bb8..f0416afdf7fbb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -35,8 +35,7 @@ public sealed partial class TimeZoneInfo private const int MaxKeyLength = 255; private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time"; - private static readonly Dictionary s_fileMuiPathCache = new(); - private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); // must be initialized after s_fileMuiPathCache + private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); private sealed partial class CachedData { @@ -738,115 +737,12 @@ private static bool TryCompareTimeZoneInformationToRegistry(in TIME_ZONE_INFORMA } } - /// - /// Helper function for getting the MUI path for a given resource and culture, using a cache to prevent duplicating work. - /// Searches the installed OS languages for either an exact matching culture, or one that has the same parent. - /// If not found, uses the preferred default OS UI language, to align with prior behavior. - /// - private static string GetCachedFileMuiPath(string filePath, CultureInfo cultureInfo) - { - string? result; - string cacheKey = $"{cultureInfo.Name};{filePath}"; - - lock (s_fileMuiPathCache) - { - if (s_fileMuiPathCache.TryGetValue(cacheKey, out result)) - { - return result; - } - } - - result = GetFileMuiPath(filePath, cultureInfo); - - lock (s_fileMuiPathCache) - { - s_fileMuiPathCache.TryAdd(cacheKey, result); - } - - return result; - } - - /// - /// Helper function for getting the MUI path for a given resource and culture. - /// Searches the installed OS languages for either an exact matching culture, or one that has the same parent. - /// If not found, uses the preferred default OS UI language, to align with prior behavior. - /// - private static unsafe string GetFileMuiPath(string filePath, CultureInfo cultureInfo) - { - char* fileMuiPath = stackalloc char[Interop.Kernel32.MAX_PATH + 1]; - char* language = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; - uint fileMuiPathLength = Interop.Kernel32.MAX_PATH; - uint languageLength = Interop.Kernel32.LOCALE_NAME_MAX_LENGTH; - ulong enumerator = 0; - - while (true) - { - // Search all installed languages. The enumerator is re-used between loop iterations. - language[0] = '\0'; - bool succeeded = Interop.Kernel32.GetFileMUIPath( - Interop.Kernel32.MUI_USE_INSTALLED_LANGUAGES, - filePath, language, ref languageLength, - fileMuiPath, ref fileMuiPathLength, ref enumerator); - fileMuiPath[Interop.Kernel32.MAX_PATH] = '\0'; - - if (!succeeded) - { - // Recurse to search using the parent of the desired culture. - if (cultureInfo.Parent.Name != string.Empty) - { - return GetFileMuiPath(filePath, cultureInfo.Parent); - } - - // Final fallback, using the preferred installed UI language. - enumerator = 0; - language[0] = '\0'; - succeeded = Interop.Kernel32.GetFileMUIPath( - Interop.Kernel32.MUI_USER_PREFERRED_UI_LANGUAGES, - filePath, language, ref languageLength, - fileMuiPath, ref fileMuiPathLength, ref enumerator); - fileMuiPath[Interop.Kernel32.MAX_PATH] = '\0'; - - if (succeeded) - { - return new string(fileMuiPath); - } - - return string.Empty; - } - - // Lookup succeeded. Check for exact match to the desired culture. - language[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH] = '\0'; - ReadOnlySpan lang = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(language); - if (lang.Equals(cultureInfo.Name, StringComparison.OrdinalIgnoreCase)) - { - return new string(fileMuiPath); - } - - // Check for match of any parent of the language returned to the desired culture. - var ci = CultureInfo.GetCultureInfo(lang.ToString()); - while (ci.Parent.Name != string.Empty) - { - if (ci.Parent.Name.Equals(cultureInfo.Name, StringComparison.OrdinalIgnoreCase)) - { - return new string(fileMuiPath); - } - - ci = ci.Parent; - } - - // Not found yet. Continue with next iteration. - } - } /// - /// Helper function for retrieving a localized string resource via MUI. - /// The function expects a string in the form: "@resource.dll, -123" - /// - /// "resource.dll" is a language-neutral portable executable (LNPE) file in - /// the %windir%\system32 directory. The OS is queried to find the best-fit - /// localized resource file for this LNPE (ex: %windir%\system32\en-us\resource.dll.mui). - /// If a localized resource file exists, we LoadString resource ID "123" and - /// return it to our caller. + /// Try to find the time zone resources Dll matching the CurrentUICulture or on eof its parent cultures. + /// We try to check of such resource module e.g. %windir%\system32\[UI Culture Name]\tzres.dll.mui exist. + /// If a localized resource file exists, we LoadString resource with the id specified inside resource input + /// string and and return it to our caller. /// private static string GetLocalizedNameByMuiNativeResource(string resource) { @@ -855,9 +751,6 @@ private static string GetLocalizedNameByMuiNativeResource(string resource) return string.Empty; } - // Use the current UI culture when culture not specified - CultureInfo cultureInfo = CultureInfo.CurrentUICulture; - // parse "@tzres.dll, -100" // // filePath = "C:\Windows\System32\tzres.dll" @@ -869,41 +762,42 @@ private static string GetLocalizedNameByMuiNativeResource(string resource) return string.Empty; } - string filePath; + // Get the resource ID + if (!int.TryParse(resources[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out int resourceId)) + { + return string.Empty; + } + resourceId = -resourceId; + + // Use the current UI culture when culture not specified + CultureInfo cultureInfo = CultureInfo.CurrentUICulture; // get the path to Windows\System32 string system32 = Environment.SystemDirectory; // trim the string "@tzres.dll" => "tzres.dll" - ReadOnlySpan tzresDll = resources[0].AsSpan().TrimStart('@'); + string tzresDll = resources[0].TrimStart('@') + ".mui"; try { - filePath = Path.Join(system32, tzresDll); + while (cultureInfo.Name.Length != 0) + { + string filePath = Path.Join(system32, cultureInfo.Name, tzresDll); + if (File.Exists(filePath)) + { + // Finally, get the resource from the resource path + return GetLocalizedNameByNativeResource(filePath, resourceId); + } + + cultureInfo = cultureInfo.Parent; + } } catch (ArgumentException) { // there were probably illegal characters in the path - return string.Empty; - } - - // Get the MUI File Path - string fileMuiPath = GetCachedFileMuiPath(filePath, cultureInfo); - if (fileMuiPath == string.Empty) - { - // not likely, but we could not resolve a MUI path - return string.Empty; } - // Get the resource ID - if (!int.TryParse(resources[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out int resourceId)) - { - return string.Empty; - } - resourceId = -resourceId; - - // Finally, get the resource from the resource path - return GetLocalizedNameByNativeResource(fileMuiPath, resourceId); + return string.Empty; } /// From d6512b74ea45b419c15424ee1bc6c256693ae1ff Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 22 Jul 2021 11:33:04 -0700 Subject: [PATCH 2/2] Address the feedback --- .../src/System/TimeZoneInfo.Unix.cs | 2 -- .../src/System/TimeZoneInfo.Win32.cs | 8 +++----- .../System.Private.CoreLib/src/System/TimeZoneInfo.cs | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index 622195782cb50..9c470c8ac5075 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -32,8 +32,6 @@ public sealed partial class TimeZoneInfo "Zulu" }; - private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); - private TimeZoneInfo(byte[] data, string id, bool dstDisabled) { _id = id; diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index f0416afdf7fbb..7bdfce06f1b69 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -35,8 +35,6 @@ public sealed partial class TimeZoneInfo private const int MaxKeyLength = 255; private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time"; - private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); - private sealed partial class CachedData { private static TimeZoneInfo GetCurrentOneYearLocal() @@ -739,7 +737,7 @@ private static bool TryCompareTimeZoneInformationToRegistry(in TIME_ZONE_INFORMA /// - /// Try to find the time zone resources Dll matching the CurrentUICulture or on eof its parent cultures. + /// Try to find the time zone resources Dll matching the CurrentUICulture or one of its parent cultures. /// We try to check of such resource module e.g. %windir%\system32\[UI Culture Name]\tzres.dll.mui exist. /// If a localized resource file exists, we LoadString resource with the id specified inside resource input /// string and and return it to our caller. @@ -775,8 +773,8 @@ private static string GetLocalizedNameByMuiNativeResource(string resource) // get the path to Windows\System32 string system32 = Environment.SystemDirectory; - // trim the string "@tzres.dll" => "tzres.dll" - string tzresDll = resources[0].TrimStart('@') + ".mui"; + // trim the string "@tzres.dll" to "tzres.dll" and append the "mui" file extension to it. + string tzresDll = $"{resources[0].AsSpan().TrimStart('@')}.mui"; try { diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs index 89844b5c11c1b..32ed950b5795d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs @@ -53,6 +53,7 @@ private enum TimeZoneInfoResult private const string UtcId = "UTC"; private const string LocalId = "Local"; + private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); private static CachedData s_cachedData = new CachedData(); //