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