CultureData.Windows.cs 29 KB

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