CultureData.Windows.cs 28 KB

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