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