+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-////////////////////////////////////////////////////////////////////////////
-//
-// Class: CalendarData
-//
-
-//
-// Purpose: This module implements the methods of the CalendarData
-// class. These methods are the helper functions for the
-// Locale class.
-//
-// Date: July 4, 2007
-//
-////////////////////////////////////////////////////////////////////////////
-
-#include "common.h"
-#include "object.h"
-#include "excep.h"
-#include "vars.hpp"
-#include "interoputil.h"
-#include "corhost.h"
-
-#include <winnls.h>
-
-#include "calendardata.h"
-#include "nlsinfo.h"
-#include "newapis.h"
-
-////////////////////////////////////////////////////////////////////////
-//
-// Call the Win32 GetCalendarInfoEx() using the specified calendar and LCTYPE.
-// The return value can be INT32 or an allocated managed string object, depending on
-// which version's called.
-//
-// Parameters:
-// OUT pOutputInt32 The output int32 value.
-// OUT pOutputRef The output string value.
-//
-////////////////////////////////////////////////////////////////////////
-BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32)
-{
- CONTRACTL
- {
- GC_NOTRIGGER;
- MODE_ANY;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- int result = 0;
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
-
- // Just stick it right into the output int
- _ASSERT((calType & CAL_RETURN_NUMBER) != 0);
- result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, NULL, 0, (LPDWORD)pOutputInt32);
-
- END_SO_INTOLERANT_CODE
-
- return (result != 0);
-}
-
-BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef)
-{
- CONTRACTL
- {
- THROWS; // We can throw since we are allocating managed string.
- DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
- MODE_ANY;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- // The maximum size for values returned from GetLocaleInfo is 80 characters.
- WCHAR buffer[80];
- int result = 0;
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; )
-
- _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
- result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
-
- if (result != 0)
- {
- _ASSERTE(pOutputStrRef != NULL);
- *pOutputStrRef = StringObject::NewString(buffer, result - 1);
- }
-
- END_SO_INTOLERANT_CODE
-
- return (result != 0);
-}
-
-////////////////////////////////////////////////////////////////////////
-//
-// Get the native day names
-//
-// NOTE: There's a disparity between .Net & windows day orders, the input day should
-// start with Sunday
-//
-// Parameters:
-// OUT pOutputStrings The output string[] value.
-//
-////////////////////////////////////////////////////////////////////////
-BOOL CalendarData::GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
-{
- CONTRACTL
- {
- THROWS; // We can throw since we are allocating managed string.
- INJECT_FAULT(COMPlusThrowOM());
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- // The maximum size for values returned from GetLocaleInfo is 80 characters.
- WCHAR buffer[80];
- int result = 0;
-
- _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
-
- //
- // We'll need a new array of 7 items
- //
- // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
- // an OutOfMemoryException if there's not enough memory.
- //
- PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(7, g_pStringClass);
-
- GCPROTECT_BEGIN(ResultArray);
-
- // Get each one of them
- for (int i = 0; i < 7; i++, calType++)
- {
- result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
-
- // Note that the returned string is null terminated, so even an empty string will be 1
- if (result != 0)
- {
- // Make a string for this entry
- STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
- ResultArray->SetAt(i, (OBJECTREF)stringResult);
- }
-
- // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens
- // This is because the framework starts on sunday and windows starts on monday when counting days
- if (i == 0) calType -= 7;
- }
- GCPROTECT_END();
-
- _ASSERTE(pOutputStrings != NULL);
- *pOutputStrings = ResultArray;
-
- END_SO_INTOLERANT_CODE
-
- return (result != 0);
-}
-
-
-
-////////////////////////////////////////////////////////////////////////
-//
-// Get the native month names
-//
-// Parameters:
-// OUT pOutputStrings The output string[] value.
-//
-////////////////////////////////////////////////////////////////////////
-BOOL CalendarData::GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings)
-{
- CONTRACTL
- {
- THROWS; // We can throw since we are allocating managed string.
- INJECT_FAULT(COMPlusThrowOM());
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- // The maximum size for values returned from GetLocaleInfo is 80 characters.
- WCHAR buffer[80];
- int result = 0;
-
- _ASSERT((calType & CAL_RETURN_NUMBER) == 0);
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;)
-
- //
- // We'll need a new array of 13 items
- //
- // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
- // an OutOfMemoryException if there's not enough memory.
- //
- PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(13, g_pStringClass);
-
- GCPROTECT_BEGIN(ResultArray);
-
- // Get each one of them
- for (int i = 0; i < 13; i++, calType++)
- {
- result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL);
-
- // If we still have failure, then mark as empty string
- if (result == 0)
- {
- buffer[0] = W('0');
- result = 1;
-
-
- }
-
- // Note that the returned string is null terminated, so even an empty string will be 1
- // Make a string for this entry
- STRINGREF stringResult = StringObject::NewString(buffer, result - 1);
- ResultArray->SetAt(i, (OBJECTREF)stringResult);
- }
- GCPROTECT_END();
-
- _ASSERTE(pOutputStrings != NULL);
- *pOutputStrings = ResultArray;
-
- END_SO_INTOLERANT_CODE
-
- return (result != 0);
-}
-
-//
-// struct to help our calendar data enumaration callback
-//
-struct enumData
-{
- int count; // # of strings found so far
- LPWSTR userOverride; // pointer to user override string if used
- LPWSTR stringsBuffer; // pointer to a buffer to use for the strings.
- LPWSTR endOfBuffer; // pointer to the end of the stringsBuffer ( must be < this to write)
-};
-
-//
-// callback itself
-//
-BOOL CALLBACK EnumCalendarInfoCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
-{
- CONTRACTL
- {
- GC_NOTRIGGER;
- MODE_COOPERATIVE;
- SO_TOLERANT;
- PRECONDITION(CheckPointer(lpCalendarInfoString));
- PRECONDITION(CheckPointer((LPVOID)lParam));
- } CONTRACTL_END;
-
- // Cast our data to the right type
- enumData* pData = (enumData*)lParam;
-
- // If we had a user override, check to make sure this differs
- if (pData->userOverride == NULL ||
- wcscmp(pData->userOverride, lpCalendarInfoString) != 0)
- {
- // They're different, add it to our buffer
- LPWSTR pStart = pData->stringsBuffer;
- LPCWSTR pEnd = pData->endOfBuffer;
- while (pStart < pEnd && *lpCalendarInfoString != 0)
- {
- *(pStart++) = *(lpCalendarInfoString++);
- }
-
- // Add a \0
- if (pStart < pEnd)
- {
- *(pStart++) = 0;
-
- // Did it finish?
- if (pStart <= pEnd)
- {
- // It finished, use it
- pData->count++;
- pData->stringsBuffer = pStart;
- }
- }
- }
-
- return TRUE;
-}
-
-//
-// CallEnumCalendarInfo
-//
-// Get the list of whichever calendar property from the OS. If user override is passed in, then check GetLocaleInfo as well
-// to see if a user override is set.
-//
-// We build a list of strings, first calling getlocaleinfo if necessary, and then the enums. The strings are null terminated,
-// with a double null ending the list. Once we have the list we can allocate our COMStrings and arrays from the count.
-//
-// We need a helper structure to pass as an lParam
-//
-BOOL CalendarData::CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType,
- __in int lcType, __inout PTRARRAYREF* pOutputStrings)
-{
- CONTRACTL
- {
- THROWS; // We can throw since we are allocating managed string.
- INJECT_FAULT(COMPlusThrowOM());
- DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now
- MODE_COOPERATIVE;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- BOOL result = TRUE;
-
- // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far.
- WCHAR stringBuffer[512];
-
- struct enumData data;
- data.count = 0;
- data.userOverride = NULL;
- data.stringsBuffer = stringBuffer;
- data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
-
- // First call GetLocaleInfo if necessary
- if ((lcType && ((lcType & LOCALE_NOUSEROVERRIDE) == 0)) &&
- // Get user locale, see if it matches localeName.
- // Note that they should match exactly, including letter case
- NewApis::GetUserDefaultLocaleName(stringBuffer, 512) && wcscmp(localeName, stringBuffer) == 0)
- {
- // They want user overrides, see if the user calendar matches the input calendar
- CALID userCalendar = 0;
- NewApis::GetLocaleInfoEx(localeName, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, (LPWSTR)&userCalendar,
- sizeof(userCalendar) / sizeof(WCHAR) );
-
- // If the calendars were the same, see if the locales were the same
- if ((int)userCalendar == calendar) // todo: cast to compile on MAC
- {
- // They matched, get the user override since locale & calendar match
- int i = NewApis::GetLocaleInfoEx(localeName, lcType, stringBuffer, 512);
-
- // if it succeeded, advance the pointer and remember the override for the later callers
- if (i > 0)
- {
- // Remember this was the override (so we can look for duplicates later in the enum function)
- data.userOverride = data.stringsBuffer;
-
- // Advance to the next free spot (i includes counting the \0)
- data.stringsBuffer += i;
-
- // And our count...
- data.count++;
- }
- }
- }
-
- // Now call the enumeration API. Work is done by our callback function
- NewApis::EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, calendar, calType, (LPARAM)&data);
-
- // Now we have a list of data, fail if we didn't find anything.
- if (data.count == 0) return FALSE;
-
- // Now we need to allocate our stringarray and populate it
- STATIC_CONTRACT_SO_TOLERANT;
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return (result); )
-
- // Get our array object (will throw, don't have to check it)
- PTRARRAYREF dataArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
-
- GCPROTECT_BEGIN(dataArray);
- LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
- for(DWORD i = 0; i < (DWORD)data.count; i++)
- {
- OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
-
- if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING)
- {
- // Eras are enumerated backwards. (oldest era name first, but
- // Japanese calendar has newest era first in array, and is only
- // calendar with multiple eras)
- dataArray->SetAt((DWORD)data.count - i - 1, o);
- }
- else
- {
- dataArray->SetAt(i, o);
- }
-
- buffer += (lstrlenW(buffer) + 1);
- }
- GCPROTECT_END();
-
- _ASSERTE(pOutputStrings != NULL);
- *pOutputStrings = dataArray;
-
- END_SO_INTOLERANT_CODE
-
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////
-//
-// For calendars like Gregorain US/Taiwan/UmAlQura, they are not available
-// in all OS or all localized versions of OS.
-// If OS does not support these calendars, we will fallback by using the
-// appropriate fallback calendar and locale combination to retrieve data from OS.
-//
-// Parameters:
-// __deref_inout pCalendarInt:
-// Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed.
-// __in_out pLocaleNameStackBuffer
-// Pointer to the StackSString object which holds the locale name to be checked.
-// This will be updated to new fallback locale name if needed.
-//
-////////////////////////////////////////////////////////////////////////
-
-void CalendarData::CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer)
-{
- // Gregorian-US isn't always available in the OS, however it is the same for all locales
- switch (*pCalendarInt)
- {
- case CAL_GREGORIAN_US:
- // See if this works
- if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
- {
- // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS
- pLocaleNameStackBuffer->Set(W("fa-IR"), 5);
- }
- // See if that works
- if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
- {
- // Failed again, just use en-US with the gregorian calendar
- pLocaleNameStackBuffer->Set(W("en-US"), 5);
- *pCalendarInt = CAL_GREGORIAN;
- }
- break;
- case CAL_TAIWAN:
- // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons.
- // It is only available in zh-TW localized versions of Windows.
- // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar.
- if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
- {
- *pCalendarInt = CAL_GREGORIAN;
- }
- break;
- case CAL_UMALQURA:
- // UmAlQura is only available in Vista and above, so we will need to fallback to Hijri if it is not available in the OS.
- if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL))
- {
- // There are no differences in DATA between UmAlQura and Hijri, and
- // UmAlQura isn't available before Vista, so just use Hijri..
- *pCalendarInt = CAL_HIJRI;
- }
- break;
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-//
-// Implementation for CalendarInfo.nativeGetCalendarData
-//
-// Retrieve calendar properties from the native side
-//
-// Parameters:
-// pCalendarData: This is passed from a managed structure CalendarData.cs
-// pLocaleNameUNSAFE: Locale name associated with the locale for this calendar
-// calendar: Calendar ID
-//
-// NOTE: Calendars depend on the locale name that creates it. Only a few
-// properties are available without locales using CalendarData.GetCalendar(int)
-//
-////////////////////////////////////////////////////////////////////////
-
-FCIMPL3(FC_BOOL_RET, CalendarData::nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
- } CONTRACTL_END;
-
-
- // The maximum allowed string length in GetLocaleInfo is 80 WCHARs.
- BOOL ret = TRUE;
-
- struct _gc
- {
- STRINGREF localeName;
- CALENDARDATAREF calendarData;
- } gc;
-
- // Dereference our gc objects
- gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
- gc.calendarData = (CALENDARDATAREF)calendarDataUNSAFE;
-
- // Need to set up the frame since we will be allocating managed strings.
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
-
- // create a local copy of the string in order to pass it to helper methods that trigger GCs like GetCalendarDayInfo and GetCalendarMonthInfo
- StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
-
- // Conveniently this is the same as LOCALE_NOUSEROVERRIDE, so we can use this for both
- int useOverrides = (gc.calendarData->bUseUserOverrides) ? 0 : CAL_NOUSEROVERRIDE;
-
- // Helper string
- STRINGREF stringResult = NULL;
-
- //
- // Windows doesn't support some calendars right now, so remap those.
- //
- if (calendar >= 13 && calendar < 23)
- {
- switch (calendar)
- {
- case RESERVED_CAL_PERSIAN: // don't change if we have Persian calendar
- break;
-
- case RESERVED_CAL_JAPANESELUNISOLAR: // Data looks like Japanese
- calendar=CAL_JAPAN;
- break;
- case RESERVED_CAL_JULIAN: // Data looks like gregorian US
- case RESERVED_CAL_CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevent
- case RESERVED_CAL_SAKA: // reserved to match Office but not implemented in our code, so data is irrelevent
- case RESERVED_CAL_LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so data is irrelevent
- case RESERVED_CAL_LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so data is irrelevent
- case RESERVED_CAL_LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so data is irrelevent
- case RESERVED_CAL_KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevent
- case RESERVED_CAL_TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevent
- default:
- calendar = CAL_GREGORIAN_US;
- break;
- }
- }
-
- //
- // Speical handling for some special calendar due to OS limitation.
- // This includes calendar like Taiwan calendar, UmAlQura calendar, etc.
- //
- CheckSpecialCalendar(&calendar, &localeNameStackBuffer);
-
-
- // Numbers
- ret &= CallGetCalendarInfoEx(localeNameStackBuffer, calendar,
- CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER | useOverrides,
- &(gc.calendarData->iTwoDigitYearMax));
-
- if (ret == FALSE) // failed call
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_ITWODIGITYEARMAX");
- }
-
- _ASSERTE(ret == TRUE);
-
- // Strings
- if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SCALNAME , &stringResult))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->sNativeName), stringResult, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SCALNAME");
- ret = FALSE;
- }
- if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SMONTHDAY | useOverrides, &stringResult))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->sMonthDay), stringResult, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read RESERVED_CAL_SMONTHDAY");
- ret = FALSE;
- }
-
- // String Arrays
- // Formats
- PTRARRAYREF array = NULL;
- if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saShortDates), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SSHORTDATE");
- ret = FALSE;
- }
- if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saLongDates), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SLONGDATE");
- ret = FALSE;
- }
-
- // Get the YearMonth pattern.
- // Before Windows Vista, NLS would not write the Year/Month pattern into the reg key.
- // This causes GetLocaleInfo() to retrieve the Gregorian localized calendar pattern even when the calendar is not Gregorian localized.
- // So we will call GetLocaleInfo() only when the reg key for sYearMonth is there.
- //
- // If the key does not exist, leave yearMonthPattern to be null, so that we will pick up the default table value.
-
- int useOverridesForYearMonthPattern = useOverrides;
- if (useOverridesForYearMonthPattern == 0)
- {
- HKEY hkey = NULL;
- useOverridesForYearMonthPattern = CAL_NOUSEROVERRIDE;
- if (WszRegOpenKeyEx(HKEY_CURRENT_USER, W("Control Panel\\International"), 0, KEY_READ, &hkey) == ERROR_SUCCESS)
- {
- if (WszRegQueryValueEx(hkey, W("sYearMonth"), 0, NULL, NULL, NULL) == ERROR_SUCCESS)
- {
- // The sYearMonth key exists. Call GetLocaleInfo() to read it.
- useOverridesForYearMonthPattern = 0; // now we can use the overrides
- }
- RegCloseKey(hkey);
- }
- }
-
- if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SYEARMONTH, LOCALE_SYEARMONTH | useOverridesForYearMonthPattern, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saYearMonths), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SYEARMONTH");
- ret = FALSE;
- }
-
- // Day & Month Names
- // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names
-
- // Day
- // Note that we're off-by-one since managed starts on sunday and windows starts on monday
- if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SDAYNAME7, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saDayNames), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SDAYNAME7");
- ret = FALSE;
- }
- if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SABBREVDAYNAME7, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevDayNames), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVDAYNAME7");
- ret = FALSE;
- }
-
- // Month names
- if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthNames), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SMONTHNAME1");
- ret = FALSE;
- }
-
- if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthNames), array, NULL);
- else
- {
- _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVMONTHNAME1");
- ret = FALSE;
- }
-
- //
- // The following LCTYPE are not supported in some platforms. If the call fails,
- // don't return a failure.
- //
- if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SSHORTESTDAYNAME7, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saSuperShortDayNames), array, NULL);
-
-
- // Gregorian may have genitive month names
- if (calendar == CAL_GREGORIAN)
- {
- if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthGenitiveNames), array, NULL);
- // else we ignore the error and let managed side copy the normal month names
- if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthGenitiveNames), array, NULL);
- // else we ignore the error and let managed side copy the normal month names
- }
-
-// leap year names are only different for month 6 in Hebrew calendar
-// PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. (Hebrew's the only one that has these)
-
- // Calendar Parts Names
- if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SERASTRING, NULL, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saEraNames), array, NULL);
- // else we set the era in managed code
- if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SABBREVERASTRING, NULL, &array))
- SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevEraNames), array, NULL);
- // else we set the era in managed code
-
- // PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English
-
- //
- // Calendar Era Info
- // Note that calendar era data (offsets, etc) is hard coded for each calendar since this
- // data is implementation specific and not dynamic (except perhaps Japanese)
- //
-
- HELPER_METHOD_FRAME_END();
-
- FC_RETURN_BOOL(ret);
-}
-FCIMPLEND
-
-//
-// Get the system two digit year max value for the specified calendar
-//
-FCIMPL1(INT32, CalendarData::nativeGetTwoDigitYearMax, INT32 calendar)
-{
- FCALL_CONTRACT;
-
- DWORD dwTwoDigitYearMax = (DWORD) -1;
-
- HELPER_METHOD_FRAME_BEGIN_RET_0();
-
- WCHAR strName[LOCALE_NAME_MAX_LENGTH];
-
- // Really we should just be able to pass NULL for the locale name since that
- // causes the OS to look up the user default locale name. The downlevel APIS could
- // emulate this as necessary
- if (NewApis::GetUserDefaultLocaleName(strName,NumItems(strName)) == 0 ||
- NewApis::GetCalendarInfoEx(strName, calendar, NULL, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER, NULL, 0,&dwTwoDigitYearMax) == 0)
- {
- dwTwoDigitYearMax = (DWORD) -1;
- goto lExit;
- }
-
-lExit: ;
- HELPER_METHOD_FRAME_END();
-
- return (dwTwoDigitYearMax);
-}
-FCIMPLEND
-
-//
-// nativeGetCalendars
-//
-// Get the list of acceptable calendars for this user/locale
-//
-// Might be a better way to marshal the int[] for calendars
-// We expect the input array to be 23 ints long. We then fill up the first "count" ints and return the count.
-// The caller should then make it a smaller array.
-//
-
-// Perhaps we could do something more like this...
-//U1ARRAYREF rgbOut = (U1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U1, cb);
-//memcpyNoGCRefs(rgbOut->GetDirectPointerToNonObjectElements(), rgbKey, cb * sizeof(BYTE));
-//
-//refRetVal = rgbOut;
-//
-//HELPER_METHOD_FRAME_END();
-//return (U1Array*) OBJECTREFToObject(refRetVal);
-
-//
-// struct to help our calendar data enumaration callback
-//
-struct enumCalendarsData
-{
- int count; // # of strings found so far
- CALID userOverride; // user override value (if found)
- INT32* calendarList; // list of calendars found so far
-};
-
-//
-// callback itself
-//
-BOOL CALLBACK EnumCalendarsCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam)
-{
- CONTRACTL
- {
- GC_NOTRIGGER;
- MODE_COOPERATIVE;
- SO_TOLERANT;
- PRECONDITION(CheckPointer(lpCalendarInfoString));
- PRECONDITION(CheckPointer((LPVOID)lParam));
- } CONTRACTL_END;
-
- // Cast our data to the right type
- enumCalendarsData* pData = (enumCalendarsData*)lParam;
-
- // If we had a user override, check to make sure this differs
- if (pData->userOverride == Calendar)
- {
- // Its the same, just return
- return TRUE;
- }
-
- // They're different, add it to our buffer, check we have room
- if (pData->count < 23)
- {
- pData->calendarList[pData->count++] = Calendar;
- }
-
- return TRUE;
-}
-
-FCIMPL3(INT32, CalendarData::nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL useOverrides, I4Array* calendarsUNSAFE)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
- } CONTRACTL_END;
-
- int ret = 0;
-
- struct _gc
- {
- STRINGREF localeName;
- I4ARRAYREF calendarsRef;
- } gc;
-
- // Dereference our string
- gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
- gc.calendarsRef = (I4ARRAYREF)calendarsUNSAFE;
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
-
- int calendarBuffer[23];
- struct enumCalendarsData data;
- data.count = 0;
- data.userOverride = 0;
- data.calendarList = calendarBuffer;
-
- // First call GetLocaleInfo if necessary
- if (useOverrides)
- {
- // They want user overrides, see if the user calendar matches the input calendar
-
- CALID userCalendar = 0;
- NewApis::GetLocaleInfoEx( gc.localeName->GetBuffer(), LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
- (LPWSTR)&userCalendar, sizeof(userCalendar) / sizeof(WCHAR) );
-
- // If we got a default, then use it as the first calendar
- if (userCalendar != 0)
- {
- data.userOverride = userCalendar;
- data.calendarList[data.count++] = userCalendar;
- }
- }
-
- // Now call the enumeration API. Work is done by our callback function
- NewApis::EnumCalendarInfoExEx(EnumCalendarsCallback, gc.localeName->GetBuffer(), ENUM_ALL_CALENDARS, CAL_ICALINTVALUE, (LPARAM)&(data));
-
- // Copy to the output array
- for (int i = 0; i < data.count; i++)
- {
- (gc.calendarsRef->GetDirectPointerToNonObjectElements())[i] = calendarBuffer[i];
- }
-
- ret = data.count;
- HELPER_METHOD_FRAME_END();
-
- // Now we have a list of data, return the count
- return ret;
-}
-FCIMPLEND
-
-//
-// nativeEnumTimeFormats
-//
-// Enumerate all of the time formats (long times) on the system.
-// Windows only has 1 time format so there's nothing like an LCTYPE here.
-//
-// Note that if the locale is the user default locale windows ALWAYS returns the user override value first.
-// (ie: there's no no-user-override option for this API)
-//
-// We reuse the enumData structure since it works for us.
-//
-
-//
-// callback itself
-//
-BOOL CALLBACK EnumTimeFormatsCallback(__in_z LPCWSTR lpTimeFormatString, __in LPARAM lParam)
-{
- CONTRACTL
- {
- GC_NOTRIGGER;
- MODE_COOPERATIVE;
- SO_TOLERANT;
- PRECONDITION(CheckPointer(lpTimeFormatString));
- PRECONDITION(CheckPointer((LPVOID)lParam));
- } CONTRACTL_END;
-
- // Cast our data to the right type
- enumData* pData = (enumData*)lParam;
-
- // Don't have to worry about user overrides (the enum adds them)
- // add it to our buffer
- LPWSTR pStart = pData->stringsBuffer;
- LPCWSTR pEnd = pData->endOfBuffer;
- while (pStart < pEnd && *lpTimeFormatString != 0)
- {
- *(pStart++) = *(lpTimeFormatString++);
- }
-
- // Add a \0
- if (pStart < pEnd)
- {
- *(pStart++) = 0;
-
- // Did it finish?
- if (pStart <= pEnd)
- {
- // It finished, use it
- pData->count++;
- pData->stringsBuffer = pStart;
- }
- }
-
- return TRUE;
-}
-
-//
-// nativeEnumTimeFormats that calls the callback above
-//
-FCIMPL3(Object*, CalendarData::nativeEnumTimeFormats,
- StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(pLocaleNameUNSAFE));
- } CONTRACTL_END;
-
-
- struct _gc
- {
- STRINGREF localeName;
- PTRARRAYREF timeFormatsArray;
- } gc;
-
- // Dereference our gc objects
- gc.localeName = (STRINGREF)pLocaleNameUNSAFE;
- gc.timeFormatsArray = NULL;
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
-
- // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far.
- WCHAR stringBuffer[512];
- struct enumData data;
- data.count = 0;
- data.userOverride = NULL;
- data.stringsBuffer = stringBuffer;
- data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes
-
- // Now call the enumeration API. Work is done by our callback function
- NewApis::EnumTimeFormatsEx((TIMEFMT_ENUMPROCEX)EnumTimeFormatsCallback, gc.localeName->GetBuffer(), dwFlags, (LPARAM)&data);
-
- if (data.count > 0)
- {
- // Now we need to allocate our stringarray and populate it
- // Get our array object (will throw, don't have to check it)
- gc.timeFormatsArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass);
-
- LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
- for(DWORD i = 0; i < (DWORD)data.count; i++) // todo: cast to compile on Mac
- {
- OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
- gc.timeFormatsArray->SetAt(i, o);
-
- buffer += (lstrlenW(buffer) + 1);
- }
-
- if(!useUserOverride && data.count > 1)
- {
- // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override
- // The override is the first entry if it is overriden.
- // We can check if we have overrides by checking the GetLocaleInfo with no override
- // If we do have an override, we don't know if it is a user defined override or if the
- // user has just selected one of the predefined formats so we can't just remove it
- // but we can move it down.
- WCHAR timeFormatNoUserOverride[LOCALE_NAME_MAX_LENGTH];
- DWORD lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT;
- lcType |= LOCALE_NOUSEROVERRIDE;
- int result = NewApis::GetLocaleInfoEx(gc.localeName->GetBuffer(), lcType, timeFormatNoUserOverride, LOCALE_NAME_MAX_LENGTH);
- if(result != 0)
- {
- STRINGREF firstTimeFormat = (STRINGREF)gc.timeFormatsArray->GetAt(0);
- if(wcscmp(timeFormatNoUserOverride, firstTimeFormat->GetBuffer())!=0)
- {
- gc.timeFormatsArray->SetAt(0, gc.timeFormatsArray->GetAt(1));
- gc.timeFormatsArray->SetAt(1, firstTimeFormat);
- }
- }
- }
-
- }
-
- HELPER_METHOD_FRAME_END();
- return OBJECTREFToObject(gc.timeFormatsArray);
-}
-FCIMPLEND
-
-
// Constant Declarations.
//
-#ifndef COMPARE_OPTIONS_IGNORECASE
-#define COMPARE_OPTIONS_IGNORECASE 0x00000001
-#endif
-
-#ifndef LOCALE_SNAME
-#define LOCALE_SNAME 0x0000005c
-#endif
-
-#ifndef LOCALE_SNAN
-#define LOCALE_SNAN 0x00000069
-#endif
-
-#ifndef LOCALE_SPOSINFINITY
-#define LOCALE_SPOSINFINITY 0x0000006a
-#endif
-
-#ifndef LOCALE_SNEGINFINITY
-#define LOCALE_SNEGINFINITY 0x0000006b
-#endif
-
-#ifndef LOCALE_SPARENT
-#define LOCALE_SPARENT 0x0000006d
-#endif
-
-#ifndef LOCALE_SCONSOLEFALLBACKNAME
-#define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e // Fallback name for within the console
-#endif
-
-#ifndef LOCALE_SISO3166CTRYNAME2
-#define LOCALE_SISO3166CTRYNAME2 0x00000068
-#endif
-
-#ifndef LOCALE_SISO639LANGNAME2
-#define LOCALE_SISO639LANGNAME2 0x00000067
-#endif
-
-#ifndef LOCALE_SSHORTESTDAYNAME1
-#define LOCALE_SSHORTESTDAYNAME1 0x00000060
-#endif
-
-// Windows 7 LCTypes
-#ifndef LOCALE_INEUTRAL
-#define LOCALE_INEUTRAL 0x00000071 // Returns 0 for specific cultures, 1 for neutral cultures.
-#endif
-
-#ifndef LCMAP_TITLECASE
-#define LCMAP_TITLECASE 0x00000300 // Title Case Letters
-#endif
-
-// Windows 8 LCTypes
-#ifndef LCMAP_SORTHANDLE
-#define LCMAP_SORTHANDLE 0x20000000
-#endif
-
-#ifndef LCMAP_HASH
-#define LCMAP_HASH 0x00040000
-#endif
-
-#ifndef LOCALE_REPLACEMENT
-#define LOCALE_REPLACEMENT 0x00000008 // locales that replace shipped locales (callback flag only)
-#endif // LOCALE_REPLACEMENT
-
-#define LOCALE_MAX_STRING_SIZE 530 // maximum sice of LOCALE_SKEYBOARDSTOINSTALL, currently 5 "long" + 2 "short" keyboard signatures (YI + 3).
-
#define MAX_STRING_VALUE 512
// TODO: NLS Arrowhead -Be nice if we could depend more on the OS for this
-// Language ID for CHT (Taiwan)
-#define LANGID_ZH_TW 0x0404
-// Language ID for CHT (Hong-Kong)
-#define LANGID_ZH_HK 0x0c04
-#define REGION_NAME_0404 W("\x53f0\x7063")
-#if BIGENDIAN
-#define INTERNATIONAL_CURRENCY_SYMBOL W("\x00a4")
-#else
-#define INTERNATIONAL_CURRENCY_SYMBOL W("\xa400")
-#endif
-
-inline BOOL IsCustomCultureId(LCID lcid)
-{
- return (lcid == LOCALE_CUSTOM_DEFAULT || lcid == LOCALE_CUSTOM_UNSPECIFIED);
-}
-
-#ifndef FEATURE_COREFX_GLOBALIZATION
-//
-// Normalization Implementation
-//
-#define NORMALIZATION_DLL MAKEDLLNAME(W("normalization"))
-HMODULE COMNlsInfo::m_hNormalization = NULL;
-PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL;
-PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL;
-PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL;
-#endif // FEATURE_COREFX_GLOBALIZATION
-
-
-// InternalIsSortable
-//
-// Called by CompareInfo.IsSortable() to determine if a string has entirely sortable (ie: defined) code points.
-BOOL QCALLTYPE COMNlsInfo::InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(string));
- } CONTRACTL_END;
- BOOL result = FALSE;
- BEGIN_QCALL;
-
- {
- // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now
- result = NewApis::IsNLSDefinedString(COMPARE_STRING, 0, NULL, string, length);
- }
-
- END_QCALL;
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////
-//
-// InternalGetUserDefaultLocaleName
-//
-// Returns a string with the name of our LCID and returns 0 in LCID.
-// If we cant return
-//
-////////////////////////////////////////////////////////////////////////////
-// This is new to longhorn
-BOOL QCALLTYPE COMNlsInfo::InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION((langType == LOCALE_SYSTEM_DEFAULT) || (langType == LOCALE_USER_DEFAULT));
- } CONTRACTL_END;
-
- BOOL result;
- BEGIN_QCALL;
-
- WCHAR strName[LOCALE_NAME_MAX_LENGTH];
- int size = 0;
-
- if (langType == LOCALE_SYSTEM_DEFAULT)
- {
- size = NewApis::GetSystemDefaultLocaleName(strName,NumItems(strName));
- }
- else
- {
- _ASSERT(langType == LOCALE_USER_DEFAULT);
- size = NewApis::GetUserDefaultLocaleName(strName,NumItems(strName));
- }
-
- // Not found, either not longhorn (no LOCALE_SNAME) or not a valid name
- if (size == 0)
- {
- result = false;
- }
- else
- {
- defaultLocaleName.Set(strName);
- result = true;
- }
- END_QCALL;
- return result;
-}
-
-BOOL QCALLTYPE COMNlsInfo::InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage)
-{
- QCALL_CONTRACT;
- BOOL result;
- BEGIN_QCALL;
-
- WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
-
- int systemDefaultUiLcid = GetSystemDefaultUILanguage();
- if(systemDefaultUiLcid == LANGID_ZH_TW)
- {
- if (!NewApis::IsZhTwSku())
- {
- systemDefaultUiLcid = LANGID_ZH_HK;
- }
- }
-
- int length = NewApis::LCIDToLocaleName(systemDefaultUiLcid, localeName, NumItems(localeName), 0);
- if (length == 0)
- {
- result = false;
- }
- else
- {
- systemDefaultUiLanguage.Set(localeName);
- result = true;
- }
-
- END_QCALL;
- return result;
-}
-
-/*
- */
-BOOL QCALLTYPE COMNlsInfo::InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage)
-{
- QCALL_CONTRACT;
- BOOL result;
- BEGIN_QCALL;
-
- WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH];
- LPCWSTR wszLangName=NULL;
-
- int res= 0;
- ULONG uLangCount=0;
- ULONG uBufLen=0;
- res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,NULL,&uBufLen);
- if (res == 0)
- ThrowLastError();
-
-
- NewArrayHolder<WCHAR> sPreferredLanguages(NULL);
-
- if (uBufLen > 0 && uLangCount > 0 )
- {
- sPreferredLanguages = new WCHAR[uBufLen];
- res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,sPreferredLanguages,&uBufLen);
-
- if (res == 0)
- ThrowLastError();
-
- wszLangName=sPreferredLanguages;
-// Review size_t to int conversion (possible loss of data).
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4267)
-#endif
- res=wcslen(wszLangName)+1;
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- }
- else
- {
- res=0;
- }
-
- if (res == 0) {
- res = NewApis::GetUserDefaultLocaleName(wszBuffer,NumItems(wszBuffer));
- wszLangName=wszBuffer;
- }
-
-
- // If not found, either not longhorn (no LOCALE_SNAME) or not a valid name
- if (res == 0)
- {
- // Didn't find string, return an empty string.
- result = false;
- }
- else
- {
- userDefaultUiLanguage.Set(wszLangName);
- result = true;
- }
-
- // Return the found language name. LCID should be found one already.
- END_QCALL;
- return result;
-}
-
-INT32 COMNlsInfo::CallGetUserDefaultUILanguage()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- static INT32 s_lcid = 0;
-
- // The user UI language cannot change within a process (in fact it cannot change within a logon session),
- // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are
- // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call
- // GetUserDefaultUILanguage needlessly, but the final result is going to be the same.
- if (s_lcid == 0)
- {
- INT32 s_lcidTemp = GetUserDefaultUILanguage();
- if (s_lcidTemp == LANGID_ZH_TW)
- {
- // If the UI language ID is 0x0404, we need to do extra check to decide
- // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID.
- if (!NewApis::IsZhTwSku())
- {
- s_lcidTemp = LANGID_ZH_HK;
- }
- }
- s_lcid = s_lcidTemp;
- }
-
- return s_lcid;
-}
-
-
-FCIMPL2(Object*, COMNlsInfo::nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- } CONTRACTL_END;
-
- struct _gc
- {
- STRINGREF localeName;
- STRINGREF refRetVal;
- } gc;
-
- // Dereference our string
- gc.refRetVal = NULL;
- gc.localeName = (STRINGREF)localeNameUNSAFE;
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
- StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
-
- WCHAR buffer[LOCALE_MAX_STRING_SIZE];
- int result = NewApis::GetLocaleInfoEx(localeNameStackBuffer, lcType, buffer, NumItems(buffer));
-
- // Make a string out of it
- if (result != 0)
- {
- // Exclude the NULL char at the end, except that LOCALE_FONTSIGNATURE isn't
- // really a string, so we need the last character too.
- gc.refRetVal = StringObject::NewString(buffer, ((lcType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_FONTSIGNATURE) ? result : result-1);
- }
- else
- {
- }
- HELPER_METHOD_FRAME_END();
- return OBJECTREFToObject(gc.refRetVal);
-}
-FCIMPLEND
-
-
-FCIMPL2(INT32, COMNlsInfo::nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- } CONTRACTL_END;
-
- INT32 result = 0;
-
- // Dereference our string
- STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
-
- lcType |= LOCALE_RETURN_NUMBER;
-
- if (NewApis::GetLocaleInfoEx(localeName->GetBuffer(), lcType, (LPWSTR)&result, sizeof(INT32) / sizeof (WCHAR)) == 0)
- {
- // return value of 0 indicates failure and error value is supposed to be set.
- // shouldn't ever really happen
- _ASSERTE(!"catastrophic failure calling NewApis::nativeGetLocaleInfoExInt! This could be a CultureInfo bug (bad localeName string) or maybe a GCHole.");
- }
-
- HELPER_METHOD_FRAME_END();
-
- return result;
-}
-FCIMPLEND
-
-
-
-////////////////////////////////////////////////////////////////////////
-//
-// Call the Win32 GetLocaleInfo() using the specified lcid to retrieve
-// the native digits, probably from the registry override. The return
-// indicates whether the call was successful.
-//
-// Parameters:
-// IN lcid the LCID to make the Win32 call with
-// OUT pOutputStrAry The output managed string array.
-//
-////////////////////////////////////////////////////////////////////////
-BOOL COMNlsInfo::GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF * pOutputStrAry, BOOL useUserOverride)
-{
- CONTRACTL
- {
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- THROWS;
- } CONTRACTL_END;
-
- WCHAR buffer[11];
- int result = 0;
-
- DWORD lcType = LOCALE_SNATIVEDIGITS;
- if(!useUserOverride)
- {
- lcType |= LOCALE_NOUSEROVERRIDE;
- }
- result = NewApis::GetLocaleInfoEx(locale, lcType, buffer, 11);
- // Be very unforgiving and only support strings of size 10 plus the NULL
- if (result == 11)
- {
- // Break up the unmanaged ten-character ZLS into what NFI wants (a managed
- // ten-string array).
- //
- // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
- // an OutOfMemoryException if there's not enough memory.
- //
- PTRARRAYREF DigitArray = (PTRARRAYREF) AllocateObjectArray(10, g_pStringClass);
-
- GCPROTECT_BEGIN(DigitArray);
- for(DWORD i = 0; i < 10; i++) {
- OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer + i, 1);
- DigitArray->SetAt(i, o);
- }
- GCPROTECT_END();
-
- _ASSERTE(pOutputStrAry != NULL);
- *pOutputStrAry = DigitArray;
- }
-
- return (result == 11);
-}
-
-
-////////////////////////////////////////////////////////////////////////
-//
-// Call the Win32 GetLocaleInfoEx() using the specified lcid and LCTYPE.
-// The return value can be INT32 or an allocated managed string object, depending on
-// which version's called.
-//
-// Parameters:
-// OUT pOutputInt32 The output int32 value.
-// OUT pOutputRef The output string value.
-//
-////////////////////////////////////////////////////////////////////////
-BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, INT32* pOutputInt32, BOOL useUserOverride)
-{
- CONTRACTL
- {
- GC_NOTRIGGER;
- MODE_ANY;
- NOTHROW;
- } CONTRACTL_END;
-
- int result = 0;
-
- _ASSERT((lcType & LOCALE_RETURN_NUMBER) != 0);
- if(!useUserOverride)
- {
- lcType |= LOCALE_NOUSEROVERRIDE;
- }
- result = NewApis::GetLocaleInfoEx(localeName, lcType, (LPWSTR)pOutputInt32, sizeof(*pOutputInt32));
-
- return (result != 0);
-}
-
-BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride)
-{
- CONTRACTL
- {
- THROWS; // We can throw since we are allocating managed string.
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- } CONTRACTL_END;
-
- WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
- int result = 0;
-
- _ASSERT((lcType & LOCALE_RETURN_NUMBER) == 0);
- if(!useUserOverride)
- {
- lcType |= LOCALE_NOUSEROVERRIDE;
- }
- result = NewApis::GetLocaleInfoEx(localeName, lcType, buffer, LOCALE_NAME_MAX_LENGTH);
-
- if (result != 0)
- {
- _ASSERTE(pOutputStrRef != NULL);
- *pOutputStrRef = StringObject::NewString(buffer, result - 1);
- }
-
- return (result != 0);
-}
-
-FCIMPL1(Object*, COMNlsInfo::LCIDToLocaleName, LCID lcid)
-{
- FCALL_CONTRACT;
-
- STRINGREF refRetVal = NULL;
-
- // The maximum size for locale name is 85 characters.
- WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
- int result = 0;
-
- HELPER_METHOD_FRAME_BEGIN_RET_0();
-
- // Note that this'll return neutral names (unlike native Vista APIs)
- result = NewApis::LCIDToLocaleName(lcid, localeName, LOCALE_NAME_MAX_LENGTH, 0);
-
- if (result != 0)
- {
- refRetVal = StringObject::NewString(localeName, result - 1);
- }
- else
- {
- refRetVal = StringObject::GetEmptyString();
- }
-
- HELPER_METHOD_FRAME_END();
-
- return OBJECTREFToObject(refRetVal);
-}
-FCIMPLEND
-
-FCIMPL1(INT32, COMNlsInfo::LocaleNameToLCID, StringObject* localeNameUNSAFE)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- } CONTRACTL_END;
-
- INT32 result = 0;
-
- // Dereference our string
- STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
-
- // Note that this'll return neutral names (unlike native Vista APIs)
- result = NewApis::LocaleNameToLCID(localeName->GetBuffer(), 0);
-
- HELPER_METHOD_FRAME_END();
-
- return result;
-}
-FCIMPLEND
-
-////////////////////////////////////////////////////////////////////////
-//
-// Implementation of CultureInfo.nativeGetNumberFormatInfoValues.
-//
-// Retrieve NumberFormatInfo (NFI) properties from windows
-//
-// Parameters:
-// IN/OUT pNumfmtUNSAFE
-// The pointer of the managed NumberFormatInfo passed
-// from the managed side.
-// Note that the native NumberFormatInfo* is defined
-// in COMNumber.h
-// Note:
-// Managed string will be allocated and assign to the string fields in
-// the managed NumberFormatInfo passed in pNumftUNSAFE
-//
-////////////////////////////////////////////////////////////////////////
-
-
-/*
- This is the list of the data members in the managed NumberFormatInfo and their
- corresponding LCTYPE().
-
- Win32 GetLocaleInfo() constants Data members in NumberFormatInfo in the defined order.
- LOCALE_SPOSITIVE // String positiveSign
- LOCALE_SNEGATIVE // String negativeSign
- LOCALE_SDECIMAL // String numberDecimalSeparator
- LOCALE_SGROUPING // String numberGroupSeparator
- LOCALE_SMONGROUPING // String currencyGroupSeparator
- LOCALE_SMONDECIMALSEP // String currencyDecimalSeparator
- LOCALE_SCURRENCY // String currencySymbol
- N/A // String ansiCurrencySymbol
- N/A // String nanSymbol
- N/A // String positiveInfinitySymbol
- N/A // String negativeInfinitySymbol
- N/A // String percentDecimalSeparator
- N/A // String percentGroupSeparator
- N/A // String percentSymbol
- N/A // String perMilleSymbol
-
- N/A // int m_dataItem
-
- LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, // int numberDecimalDigits
- LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, // int currencyDecimalDigits
- LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER, // int currencyPositivePattern
- LOCALE_INEGCURR | LOCALE_RETURN_NUMBER, // int currencyNegativePattern
- LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER, // int numberNegativePattern
- N/A // int percentPositivePattern
- N/A // int percentNegativePattern
- N/A // int percentDecimalDigits
- N/A // bool isReadOnly=false;
- N/A // internal bool m_useUserOverride;
-*/
-FCIMPL3(FC_BOOL_RET, COMNlsInfo::nativeGetNumberFormatInfoValues,
- StringObject* localeNameUNSAFE, NumberFormatInfo* pNumfmtUNSAFE, CLR_BOOL useUserOverride) {
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- } CONTRACTL_END;
-
- BOOL ret = TRUE;
-
- struct _gc
- {
- STRINGREF localeName;
- STRINGREF stringResult;
- NUMFMTREF numfmt;
- PTRARRAYREF tempArray;
- } gc;
-
- // Dereference our string
- gc.localeName = (STRINGREF)localeNameUNSAFE;
- gc.numfmt = (NUMFMTREF) pNumfmtUNSAFE;
- gc.stringResult = NULL;
- gc.tempArray = NULL;
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
- StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
-
- // Calling SString::ConvertToUnicode once
- LPCWSTR pLocaleName = localeNameStackBuffer;
-
- //
- // NOTE: We pass the stringResult allocated in the stack and assign it to the fields
- // in numfmt after calling CallGetLocaleInfo(). The reason for this is that CallGetLocaleInfo()
- // allocates a string object, and it may trigger a GC and cause numfmt to be moved.
- // That's why we use the stringResult allocated in the stack since it will not be moved.
- // After CallGetLocaleInfo(), we know that numfmt will not be moved, and it's safe to assign
- // the stringResult to its field.
- //
-
- // String values
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SPOSITIVESIGN , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sPositive), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
-
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SNEGATIVESIGN , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sNegative), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SDECIMAL , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberDecimal), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_STHOUSAND , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberGroup), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONTHOUSANDSEP , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyGroup), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONDECIMALSEP , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyDecimal), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
- if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SCURRENCY , &gc.stringResult, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrency), gc.stringResult, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
-
-
- // Numeric values
- ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNumberDecimals), useUserOverride);
- _ASSERT(ret == TRUE);
- ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cCurrencyDecimals), useUserOverride);
- _ASSERT(ret == TRUE);
- ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER , &(gc.numfmt->cPosCurrencyFormat), useUserOverride);
- _ASSERT(ret == TRUE);
- ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegCurrencyFormat), useUserOverride);
- _ASSERT(ret == TRUE);
- ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegativeNumberFormat), useUserOverride);
- _ASSERT(ret == TRUE);
- ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, &(gc.numfmt->iDigitSubstitution), useUserOverride);
- _ASSERT(ret == TRUE);
-
- // LOCALE_SNATIVEDIGITS (gc.tempArray of strings)
- if (GetNativeDigitsFromWin32(pLocaleName, &gc.tempArray, useUserOverride)) {
- SetObjectReference((OBJECTREF*)&(gc.numfmt->sNativeDigits), gc.tempArray, NULL);
- }
- else {
- ret = FALSE;
- _ASSERT(FALSE);
- }
-
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(ret);
-}
-FCIMPLEND
-
-
-////////////////////////////////////////////////////////////////////////
-//
-// Culture enumeration helper functions
-//
-////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////
-//
-// Enum values for System.Globalization.CultureTypes
-//
-////////////////////////////////////////////////////////////////////////////
-
-// Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags
-#define CULTURETYPES_NEUTRALCULTURES 0x0001
-
-// Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags
-#define CULTURETYPES_SPECIFICCULTURES 0x0002
-
-// Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures
-#define CULTURETYPES_INSTALLEDWIN32CULTURES 0x0004
-
-// User defined custom culture
-#define CULTURETYPES_USERCUSTOMCULTURE 0x0008
-
-// User defined replacement custom culture.
-#define CULTURETYPES_REPLACEMENTCULTURES 0x0010
-// [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
-// Culture exists in Win32 but not in the Framework. // TODO: All cultures or no cultures?
-#define CULTURETYPES_WINDOWSONLYCULTURES 0x0020
-// [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
-// the language tag match a culture that ships with the .NET framework, effectively all cultures since we get them from windows
-#define CULTURETYPES_FRAMEWORKCULTURES 0x0040
-
-
-const LPCWSTR WHIDBEY_FRAMEWORK_CULTURE_LIST [] =
-{
- W(""),
- W("af"),
- W("af-za"),
- W("ar"),
- W("ar-ae"),
- W("ar-bh"),
- W("ar-dz"),
- W("ar-eg"),
- W("ar-iq"),
- W("ar-jo"),
- W("ar-kw"),
- W("ar-lb"),
- W("ar-ly"),
- W("ar-ma"),
- W("ar-om"),
- W("ar-qa"),
- W("ar-sa"),
- W("ar-sy"),
- W("ar-tn"),
- W("ar-ye"),
- W("az"),
- W("az-cyrl-az"),
- W("az-latn-az"),
- W("be"),
- W("be-by"),
- W("bg"),
- W("bg-bg"),
- W("ca"),
- W("ca-es"),
- W("cs"),
- W("cs-cz"),
- W("da"),
- W("da-dk"),
- W("de"),
- W("de-at"),
- W("de-ch"),
- W("de-de"),
- W("de-li"),
- W("de-lu"),
- W("dv"),
- W("dv-mv"),
- W("el"),
- W("el-gr"),
- W("en"),
- W("en-029"),
- W("en-au"),
- W("en-bz"),
- W("en-ca"),
- W("en-gb"),
- W("en-ie"),
- W("en-jm"),
- W("en-nz"),
- W("en-ph"),
- W("en-tt"),
- W("en-us"),
- W("en-za"),
- W("en-zw"),
- W("es"),
- W("es-ar"),
- W("es-bo"),
- W("es-cl"),
- W("es-co"),
- W("es-cr"),
- W("es-do"),
- W("es-ec"),
- W("es-es"),
- W("es-gt"),
- W("es-hn"),
- W("es-mx"),
- W("es-ni"),
- W("es-pa"),
- W("es-pe"),
- W("es-pr"),
- W("es-py"),
- W("es-sv"),
- W("es-uy"),
- W("es-ve"),
- W("et"),
- W("et-ee"),
- W("eu"),
- W("eu-es"),
- W("fa"),
- W("fa-ir"),
- W("fi"),
- W("fi-fi"),
- W("fo"),
- W("fo-fo"),
- W("fr"),
- W("fr-be"),
- W("fr-ca"),
- W("fr-ch"),
- W("fr-fr"),
- W("fr-lu"),
- W("fr-mc"),
- W("gl"),
- W("gl-es"),
- W("gu"),
- W("gu-in"),
- W("he"),
- W("he-il"),
- W("hi"),
- W("hi-in"),
- W("hr"),
- W("hr-hr"),
- W("hu"),
- W("hu-hu"),
- W("hy"),
- W("hy-am"),
- W("id"),
- W("id-id"),
- W("is"),
- W("is-is"),
- W("it"),
- W("it-ch"),
- W("it-it"),
- W("ja"),
- W("ja-jp"),
- W("ka"),
- W("ka-ge"),
- W("kk"),
- W("kk-kz"),
- W("kn"),
- W("kn-in"),
- W("ko"),
- W("ko-kr"),
- W("kok"),
- W("kok-in"),
- W("ky"),
- W("ky-kg"),
- W("lt"),
- W("lt-lt"),
- W("lv"),
- W("lv-lv"),
- W("mk"),
- W("mk-mk"),
- W("mn"),
- W("mn-mn"),
- W("mr"),
- W("mr-in"),
- W("ms"),
- W("ms-bn"),
- W("ms-my"),
- W("nb-no"),
- W("nl"),
- W("nl-be"),
- W("nl-nl"),
- W("nn-no"),
- W("no"),
- W("pa"),
- W("pa-in"),
- W("pl"),
- W("pl-pl"),
- W("pt"),
- W("pt-br"),
- W("pt-pt"),
- W("ro"),
- W("ro-ro"),
- W("ru"),
- W("ru-ru"),
- W("sa"),
- W("sa-in"),
- W("sk"),
- W("sk-sk"),
- W("sl"),
- W("sl-si"),
- W("sq"),
- W("sq-al"),
- W("sr"),
- W("sr-cyrl-cs"),
- W("sr-latn-cs"),
- W("sv"),
- W("sv-fi"),
- W("sv-se"),
- W("sw"),
- W("sw-ke"),
- W("syr"),
- W("syr-sy"),
- W("ta"),
- W("ta-in"),
- W("te"),
- W("te-in"),
- W("th"),
- W("th-th"),
- W("tr"),
- W("tr-tr"),
- W("tt"),
- W("tt-ru"),
- W("uk"),
- W("uk-ua"),
- W("ur"),
- W("ur-pk"),
- W("uz"),
- W("uz-cyrl-uz"),
- W("uz-latn-uz"),
- W("vi"),
- W("vi-vn"),
- W("zh-chs"),
- W("zh-cht"),
- W("zh-cn"),
- W("zh-hans"),
- W("zh-hant"),
- W("zh-hk"),
- W("zh-mo"),
- W("zh-sg"),
- W("zh-tw")
-};
-#define WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH (sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST) / sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST[0]))
-
-////////////////////////////////////////////////////////////////////////////
-//
-// NlsCompareInvariantNoCase
-//
-// This routine does fast caseless comparison without needing the tables.
-// This helps us do the comparisons we need to load the tables :-)
-//
-// Returns 0 if identical, <0 if pFirst if first string sorts first.
-//
-// This is only intended to help with our locale name comparisons,
-// which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
-// compare as equal.
-//
-// WARNING: [\]^_` will be less than A-Z because we make everything lower
-// case before comparing them.
-//
-// When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
-// When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
-// or when null terminators are reached, whichever happens first (strncmp-like behavior)
-//
-////////////////////////////////////////////////////////////////////////////
-
-int NlsCompareInvariantNoCase(
- LPCWSTR pFirst,
- LPCWSTR pSecond,
- int size,
- BOOL bNullEnd)
-{
- int i=0;
- WCHAR first;
- WCHAR second;
-
- for (;
- size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
- size--, pFirst++, pSecond++)
- {
- // Make them lower case
- if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
- if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
-
- // Get the diff
- i = (first - second);
-
- // Are they the same?
- if (i == 0)
- continue;
-
- // Otherwise the difference. Remember we made A-Z into lower case, so
- // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the
- // characters between A-Z and a-Z in ascii)
- return i;
- }
-
- // When we are here, one of these holds:
- // size == 0
- // or one of the strings has a null terminator
- // or both of the string reaches null terminator
-
- if (bNullEnd || size != 0)
- {
- // If bNullEnd is TRUE, always check for null terminator.
- // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
- // than another (hense the size != 0 check).
-
- // See if one string ended first
- if (*pFirst != 0 || *pSecond != 0)
- {
- // Which one?
- return *pFirst == 0 ? -1 : 1;
- }
- }
-
- // Return our difference (0)
- return i;
-}
-
-BOOL IsWhidbeyFrameworkCulture(__in LPCWSTR lpLocaleString)
-{
- int iBottom = 0;
- int iTop = WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH - 1;
-
- // Do a binary search for our name
- while (iBottom <= iTop)
- {
- int iMiddle = (iBottom + iTop) / 2;
- int result = NlsCompareInvariantNoCase(lpLocaleString, WHIDBEY_FRAMEWORK_CULTURE_LIST[iMiddle], LOCALE_NAME_MAX_LENGTH, TRUE);
- if (result == 0)
- {
- return TRUE;
- }
- if (result < 0)
- {
- // pLocaleName was < pTest
- iTop = iMiddle - 1;
- }
- else
- {
- // pLocaleName was > pTest
- iBottom = iMiddle + 1;
- }
- }
-
- return FALSE;
-}
-
-// Just Check to see if the OS thinks it is a valid locle
-BOOL WINAPI IsOSValidLocaleName(__in LPCWSTR lpLocaleName, bool bIsNeutralLocale)
-{
-#ifndef ENABLE_DOWNLEVEL_FOR_NLS
- return ::IsValidLocaleName(lpLocaleName);
-#else
- BOOL IsWindows7 = NewApis::IsWindows7Platform();
- // if we're < Win7, we didn't know about neutrals or the invariant.
- if (!IsWindows7 && (bIsNeutralLocale || (lpLocaleName[0] == 0)))
- {
- return false;
- }
-
- // Work around the name/lcid thingy (can't just link to ::IsValidLocaleName())
- LCID lcid = NewApis::LocaleNameToLCID(lpLocaleName, 0);
-
- if (IsCustomCultureId(lcid))
- {
- return false;
- }
-
- if (bIsNeutralLocale)
- {
- // In this case, we're running on Windows 7.
- // For neutral locales, use GetLocaleInfoW.
- // If GetLocaleInfoW works, then the OS knows about it.
- return (::GetLocaleInfoW(lcid, LOCALE_ILANGUAGE, NULL, 0) != 0);
- }
-
- // This is not a custom locale.
- // Call IsValidLocale() to check if the LCID is installed.
- // IsValidLocale doesn't work for neutral locales.
- return IsValidLocale(lcid, LCID_INSTALLED);
-#endif
-}
-
-
-////////////////////////////////////////////////////////////////////////////
-//
-// Check the dwFlags, which has the 'attributes' of the locale, and decide
-// if the locale should be included in the enumeration based on
-// the desired CultureTypes.
-//
-////////////////////////////////////////////////////////////////////////////
-
-BOOL ShouldIncludeByCultureType(INT32 cultureTypes, LPCWSTR lpLocaleString, INT32 dwFlags)
-{
-
- if ((cultureTypes & CULTURETYPES_NEUTRALCULTURES) &&
- ((dwFlags & LOCALE_NEUTRALDATA) || (lpLocaleString[0] == 0))) // Invariant culture get enumerated with the neutrals
- {
- return TRUE;
- }
-
- if ((cultureTypes & CULTURETYPES_SPECIFICCULTURES) &&
- ((dwFlags & LOCALE_SPECIFICDATA) && (lpLocaleString[0] != 0))) // Invariant culture does not get enumerated with the specifics
- {
- return TRUE;
- }
-
- if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
- {
- // The user asks for installed Win32 culture, so check
- // if this locale is installed. In W7 and above, when ::IsValidLocaleName()
- // returns true, it means that it is installed.
- // In downlevel (including Vista), we will convert the name to LCID.
- // When the LCID is not a custom locale, we will call ::IsValidLocale(.., LCID_INSTALLED)
- // to verify if the locale is installed.
- // In Vista, we treat custom locale as installed.
- if (IsOSValidLocaleName(lpLocaleString, (dwFlags & LOCALE_NEUTRALDATA) == LOCALE_NEUTRALDATA))
- {
- return TRUE;
- }
- }
-
- if ((cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) &&
- (dwFlags & LOCALE_SUPPLEMENTAL))
- {
- return TRUE;
- }
-
- if ((cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) &&
- (dwFlags & LOCALE_REPLACEMENT))
- {
- return TRUE;
- }
-
- if ((cultureTypes & CULTURETYPES_FRAMEWORKCULTURES) &&
- IsWhidbeyFrameworkCulture(lpLocaleString))
- {
- return TRUE;
- }
-
- //
- // No need to check CULTURETYPES_WINDOWSONLYCULTURES and CULTURETYPES_FRAMEWORKCULTURES
- // since they are deprecated, and they are handled in the managed code before calling
- // nativeEnumCultureNames.
- //
-
- return FALSE;
-}
-
-////////////////////////////////////////////////////////////////////////////
-//
-// Struct to hold context to be used in the callback for
-// EnumLocaleProcessingCallback
-//
-////////////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- PTRARRAYREF pCultureNamesArray;
- INT32 count;
- INT32 cultureTypes;
-} ENUM_LOCALE_DATA;
-
-////////////////////////////////////////////////////////////////////////////
-//
-// Callback for NewApis::EnumSystemLocalesEx to count the number of
-// locales to be enumerated.
-//
-////////////////////////////////////////////////////////////////////////////
-
-BOOL CALLBACK EnumLocaleCountCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
-{
- ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
-
- if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
- {
- (pData->count)++;
- }
- return TRUE;
-}
-
-
-////////////////////////////////////////////////////////////////////////////
-//
-// Callback for NewApis::EnumSystemLocalesEx to add the locale name
-// into the allocated managed string array.
-//
-////////////////////////////////////////////////////////////////////////////
-
-BOOL CALLBACK EnumLocaleProcessingCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
-{
- ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
-
- if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
- {
- GCX_COOP();
-
- GCPROTECT_BEGIN(pData->pCultureNamesArray);
-
- OBJECTREF cultureString = (OBJECTREF) StringObject::NewString(lpLocaleString);
- pData->pCultureNamesArray->SetAt(pData->count, cultureString);
- pData->count++;
-
- GCPROTECT_END();
- }
-
- return TRUE;
-}
-
-
-////////////////////////////////////////////////////////////////////////////
-//
-// Called by CultureData.GetCultures() to enumerate the names of cultures.
-// It first calls NewApis::EnumSystemLocalesEx to count the number of
-// locales to be enumerated. And it will allocate an managed string
-// array with the count. And fill the array with the culture names in
-// the 2nd call to NewAPis::EnumSystemLocalesEx.
-//
-////////////////////////////////////////////////////////////////////////////
-
-
-int QCALLTYPE COMNlsInfo::nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- // Check CultureTypes.WindowsOnlyCultures and CultureTYpes.FrameworkCultures are deprecated and is
- // handled in the managed code side to provide fallback behavior.
- //PRECONDITION((cultureTypes & (CULTURETYPES_WINDOWSONLYCULTURES | CULTURETYPES_FRAMEWORKCULTURES) == 0));
- } CONTRACTL_END;
-
-
- int result;
- DWORD dwFlags = 0;
- PTRARRAYREF cultureNamesArray = NULL;
- ENUM_LOCALE_DATA enumData = { NULL, 0, cultureTypes};
-
- BEGIN_QCALL;
-
- //
- // if CultureTypes.FrameworkCulture is specified we'll enumerate all cultures
- // and filter according to the Whidbey framework culture list (for compatibility)
- //
-
- if (cultureTypes & CULTURETYPES_FRAMEWORKCULTURES)
- {
- dwFlags |= LOCALE_NEUTRALDATA | LOCALE_SPECIFICDATA;
- }
-
- // Map CultureTypes to Windows enumeration values.
- if (cultureTypes & CULTURETYPES_NEUTRALCULTURES)
- {
- dwFlags |= LOCALE_NEUTRALDATA;
- }
-
- if (cultureTypes & CULTURETYPES_SPECIFICCULTURES)
- {
- dwFlags |= LOCALE_SPECIFICDATA;
- }
-
- if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
- {
- // Windows 7 knows about neutrals, whereas Vista and lower don't.
- if (NewApis::IsWindows7Platform())
- {
- dwFlags |= LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA;
- }
- else
- {
- dwFlags |= LOCALE_SPECIFICDATA;
- }
- }
-
- dwFlags |= (cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) ? LOCALE_SUPPLEMENTAL: 0;
-
- // We need special handling for Replacement cultures because Windows does not have a way to enumerate it directly.
- // Replacement locale check will be only used when CultureTypes.SpecificCultures is NOT used.
- dwFlags |= (cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) ? LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA: 0;
-
-
- result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleCountCallback, dwFlags, (LPARAM)&enumData, NULL) == TRUE ? 1 : 0;
-
- if (result)
- {
-
- GCX_COOP();
-
- GCPROTECT_BEGIN(cultureNamesArray);
-
- // Now we need to allocate our culture names string array and populate it
- // Get our array object (will throw, don't have to check it)
- cultureNamesArray = (PTRARRAYREF) AllocateObjectArray(enumData.count, g_pStringClass);
-
- // In the context struct passed to EnumSystemLocalesEx, reset the count and assign the newly allocated string array
- // to hold culture names to be enumerated.
- enumData.count = 0;
- enumData.pCultureNamesArray = cultureNamesArray;
-
- result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleProcessingCallback, dwFlags, (LPARAM)&enumData, NULL);
-
- if (result)
- {
- retStringArray.Set(cultureNamesArray);
- }
- GCPROTECT_END();
- }
- END_QCALL
-
- return result;
-
-}
-
-//
-// InternalCompareString is used in the managed side to handle the synthetic CompareInfo methods (IndexOf, LastIndexOf, IsPrfix, and IsSuffix)
-//
-INT32 QCALLTYPE COMNlsInfo::InternalCompareString(
- INT_PTR handle,
- INT_PTR handleOrigin,
- LPCWSTR localeName,
- LPCWSTR string1, INT32 offset1, INT32 length1,
- LPCWSTR string2, INT32 offset2, INT32 length2,
- INT32 flags)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(string1));
- PRECONDITION(CheckPointer(string2));
- PRECONDITION(CheckPointer(localeName));
- } CONTRACTL_END;
-
- INT32 result = 1;
- BEGIN_QCALL;
-
- {
- result = NewApis::CompareStringEx(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2,NULL,NULL, (LPARAM) handle);
- }
-
- switch (result)
- {
- case CSTR_LESS_THAN:
- result = -1;
- break;
-
- case CSTR_EQUAL:
- result = 0;
- break;
-
- case CSTR_GREATER_THAN:
- result = 1;
- break;
-
- case 0:
- default:
- _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx! This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole.");
- break;
- }
-
- END_QCALL;
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////
-//
-// UseConstantSpaceHashAlgorithm
-// Check for the DWORD "NetFx45_CultureAwareComparerGetHashCode_LongStrings" CLR config option.
-//
-// .Net 4.5 introduces an opt-in algorithm for determining the hash code of strings that
-// uses a constant amount of memory instead of memory proportional to the size of the string
-//
-// A non-zero value will enable the new algorithm:
-//
-// 1) Config file (MyApp.exe.config)
-// <?xml version ="1.0"?>
-// <configuration>
-// <runtime>
-// <NetFx45_CultureAwareComparerGetHashCode_LongStrings enabled="1"/>
-// </runtime>
-// </configuration>
-// 2) Environment variable
-// set NetFx45_CultureAwareComparerGetHashCode_LongStrings=1
-// 3) RegistryKey
-// [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
-// "NetFx45_CultureAwareComparerGetHashCode_LongStrings"=dword:00000001
-//
-////////////////////////////////////////////////////////////////////////////
-BOOL UseConstantSpaceHashAlgorithm()
-{
- static bool configChecked = false;
- static BOOL useConstantSpaceHashAlgorithm = FALSE;
-
- if(!configChecked)
- {
- BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
- useConstantSpaceHashAlgorithm = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NetFx45_CultureAwareComparerGetHashCode_LongStrings) != 0;
- END_SO_INTOLERANT_CODE;
-
- configChecked = true;
- }
- return useConstantSpaceHashAlgorithm;
-}
-
-
-
-////////////////////////////////////////////////////////////////////////////
-//
-// InternalGetGlobalizedHashCode
-//
-////////////////////////////////////////////////////////////////////////////
-INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(localeName));
- PRECONDITION(CheckPointer(string, NULL_OK));
- } CONTRACTL_END;
-
- INT32 iReturnHash = 0;
- BEGIN_QCALL;
-
- int byteCount = 0;
-
- //
- // Make sure there is a string.
- //
- if (!string) {
- COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String"));
- }
-
- DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn);
-
- //
- // Caller has already verified that the string is not of zero length
- //
- // Assert if we might hit an AV in LCMapStringEx for the invariant culture.
- _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0);
- {
- byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
- }
-
- //A count of 0 indicates that we either had an error or had a zero length string originally.
- if (byteCount==0)
- {
- COMPlusThrow(kArgumentException, W("Arg_MustBeString"));
- }
-
- // We used to use a NewArrayHolder here, but it turns out that hurts our large # process
- // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack
- // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case
- {
- CQuickBytesSpecifySize<MAX_STRING_VALUE * sizeof(WCHAR)> qbBuffer;
- BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount);
-
- {
- NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle);
- }
-
- iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, true, additionalEntropy);
- }
- END_QCALL;
- return(iReturnHash);
-}
-
-inline BOOL IsInvariantLocale(STRINGREF localeName)
-{
- return localeName->GetStringLength() == 0;
-}
-
-// InternalChangeCaseChar
-//
-// Call LCMapStringEx with a char to make it upper or lower case
-// Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
-FCIMPL5(FC_CHAR_RET, COMNlsInfo::InternalChangeCaseChar,
- INT_PTR handle, // optional sort handle
- INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- } CONTRACTL_END;
-
- CLR_CHAR retVal = '\0';
- int ret_LCMapStringEx = -1;
-
- // Dereference our string
- STRINGREF localeName(localeNameUNSAFE);
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
-
- BOOL isInvariantLocale = IsInvariantLocale(localeName);
- // Check for Invariant to avoid A/V in LCMapStringEx
- DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
-
- {
- ret_LCMapStringEx = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
- bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
- LCMAP_LOWERCASE | linguisticCasing,
- &wch,
- 1,
- &retVal,
- 1,
- NULL,
- NULL,
- (LPARAM) handle);
- }
-
- if (0 == ret_LCMapStringEx)
- {
- // return value of 0 indicates failure and error value is supposed to be set.
- // shouldn't ever really happen
- _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseChar! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
- }
-
- HELPER_METHOD_FRAME_END(); // localeName is now unprotected
- return retVal;
-}
-FCIMPLEND
-
-// InternalChangeCaseString
-//
-// Call LCMapStringEx with a string to make it upper or lower case
-// Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
-//
-// We typically expect the output string to be the same size as the input. If not
-// we have to count, reallocate the output buffer, and try again.
-FCIMPL5(Object*, COMNlsInfo::InternalChangeCaseString,
- INT_PTR handle, // optional sort handle
- INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pStringUNSAFE, CLR_BOOL bIsToUpper)
-{
- CONTRACTL
- {
- FCALL_CHECK;
- PRECONDITION(CheckPointer(pStringUNSAFE));
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- } CONTRACTL_END;
-
- struct _gc
- {
- STRINGREF pResult;
- STRINGREF pString;
- STRINGREF pLocale;
- } gc;
-
- gc.pResult = NULL;
- gc.pString = ObjectToSTRINGREF(pStringUNSAFE);
- gc.pLocale = ObjectToSTRINGREF(localeNameUNSAFE);
-
- HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc)
-
- //
- // Get the length of the string.
- //
- int nLengthInput = gc.pString->GetStringLength();
- int nLengthOutput = nLengthInput; // initially we assume the string length does not change.
-
- BOOL isInvariantLocale = IsInvariantLocale(gc.pLocale);
- // Check for Invariant to avoid A/V in LCMapStringEx
- DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
- // Check for Invariant to avoid A/V in LCMapStringEx
-
- //
- // Check if we have the empty string.
- //
- if (nLengthInput == 0)
- {
- gc.pResult = ObjectToSTRINGREF(gc.pString);
- }
- else
- {
- //
- // Create the result string.
- //
- gc.pResult = StringObject::NewString(nLengthOutput);
- LPWSTR pResultStr = gc.pResult->GetBuffer();
-
- int result;
- {
- result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
- bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
- LCMAP_LOWERCASE | linguisticCasing,
- gc.pString->GetBuffer(),
- nLengthInput,
- pResultStr,
- nLengthOutput,
- NULL,
- NULL,
- (LPARAM) handle);
- }
-
- if(0 == result)
- {
- // Failure: Detect if that's due to insufficient buffer
- if (GetLastError()!= ERROR_INSUFFICIENT_BUFFER)
- {
- ThrowLastError();
- }
- // need to update buffer
- {
- nLengthOutput = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
- bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
- LCMAP_LOWERCASE | linguisticCasing,
- gc.pString->GetBuffer(),
- nLengthInput,
- NULL,
- 0,
- NULL,
- NULL,
- (LPARAM) handle);
- }
- if (nLengthOutput == 0)
- {
- // return value of 0 indicates failure and error value is supposed to be set.
- // shouldn't ever really happen
- _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
- }
- _ASSERTE(nLengthOutput > 0);
- // NOTE: The length of the required buffer does not include the terminating null character.
- // So it can be used as-is for our calculations -- the length we pass in to NewString also does
- // not include the terminating null character.
- // MSDN documentation could be interpreted to mean that the length returned includes the terminating
- // NULL character, but that's not the case.
-
- // NOTE: Also note that we let the GC take care of the previously allocated pResult.
-
- gc.pResult = StringObject::NewString(nLengthOutput);
- pResultStr = gc.pResult->GetBuffer();
- {
- result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
- bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
- LCMAP_LOWERCASE | linguisticCasing,
- gc.pString->GetBuffer(),
- nLengthInput,
- pResultStr,
- nLengthOutput,
- NULL,
- NULL,
- (LPARAM) handle);
- }
-
- if(0 == result)
- {
- // return value of 0 indicates failure and error value is supposed to be set.
- // shouldn't ever really happen
- _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
- }
- }
-
- pResultStr[nLengthOutput] = 0;
- }
-
- HELPER_METHOD_FRAME_END();
+// Language ID for CHT (Taiwan)
+#define LANGID_ZH_TW 0x0404
+// Language ID for CHT (Hong-Kong)
+#define LANGID_ZH_HK 0x0c04
- return OBJECTREFToObject(gc.pResult);
-}
-FCIMPLEND
+#ifndef FEATURE_COREFX_GLOBALIZATION
+//
+// Normalization Implementation
+//
+#define NORMALIZATION_DLL MAKEDLLNAME(W("normalization"))
+HMODULE COMNlsInfo::m_hNormalization = NULL;
+PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL;
+PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL;
+PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL;
+#endif // FEATURE_COREFX_GLOBALIZATION
-/*================================InternalGetCaseInsHash================================
-**Action:
-**Returns:
-**Arguments:
-**Exceptions:
-==============================================================================*/
-FCIMPL6(INT32, COMNlsInfo::InternalGetCaseInsHash,
- INT_PTR handle, // optional sort handle
- INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID pvStrA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy)
+INT32 COMNlsInfo::CallGetUserDefaultUILanguage()
{
CONTRACTL
{
- FCALL_CHECK;
- PRECONDITION(CheckPointer(localeNameUNSAFE));
- PRECONDITION(CheckPointer(pvStrA));
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
} CONTRACTL_END;
- STRINGREF localeName = ObjectToSTRINGREF(localeNameUNSAFE);
- STRINGREF strA;
-
- INT32 result;
-
- BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException))
-
- *((LPVOID *)&strA)=pvStrA;
-
+ static INT32 s_lcid = 0;
- //
- // If we know that we don't have any high characters (the common case) we can
- // call a hash function that knows how to do a very fast case conversion. If
- // we find characters above 0x80, it's much faster to convert the entire string
- // to Uppercase and then call the standard hash function on it.
- //
- // TODO: NLS Arrowhead -We aren't consistent with the fast casing cultures (any en? fr? de?)
- if (IsCultureEnglishOrInvariant((localeName)->GetBuffer()) && // If we're en-US or Invariant
- ((IS_STRING_STATE_UNDETERMINED(strA->GetHighCharState()) && // and we're undetermined
- IS_FAST_CASING(strA->InternalCheckHighChars())) || // and its fast casing when determined
- IS_FAST_CASING(strA->GetHighCharState()))) // or we're fast casing that's already determined
- {
- // Notice that for Turkish and Azeri we don't get here and shouldn't use this
- // fast path because of their special Latin casing rules.
- result = COMNlsHashProvider::s_NlsHashProvider.HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
- }
- else
+ // The user UI language cannot change within a process (in fact it cannot change within a logon session),
+ // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are
+ // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call
+ // GetUserDefaultUILanguage needlessly, but the final result is going to be the same.
+ if (s_lcid == 0)
{
- // Make it upper case
- CQuickBytes newBuffer;
- INT32 length = strA->GetStringLength();
- WCHAR *pNewStr = (WCHAR *)newBuffer.AllocThrows((length + 1) * sizeof(WCHAR));
-
- // Work around an A/V in LCMapStringEx for the invariant culture.
- // Revisit this after Vista SP2 has been deployed everywhere.
- DWORD linguisticCasing = 0;
- if (localeName->GetStringLength() > 0) // if not the invariant culture...
- {
- linguisticCasing = LCMAP_LINGUISTIC_CASING;
- }
-
- int lcmapResult;
- {
- lcmapResult = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
- LCMAP_UPPERCASE | linguisticCasing,
- strA->GetBuffer(),
- length,
- pNewStr,
- length,
- NULL, NULL, (LPARAM) handle);
- }
-
- if (lcmapResult == 0)
+ INT32 s_lcidTemp = GetUserDefaultUILanguage();
+ if (s_lcidTemp == LANGID_ZH_TW)
{
- // return value of 0 indicates failure and error value is supposed to be set.
- // shouldn't ever really happen
- _ASSERTE(!"catastrophic failure calling NewApis::InternalGetCaseInsHash! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
+ // If the UI language ID is 0x0404, we need to do extra check to decide
+ // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID.
+ if (!NewApis::IsZhTwSku())
+ {
+ s_lcidTemp = LANGID_ZH_HK;
+ }
}
- pNewStr[length]='\0';
-
- // Get hash for the upper case of the new string
-
- result = COMNlsHashProvider::s_NlsHashProvider.HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
+ s_lcid = s_lcidTemp;
}
- END_SO_INTOLERANT_CODE
-
- return result;
+ return s_lcid;
}
-FCIMPLEND
-// Fast path for finding a String using OrdinalIgnoreCase rules
-// returns true if the fast path succeeded, with foundIndex set to the location where the String was found or -1
-// Returns false when FindStringOrdinal isn't handled (we don't have our own general version of this function to fall back on)
-// Note for future optimizations: kernel32!FindStringOrdinal(ignoreCase=TRUE) uses per-character table lookup
-// to map to upper case before comparison, but isn't otherwise optimized
-BOOL QCALLTYPE COMNlsInfo::InternalTryFindStringOrdinalIgnoreCase(
- __in DWORD dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH
- __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
- __in int cchSource, // number of characters lpStringSource after sourceIndex
- __in int sourceIndex, // index from where the search will start in lpStringSource
- __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
- __in int cchValue,
- __out int* foundIndex) // the index in lpStringSource where we found lpStringValue
+////////////////////////////////////////////////////////////////////////////
+//
+// InternalGetGlobalizedHashCode
+//
+////////////////////////////////////////////////////////////////////////////
+INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy)
{
CONTRACTL
{
QCALL_CHECK;
- PRECONDITION(lpStringSource != NULL);
- PRECONDITION(lpStringValue != NULL);
- PRECONDITION(cchSource>=0);
- PRECONDITION(cchValue>=0);
- PRECONDITION((dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION) == 0);
+ PRECONDITION(CheckPointer(localeName));
+ PRECONDITION(CheckPointer(string, NULL_OK));
} CONTRACTL_END;
- BOOL result = FALSE;
-
+ INT32 iReturnHash = 0;
BEGIN_QCALL;
- LPCWSTR lpSearchStart = NULL;
- if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
- {
- lpSearchStart = &lpStringSource[sourceIndex - cchSource + 1];
- }
- else {
- lpSearchStart = &lpStringSource[sourceIndex];
- }
- {
-#ifndef FEATURE_CORESYSTEM
- // kernel function pointer
- typedef int (WINAPI *PFNFindStringOrdinal)(DWORD, LPCWSTR, INT, LPCWSTR, INT, BOOL);
- static PFNFindStringOrdinal FindStringOrdinal = NULL;
-
- // initizalize kernel32!FindStringOrdinal
- if (FindStringOrdinal == NULL)
- {
- PFNFindStringOrdinal result = NULL;
-
- HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
- if(hMod != NULL)
- result=(PFNFindStringOrdinal)GetProcAddress(hMod,"FindStringOrdinal");
-
- FindStringOrdinal = (result != NULL) ? result : (PFNFindStringOrdinal)-1;
- }
+ int byteCount = 0;
- // call into the kernel
- if (FindStringOrdinal != (PFNFindStringOrdinal)-1)
-#endif
- {
- *foundIndex = FindStringOrdinal(
- dwFindNLSStringFlags,
- lpSearchStart,
- cchSource,
- lpStringValue,
- cchValue,
- TRUE);
- result = TRUE;
- }
- }
- // if we found the pattern string, fixup the index before we return
- if (*foundIndex >= 0)
- {
- if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
- *foundIndex += (sourceIndex - cchSource + 1);
- else
- *foundIndex += sourceIndex;
+ //
+ // Make sure there is a string.
+ //
+ if (!string) {
+ COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String"));
}
- END_QCALL;
-
- return result;
-}
-
-
-// InternalCompareStringOrdinalIgnoreCase
-//
-// Call ::CompareStringOrdinal for native ordinal behavior
-INT32 QCALLTYPE COMNlsInfo::InternalCompareStringOrdinalIgnoreCase(
- LPCWSTR string1, INT32 index1,
- LPCWSTR string2, INT32 index2,
- INT32 length1,
- INT32 length2)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(string1));
- PRECONDITION(CheckPointer(string2));
- } CONTRACTL_END;
- INT32 result = 0;
+ DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn);
- BEGIN_QCALL;
//
- // Get the arguments.
- // We assume the caller checked them before calling us
+ // Caller has already verified that the string is not of zero length
//
-
- // We don't allow the -1 that native code allows
- _ASSERT(length1 >= 0);
- _ASSERT(length2 >= 0);
-
- // Do the comparison
+ // Assert if we might hit an AV in LCMapStringEx for the invariant culture.
+ _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0);
{
- result = NewApis::CompareStringOrdinal(string1 + index1, length1, string2 + index2, length2, TRUE);
+ byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
}
- // The native call shouldn't fail
- _ASSERT(result != 0);
- if (result == 0)
+ //A count of 0 indicates that we either had an error or had a zero length string originally.
+ if (byteCount==0)
{
- // return value of 0 indicates failure and error value is supposed to be set.
- // shouldn't ever really happen
- _ASSERTE(!"catastrophic failure calling NewApis::CompareStringOrdinal! This is usually due to bad arguments.");
+ COMPlusThrow(kArgumentException, W("Arg_MustBeString"));
}
- // Adjust the result to the expected -1, 0, 1 result
- result -= 2;
+ // We used to use a NewArrayHolder here, but it turns out that hurts our large # process
+ // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack
+ // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case
+ {
+ CQuickBytesSpecifySize<MAX_STRING_VALUE * sizeof(WCHAR)> qbBuffer;
+ BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount);
+
+ {
+ NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle);
+ }
+ iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, true, additionalEntropy);
+ }
END_QCALL;
-
- return result;
+ return(iReturnHash);
}
/**
}
FCIMPLEND
-
#ifndef FEATURE_COREFX_GLOBALIZATION
//
// Normalization
#endif // FEATURE_COREFX_GLOBALIZATION
-
-//
-// This table should be sorted using case-insensitive ordinal order.
-// In the managed code, String.CompareStringOrdinalWC() is used to sort this.
-//
-
-
/**
* This function returns the number of items in EncodingDataTable.
*/
return (m_nEncodingDataTableItems);
}
FCIMPLEND
-
-// Return true if we're on Windows 7 (ie: if we have neutral native support)
-BOOL COMNlsInfo::IsWindows7()
-{
- static BOOL bChecked=FALSE;
- static BOOL bIsWindows7=FALSE;
-
- if (!bChecked)
- {
- // LOCALE_INEUTRAL is first supported on Windows 7
- if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEUTRAL, NULL, 0) != 0)
- {
- // Success, we're win7
- bIsWindows7 = TRUE;
- }
-
- // Either way we checked now
- bChecked = TRUE;
- }
-
- return bIsWindows7;
-}
-
-//
-// QCall implementation
-//
-int QCALLTYPE COMNlsInfo::InternalFindNLSStringEx(
- __in_opt INT_PTR handle, // optional sort handle
- __in_opt INT_PTR handleOrigin,
- __in_z LPCWSTR lpLocaleName, // locale name
- __in int dwFindNLSStringFlags, // search falg
- __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
- __in int cchSource, // number of characters lpStringSource after sourceIndex
- __in int sourceIndex, // index from where the search will start in lpStringSource
- __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
- __in int cchValue, // length of the string we search for
- __out_opt LPINT pcchFound) // length of the string we found in source
-{
- CONTRACTL {
- QCALL_CHECK;
- PRECONDITION(lpLocaleName != NULL);
- PRECONDITION(lpStringSource != NULL);
- PRECONDITION(lpStringValue != NULL);
- PRECONDITION(cchSource>=0);
- PRECONDITION(cchValue>=0);
- } CONTRACTL_END;
-
- int retValue = -1;
-
- BEGIN_QCALL;
-
-
- #define RESERVED_FIND_ASCII_STRING 0x20000000 // This flag used only to tell the sorting DLL can assume the string characters are in ASCII.
-
-
- dwFindNLSStringFlags &= ~RESERVED_FIND_ASCII_STRING;
-
- if (cchValue == 0)
- {
- retValue = sourceIndex; // keep Whidbey compatibility
- goto lExit;
- }
-
- if (sourceIndex<0 || cchSource<0 ||
- ((dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) && (sourceIndex+1<cchSource)))
- {
- goto lExit;
- }
-
- if (dwFindNLSStringFlags & COMPARE_OPTIONS_ORDINAL)
- {
- if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
- {
- retValue = NewApis::LastIndexOfString(
- lpLocaleName,
- &lpStringSource[sourceIndex - cchSource + 1],
- cchSource,
- lpStringValue,
- cchValue,
- dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
- dwFindNLSStringFlags & FIND_ENDSWITH,
- pcchFound);
- if (retValue >= 0)
- {
- retValue += sourceIndex - cchSource + 1;
- }
- }
- else
- {
- retValue = NewApis::IndexOfString(
- lpLocaleName,
- &lpStringSource[sourceIndex],
- cchSource,
- lpStringValue,
- cchValue,
- dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
- dwFindNLSStringFlags & FIND_STARTSWITH,
- pcchFound);
-
- if (retValue >= 0)
- {
- retValue += sourceIndex;
- }
- }
- }
- else
- {
- if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
- {
- {
- retValue = NewApis::FindNLSStringEx(
- handle != NULL ? NULL : lpLocaleName,
- dwFindNLSStringFlags,
- &lpStringSource[sourceIndex - cchSource + 1],
- cchSource,
- lpStringValue,
- cchValue,
- pcchFound,
- NULL, NULL, (LPARAM) handle);
- }
-
- if (retValue >= 0)
- {
- retValue += sourceIndex - cchSource + 1;
- }
- }
- else
- {
- {
- retValue = NewApis::FindNLSStringEx(
- handle != NULL ? NULL : lpLocaleName,
- dwFindNLSStringFlags,
- &lpStringSource[sourceIndex],
- cchSource,
- lpStringValue,
- cchValue,
- pcchFound,
- NULL, NULL, (LPARAM) handle);
- }
-
- if (retValue >= 0)
- {
- retValue += sourceIndex;
- }
- }
- }
-
-lExit:
-
- END_QCALL;
-
- return retValue;
-}
-
-
-int QCALLTYPE COMNlsInfo::InternalGetSortKey(
- __in_opt INT_PTR handle, // PSORTHANDLE
- __in_opt INT_PTR handleOrigin,
- __in_z LPCWSTR pLocaleName, // locale name
- __in int flags, // flags
- __in_ecount(cchSource) LPCWSTR pStringSource, // Source string
- __in int cchSource, // number of characters in lpStringSource
- __in_ecount(cchTarget) PBYTE pTarget, // Target data buffer (may be null to count)
- __in int cchTarget) // Character count for target buffer
-{
- CONTRACTL {
- QCALL_CHECK;
- PRECONDITION(pLocaleName != NULL);
- PRECONDITION(pStringSource != NULL);
-// PRECONDITION(pTarget != NULL);
- PRECONDITION(cchSource>=0);
-// PRECONDITION(cchTarget>=0);
- } CONTRACTL_END;
-
- int retValue = 0;
-
- BEGIN_QCALL;
-
-
- {
- // Just call NewApis::LCMapStringEx to do our work
- retValue = NewApis::LCMapStringEx(handle != NULL ? NULL : pLocaleName,
- flags | LCMAP_SORTKEY,
- pStringSource,
- cchSource,
- (LPWSTR)pTarget,
- cchTarget,
- NULL,
- NULL,
- (LPARAM) handle);
- }
- END_QCALL;
-
- return retValue;
-}
-
-
-// We allow InternalInitSortHandle to return a NULL value
-// this is the case for Silverlight or when the appdomain has custom sorting.
-// So all the methods that take a SortHandle, also have to
-// be able to just call the slower api that looks up the tables based on the locale name
-INT_PTR QCALLTYPE COMNlsInfo::InternalInitSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
-{
- CONTRACTL {
- QCALL_CHECK;
- PRECONDITION(localeName != NULL);
- } CONTRACTL_END;
-
- INT_PTR pSort = NULL;
-
- BEGIN_QCALL;
-
- pSort = InitSortHandleHelper(localeName, handleOrigin);
-
- END_QCALL;
-
- return pSort;
-}
-
-INT_PTR COMNlsInfo::InitSortHandleHelper(LPCWSTR localeName, __out INT_PTR* handleOrigin)
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- CANNOT_TAKE_LOCK;
- PRECONDITION(localeName != NULL);
- } CONTRACTL_END;
-
- INT_PTR pSort = NULL;
-
- // coreclr will try to initialize the handle and if running on downlevel it'll just return null
- pSort = InternalInitOsSortHandle(localeName, handleOrigin);
- return pSort;
-}
-
-
-// Can return NULL
-INT_PTR COMNlsInfo::InternalInitOsSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- CANNOT_TAKE_LOCK;
- PRECONDITION(localeName != NULL);
- } CONTRACTL_END;
-
- INT_PTR pSort = NULL;
-
- AppDomain* curDomain = GetAppDomain();
-
- if (RunningOnWin8())
- {
- LPARAM lSortHandle;
- int ret;
-
- {
- ret = NewApis::LCMapStringEx(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
- *handleOrigin = (INT_PTR) NewApis::LCMapStringEx;
- }
-
- if (ret != 0)
- {
- pSort = (INT_PTR) lSortHandle;
- }
- }
-
- return pSort;
-}
-
-BOOL QCALLTYPE COMNlsInfo::InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation)
-{
- CONTRACTL {
- QCALL_CHECK;
- } CONTRACTL_END;
-
- BOOL ret = FALSE;
-
- BEGIN_QCALL;
- ret = GetNLSVersionEx(COMPARE_STRING, lpLocaleName, lpVersionInformation);
- END_QCALL;
-
- return ret;
-}
-