CultureData.Windows.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using Internal.Runtime.CompilerServices;
  10. #if ENABLE_WINRT
  11. using Internal.Runtime.Augments;
  12. #endif
  13. namespace System.Globalization
  14. {
  15. internal partial class CultureData
  16. {
  17. /// <summary>
  18. /// Check with the OS to see if this is a valid culture.
  19. /// If so we populate a limited number of fields. If its not valid we return false.
  20. ///
  21. /// The fields we populate:
  22. ///
  23. /// sWindowsName -- The name that windows thinks this culture is, ie:
  24. /// en-US if you pass in en-US
  25. /// de-DE_phoneb if you pass in de-DE_phoneb
  26. /// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
  27. /// fj if you pass in fj (neutral, post-Windows 7 machine)
  28. ///
  29. /// sRealName -- The name you used to construct the culture, in pretty form
  30. /// en-US if you pass in EN-us
  31. /// en if you pass in en
  32. /// de-DE_phoneb if you pass in de-DE_phoneb
  33. ///
  34. /// sSpecificCulture -- The specific culture for this culture
  35. /// en-US for en-US
  36. /// en-US for en
  37. /// de-DE_phoneb for alt sort
  38. /// fj-FJ for fj (neutral)
  39. ///
  40. /// sName -- The IETF name of this culture (ie: no sort info, could be neutral)
  41. /// en-US if you pass in en-US
  42. /// en if you pass in en
  43. /// de-DE if you pass in de-DE_phoneb
  44. ///
  45. /// bNeutral -- TRUE if it is a neutral locale
  46. ///
  47. /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the
  48. /// windows locale that's going to provide data for us.
  49. /// </summary>
  50. private unsafe bool InitCultureData()
  51. {
  52. Debug.Assert(!GlobalizationMode.Invariant);
  53. int result;
  54. string realNameBuffer = _sRealName;
  55. char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH];
  56. result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
  57. // Did it fail?
  58. if (result == 0)
  59. {
  60. return false;
  61. }
  62. // It worked, note that the name is the locale name, so use that (even for neutrals)
  63. // We need to clean up our "real" name, which should look like the windows name right now
  64. // so overwrite the input with the cleaned up name
  65. _sRealName = new string(pBuffer, 0, result - 1);
  66. realNameBuffer = _sRealName;
  67. // Check for neutrality, don't expect to fail
  68. // (buffer has our name in it, so we don't have to do the gc. stuff)
  69. result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_INEUTRAL | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
  70. if (result == 0)
  71. {
  72. return false;
  73. }
  74. // Remember our neutrality
  75. _bNeutral = *((uint*)pBuffer) != 0;
  76. // Note: Parents will be set dynamically
  77. // Start by assuming the windows name will be the same as the specific name since windows knows
  78. // about specifics on all versions. Only for downlevel Neutral locales does this have to change.
  79. _sWindowsName = realNameBuffer;
  80. // Neutrals and non-neutrals are slightly different
  81. if (_bNeutral)
  82. {
  83. // Neutral Locale
  84. // IETF name looks like neutral name
  85. _sName = realNameBuffer;
  86. // Specific locale name is whatever ResolveLocaleName (win7+) returns.
  87. // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
  88. result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
  89. // 0 is failure, 1 is invariant (""), which we expect
  90. if (result < 1)
  91. {
  92. return false;
  93. }
  94. // We found a locale name, so use it.
  95. // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
  96. _sSpecificCulture = new string(pBuffer, 0, result - 1);
  97. }
  98. else
  99. {
  100. // Specific Locale
  101. // Specific culture's the same as the locale name since we know its not neutral
  102. // On mac we'll use this as well, even for neutrals. There's no obvious specific
  103. // culture to use and this isn't exposed, but behaviorally this is correct on mac.
  104. // Note that specifics include the sort name (de-DE_phoneb)
  105. _sSpecificCulture = realNameBuffer;
  106. _sName = realNameBuffer;
  107. // We need the IETF name (sname)
  108. // If we aren't an alt sort locale then this is the same as the windows name.
  109. // If we are an alt sort locale then this is the same as the part before the _ in the windows name
  110. // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
  111. result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_ILANGUAGE | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
  112. if (result == 0)
  113. {
  114. return false;
  115. }
  116. _iLanguage = *((int*)pBuffer);
  117. if (!IsCustomCultureId(_iLanguage))
  118. {
  119. // not custom locale
  120. int index = realNameBuffer.IndexOf('_');
  121. if (index > 0 && index < realNameBuffer.Length)
  122. {
  123. _sName = realNameBuffer.Substring(0, index);
  124. }
  125. }
  126. }
  127. // It succeeded.
  128. return true;
  129. }
  130. // Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned
  131. // data as either and Int or string.
  132. internal static unsafe string? GetLocaleInfoEx(string localeName, uint field)
  133. {
  134. // REVIEW: Determine the maximum size for the buffer
  135. const int BUFFER_SIZE = 530;
  136. char* pBuffer = stackalloc char[BUFFER_SIZE];
  137. int resultCode = GetLocaleInfoEx(localeName, field, pBuffer, BUFFER_SIZE);
  138. if (resultCode > 0)
  139. {
  140. return new string(pBuffer);
  141. }
  142. return null;
  143. }
  144. internal static unsafe int GetLocaleInfoExInt(string localeName, uint field)
  145. {
  146. field |= Interop.Kernel32.LOCALE_RETURN_NUMBER;
  147. int value = 0;
  148. GetLocaleInfoEx(localeName, field, (char*) &value, sizeof(int));
  149. return value;
  150. }
  151. internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, char* lpLCData, int cchData)
  152. {
  153. Debug.Assert(!GlobalizationMode.Invariant);
  154. return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData);
  155. }
  156. private string GetLocaleInfo(LocaleStringData type)
  157. {
  158. Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected _sWindowsName to be populated by already");
  159. return GetLocaleInfo(_sWindowsName, type);
  160. }
  161. // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the
  162. // "windows" name, which can be specific for downlevel (< windows 7) os's.
  163. private string GetLocaleInfo(string localeName, LocaleStringData type)
  164. {
  165. uint lctype = (uint)type;
  166. return GetLocaleInfoFromLCType(localeName, lctype, UseUserOverride);
  167. }
  168. private int GetLocaleInfo(LocaleNumberData type)
  169. {
  170. uint lctype = (uint)type;
  171. // Fix lctype if we don't want overrides
  172. if (!UseUserOverride)
  173. {
  174. lctype |= Interop.Kernel32.LOCALE_NOUSEROVERRIDE;
  175. }
  176. // Ask OS for data, note that we presume it returns success, so we have to know that
  177. // sWindowsName is valid before calling.
  178. Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
  179. return GetLocaleInfoExInt(_sWindowsName, lctype);
  180. }
  181. private int[] GetLocaleInfo(LocaleGroupingData type)
  182. {
  183. Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
  184. return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, UseUserOverride));
  185. }
  186. private string? GetTimeFormatString()
  187. {
  188. Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
  189. return ReescapeWin32String(GetLocaleInfoFromLCType(_sWindowsName, Interop.Kernel32.LOCALE_STIMEFORMAT, UseUserOverride));
  190. }
  191. private int GetFirstDayOfWeek()
  192. {
  193. Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already");
  194. int result = GetLocaleInfoExInt(_sWindowsName, Interop.Kernel32.LOCALE_IFIRSTDAYOFWEEK | (!UseUserOverride ? Interop.Kernel32.LOCALE_NOUSEROVERRIDE : 0));
  195. // Win32 and .NET disagree on the numbering for days of the week, so we have to convert.
  196. return ConvertFirstDayOfWeekMonToSun(result);
  197. }
  198. private string[]? GetTimeFormats()
  199. {
  200. // Note that this gets overrides for us all the time
  201. Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already");
  202. string[]? result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride));
  203. return result;
  204. }
  205. private string[]? GetShortTimeFormats()
  206. {
  207. // Note that this gets overrides for us all the time
  208. Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already");
  209. string[]? result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, Interop.Kernel32.TIME_NOSECONDS, UseUserOverride));
  210. return result;
  211. }
  212. // Enumerate all system cultures and then try to find out which culture has
  213. // region name match the requested region name
  214. private static CultureData? GetCultureDataFromRegionName(string regionName)
  215. {
  216. Debug.Assert(!GlobalizationMode.Invariant);
  217. Debug.Assert(regionName != null);
  218. EnumLocaleData context = new EnumLocaleData();
  219. context.cultureName = null;
  220. context.regionName = regionName;
  221. unsafe
  222. {
  223. Interop.Kernel32.EnumSystemLocalesEx(EnumSystemLocalesProc, Interop.Kernel32.LOCALE_SPECIFICDATA | Interop.Kernel32.LOCALE_SUPPLEMENTAL, Unsafe.AsPointer(ref context), IntPtr.Zero);
  224. }
  225. if (context.cultureName != null)
  226. {
  227. // we got a matched culture
  228. return GetCultureData(context.cultureName, true);
  229. }
  230. return null;
  231. }
  232. private string GetLanguageDisplayName(string cultureName)
  233. {
  234. #if ENABLE_WINRT
  235. return WinRTInterop.Callbacks.GetLanguageDisplayName(cultureName);
  236. #else
  237. // Usually the UI culture shouldn't be different than what we got from WinRT except
  238. // if DefaultThreadCurrentUICulture was set
  239. CultureInfo? ci;
  240. if (CultureInfo.DefaultThreadCurrentUICulture != null &&
  241. ((ci = GetUserDefaultCulture()) != null) &&
  242. !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name))
  243. {
  244. return NativeName;
  245. }
  246. else
  247. {
  248. return GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName);
  249. }
  250. #endif // ENABLE_WINRT
  251. }
  252. private string GetRegionDisplayName(string isoCountryCode)
  253. {
  254. #if ENABLE_WINRT
  255. return WinRTInterop.Callbacks.GetRegionDisplayName(isoCountryCode);
  256. #else
  257. // If the current UI culture matching the OS UI language, we'll get the display name from the OS.
  258. // otherwise, we use the native name as we don't carry resources for the region display names anyway.
  259. if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name))
  260. {
  261. return GetLocaleInfo(LocaleStringData.LocalizedCountryName);
  262. }
  263. return NativeCountryName;
  264. #endif // ENABLE_WINRT
  265. }
  266. private static CultureInfo GetUserDefaultCulture()
  267. {
  268. #if ENABLE_WINRT
  269. return (CultureInfo)WinRTInterop.Callbacks.GetUserDefaultCulture();
  270. #else
  271. return CultureInfo.GetUserDefaultCulture();
  272. #endif // ENABLE_WINRT
  273. }
  274. // PAL methods end here.
  275. private static string GetLocaleInfoFromLCType(string localeName, uint lctype, bool useUserOveride)
  276. {
  277. Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoFromLCType] Expected localeName to be not be null");
  278. // Fix lctype if we don't want overrides
  279. if (!useUserOveride)
  280. {
  281. lctype |= Interop.Kernel32.LOCALE_NOUSEROVERRIDE;
  282. }
  283. // Ask OS for data
  284. // Failed? Just use empty string
  285. return GetLocaleInfoEx(localeName, lctype) ?? string.Empty;
  286. }
  287. /// <summary>
  288. /// Reescape a Win32 style quote string as a NLS+ style quoted string
  289. ///
  290. /// This is also the escaping style used by custom culture data files
  291. ///
  292. /// NLS+ uses \ to escape the next character, whether in a quoted string or
  293. /// not, so we always have to change \ to \\.
  294. ///
  295. /// NLS+ uses \' to escape a quote inside a quoted string so we have to change
  296. /// '' to \' (if inside a quoted string)
  297. ///
  298. /// We don't build the stringbuilder unless we find something to change
  299. /// </summary>
  300. [return: NotNullIfNotNull("str")]
  301. internal static string? ReescapeWin32String(string? str)
  302. {
  303. // If we don't have data, then don't try anything
  304. if (str == null)
  305. {
  306. return null;
  307. }
  308. StringBuilder? result = null;
  309. bool inQuote = false;
  310. for (int i = 0; i < str.Length; i++)
  311. {
  312. // Look for quote
  313. if (str[i] == '\'')
  314. {
  315. // Already in quote?
  316. if (inQuote)
  317. {
  318. // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote?
  319. if (i + 1 < str.Length && str[i + 1] == '\'')
  320. {
  321. // Found another ', so we have ''. Need to add \' instead.
  322. // 1st make sure we have our stringbuilder
  323. if (result == null)
  324. result = new StringBuilder(str, 0, i, str.Length * 2);
  325. // Append a \' and keep going (so we don't turn off quote mode)
  326. result.Append("\\'");
  327. i++;
  328. continue;
  329. }
  330. // Turning off quote mode, fall through to add it
  331. inQuote = false;
  332. }
  333. else
  334. {
  335. // Found beginning quote, fall through to add it
  336. inQuote = true;
  337. }
  338. }
  339. // Is there a single \ character?
  340. else if (str[i] == '\\')
  341. {
  342. // Found a \, need to change it to \\
  343. // 1st make sure we have our stringbuilder
  344. if (result == null)
  345. result = new StringBuilder(str, 0, i, str.Length * 2);
  346. // Append our \\ to the string & continue
  347. result.Append("\\\\");
  348. continue;
  349. }
  350. // If we have a builder we need to add our character
  351. if (result != null)
  352. result.Append(str[i]);
  353. }
  354. // Unchanged string? , just return input string
  355. if (result == null)
  356. return str;
  357. // String changed, need to use the builder
  358. return result.ToString();
  359. }
  360. [return: NotNullIfNotNull("array")]
  361. internal static string[]? ReescapeWin32Strings(string[]? array)
  362. {
  363. if (array != null)
  364. {
  365. for (int i = 0; i < array.Length; i++)
  366. {
  367. array[i] = ReescapeWin32String(array[i]);
  368. }
  369. }
  370. return array;
  371. }
  372. // If we get a group from windows, then its in 3;0 format with the 0 backwards
  373. // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa)
  374. // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning.
  375. private static int[] ConvertWin32GroupString(string win32Str)
  376. {
  377. // None of these cases make any sense
  378. if (win32Str == null || win32Str.Length == 0)
  379. {
  380. return (new int[] { 3 });
  381. }
  382. if (win32Str[0] == '0')
  383. {
  384. return (new int[] { 0 });
  385. }
  386. // Since its in n;n;n;n;n format, we can always get the length quickly
  387. int[] values;
  388. if (win32Str[win32Str.Length - 1] == '0')
  389. {
  390. // Trailing 0 gets dropped. 1;0 -> 1
  391. values = new int[(win32Str.Length / 2)];
  392. }
  393. else
  394. {
  395. // Need extra space for trailing zero 1 -> 1;0
  396. values = new int[(win32Str.Length / 2) + 2];
  397. values[values.Length - 1] = 0;
  398. }
  399. int i;
  400. int j;
  401. for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++)
  402. {
  403. // Note that this # shouldn't ever be zero, 'cause 0 is only at end
  404. // But we'll test because its registry that could be anything
  405. if (win32Str[i] < '1' || win32Str[i] > '9')
  406. return new int[] { 3 };
  407. values[j] = (int)(win32Str[i] - '0');
  408. }
  409. return (values);
  410. }
  411. private static int ConvertFirstDayOfWeekMonToSun(int iTemp)
  412. {
  413. // Convert Mon-Sun to Sun-Sat format
  414. iTemp++;
  415. if (iTemp > 6)
  416. {
  417. // Wrap Sunday and convert invalid data to Sunday
  418. iTemp = 0;
  419. }
  420. return iTemp;
  421. }
  422. // Context for EnumCalendarInfoExEx callback.
  423. private struct EnumLocaleData
  424. {
  425. public string regionName;
  426. public string? cultureName;
  427. }
  428. // EnumSystemLocaleEx callback.
  429. // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
  430. private static unsafe Interop.BOOL EnumSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
  431. {
  432. ref EnumLocaleData context = ref Unsafe.As<byte, EnumLocaleData>(ref *(byte*)contextHandle);
  433. try
  434. {
  435. string cultureName = new string(lpLocaleString);
  436. string? regionName = GetLocaleInfoEx(cultureName, Interop.Kernel32.LOCALE_SISO3166CTRYNAME);
  437. if (regionName != null && regionName.Equals(context.regionName, StringComparison.OrdinalIgnoreCase))
  438. {
  439. context.cultureName = cultureName;
  440. return Interop.BOOL.FALSE; // we found a match, then stop the enumeration
  441. }
  442. return Interop.BOOL.TRUE;
  443. }
  444. catch (Exception)
  445. {
  446. return Interop.BOOL.FALSE;
  447. }
  448. }
  449. // EnumSystemLocaleEx callback.
  450. // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
  451. private static unsafe Interop.BOOL EnumAllSystemLocalesProc(char* lpLocaleString, uint flags, void* contextHandle)
  452. {
  453. ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)contextHandle);
  454. try
  455. {
  456. context.strings.Add(new string(lpLocaleString));
  457. return Interop.BOOL.TRUE;
  458. }
  459. catch (Exception)
  460. {
  461. return Interop.BOOL.FALSE;
  462. }
  463. }
  464. // Context for EnumTimeFormatsEx callback.
  465. private struct EnumData
  466. {
  467. public List<string> strings;
  468. }
  469. // EnumTimeFormatsEx callback itself.
  470. // [NativeCallable(CallingConvention = CallingConvention.StdCall)]
  471. private static unsafe Interop.BOOL EnumTimeCallback(char* lpTimeFormatString, void* lParam)
  472. {
  473. ref EnumData context = ref Unsafe.As<byte, EnumData>(ref *(byte*)lParam);
  474. try
  475. {
  476. context.strings.Add(new string(lpTimeFormatString));
  477. return Interop.BOOL.TRUE;
  478. }
  479. catch (Exception)
  480. {
  481. return Interop.BOOL.FALSE;
  482. }
  483. }
  484. private static unsafe string[]? nativeEnumTimeFormats(string localeName, uint dwFlags, bool useUserOverride)
  485. {
  486. EnumData data = new EnumData();
  487. data.strings = new List<string>();
  488. // Now call the enumeration API. Work is done by our callback function
  489. Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, Unsafe.AsPointer(ref data));
  490. if (data.strings.Count > 0)
  491. {
  492. // Now we need to allocate our stringarray and populate it
  493. string[] results = data.strings.ToArray();
  494. if (!useUserOverride && data.strings.Count > 1)
  495. {
  496. // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
  497. // The override is the first entry if it is overriden.
  498. // We can check if we have overrides by checking the GetLocaleInfo with no override
  499. // If we do have an override, we don't know if it is a user defined override or if the
  500. // user has just selected one of the predefined formats so we can't just remove it
  501. // but we can move it down.
  502. uint lcType = (dwFlags == Interop.Kernel32.TIME_NOSECONDS) ? Interop.Kernel32.LOCALE_SSHORTTIME : Interop.Kernel32.LOCALE_STIMEFORMAT;
  503. string timeFormatNoUserOverride = GetLocaleInfoFromLCType(localeName, lcType, useUserOverride);
  504. if (timeFormatNoUserOverride != "")
  505. {
  506. string firstTimeFormat = results[0];
  507. if (timeFormatNoUserOverride != firstTimeFormat)
  508. {
  509. results[0] = results[1];
  510. results[1] = firstTimeFormat;
  511. }
  512. }
  513. }
  514. return results;
  515. }
  516. return null;
  517. }
  518. private static int LocaleNameToLCID(string cultureName)
  519. {
  520. Debug.Assert(!GlobalizationMode.Invariant);
  521. return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
  522. }
  523. private static unsafe string? LCIDToLocaleName(int culture)
  524. {
  525. Debug.Assert(!GlobalizationMode.Invariant);
  526. char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination
  527. int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
  528. if (length > 0)
  529. {
  530. return new string(pBuffer);
  531. }
  532. return null;
  533. }
  534. private int GetAnsiCodePage(string cultureName)
  535. {
  536. return GetLocaleInfo(LocaleNumberData.AnsiCodePage);
  537. }
  538. private int GetOemCodePage(string cultureName)
  539. {
  540. return GetLocaleInfo(LocaleNumberData.OemCodePage);
  541. }
  542. private int GetMacCodePage(string cultureName)
  543. {
  544. return GetLocaleInfo(LocaleNumberData.MacCodePage);
  545. }
  546. private int GetEbcdicCodePage(string cultureName)
  547. {
  548. return GetLocaleInfo(LocaleNumberData.EbcdicCodePage);
  549. }
  550. private int GetGeoId(string cultureName)
  551. {
  552. return GetLocaleInfo(LocaleNumberData.GeoId);
  553. }
  554. private int GetDigitSubstitution(string cultureName)
  555. {
  556. return GetLocaleInfo(LocaleNumberData.DigitSubstitution);
  557. }
  558. private string GetThreeLetterWindowsLanguageName(string cultureName)
  559. {
  560. return GetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName);
  561. }
  562. private static CultureInfo[] EnumCultures(CultureTypes types)
  563. {
  564. Debug.Assert(!GlobalizationMode.Invariant);
  565. uint flags = 0;
  566. #pragma warning disable 618
  567. if ((types & (CultureTypes.FrameworkCultures | CultureTypes.InstalledWin32Cultures | CultureTypes.ReplacementCultures)) != 0)
  568. {
  569. flags |= Interop.Kernel32.LOCALE_NEUTRALDATA | Interop.Kernel32.LOCALE_SPECIFICDATA;
  570. }
  571. #pragma warning restore 618
  572. if ((types & CultureTypes.NeutralCultures) != 0)
  573. {
  574. flags |= Interop.Kernel32.LOCALE_NEUTRALDATA;
  575. }
  576. if ((types & CultureTypes.SpecificCultures) != 0)
  577. {
  578. flags |= Interop.Kernel32.LOCALE_SPECIFICDATA;
  579. }
  580. if ((types & CultureTypes.UserCustomCulture) != 0)
  581. {
  582. flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL;
  583. }
  584. if ((types & CultureTypes.ReplacementCultures) != 0)
  585. {
  586. flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL;
  587. }
  588. EnumData context = new EnumData();
  589. context.strings = new List<string>();
  590. unsafe
  591. {
  592. Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, Unsafe.AsPointer(ref context), IntPtr.Zero);
  593. }
  594. CultureInfo [] cultures = new CultureInfo[context.strings.Count];
  595. for (int i = 0; i < cultures.Length; i++)
  596. {
  597. cultures[i] = new CultureInfo(context.strings[i]);
  598. }
  599. return cultures;
  600. }
  601. private string GetConsoleFallbackName(string cultureName)
  602. {
  603. return GetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName);
  604. }
  605. internal bool IsFramework
  606. {
  607. get { return false; }
  608. }
  609. internal bool IsWin32Installed
  610. {
  611. get { return true; }
  612. }
  613. internal bool IsReplacementCulture
  614. {
  615. get
  616. {
  617. EnumData context = new EnumData();
  618. context.strings = new List<string>();
  619. unsafe
  620. {
  621. Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, Unsafe.AsPointer(ref context), IntPtr.Zero);
  622. }
  623. for (int i=0; i<context.strings.Count; i++)
  624. {
  625. if (string.Compare(context.strings[i], _sWindowsName, StringComparison.OrdinalIgnoreCase) == 0)
  626. return true;
  627. }
  628. return false;
  629. }
  630. }
  631. }
  632. }