1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 ////////////////////////////////////////////////////////////////////////////
10 // Purpose: This module implements the methods of the COMNlsInfo
11 // class. These methods are the helper functions for the
14 // Date: August 12, 1998
16 ////////////////////////////////////////////////////////////////////////////
25 #include "interoputil.h"
39 #include "sortversioning.h"
44 // Constant Declarations.
47 #ifndef COMPARE_OPTIONS_IGNORECASE
48 #define COMPARE_OPTIONS_IGNORECASE 0x00000001
52 #define LOCALE_SNAME 0x0000005c
56 #define LOCALE_SNAN 0x00000069
59 #ifndef LOCALE_SPOSINFINITY
60 #define LOCALE_SPOSINFINITY 0x0000006a
63 #ifndef LOCALE_SNEGINFINITY
64 #define LOCALE_SNEGINFINITY 0x0000006b
67 #ifndef LOCALE_SPARENT
68 #define LOCALE_SPARENT 0x0000006d
71 #ifndef LOCALE_SCONSOLEFALLBACKNAME
72 #define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e // Fallback name for within the console
75 #ifndef LOCALE_SISO3166CTRYNAME2
76 #define LOCALE_SISO3166CTRYNAME2 0x00000068
79 #ifndef LOCALE_SISO639LANGNAME2
80 #define LOCALE_SISO639LANGNAME2 0x00000067
83 #ifndef LOCALE_SSHORTESTDAYNAME1
84 #define LOCALE_SSHORTESTDAYNAME1 0x00000060
88 #ifndef LOCALE_INEUTRAL
89 #define LOCALE_INEUTRAL 0x00000071 // Returns 0 for specific cultures, 1 for neutral cultures.
92 #ifndef LCMAP_TITLECASE
93 #define LCMAP_TITLECASE 0x00000300 // Title Case Letters
97 #ifndef LCMAP_SORTHANDLE
98 #define LCMAP_SORTHANDLE 0x20000000
102 #define LCMAP_HASH 0x00040000
105 #ifndef LOCALE_REPLACEMENT
106 #define LOCALE_REPLACEMENT 0x00000008 // locales that replace shipped locales (callback flag only)
107 #endif // LOCALE_REPLACEMENT
109 #define LOCALE_MAX_STRING_SIZE 530 // maximum sice of LOCALE_SKEYBOARDSTOINSTALL, currently 5 "long" + 2 "short" keyboard signatures (YI + 3).
111 #define MAX_STRING_VALUE 512
113 // TODO: NLS Arrowhead -Be nice if we could depend more on the OS for this
114 // Language ID for CHT (Taiwan)
115 #define LANGID_ZH_TW 0x0404
116 // Language ID for CHT (Hong-Kong)
117 #define LANGID_ZH_HK 0x0c04
118 #define REGION_NAME_0404 W("\x53f0\x7063")
120 #define INTERNATIONAL_CURRENCY_SYMBOL W("\x00a4")
122 #define INTERNATIONAL_CURRENCY_SYMBOL W("\xa400")
125 inline BOOL IsCustomCultureId(LCID lcid)
127 return (lcid == LOCALE_CUSTOM_DEFAULT || lcid == LOCALE_CUSTOM_UNSPECIFIED);
130 #ifndef FEATURE_CORECLR
132 // Normalization Implementation
134 #define NORMALIZATION_DLL MAKEDLLNAME(W("normalization"))
135 HMODULE COMNlsInfo::m_hNormalization = NULL;
136 PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL;
137 PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL;
138 PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL;
141 #if FEATURE_CODEPAGES_FILE
142 /*============================nativeCreateOpenFileMapping============================
143 **Action: Create or open a named memory file mapping.
144 **Returns: Pointer to named section, or NULL if failed
146 ** StringObject* inSectionName - name of section to open/create
147 ** int inBytesToAllocate - desired size of memory section in bytes
148 ** We use the last 4 bytes (must be aligned, so only choose
149 ** inBytesToAllocate in multiples of 4) to indicate if the
150 ** section is set or not. AFTER section is initialized, set
151 ** those 4 bytes to non-0, otherwise you'll get get new
152 ** heap memory all the time.
153 ** HANDLE* mappedFile - is the handle of the memory mapped file. this is
156 ** NOTE: We'll try to open the same object, so we can share names. We don't lock
157 ** though, so 2 thread could get the same object, but thread 1 might not
158 ** have initialized it yet.
160 ** NOTE: For NT you should add a Global\ to the beginning of the name if you
161 ** want to share it machine wide.
163 ==============================================================================*/
164 FCIMPL3(LPVOID, COMNlsInfo::nativeCreateOpenFileMapping,
165 StringObject* inSectionNameUNSAFE, int inBytesToAllocate, HANDLE *mappedFile)
170 PRECONDITION(CheckPointer(inSectionNameUNSAFE));
171 PRECONDITION(inBytesToAllocate % 4 == 0);
172 PRECONDITION(inBytesToAllocate > 0);
173 PRECONDITION(CheckPointer(mappedFile));
176 // Need a place for our result
177 LPVOID pResult = NULL;
179 STRINGREF inString(inSectionNameUNSAFE);
180 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(inString);
182 _ASSERTE(inBytesToAllocate % 4 == 0); // Expected 4 bytes boundaries so we don't get unaligned
183 _ASSERTE(inBytesToAllocate > 0); // Pointless to have <=0 allocation
185 StackSString inNameStackBuffer (inString->GetBuffer());
186 pResult = NLSTable::OpenOrCreateMemoryMapping((LPCWSTR)inNameStackBuffer, inBytesToAllocate, mappedFile);
188 // Worst case allocate some memory, use holder
189 // if (pResult == NULL) pResult = new BYTE[inBytesToAllocate];
192 // Need to use a NewHolder
193 NewArrayHolder<BYTE> holder (new BYTE[inBytesToAllocate]);
195 // Zero out the mapCodePageCached field (an int value, and it's used to check if the section is initialized or not.)
196 BYTE* pByte = (BYTE*)pResult;
197 FillMemory(pByte + inBytesToAllocate - sizeof(int), sizeof(int), 0);
198 holder.SuppressRelease();
201 HELPER_METHOD_FRAME_END();
206 #endif // FEATURE_CODEPAGES_FILE
208 // InternalIsSortable
210 // Called by CompareInfo.IsSortable() to determine if a string has entirely sortable (ie: defined) code points.
211 BOOL QCALLTYPE COMNlsInfo::InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length)
216 PRECONDITION(CheckPointer(string));
221 #ifndef FEATURE_CORECLR
222 AppDomain* curDomain = GetAppDomain();
224 if(!(curDomain->m_bUseOsSorting))
226 handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
227 result = SortVersioning::SortDllIsDefinedString((SortVersioning::PSORTHANDLE) handle, COMPARE_STRING, 0, string, length);
229 else if(curDomain->m_pCustomSortLibrary != NULL)
231 result = (curDomain->m_pCustomSortLibrary->pIsNLSDefinedString)(COMPARE_STRING, 0, NULL, string, length);
236 // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now
237 result = NewApis::IsNLSDefinedString(COMPARE_STRING, 0, NULL, string, length);
244 ////////////////////////////////////////////////////////////////////////////
246 // InternalGetUserDefaultLocaleName
248 // Returns a string with the name of our LCID and returns 0 in LCID.
251 ////////////////////////////////////////////////////////////////////////////
252 // This is new to longhorn
253 BOOL QCALLTYPE COMNlsInfo::InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName)
258 PRECONDITION((langType == LOCALE_SYSTEM_DEFAULT) || (langType == LOCALE_USER_DEFAULT));
264 WCHAR strName[LOCALE_NAME_MAX_LENGTH];
267 if (langType == LOCALE_SYSTEM_DEFAULT)
269 size = NewApis::GetSystemDefaultLocaleName(strName,NumItems(strName));
273 _ASSERT(langType == LOCALE_USER_DEFAULT);
274 size = NewApis::GetUserDefaultLocaleName(strName,NumItems(strName));
277 // Not found, either not longhorn (no LOCALE_SNAME) or not a valid name
284 defaultLocaleName.Set(strName);
291 BOOL QCALLTYPE COMNlsInfo::InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage)
297 WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
299 int systemDefaultUiLcid = GetSystemDefaultUILanguage();
300 if(systemDefaultUiLcid == LANGID_ZH_TW)
302 if (!NewApis::IsZhTwSku())
304 systemDefaultUiLcid = LANGID_ZH_HK;
308 int length = NewApis::LCIDToLocaleName(systemDefaultUiLcid, localeName, NumItems(localeName), 0);
315 systemDefaultUiLanguage.Set(localeName);
325 BOOL QCALLTYPE COMNlsInfo::InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage)
331 WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH];
332 LPCWSTR wszLangName=NULL;
337 res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,NULL,&uBufLen);
342 NewArrayHolder<WCHAR> sPreferredLanguages(NULL);
344 if (uBufLen > 0 && uLangCount > 0 )
346 sPreferredLanguages = new WCHAR[uBufLen];
347 res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,sPreferredLanguages,&uBufLen);
352 wszLangName=sPreferredLanguages;
353 // Review size_t to int conversion (possible loss of data).
355 #pragma warning(push)
356 #pragma warning(disable:4267)
358 res=wcslen(wszLangName)+1;
369 res = NewApis::GetUserDefaultLocaleName(wszBuffer,NumItems(wszBuffer));
370 wszLangName=wszBuffer;
374 // If not found, either not longhorn (no LOCALE_SNAME) or not a valid name
377 // Didn't find string, return an empty string.
382 userDefaultUiLanguage.Set(wszLangName);
386 // Return the found language name. LCID should be found one already.
391 // Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
392 #ifdef FEATURE_CORECLR
393 FCIMPL0(Object*, COMNlsInfo::nativeGetResourceFallbackArray)
400 DWORD dwFlags = MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
401 ULONG cchLanguagesBuffer = 0;
402 ULONG ulNumLanguages = 0;
407 PTRARRAYREF resourceFallbackArray;
410 gc.resourceFallbackArray = NULL;
412 // If the resource lookups we're planning on doing are going to be written to a non-Unicode console,
413 // then we should ideally only return languages that can be displayed correctly on the console. The
414 // trick is guessing at whether we're writing this data to the console, which we can't do well.
415 // Instead, we ask new apps to call GetConsoleFallbackUICulture & fall back to en-US.
416 bool disableUserFallback;
417 BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
418 disableUserFallback = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Resources_DisableUserPreferredFallback) == 1
419 END_SO_INTOLERANT_CODE;
421 if (disableUserFallback)
424 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
426 // first call with null buffer to get size
427 result = NewApis::GetThreadPreferredUILanguages(dwFlags, &ulNumLanguages, NULL, &cchLanguagesBuffer);
428 if (cchLanguagesBuffer > 0)
430 NewArrayHolder<WCHAR> stringBuffer = new (nothrow) WCHAR[cchLanguagesBuffer];
431 if (stringBuffer != NULL)
433 result = NewApis::GetThreadPreferredUILanguages(dwFlags, &ulNumLanguages, stringBuffer, &cchLanguagesBuffer);
436 // now string into strings
437 gc.resourceFallbackArray = (PTRARRAYREF) AllocateObjectArray(ulNumLanguages, g_pStringClass);
439 LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning
440 for(DWORD i = 0; i < ulNumLanguages; i++)
442 OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
443 gc.resourceFallbackArray->SetAt(i, o);
444 buffer += (lstrlenW(buffer) + 1);
449 HELPER_METHOD_FRAME_END();
451 return OBJECTREFToObject(gc.resourceFallbackArray);
455 #endif // FEATURE_CORECLR
457 INT32 COMNlsInfo::CallGetUserDefaultUILanguage()
467 static INT32 s_lcid = 0;
469 // The user UI language cannot change within a process (in fact it cannot change within a logon session),
470 // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are
471 // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call
472 // GetUserDefaultUILanguage needlessly, but the final result is going to be the same.
475 INT32 s_lcidTemp = GetUserDefaultUILanguage();
476 if (s_lcidTemp == LANGID_ZH_TW)
478 // If the UI language ID is 0x0404, we need to do extra check to decide
479 // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID.
480 if (!NewApis::IsZhTwSku())
482 s_lcidTemp = LANGID_ZH_HK;
491 INT_PTR COMNlsInfo::EnsureValidSortHandle(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName)
493 LIMITED_METHOD_CONTRACT;
495 #ifndef FEATURE_CORECLR
496 AppDomain* curDomain = GetAppDomain();
498 if(!(curDomain->m_bUseOsSorting) && handleOrigin == (INT_PTR) SortVersioning::GetSortHandle && ((SortVersioning::PSORTHANDLE) handle)->dwNLSVersion == curDomain->m_sortVersion)
503 if(curDomain->m_bUseOsSorting && curDomain->m_pCustomSortLibrary == NULL && handleOrigin == (INT_PTR) NewApis::LCMapStringEx)
508 if(curDomain->m_bUseOsSorting && curDomain->m_pCustomSortLibrary != NULL && handleOrigin == (INT_PTR) curDomain->m_pCustomSortLibrary->pLCMapStringEx)
513 // At this point, we can't reuse the sort handle (it has different sort semantics than this domain) so we need to get a new one.
514 INT_PTR newHandleOrigin;
515 return InitSortHandleHelper(localeName, &newHandleOrigin);
517 // For CoreCLR, on Windows 8 and up the handle will be valid. on downlevels the handle will be null
522 #ifdef FEATURE_SYNTHETIC_CULTURES
523 ////////////////////////////////////////////////////////////////////////////
527 ////////////////////////////////////////////////////////////////////////////
529 /*=================================WstrToInteger4==================================
530 **Action: Convert a Unicode string to an integer. Error checking is ignored.
531 **Returns: The integer value of wstr
533 ** wstr: NULL terminated wide string. Can have character 0'-'9', 'a'-'f', and 'A' - 'F'
534 ** Radix: radix to be used in the conversion.
536 ==============================================================================*/
538 INT32 COMNlsInfo::WstrToInteger4(
547 PRECONDITION(CheckPointer(wstr));
548 PRECONDITION(Radix > 1 && Radix <= 16);
553 for (int Length = Wszlstrlen(wstr) - 1; Length >= 0; Length--)
556 WCHAR ch = wstr[Length];
557 _ASSERTE((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'));
563 Value += ((ch >= 'A') ? (ch - 'A' + 10) : (ch - '0')) * Base;
569 #endif // FEATURE_SYNTHETIC_CULTURES
572 #ifndef FEATURE_CORECLR
573 FCIMPL1(FC_BOOL_RET, COMNlsInfo::nativeSetThreadLocale, StringObject* localeNameUNSAFE)
578 PRECONDITION(CheckPointer(localeNameUNSAFE));
583 // TODO: NLS Arrowhead -A bit scary becausue Set ThreadLocale can't handle custom cultures?
584 STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
585 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
586 lcid=NewApis::LocaleNameToLCID(localeName->GetBuffer(),0);
589 ThrowHR(HRESULT_FROM_WIN32(GetLastError()));
591 HELPER_METHOD_FRAME_END();
596 // SetThreadLocale doesn't handle names/custom cultures
598 // Get rid of the SetThreadLocale warning in OACR:
599 #pragma warning(push)
600 #pragma warning(disable:38010)
602 result = ::SetThreadLocale(lcid);
607 FC_RETURN_BOOL(result);
613 FCIMPL2(Object*, COMNlsInfo::nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType)
618 PRECONDITION(CheckPointer(localeNameUNSAFE));
623 STRINGREF localeName;
627 // Dereference our string
629 gc.localeName = (STRINGREF)localeNameUNSAFE;
631 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
632 StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
634 WCHAR buffer[LOCALE_MAX_STRING_SIZE];
635 int result = NewApis::GetLocaleInfoEx(localeNameStackBuffer, lcType, buffer, NumItems(buffer));
637 // Make a string out of it
640 // Exclude the NULL char at the end, except that LOCALE_FONTSIGNATURE isn't
641 // really a string, so we need the last character too.
642 gc.refRetVal = StringObject::NewString(buffer, ((lcType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_FONTSIGNATURE) ? result : result-1);
647 HELPER_METHOD_FRAME_END();
648 return OBJECTREFToObject(gc.refRetVal);
653 FCIMPL2(INT32, COMNlsInfo::nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType)
658 PRECONDITION(CheckPointer(localeNameUNSAFE));
663 // Dereference our string
664 STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
665 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
667 lcType |= LOCALE_RETURN_NUMBER;
669 if (NewApis::GetLocaleInfoEx(localeName->GetBuffer(), lcType, (LPWSTR)&result, sizeof(INT32) / sizeof (WCHAR)) == 0)
671 // return value of 0 indicates failure and error value is supposed to be set.
672 // shouldn't ever really happen
673 _ASSERTE(!"catastrophic failure calling NewApis::nativeGetLocaleInfoExInt! This could be a CultureInfo bug (bad localeName string) or maybe a GCHole.");
676 HELPER_METHOD_FRAME_END();
684 ////////////////////////////////////////////////////////////////////////
686 // Call the Win32 GetLocaleInfo() using the specified lcid to retrieve
687 // the native digits, probably from the registry override. The return
688 // indicates whether the call was successful.
691 // IN lcid the LCID to make the Win32 call with
692 // OUT pOutputStrAry The output managed string array.
694 ////////////////////////////////////////////////////////////////////////
695 BOOL COMNlsInfo::GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF * pOutputStrAry, BOOL useUserOverride)
707 DWORD lcType = LOCALE_SNATIVEDIGITS;
710 lcType |= LOCALE_NOUSEROVERRIDE;
712 result = NewApis::GetLocaleInfoEx(locale, lcType, buffer, 11);
713 // Be very unforgiving and only support strings of size 10 plus the NULL
716 // Break up the unmanaged ten-character ZLS into what NFI wants (a managed
717 // ten-string array).
719 // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw
720 // an OutOfMemoryException if there's not enough memory.
722 PTRARRAYREF DigitArray = (PTRARRAYREF) AllocateObjectArray(10, g_pStringClass);
724 GCPROTECT_BEGIN(DigitArray);
725 for(DWORD i = 0; i < 10; i++) {
726 OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer + i, 1);
727 DigitArray->SetAt(i, o);
731 _ASSERTE(pOutputStrAry != NULL);
732 *pOutputStrAry = DigitArray;
735 return (result == 11);
739 ////////////////////////////////////////////////////////////////////////
741 // Call the Win32 GetLocaleInfoEx() using the specified lcid and LCTYPE.
742 // The return value can be INT32 or an allocated managed string object, depending on
743 // which version's called.
746 // OUT pOutputInt32 The output int32 value.
747 // OUT pOutputRef The output string value.
749 ////////////////////////////////////////////////////////////////////////
750 BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, INT32* pOutputInt32, BOOL useUserOverride)
761 _ASSERT((lcType & LOCALE_RETURN_NUMBER) != 0);
764 lcType |= LOCALE_NOUSEROVERRIDE;
766 result = NewApis::GetLocaleInfoEx(localeName, lcType, (LPWSTR)pOutputInt32, sizeof(*pOutputInt32));
768 return (result != 0);
771 BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride)
775 THROWS; // We can throw since we are allocating managed string.
780 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
783 _ASSERT((lcType & LOCALE_RETURN_NUMBER) == 0);
786 lcType |= LOCALE_NOUSEROVERRIDE;
788 result = NewApis::GetLocaleInfoEx(localeName, lcType, buffer, LOCALE_NAME_MAX_LENGTH);
792 _ASSERTE(pOutputStrRef != NULL);
793 *pOutputStrRef = StringObject::NewString(buffer, result - 1);
796 return (result != 0);
799 FCIMPL1(Object*, COMNlsInfo::LCIDToLocaleName, LCID lcid)
803 STRINGREF refRetVal = NULL;
805 // The maximum size for locale name is 85 characters.
806 WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
809 HELPER_METHOD_FRAME_BEGIN_RET_0();
811 // Note that this'll return neutral names (unlike native Vista APIs)
812 result = NewApis::LCIDToLocaleName(lcid, localeName, LOCALE_NAME_MAX_LENGTH, 0);
816 refRetVal = StringObject::NewString(localeName, result - 1);
820 refRetVal = StringObject::GetEmptyString();
823 HELPER_METHOD_FRAME_END();
825 return OBJECTREFToObject(refRetVal);
829 FCIMPL1(INT32, COMNlsInfo::LocaleNameToLCID, StringObject* localeNameUNSAFE)
834 PRECONDITION(CheckPointer(localeNameUNSAFE));
839 // Dereference our string
840 STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
841 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
843 // Note that this'll return neutral names (unlike native Vista APIs)
844 result = NewApis::LocaleNameToLCID(localeName->GetBuffer(), 0);
846 HELPER_METHOD_FRAME_END();
852 ////////////////////////////////////////////////////////////////////////
854 // Implementation of CultureInfo.nativeGetNumberFormatInfoValues.
856 // Retrieve NumberFormatInfo (NFI) properties from windows
859 // IN/OUT pNumfmtUNSAFE
860 // The pointer of the managed NumberFormatInfo passed
861 // from the managed side.
862 // Note that the native NumberFormatInfo* is defined
865 // Managed string will be allocated and assign to the string fields in
866 // the managed NumberFormatInfo passed in pNumftUNSAFE
868 ////////////////////////////////////////////////////////////////////////
872 This is the list of the data members in the managed NumberFormatInfo and their
873 corresponding LCTYPE().
875 Win32 GetLocaleInfo() constants Data members in NumberFormatInfo in the defined order.
876 LOCALE_SPOSITIVE // String positiveSign
877 LOCALE_SNEGATIVE // String negativeSign
878 LOCALE_SDECIMAL // String numberDecimalSeparator
879 LOCALE_SGROUPING // String numberGroupSeparator
880 LOCALE_SMONGROUPING // String currencyGroupSeparator
881 LOCALE_SMONDECIMALSEP // String currencyDecimalSeparator
882 LOCALE_SCURRENCY // String currencySymbol
883 N/A // String ansiCurrencySymbol
884 N/A // String nanSymbol
885 N/A // String positiveInfinitySymbol
886 N/A // String negativeInfinitySymbol
887 N/A // String percentDecimalSeparator
888 N/A // String percentGroupSeparator
889 N/A // String percentSymbol
890 N/A // String perMilleSymbol
892 N/A // int m_dataItem
894 LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, // int numberDecimalDigits
895 LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, // int currencyDecimalDigits
896 LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER, // int currencyPositivePattern
897 LOCALE_INEGCURR | LOCALE_RETURN_NUMBER, // int currencyNegativePattern
898 LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER, // int numberNegativePattern
899 N/A // int percentPositivePattern
900 N/A // int percentNegativePattern
901 N/A // int percentDecimalDigits
902 N/A // bool isReadOnly=false;
903 N/A // internal bool m_useUserOverride;
905 FCIMPL3(FC_BOOL_RET, COMNlsInfo::nativeGetNumberFormatInfoValues,
906 StringObject* localeNameUNSAFE, NumberFormatInfo* pNumfmtUNSAFE, CLR_BOOL useUserOverride) {
910 PRECONDITION(CheckPointer(localeNameUNSAFE));
917 STRINGREF localeName;
918 STRINGREF stringResult;
920 PTRARRAYREF tempArray;
923 // Dereference our string
924 gc.localeName = (STRINGREF)localeNameUNSAFE;
925 gc.numfmt = (NUMFMTREF) pNumfmtUNSAFE;
926 gc.stringResult = NULL;
929 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
930 StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
932 // Calling SString::ConvertToUnicode once
933 LPCWSTR pLocaleName = localeNameStackBuffer;
936 // NOTE: We pass the stringResult allocated in the stack and assign it to the fields
937 // in numfmt after calling CallGetLocaleInfo(). The reason for this is that CallGetLocaleInfo()
938 // allocates a string object, and it may trigger a GC and cause numfmt to be moved.
939 // That's why we use the stringResult allocated in the stack since it will not be moved.
940 // After CallGetLocaleInfo(), we know that numfmt will not be moved, and it's safe to assign
941 // the stringResult to its field.
945 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SPOSITIVESIGN , &gc.stringResult, useUserOverride)) {
946 SetObjectReference((OBJECTREF*)&(gc.numfmt->sPositive), gc.stringResult, NULL);
953 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SNEGATIVESIGN , &gc.stringResult, useUserOverride)) {
954 SetObjectReference((OBJECTREF*)&(gc.numfmt->sNegative), gc.stringResult, NULL);
960 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SDECIMAL , &gc.stringResult, useUserOverride)) {
961 SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberDecimal), gc.stringResult, NULL);
967 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_STHOUSAND , &gc.stringResult, useUserOverride)) {
968 SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberGroup), gc.stringResult, NULL);
974 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONTHOUSANDSEP , &gc.stringResult, useUserOverride)) {
975 SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyGroup), gc.stringResult, NULL);
981 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONDECIMALSEP , &gc.stringResult, useUserOverride)) {
982 SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyDecimal), gc.stringResult, NULL);
988 if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SCURRENCY , &gc.stringResult, useUserOverride)) {
989 SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrency), gc.stringResult, NULL);
998 ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNumberDecimals), useUserOverride);
999 _ASSERT(ret == TRUE);
1000 ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cCurrencyDecimals), useUserOverride);
1001 _ASSERT(ret == TRUE);
1002 ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER , &(gc.numfmt->cPosCurrencyFormat), useUserOverride);
1003 _ASSERT(ret == TRUE);
1004 ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegCurrencyFormat), useUserOverride);
1005 _ASSERT(ret == TRUE);
1006 ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegativeNumberFormat), useUserOverride);
1007 _ASSERT(ret == TRUE);
1008 ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, &(gc.numfmt->iDigitSubstitution), useUserOverride);
1009 _ASSERT(ret == TRUE);
1011 // LOCALE_SNATIVEDIGITS (gc.tempArray of strings)
1012 if (GetNativeDigitsFromWin32(pLocaleName, &gc.tempArray, useUserOverride)) {
1013 SetObjectReference((OBJECTREF*)&(gc.numfmt->sNativeDigits), gc.tempArray, NULL);
1020 HELPER_METHOD_FRAME_END();
1021 FC_RETURN_BOOL(ret);
1026 ////////////////////////////////////////////////////////////////////////
1028 // Culture enumeration helper functions
1030 ////////////////////////////////////////////////////////////////////////
1032 ////////////////////////////////////////////////////////////////////////////
1034 // Enum values for System.Globalization.CultureTypes
1036 ////////////////////////////////////////////////////////////////////////////
1038 // Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags
1039 #define CULTURETYPES_NEUTRALCULTURES 0x0001
1041 // Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags
1042 #define CULTURETYPES_SPECIFICCULTURES 0x0002
1044 // Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures
1045 #define CULTURETYPES_INSTALLEDWIN32CULTURES 0x0004
1047 // User defined custom culture
1048 #define CULTURETYPES_USERCUSTOMCULTURE 0x0008
1050 // User defined replacement custom culture.
1051 #define CULTURETYPES_REPLACEMENTCULTURES 0x0010
1052 // [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
1053 // Culture exists in Win32 but not in the Framework. // TODO: All cultures or no cultures?
1054 #define CULTURETYPES_WINDOWSONLYCULTURES 0x0020
1055 // [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
1056 // the language tag match a culture that ships with the .NET framework, effectively all cultures since we get them from windows
1057 #define CULTURETYPES_FRAMEWORKCULTURES 0x0040
1060 const LPWSTR WHIDBEY_FRAMEWORK_CULTURE_LIST [] =
1268 #define WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH (sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST) / sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST[0]))
1270 ////////////////////////////////////////////////////////////////////////////
1272 // NlsCompareInvariantNoCase
1274 // This routine does fast caseless comparison without needing the tables.
1275 // This helps us do the comparisons we need to load the tables :-)
1277 // Returns 0 if identical, <0 if pFirst if first string sorts first.
1279 // This is only intended to help with our locale name comparisons,
1280 // which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
1281 // compare as equal.
1283 // WARNING: [\]^_` will be less than A-Z because we make everything lower
1284 // case before comparing them.
1286 // When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
1287 // When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
1288 // or when null terminators are reached, whichever happens first (strncmp-like behavior)
1290 ////////////////////////////////////////////////////////////////////////////
1292 int NlsCompareInvariantNoCase(
1303 size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
1304 size--, pFirst++, pSecond++)
1306 // Make them lower case
1307 if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
1308 if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
1311 i = (first - second);
1313 // Are they the same?
1317 // Otherwise the difference. Remember we made A-Z into lower case, so
1318 // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the
1319 // characters between A-Z and a-Z in ascii)
1323 // When we are here, one of these holds:
1325 // or one of the strings has a null terminator
1326 // or both of the string reaches null terminator
1328 if (bNullEnd || size != 0)
1330 // If bNullEnd is TRUE, always check for null terminator.
1331 // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
1332 // than another (hense the size != 0 check).
1334 // See if one string ended first
1335 if (*pFirst != 0 || *pSecond != 0)
1338 return *pFirst == 0 ? -1 : 1;
1342 // Return our difference (0)
1346 BOOL IsWhidbeyFrameworkCulture(__in LPCWSTR lpLocaleString)
1349 int iTop = WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH - 1;
1351 // Do a binary search for our name
1352 while (iBottom <= iTop)
1354 int iMiddle = (iBottom + iTop) / 2;
1355 int result = NlsCompareInvariantNoCase(lpLocaleString, WHIDBEY_FRAMEWORK_CULTURE_LIST[iMiddle], LOCALE_NAME_MAX_LENGTH, TRUE);
1362 // pLocaleName was < pTest
1367 // pLocaleName was > pTest
1368 iBottom = iMiddle + 1;
1375 // Just Check to see if the OS thinks it is a valid locle
1376 BOOL WINAPI IsOSValidLocaleName(__in LPCWSTR lpLocaleName, bool bIsNeutralLocale)
1378 #ifndef ENABLE_DOWNLEVEL_FOR_NLS
1379 return ::IsValidLocaleName(lpLocaleName);
1381 BOOL IsWindows7 = NewApis::IsWindows7Platform();
1382 // if we're < Win7, we didn't know about neutrals or the invariant.
1383 if (!IsWindows7 && (bIsNeutralLocale || (lpLocaleName[0] == 0)))
1388 // Work around the name/lcid thingy (can't just link to ::IsValidLocaleName())
1389 LCID lcid = NewApis::LocaleNameToLCID(lpLocaleName, 0);
1391 if (IsCustomCultureId(lcid))
1396 if (bIsNeutralLocale)
1398 // In this case, we're running on Windows 7.
1399 // For neutral locales, use GetLocaleInfoW.
1400 // If GetLocaleInfoW works, then the OS knows about it.
1401 return (::GetLocaleInfoW(lcid, LOCALE_ILANGUAGE, NULL, 0) != 0);
1404 // This is not a custom locale.
1405 // Call IsValidLocale() to check if the LCID is installed.
1406 // IsValidLocale doesn't work for neutral locales.
1407 return IsValidLocale(lcid, LCID_INSTALLED);
1412 ////////////////////////////////////////////////////////////////////////////
1414 // Check the dwFlags, which has the 'attributes' of the locale, and decide
1415 // if the locale should be included in the enumeration based on
1416 // the desired CultureTypes.
1418 ////////////////////////////////////////////////////////////////////////////
1420 BOOL ShouldIncludeByCultureType(INT32 cultureTypes, LPCWSTR lpLocaleString, INT32 dwFlags)
1423 if ((cultureTypes & CULTURETYPES_NEUTRALCULTURES) &&
1424 ((dwFlags & LOCALE_NEUTRALDATA) || (lpLocaleString[0] == 0))) // Invariant culture get enumerated with the neutrals
1429 if ((cultureTypes & CULTURETYPES_SPECIFICCULTURES) &&
1430 ((dwFlags & LOCALE_SPECIFICDATA) && (lpLocaleString[0] != 0))) // Invariant culture does not get enumerated with the specifics
1435 if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
1437 // The user asks for installed Win32 culture, so check
1438 // if this locale is installed. In W7 and above, when ::IsValidLocaleName()
1439 // returns true, it means that it is installed.
1440 // In downlevel (including Vista), we will convert the name to LCID.
1441 // When the LCID is not a custom locale, we will call ::IsValidLocale(.., LCID_INSTALLED)
1442 // to verify if the locale is installed.
1443 // In Vista, we treat custom locale as installed.
1444 if (IsOSValidLocaleName(lpLocaleString, (dwFlags & LOCALE_NEUTRALDATA) == LOCALE_NEUTRALDATA))
1450 if ((cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) &&
1451 (dwFlags & LOCALE_SUPPLEMENTAL))
1456 if ((cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) &&
1457 (dwFlags & LOCALE_REPLACEMENT))
1462 if ((cultureTypes & CULTURETYPES_FRAMEWORKCULTURES) &&
1463 IsWhidbeyFrameworkCulture(lpLocaleString))
1469 // No need to check CULTURETYPES_WINDOWSONLYCULTURES and CULTURETYPES_FRAMEWORKCULTURES
1470 // since they are deprecated, and they are handled in the managed code before calling
1471 // nativeEnumCultureNames.
1477 ////////////////////////////////////////////////////////////////////////////
1479 // Struct to hold context to be used in the callback for
1480 // EnumLocaleProcessingCallback
1482 ////////////////////////////////////////////////////////////////////////////
1486 PTRARRAYREF pCultureNamesArray;
1491 ////////////////////////////////////////////////////////////////////////////
1493 // Callback for NewApis::EnumSystemLocalesEx to count the number of
1494 // locales to be enumerated.
1496 ////////////////////////////////////////////////////////////////////////////
1498 BOOL CALLBACK EnumLocaleCountCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
1500 ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
1502 if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
1510 ////////////////////////////////////////////////////////////////////////////
1512 // Callback for NewApis::EnumSystemLocalesEx to add the locale name
1513 // into the allocated managed string array.
1515 ////////////////////////////////////////////////////////////////////////////
1517 BOOL CALLBACK EnumLocaleProcessingCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
1519 ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
1521 if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
1525 GCPROTECT_BEGIN(pData->pCultureNamesArray);
1527 OBJECTREF cultureString = (OBJECTREF) StringObject::NewString(lpLocaleString);
1528 pData->pCultureNamesArray->SetAt(pData->count, cultureString);
1538 ////////////////////////////////////////////////////////////////////////////
1540 // Called by CultureData.GetCultures() to enumerate the names of cultures.
1541 // It first calls NewApis::EnumSystemLocalesEx to count the number of
1542 // locales to be enumerated. And it will allocate an managed string
1543 // array with the count. And fill the array with the culture names in
1544 // the 2nd call to NewAPis::EnumSystemLocalesEx.
1546 ////////////////////////////////////////////////////////////////////////////
1549 int QCALLTYPE COMNlsInfo::nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray)
1554 // Check CultureTypes.WindowsOnlyCultures and CultureTYpes.FrameworkCultures are deprecated and is
1555 // handled in the managed code side to provide fallback behavior.
1556 //PRECONDITION((cultureTypes & (CULTURETYPES_WINDOWSONLYCULTURES | CULTURETYPES_FRAMEWORKCULTURES) == 0));
1562 PTRARRAYREF cultureNamesArray = NULL;
1563 ENUM_LOCALE_DATA enumData = { NULL, 0, cultureTypes};
1568 // if CultureTypes.FrameworkCulture is specified we'll enumerate all cultures
1569 // and filter according to the Whidbey framework culture list (for compatibility)
1572 if (cultureTypes & CULTURETYPES_FRAMEWORKCULTURES)
1574 dwFlags |= LOCALE_NEUTRALDATA | LOCALE_SPECIFICDATA;
1577 // Map CultureTypes to Windows enumeration values.
1578 if (cultureTypes & CULTURETYPES_NEUTRALCULTURES)
1580 dwFlags |= LOCALE_NEUTRALDATA;
1583 if (cultureTypes & CULTURETYPES_SPECIFICCULTURES)
1585 dwFlags |= LOCALE_SPECIFICDATA;
1588 if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
1590 // Windows 7 knows about neutrals, whereas Vista and lower don't.
1591 if (NewApis::IsWindows7Platform())
1593 dwFlags |= LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA;
1597 dwFlags |= LOCALE_SPECIFICDATA;
1601 dwFlags |= (cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) ? LOCALE_SUPPLEMENTAL: 0;
1603 // We need special handling for Replacement cultures because Windows does not have a way to enumerate it directly.
1604 // Replacement locale check will be only used when CultureTypes.SpecificCultures is NOT used.
1605 dwFlags |= (cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) ? LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA: 0;
1608 result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleCountCallback, dwFlags, (LPARAM)&enumData, NULL) == TRUE ? 1 : 0;
1615 GCPROTECT_BEGIN(cultureNamesArray);
1617 // Now we need to allocate our culture names string array and populate it
1618 // Get our array object (will throw, don't have to check it)
1619 cultureNamesArray = (PTRARRAYREF) AllocateObjectArray(enumData.count, g_pStringClass);
1621 // In the context struct passed to EnumSystemLocalesEx, reset the count and assign the newly allocated string array
1622 // to hold culture names to be enumerated.
1624 enumData.pCultureNamesArray = cultureNamesArray;
1626 result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleProcessingCallback, dwFlags, (LPARAM)&enumData, NULL);
1630 retStringArray.Set(cultureNamesArray);
1641 // InternalCompareString is used in the managed side to handle the synthetic CompareInfo methods (IndexOf, LastIndexOf, IsPrfix, and IsSuffix)
1643 INT32 QCALLTYPE COMNlsInfo::InternalCompareString(
1645 INT_PTR handleOrigin,
1647 LPCWSTR string1, INT32 offset1, INT32 length1,
1648 LPCWSTR string2, INT32 offset2, INT32 length2,
1654 PRECONDITION(CheckPointer(string1));
1655 PRECONDITION(CheckPointer(string2));
1656 PRECONDITION(CheckPointer(localeName));
1662 handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
1664 #ifndef FEATURE_CORECLR
1665 AppDomain* curDomain = GetAppDomain();
1667 if(!(curDomain->m_bUseOsSorting))
1669 result = SortVersioning::SortDllCompareString((SortVersioning::PSORTHANDLE) handle, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, 0);
1671 else if (curDomain->m_pCustomSortLibrary != NULL) {
1672 result = (curDomain->m_pCustomSortLibrary->pCompareStringEx)(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, NULL, (LPARAM) handle);
1677 result = NewApis::CompareStringEx(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2,NULL,NULL, (LPARAM) handle);
1682 case CSTR_LESS_THAN:
1690 case CSTR_GREATER_THAN:
1696 _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx! This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole.");
1704 ////////////////////////////////////////////////////////////////////////////
1706 // UseConstantSpaceHashAlgorithm
1707 // Check for the DWORD "NetFx45_CultureAwareComparerGetHashCode_LongStrings" CLR config option.
1709 // .Net 4.5 introduces an opt-in algorithm for determining the hash code of strings that
1710 // uses a constant amount of memory instead of memory proportional to the size of the string
1712 // A non-zero value will enable the new algorithm:
1714 // 1) Config file (MyApp.exe.config)
1715 // <?xml version ="1.0"?>
1718 // <NetFx45_CultureAwareComparerGetHashCode_LongStrings enabled="1"/>
1721 // 2) Environment variable
1722 // set NetFx45_CultureAwareComparerGetHashCode_LongStrings=1
1724 // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
1725 // "NetFx45_CultureAwareComparerGetHashCode_LongStrings"=dword:00000001
1727 ////////////////////////////////////////////////////////////////////////////
1728 BOOL UseConstantSpaceHashAlgorithm()
1730 static bool configChecked = false;
1731 static BOOL useConstantSpaceHashAlgorithm = FALSE;
1735 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
1736 useConstantSpaceHashAlgorithm = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NetFx45_CultureAwareComparerGetHashCode_LongStrings) != 0;
1737 END_SO_INTOLERANT_CODE;
1739 configChecked = true;
1741 return useConstantSpaceHashAlgorithm;
1746 ////////////////////////////////////////////////////////////////////////////
1748 // InternalGetGlobalizedHashCode
1750 ////////////////////////////////////////////////////////////////////////////
1751 INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, BOOL bForceRandomizedHashing, INT64 additionalEntropy)
1756 PRECONDITION(CheckPointer(localeName));
1757 PRECONDITION(CheckPointer(string, NULL_OK));
1760 INT32 iReturnHash = 0;
1763 handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
1767 // Make sure there is a string.
1770 COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String"));
1773 #ifndef FEATURE_CORECLR
1774 AppDomain* curDomain = GetAppDomain();
1775 #endif // FEATURE_CORECLR
1777 if(length > 0 && UseConstantSpaceHashAlgorithm()
1778 // Note that we can't simply do the hash without the entropy and then try to add it after the fact we need the hash function itself to pass entropy to its inputs.
1779 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
1780 && !bForceRandomizedHashing
1781 #ifndef FEATURE_CORECLR
1782 && !curDomain->m_pNlsHashProvider->GetUseRandomHashing()
1784 && !COMNlsHashProvider::s_NlsHashProvider.GetUseRandomHashing()
1785 #endif // FEATURE_CORECLR
1786 #endif // FEATURE_RANDOMIZED_STRING_HASHING
1789 #ifndef FEATURE_CORECLR
1790 if(!(curDomain->m_bUseOsSorting))
1792 iReturnHash=SortVersioning::SortDllGetHashCode((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, NULL, 0);
1800 #ifndef FEATURE_CORECLR
1801 if (curDomain->m_pCustomSortLibrary != NULL)
1803 iRes = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(localeName, dwFlagsIn | LCMAP_HASH, string, length, (LPWSTR) &iHashValue, sizeof(INT32), NULL, NULL, 0);
1808 iRes = NewApis::LCMapStringEx(localeName, dwFlagsIn | LCMAP_HASH, string, length, (LPWSTR) &iHashValue, sizeof(INT32), NULL, NULL, 0);
1813 iReturnHash = iHashValue;
1818 if(iReturnHash == 0)
1820 DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn);
1823 // Caller has already verified that the string is not of zero length
1825 // Assert if we might hit an AV in LCMapStringEx for the invariant culture.
1826 _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0);
1827 #ifndef FEATURE_CORECLR
1828 if(!(curDomain->m_bUseOsSorting))
1830 byteCount=SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, NULL, 0, NULL, 0);
1832 else if (curDomain->m_pCustomSortLibrary != NULL)
1834 byteCount = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
1839 byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
1842 //A count of 0 indicates that we either had an error or had a zero length string originally.
1844 COMPlusThrow(kArgumentException, W("Arg_MustBeString"));
1847 // We used to use a NewArrayHolder here, but it turns out that hurts our large # process
1848 // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack
1849 // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case
1851 CQuickBytesSpecifySize<MAX_STRING_VALUE * sizeof(WCHAR)> qbBuffer;
1852 BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount);
1854 #ifndef FEATURE_CORECLR
1855 if(!(curDomain->m_bUseOsSorting))
1857 SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, pByte, byteCount, NULL, 0);
1859 else if(curDomain->m_pCustomSortLibrary != NULL)
1861 (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL, NULL, (LPARAM) handle);
1866 NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle);
1869 #ifndef FEATURE_CORECLR
1870 iReturnHash = curDomain->m_pNlsHashProvider->HashSortKey(pByte, byteCount, bForceRandomizedHashing, additionalEntropy);
1872 iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, bForceRandomizedHashing, additionalEntropy);
1873 #endif // FEATURE_CORECLR
1877 return(iReturnHash);
1880 #ifndef FEATURE_CORECLR // FCalls used by System.TimeZone
1882 FCIMPL0(LONG, COMNlsInfo::nativeGetTimeZoneMinuteOffset)
1886 TIME_ZONE_INFORMATION timeZoneInfo;
1888 GetTimeZoneInformation(&timeZoneInfo);
1891 // In Win32, UTC = local + offset. So for Pacific Standard Time, offset = 8.
1892 // In NLS+, Local time = UTC + offset. So for PST, offset = -8.
1893 // So we have to reverse the sign here.
1895 return (timeZoneInfo.Bias * -1);
1899 FCIMPL0(Object*, COMNlsInfo::nativeGetStandardName)
1903 STRINGREF refRetVal = NULL;
1904 HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
1906 TIME_ZONE_INFORMATION timeZoneInfo;
1907 GetTimeZoneInformation(&timeZoneInfo);
1909 refRetVal = StringObject::NewString(timeZoneInfo.StandardName);
1911 HELPER_METHOD_FRAME_END();
1912 return OBJECTREFToObject(refRetVal);
1916 FCIMPL0(Object*, COMNlsInfo::nativeGetDaylightName)
1920 STRINGREF refRetVal = NULL;
1921 HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
1923 TIME_ZONE_INFORMATION timeZoneInfo;
1924 GetTimeZoneInformation(&timeZoneInfo);
1925 // Instead of returning null when daylight saving is not used, now we return the same result as the OS.
1926 //In this case, if daylight saving time is used, the standard name is returned.
1929 if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightDate.wMonth == 0) {
1930 // If daylight saving time is not used in this timezone, return null.
1932 // Windows NT/2000: TIME_ZONE_ID_UNKNOWN is returned if daylight saving time is not used in
1933 // the current time zone, because there are no transition dates.
1935 // For Windows 9x, a zero in the wMonth in DaylightDate means daylight saving time
1936 // is not specified.
1938 // If the current timezone uses daylight saving rule, but user unchekced the
1939 // "Automatically adjust clock for daylight saving changes", the value
1940 // for DaylightBias will be 0.
1941 return (I2ARRAYREF)NULL;
1945 refRetVal = StringObject::NewString(timeZoneInfo.DaylightName);
1947 HELPER_METHOD_FRAME_END();
1948 return OBJECTREFToObject(refRetVal);
1952 FCIMPL1(Object*, COMNlsInfo::nativeGetDaylightChanges, int year)
1956 I2ARRAYREF pResultArray = NULL;
1957 HELPER_METHOD_FRAME_BEGIN_RET_1(pResultArray);
1959 TIME_ZONE_INFORMATION timeZoneInfo;
1960 DWORD result = GetTimeZoneInformation(&timeZoneInfo);
1962 if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightBias == 0
1963 || timeZoneInfo.DaylightDate.wMonth == 0
1965 // If daylight saving time is not used in this timezone, return null.
1967 // If the current timezone uses daylight saving rule, but user unchekced the
1968 // "Automatically adjust clock for daylight saving changes", the value
1969 // for DaylightBias will be 0.
1973 pResultArray = (I2ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I2, 17);
1976 // The content of timeZoneInfo.StandardDate is 8 words, which
1977 // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
1979 memcpyNoGCRefs(pResultArray->m_Array,
1980 (LPVOID)&timeZoneInfo.DaylightDate,
1984 // The content of timeZoneInfo.DaylightDate is 8 words, which
1985 // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
1987 memcpyNoGCRefs(((INT16*)pResultArray->m_Array) + 8,
1988 (LPVOID)&timeZoneInfo.StandardDate,
1991 ((INT16*)pResultArray->m_Array)[16] = (INT16)timeZoneInfo.DaylightBias * -1;
1994 HELPER_METHOD_FRAME_END();
1995 return OBJECTREFToObject(pResultArray);
1999 #endif // FEATURE_CORECLR
2001 inline BOOL IsInvariantLocale(STRINGREF localeName)
2003 return localeName->GetStringLength() == 0;
2006 // InternalChangeCaseChar
2008 // Call LCMapStringEx with a char to make it upper or lower case
2009 // Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
2010 FCIMPL5(FC_CHAR_RET, COMNlsInfo::InternalChangeCaseChar,
2011 INT_PTR handle, // optional sort handle
2012 INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper)
2017 PRECONDITION(CheckPointer(localeNameUNSAFE));
2020 CLR_CHAR retVal = '\0';
2021 int ret_LCMapStringEx = -1;
2023 // Dereference our string
2024 STRINGREF localeName(localeNameUNSAFE);
2026 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
2028 BOOL isInvariantLocale = IsInvariantLocale(localeName);
2029 // Check for Invariant to avoid A/V in LCMapStringEx
2030 DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
2032 handle = EnsureValidSortHandle(handle, handleOrigin, localeName->GetBuffer());
2034 #ifndef FEATURE_CORECLR
2035 AppDomain* curDomain = GetAppDomain();
2037 //For a versioned sort, Invariant should still use the OS
2038 if(!(curDomain->m_bUseOsSorting) && !isInvariantLocale)
2040 ret_LCMapStringEx = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2041 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2042 LCMAP_LOWERCASE | linguisticCasing,
2049 else if(curDomain->m_pCustomSortLibrary != NULL)
2051 ret_LCMapStringEx = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName->GetBuffer(),
2052 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2053 LCMAP_LOWERCASE | linguisticCasing,
2065 ret_LCMapStringEx = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
2066 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2067 LCMAP_LOWERCASE | linguisticCasing,
2077 if (0 == ret_LCMapStringEx)
2079 // return value of 0 indicates failure and error value is supposed to be set.
2080 // shouldn't ever really happen
2081 _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseChar! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2084 HELPER_METHOD_FRAME_END(); // localeName is now unprotected
2089 // InternalChangeCaseString
2091 // Call LCMapStringEx with a string to make it upper or lower case
2092 // Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
2094 // We typically expect the output string to be the same size as the input. If not
2095 // we have to count, reallocate the output buffer, and try again.
2096 FCIMPL5(Object*, COMNlsInfo::InternalChangeCaseString,
2097 INT_PTR handle, // optional sort handle
2098 INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pStringUNSAFE, CLR_BOOL bIsToUpper)
2103 PRECONDITION(CheckPointer(pStringUNSAFE));
2104 PRECONDITION(CheckPointer(localeNameUNSAFE));
2115 gc.pString = ObjectToSTRINGREF(pStringUNSAFE);
2116 gc.pLocale = ObjectToSTRINGREF(localeNameUNSAFE);
2118 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc)
2120 handle = EnsureValidSortHandle(handle, handleOrigin, gc.pLocale->GetBuffer());
2123 // Get the length of the string.
2125 int nLengthInput = gc.pString->GetStringLength();
2126 int nLengthOutput = nLengthInput; // initially we assume the string length does not change.
2128 BOOL isInvariantLocale = IsInvariantLocale(gc.pLocale);
2129 // Check for Invariant to avoid A/V in LCMapStringEx
2130 DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
2131 // Check for Invariant to avoid A/V in LCMapStringEx
2134 // Check if we have the empty string.
2136 if (nLengthInput == 0)
2138 gc.pResult = ObjectToSTRINGREF(gc.pString);
2143 // Create the result string.
2145 gc.pResult = StringObject::NewString(nLengthOutput);
2146 LPWSTR pResultStr = gc.pResult->GetBuffer();
2149 #ifndef FEATURE_CORECLR
2150 AppDomain* curDomain = GetAppDomain();
2152 //Invariant should always use OS
2153 if(!(curDomain->m_bUseOsSorting) && !isInvariantLocale)
2155 result = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2156 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2157 LCMAP_LOWERCASE | linguisticCasing,
2158 gc.pString->GetBuffer(),
2164 else if(curDomain->m_pCustomSortLibrary != NULL)
2166 result = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2167 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2168 LCMAP_LOWERCASE | linguisticCasing,
2169 gc.pString->GetBuffer(),
2180 result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2181 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2182 LCMAP_LOWERCASE | linguisticCasing,
2183 gc.pString->GetBuffer(),
2194 // Failure: Detect if that's due to insufficient buffer
2195 if (GetLastError()!= ERROR_INSUFFICIENT_BUFFER)
2199 // need to update buffer
2200 #ifndef FEATURE_CORECLR
2201 //Invariant should always use OS
2202 if(!(curDomain->m_bUseOsSorting) && !IsInvariantLocale(gc.pLocale))
2204 nLengthOutput = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2205 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2206 LCMAP_LOWERCASE | linguisticCasing,
2207 gc.pString->GetBuffer(),
2213 else if(curDomain->m_pCustomSortLibrary != NULL)
2215 nLengthOutput = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2216 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2217 LCMAP_LOWERCASE | linguisticCasing,
2218 gc.pString->GetBuffer(),
2229 nLengthOutput = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2230 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2231 LCMAP_LOWERCASE | linguisticCasing,
2232 gc.pString->GetBuffer(),
2240 if (nLengthOutput == 0)
2242 // return value of 0 indicates failure and error value is supposed to be set.
2243 // shouldn't ever really happen
2244 _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2246 _ASSERTE(nLengthOutput > 0);
2247 // NOTE: The length of the required buffer does not include the terminating null character.
2248 // So it can be used as-is for our calculations -- the length we pass in to NewString also does
2249 // not include the terminating null character.
2250 // MSDN documentation could be interpreted to mean that the length returned includes the terminating
2251 // NULL character, but that's not the case.
2253 // NOTE: Also note that we let the GC take care of the previously allocated pResult.
2255 gc.pResult = StringObject::NewString(nLengthOutput);
2256 pResultStr = gc.pResult->GetBuffer();
2257 #ifndef FEATURE_CORECLR
2258 //Invariant should always use OS
2259 if(!(curDomain->m_bUseOsSorting) && !IsInvariantLocale(gc.pLocale))
2261 result = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2262 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2263 LCMAP_LOWERCASE | linguisticCasing,
2264 gc.pString->GetBuffer(),
2270 else if(curDomain->m_pCustomSortLibrary != NULL)
2272 result = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2273 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2274 LCMAP_LOWERCASE | linguisticCasing,
2275 gc.pString->GetBuffer(),
2286 result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2287 bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2288 LCMAP_LOWERCASE | linguisticCasing,
2289 gc.pString->GetBuffer(),
2300 // return value of 0 indicates failure and error value is supposed to be set.
2301 // shouldn't ever really happen
2302 _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2306 pResultStr[nLengthOutput] = 0;
2309 HELPER_METHOD_FRAME_END();
2311 return OBJECTREFToObject(gc.pResult);
2315 /*================================InternalGetCaseInsHash================================
2320 ==============================================================================*/
2321 FCIMPL6(INT32, COMNlsInfo::InternalGetCaseInsHash,
2322 INT_PTR handle, // optional sort handle
2323 INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID pvStrA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy)
2328 PRECONDITION(CheckPointer(localeNameUNSAFE));
2329 PRECONDITION(CheckPointer(pvStrA));
2332 STRINGREF localeName = ObjectToSTRINGREF(localeNameUNSAFE);
2337 BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException))
2339 *((LPVOID *)&strA)=pvStrA;
2341 #ifndef FEATURE_CORECLR
2342 AppDomain* curDomain = GetAppDomain();
2346 // If we know that we don't have any high characters (the common case) we can
2347 // call a hash function that knows how to do a very fast case conversion. If
2348 // we find characters above 0x80, it's much faster to convert the entire string
2349 // to Uppercase and then call the standard hash function on it.
2351 // TODO: NLS Arrowhead -We aren't consistent with the fast casing cultures (any en? fr? de?)
2352 if (IsCultureEnglishOrInvariant((localeName)->GetBuffer()) && // If we're en-US or Invariant
2353 ((IS_STRING_STATE_UNDETERMINED(strA->GetHighCharState()) && // and we're undetermined
2354 IS_FAST_CASING(strA->InternalCheckHighChars())) || // and its fast casing when determined
2355 IS_FAST_CASING(strA->GetHighCharState()))) // or we're fast casing that's already determined
2357 // Notice that for Turkish and Azeri we don't get here and shouldn't use this
2358 // fast path because of their special Latin casing rules.
2359 #ifndef FEATURE_CORECLR
2360 result = curDomain->m_pNlsHashProvider->HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
2362 result = COMNlsHashProvider::s_NlsHashProvider.HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
2363 #endif // FEATURE_CORECLR
2367 handle = EnsureValidSortHandle(handle, handleOrigin, localeName->GetBuffer());
2369 // Make it upper case
2370 CQuickBytes newBuffer;
2371 INT32 length = strA->GetStringLength();
2372 WCHAR *pNewStr = (WCHAR *)newBuffer.AllocThrows((length + 1) * sizeof(WCHAR));
2374 // Work around an A/V in LCMapStringEx for the invariant culture.
2375 // Revisit this after Vista SP2 has been deployed everywhere.
2376 DWORD linguisticCasing = 0;
2377 if (localeName->GetStringLength() > 0) // if not the invariant culture...
2379 linguisticCasing = LCMAP_LINGUISTIC_CASING;
2383 #ifndef FEATURE_CORECLR
2384 if(!(curDomain->m_bUseOsSorting))
2386 lcmapResult = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2387 LCMAP_UPPERCASE | linguisticCasing,
2394 else if(curDomain->m_pCustomSortLibrary != NULL)
2396 lcmapResult = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName->GetBuffer(),
2397 LCMAP_UPPERCASE | linguisticCasing,
2402 NULL, NULL, (LPARAM) handle);
2407 lcmapResult = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
2408 LCMAP_UPPERCASE | linguisticCasing,
2413 NULL, NULL, (LPARAM) handle);
2416 if (lcmapResult == 0)
2418 // return value of 0 indicates failure and error value is supposed to be set.
2419 // shouldn't ever really happen
2420 _ASSERTE(!"catastrophic failure calling NewApis::InternalGetCaseInsHash! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2422 pNewStr[length]='\0';
2424 // Get hash for the upper case of the new string
2426 #ifndef FEATURE_CORECLR
2427 result = curDomain->m_pNlsHashProvider->HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
2429 result = COMNlsHashProvider::s_NlsHashProvider.HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
2430 #endif // FEATURE_CORECLR
2433 END_SO_INTOLERANT_CODE
2439 // Fast path for finding a String using OrdinalIgnoreCase rules
2440 // returns true if the fast path succeeded, with foundIndex set to the location where the String was found or -1
2441 // Returns false when FindStringOrdinal isn't handled (we don't have our own general version of this function to fall back on)
2442 // Note for future optimizations: kernel32!FindStringOrdinal(ignoreCase=TRUE) uses per-character table lookup
2443 // to map to upper case before comparison, but isn't otherwise optimized
2444 BOOL QCALLTYPE COMNlsInfo::InternalTryFindStringOrdinalIgnoreCase(
2445 __in DWORD dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH
2446 __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
2447 __in int cchSource, // number of characters lpStringSource after sourceIndex
2448 __in int sourceIndex, // index from where the search will start in lpStringSource
2449 __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
2451 __out int* foundIndex) // the index in lpStringSource where we found lpStringValue
2456 PRECONDITION(lpStringSource != NULL);
2457 PRECONDITION(lpStringValue != NULL);
2458 PRECONDITION(cchSource>=0);
2459 PRECONDITION(cchValue>=0);
2460 PRECONDITION((dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION) == 0);
2463 BOOL result = FALSE;
2467 LPCWSTR lpSearchStart = NULL;
2468 if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
2470 lpSearchStart = &lpStringSource[sourceIndex - cchSource + 1];
2473 lpSearchStart = &lpStringSource[sourceIndex];
2475 #ifndef FEATURE_CORECLR
2476 AppDomain* curDomain = GetAppDomain();
2478 // Check if the default sorting is overridden
2479 if (curDomain->m_pCustomSortLibrary != NULL)
2481 *foundIndex = (curDomain->m_pCustomSortLibrary->pFindStringOrdinal)(
2482 dwFindNLSStringFlags,
2493 #ifndef FEATURE_CORESYSTEM
2494 // kernel function pointer
2495 typedef int (WINAPI *PFNFindStringOrdinal)(DWORD, LPCWSTR, INT, LPCWSTR, INT, BOOL);
2496 static PFNFindStringOrdinal FindStringOrdinal = NULL;
2498 // initizalize kernel32!FindStringOrdinal
2499 if (FindStringOrdinal == NULL)
2501 PFNFindStringOrdinal result = NULL;
2503 HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
2505 result=(PFNFindStringOrdinal)GetProcAddress(hMod,"FindStringOrdinal");
2507 FindStringOrdinal = (result != NULL) ? result : (PFNFindStringOrdinal)-1;
2510 // call into the kernel
2511 if (FindStringOrdinal != (PFNFindStringOrdinal)-1)
2514 *foundIndex = FindStringOrdinal(
2515 dwFindNLSStringFlags,
2524 // if we found the pattern string, fixup the index before we return
2525 if (*foundIndex >= 0)
2527 if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
2528 *foundIndex += (sourceIndex - cchSource + 1);
2530 *foundIndex += sourceIndex;
2538 // InternalCompareStringOrdinalIgnoreCase
2540 // Call ::CompareStringOrdinal for native ordinal behavior
2541 INT32 QCALLTYPE COMNlsInfo::InternalCompareStringOrdinalIgnoreCase(
2542 LPCWSTR string1, INT32 index1,
2543 LPCWSTR string2, INT32 index2,
2550 PRECONDITION(CheckPointer(string1));
2551 PRECONDITION(CheckPointer(string2));
2558 // Get the arguments.
2559 // We assume the caller checked them before calling us
2562 // We don't allow the -1 that native code allows
2563 _ASSERT(length1 >= 0);
2564 _ASSERT(length2 >= 0);
2566 // Do the comparison
2567 #ifndef FEATURE_CORECLR
2568 AppDomain* curDomain = GetAppDomain();
2570 if (curDomain->m_pCustomSortLibrary != NULL) {
2571 result = (curDomain->m_pCustomSortLibrary->pCompareStringOrdinal)(string1 + index1, length1, string2 + index2, length2, TRUE);
2576 result = NewApis::CompareStringOrdinal(string1 + index1, length1, string2 + index2, length2, TRUE);
2579 // The native call shouldn't fail
2580 _ASSERT(result != 0);
2583 // return value of 0 indicates failure and error value is supposed to be set.
2584 // shouldn't ever really happen
2585 _ASSERTE(!"catastrophic failure calling NewApis::CompareStringOrdinal! This is usually due to bad arguments.");
2588 // Adjust the result to the expected -1, 0, 1 result
2597 * This function returns a pointer to this table that we use in System.Globalization.EncodingTable.
2598 * No error checking of any sort is performed. Range checking is entirely the responsibility of the managed
2601 FCIMPL0(EncodingDataItem *, COMNlsInfo::nativeGetEncodingTableDataPointer)
2603 LIMITED_METHOD_CONTRACT;
2604 STATIC_CONTRACT_SO_TOLERANT;
2606 return (EncodingDataItem *)EncodingDataTable;
2611 * This function returns a pointer to this table that we use in System.Globalization.EncodingTable.
2612 * No error checking of any sort is performed. Range checking is entirely the responsibility of the managed
2615 FCIMPL0(CodePageDataItem *, COMNlsInfo::nativeGetCodePageTableDataPointer)
2617 LIMITED_METHOD_CONTRACT;
2619 STATIC_CONTRACT_SO_TOLERANT;
2621 return ((CodePageDataItem*) CodePageDataTable);
2626 #ifndef FEATURE_CORECLR
2631 FCIMPL6(int, COMNlsInfo::nativeNormalizationNormalizeString,
2632 int NormForm, int& iError,
2633 StringObject* inChars, int inLength,
2634 CHARArray* outChars, int outLength )
2639 PRECONDITION(CheckPointer(inChars));
2640 PRECONDITION(CheckPointer(outChars, NULL_OK));
2643 // Dereference our string
2644 STRINGREF inString(inChars);
2645 LPWSTR inCharsBuffer = inString->GetBuffer();
2647 CHARARRAYREF outCharArray(outChars);
2648 LPWSTR outCharsBuffer = (outCharArray != NULL) ? ((LPWSTR) (outCharArray->GetDirectPointerToNonObjectElements())) : NULL;
2650 // The OS APIs do not always set last error in success, so we have to do it explicitly
2651 SetLastError(ERROR_SUCCESS);
2653 int iResult = m_pfnNormalizationNormalizeStringFunc(
2654 NormForm, inCharsBuffer, inLength, outCharsBuffer, outLength);
2656 // Get our error if necessary
2659 // if the length is <= 0 there was an error
2660 iError = GetLastError();
2662 // Go ahead and return positive lengths/indexes so we don't get confused
2667 iError = 0; // ERROR_SUCCESS
2674 FCIMPL4( FC_BOOL_RET, COMNlsInfo::nativeNormalizationIsNormalizedString,
2675 int NormForm, int& iError,
2676 StringObject* chars, int inLength )
2681 PRECONDITION(CheckPointer(chars));
2684 STRINGREF inString(chars);
2685 LPWSTR charsBuffer = inString->GetBuffer();
2687 // The OS APIs do not always set last error in success, so we have to do it explicitly
2688 SetLastError(ERROR_SUCCESS);
2690 // Ask if its normalized
2691 BOOL bResult = m_pfnNormalizationIsNormalizedStringFunc( NormForm, charsBuffer, inLength);
2693 // May need an error
2694 if (bResult == false)
2696 // If its false there may have been an error
2697 iError = GetLastError();
2701 iError = 0; // ERROR_SUCCESS
2704 FC_RETURN_BOOL(bResult);
2708 void QCALLTYPE COMNlsInfo::nativeNormalizationInitNormalization(int NormForm, BYTE* pTableData)
2714 if (m_hNormalization == NULL)
2716 HMODULE hNormalization = NULL;
2718 if (pTableData == NULL)
2720 // Use OS implementation
2721 hNormalization = GetModuleHandleW(W("kernel32.dll"));
2722 if (!hNormalization)
2727 HRESULT hr = g_pCLRRuntime->LoadLibrary(NORMALIZATION_DLL, &hNormalization);
2732 _ASSERTE(hNormalization != NULL);
2733 m_hNormalization = hNormalization;
2736 if (m_pfnNormalizationIsNormalizedStringFunc == NULL)
2738 FARPROC pfn = GetProcAddress(m_hNormalization, "IsNormalizedString");
2741 m_pfnNormalizationIsNormalizedStringFunc = (PFN_NORMALIZATION_IS_NORMALIZED_STRING)pfn;
2744 if (m_pfnNormalizationNormalizeStringFunc == NULL)
2746 FARPROC pfn = GetProcAddress(m_hNormalization, "NormalizeString");
2749 m_pfnNormalizationNormalizeStringFunc = (PFN_NORMALIZATION_NORMALIZE_STRING)pfn;
2752 if (pTableData != NULL)
2754 if (m_pfnNormalizationInitNormalizationFunc == NULL)
2756 FARPROC pfn = GetProcAddress(m_hNormalization, "InitNormalization");
2759 m_pfnNormalizationInitNormalizationFunc = (PFN_NORMALIZATION_INIT_NORMALIZATION)pfn;
2762 BYTE* pResult = m_pfnNormalizationInitNormalizationFunc( NormForm, pTableData);
2763 if (pResult == NULL)
2770 #endif // FEATURE_CORECLR
2774 // This table should be sorted using case-insensitive ordinal order.
2775 // In the managed code, String.CompareStringOrdinalWC() is used to sort this.
2780 * This function returns the number of items in EncodingDataTable.
2782 FCIMPL0(INT32, COMNlsInfo::nativeGetNumEncodingItems)
2784 LIMITED_METHOD_CONTRACT;
2785 STATIC_CONTRACT_SO_TOLERANT;
2787 return (m_nEncodingDataTableItems);
2793 typedef CultureDataBaseObject* CULTUREDATAREF;
2795 // nativeInitCultureData checks with the OS to see if this is a valid culture.
2796 // If so we populate a limited number of fields. If its not valid we return false.
2798 // The fields we populate:
2800 // sWindowsName -- The name that windows thinks this culture is, ie:
2801 // en-US if you pass in en-US
2802 // de-DE_phoneb if you pass in de-DE_phoneb
2803 // fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
2804 // fj if you pass in fj (neutral, post-Windows 7 machine)
2806 // sRealName -- The name you used to construct the culture, in pretty form
2807 // en-US if you pass in EN-us
2808 // en if you pass in en
2809 // de-DE_phoneb if you pass in de-DE_phoneb
2811 // sSpecificCulture -- The specific culture for this culture
2814 // de-DE_phoneb for alt sort
2815 // fj-FJ for fj (neutral)
2817 // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
2818 // en-US if you pass in en-US
2819 // en if you pass in en
2820 // de-DE if you pass in de-DE_phoneb
2822 // bNeutral -- TRUE if it is a neutral locale
2824 // For a neutral we just populate the neutral name, but we leave the windows name pointing to the
2825 // windows locale that's going to provide data for us.
2827 FCIMPL1(FC_BOOL_RET, COMNlsInfo::nativeInitCultureData, CultureDataBaseObject *cultureDataUNSAFE)
2835 STRINGREF stringResult;
2836 CULTUREDATAREF cultureData;
2839 gc.stringResult = NULL;
2840 gc.cultureData = (CULTUREDATAREF) cultureDataUNSAFE;
2841 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
2843 WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
2846 StackSString realNameBuffer( ((STRINGREF)gc.cultureData->sRealName)->GetBuffer() );
2848 // Call GetLocaleInfoEx and see if the OS knows about it.
2849 // Note that GetLocaleInfoEx has variations:
2850 // * Pre-Vista it fails and has to go downlevel
2851 // * Vista succeeds, but not for neutrals
2852 // * Win7 succeeds for all locales.
2854 // The differences should be handled by the NewApis wrapper
2855 result = NewApis::GetLocaleInfoEx(realNameBuffer, LOCALE_SNAME, buffer, NumItems(buffer));
2860 // Not a real locale, fail
2864 // It worked, note that the name is the locale name, so use that (even for neutrals)
2865 // We need to clean up our "real" name, which should look like the windows name right now
2866 // so overwrite the input with the cleaned up name
2867 gc.stringResult = StringObject::NewString(buffer, result-1);
2868 SetObjectReference((OBJECTREF*)&(gc.cultureData->sRealName), gc.stringResult, NULL);
2870 // Check for neutrality, don't expect to fail
2871 // (buffer has our name in it, so we don't have to do the gc. stuff)
2873 if (0 == NewApis::GetLocaleInfoEx(buffer, LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER, (LPWSTR)&bNeutral, sizeof(bNeutral)/sizeof(WCHAR)))
2876 // Remember our neutrality
2877 gc.cultureData->bNeutral = (bNeutral != 0);
2879 gc.cultureData->bWin32Installed = (IsOSValidLocaleName(buffer, gc.cultureData->bNeutral) != 0);
2880 gc.cultureData->bFramework = (IsWhidbeyFrameworkCulture(buffer) != 0);
2883 // Note: Parents will be set dynamically
2885 // Start by assuming the windows name'll be the same as the specific name since windows knows
2886 // about specifics on all versions. For macs it also works. Only for downlevel Neutral locales
2887 // does this have to change.
2888 gc.stringResult = StringObject::NewString(buffer, result-1);
2889 SetObjectReference((OBJECTREF*)&(gc.cultureData->sWindowsName), gc.stringResult, NULL);
2891 // Neutrals and non-neutrals are slightly different
2892 if (gc.cultureData->bNeutral)
2896 // IETF name looks like neutral name
2897 gc.stringResult = StringObject::NewString(buffer, result-1);
2898 SetObjectReference((OBJECTREF*)&(gc.cultureData->sName), gc.stringResult, NULL);
2900 // Specific locale name is whatever ResolveLocaleName (win7+) returns.
2901 // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
2902 result = NewApis::ResolveLocaleName(buffer, buffer, NumItems(buffer));
2904 // 0 is failure, 1 is invariant (""), which we expect
2905 if (result < 1) goto Exit;
2907 // We found a locale name, so use it.
2908 // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
2909 gc.stringResult = StringObject::NewString(buffer, result - 1);
2910 SetObjectReference((OBJECTREF*)&(gc.cultureData->sSpecificCulture), gc.stringResult, NULL);
2912 #ifdef FEATURE_CORECLR
2915 // For neutrals on Windows 7 + the neutral windows name can be the same as the neutral name,
2916 // but on pre windows 7 names it has to be the specific, so we have to fix it in that case.
2917 gc.stringResult = StringObject::NewString(buffer, result - 1);
2918 SetObjectReference((OBJECTREF*)&(gc.cultureData->sWindowsName), gc.stringResult, NULL);
2928 // Specific culture's the same as the locale name since we know its not neutral
2929 // On mac we'll use this as well, even for neutrals. There's no obvious specific
2930 // culture to use and this isn't exposed, but behaviorally this is correct on mac.
2931 // Note that specifics include the sort name (de-DE_phoneb)
2932 gc.stringResult = StringObject::NewString(buffer, result-1);
2933 SetObjectReference((OBJECTREF*)&(gc.cultureData->sSpecificCulture), gc.stringResult, NULL);
2935 // We need the IETF name (sname)
2936 // If we aren't an alt sort locale then this is the same as the windows name.
2937 // If we are an alt sort locale then this is the same as the part before the _ in the windows name
2938 // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
2940 int localeNameLength = result - 1;
2942 LCID lcid = NewApis::LocaleNameToLCID(buffer, 0);
2943 if (!IsCustomCultureId(lcid))
2945 LPCWSTR index = wcschr(buffer, W('_'));
2946 if(index) // Not a custom culture and looks like an alt sort name
2948 // Looks like an alt sort, and has a appropriate sort LCID (so not custom), make it smaller for the RFC 4646 style name
2949 localeNameLength = static_cast<int>(index - buffer);
2953 gc.stringResult = StringObject::NewString(buffer, localeNameLength);
2954 _ASSERTE(gc.stringResult != NULL);
2956 // Now use that name
2957 SetObjectReference((OBJECTREF*)&(gc.cultureData->sName), gc.stringResult, NULL);
2960 #ifdef FEATURE_CORECLR
2961 // For Silverlight make sure that the sorting tables are available (< Vista may not have east asian installed)
2962 result = NewApis::CompareStringEx(((STRINGREF)gc.cultureData->sWindowsName)->GetBuffer(),
2963 0, W("A"), 1, W("B"), 1, NULL, NULL, 0);
2964 if (result == 0) goto Exit;
2972 HELPER_METHOD_FRAME_END();
2974 FC_RETURN_BOOL(success);
2979 // Return true if we're on Windows 7 (ie: if we have neutral native support)
2980 BOOL COMNlsInfo::IsWindows7()
2982 static BOOL bChecked=FALSE;
2983 static BOOL bIsWindows7=FALSE;
2987 // LOCALE_INEUTRAL is first supported on Windows 7
2988 if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEUTRAL, NULL, 0) != 0)
2990 // Success, we're win7
2994 // Either way we checked now
3002 // QCall implementation
3004 int QCALLTYPE COMNlsInfo::InternalFindNLSStringEx(
3005 __in_opt INT_PTR handle, // optional sort handle
3006 __in_opt INT_PTR handleOrigin,
3007 __in_z LPCWSTR lpLocaleName, // locale name
3008 __in int dwFindNLSStringFlags, // search falg
3009 __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in
3010 __in int cchSource, // number of characters lpStringSource after sourceIndex
3011 __in int sourceIndex, // index from where the search will start in lpStringSource
3012 __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for
3013 __in int cchValue) // length of the string we search for
3017 PRECONDITION(lpLocaleName != NULL);
3018 PRECONDITION(lpStringSource != NULL);
3019 PRECONDITION(lpStringValue != NULL);
3020 PRECONDITION(cchSource>=0);
3021 PRECONDITION(cchValue>=0);
3028 #ifndef FEATURE_CORECLR
3029 AppDomain* curDomain = GetAppDomain();
3030 handle = EnsureValidSortHandle(handle, handleOrigin, lpLocaleName);
3033 #define RESERVED_FIND_ASCII_STRING 0x20000000 // This flag used only to tell the sorting DLL can assume the string characters are in ASCII.
3035 #ifndef FEATURE_CORECLR
3036 int asciiFlag = (dwFindNLSStringFlags & RESERVED_FIND_ASCII_STRING);
3037 #endif // FEATURE_CORECLR
3039 dwFindNLSStringFlags &= ~RESERVED_FIND_ASCII_STRING;
3043 retValue = sourceIndex; // keep Whidbey compatibility
3047 if (sourceIndex<0 || cchSource<0 ||
3048 ((dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) && (sourceIndex+1<cchSource)))
3053 if (dwFindNLSStringFlags & COMPARE_OPTIONS_ORDINAL)
3055 if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
3057 retValue = NewApis::LastIndexOfString(
3059 &lpStringSource[sourceIndex - cchSource + 1],
3063 dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
3064 dwFindNLSStringFlags & FIND_ENDSWITH);
3067 retValue += sourceIndex - cchSource + 1;
3072 retValue = NewApis::IndexOfString(
3074 &lpStringSource[sourceIndex],
3078 dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
3079 dwFindNLSStringFlags & FIND_STARTSWITH);
3083 retValue += sourceIndex;
3089 if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
3091 #ifndef FEATURE_CORECLR
3092 if(!(curDomain->m_bUseOsSorting))
3094 retValue = SortVersioning::SortDllFindString((SortVersioning::PSORTHANDLE) handle,
3095 dwFindNLSStringFlags | asciiFlag,
3096 &lpStringSource[sourceIndex - cchSource + 1],
3102 else if(curDomain->m_pCustomSortLibrary != NULL)
3104 retValue = (curDomain->m_pCustomSortLibrary->pFindNLSStringEx)(
3105 handle != NULL ? NULL : lpLocaleName,
3106 dwFindNLSStringFlags,
3107 &lpStringSource[sourceIndex - cchSource + 1],
3110 cchValue, NULL, NULL, NULL, (LPARAM) handle);
3115 retValue = NewApis::FindNLSStringEx(
3116 handle != NULL ? NULL : lpLocaleName,
3117 dwFindNLSStringFlags,
3118 &lpStringSource[sourceIndex - cchSource + 1],
3121 cchValue, NULL, NULL, NULL, (LPARAM) handle);
3126 retValue += sourceIndex - cchSource + 1;
3131 #ifndef FEATURE_CORECLR
3132 if(!(curDomain->m_bUseOsSorting))
3134 retValue = SortVersioning::SortDllFindString((SortVersioning::PSORTHANDLE) handle,
3135 dwFindNLSStringFlags | asciiFlag,
3136 &lpStringSource[sourceIndex],
3142 else if(curDomain->m_pCustomSortLibrary != NULL)
3144 retValue = (curDomain->m_pCustomSortLibrary->pFindNLSStringEx)(
3145 handle != NULL ? NULL : lpLocaleName,
3146 dwFindNLSStringFlags,
3147 &lpStringSource[sourceIndex],
3150 cchValue, NULL, NULL, NULL, (LPARAM) handle);
3155 retValue = NewApis::FindNLSStringEx(
3156 handle != NULL ? NULL : lpLocaleName,
3157 dwFindNLSStringFlags,
3158 &lpStringSource[sourceIndex],
3161 cchValue, NULL, NULL, NULL, (LPARAM) handle);
3166 retValue += sourceIndex;
3179 int QCALLTYPE COMNlsInfo::InternalGetSortKey(
3180 __in_opt INT_PTR handle, // PSORTHANDLE
3181 __in_opt INT_PTR handleOrigin,
3182 __in_z LPCWSTR pLocaleName, // locale name
3183 __in int flags, // flags
3184 __in_ecount(cchSource) LPCWSTR pStringSource, // Source string
3185 __in int cchSource, // number of characters in lpStringSource
3186 __in_ecount(cchTarget) PBYTE pTarget, // Target data buffer (may be null to count)
3187 __in int cchTarget) // Character count for target buffer
3191 PRECONDITION(pLocaleName != NULL);
3192 PRECONDITION(pStringSource != NULL);
3193 // PRECONDITION(pTarget != NULL);
3194 PRECONDITION(cchSource>=0);
3195 // PRECONDITION(cchTarget>=0);
3203 #ifndef FEATURE_CORECLR
3204 handle = EnsureValidSortHandle(handle, handleOrigin, pLocaleName);
3206 AppDomain* curDomain = GetAppDomain();
3208 if(!(curDomain->m_bUseOsSorting))
3210 retValue = SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle,
3218 else if(curDomain->m_pCustomSortLibrary != NULL)
3220 retValue = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : pLocaleName,
3221 flags | LCMAP_SORTKEY,
3233 // Just call NewApis::LCMapStringEx to do our work
3234 retValue = NewApis::LCMapStringEx(handle != NULL ? NULL : pLocaleName,
3235 flags | LCMAP_SORTKEY,
3250 // We allow InternalInitSortHandle to return a NULL value
3251 // this is the case for Silverlight or when the appdomain has custom sorting.
3252 // So all the methods that take a SortHandle, also have to
3253 // be able to just call the slower api that looks up the tables based on the locale name
3254 INT_PTR QCALLTYPE COMNlsInfo::InternalInitSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3258 PRECONDITION(localeName != NULL);
3261 INT_PTR pSort = NULL;
3265 pSort = InitSortHandleHelper(localeName, handleOrigin);
3272 INT_PTR COMNlsInfo::InitSortHandleHelper(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3279 PRECONDITION(localeName != NULL);
3282 INT_PTR pSort = NULL;
3284 #ifndef FEATURE_CORECLR
3285 AppDomain* curDomain = GetAppDomain();
3288 _ASSERTE(curDomain->m_bSortingInitialized);
3291 if(curDomain->m_bUseOsSorting)
3293 pSort = InternalInitOsSortHandle(localeName, handleOrigin);
3297 pSort = InternalInitVersionedSortHandle(localeName, handleOrigin);
3300 // coreclr will try to initialize the handle and if running on downlevel it'll just return null
3301 pSort = InternalInitOsSortHandle(localeName, handleOrigin);
3302 #endif // FEATURE_CORECLR
3306 #ifndef FEATURE_CORECLR
3307 INT_PTR COMNlsInfo::InternalInitVersionedSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3314 PRECONDITION(localeName != NULL);
3317 AppDomain* curDomain = GetAppDomain();
3319 if(curDomain->m_pCustomSortLibrary != NULL)
3324 return InternalInitVersionedSortHandle(localeName, handleOrigin, curDomain->m_sortVersion);
3327 INT_PTR COMNlsInfo::InternalInitVersionedSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin, DWORD sortVersion)
3334 PRECONDITION(localeName != NULL);
3337 INT_PTR pSort = NULL;
3339 _ASSERTE(NewApis::NotLeakingFrameworkOnlyCultures(localeName));
3341 *handleOrigin = (INT_PTR) SortVersioning::GetSortHandle;
3343 // try the requested version
3344 if(sortVersion != DEFAULT_SORT_VERSION)
3346 NLSVERSIONINFO sortVersionInfo;
3347 sortVersionInfo.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
3348 sortVersionInfo.dwNLSVersion = sortVersion;
3349 sortVersionInfo.dwDefinedVersion = sortVersion;
3350 pSort = (INT_PTR) SortVersioning::GetSortHandle(localeName, &sortVersionInfo);
3353 // fallback to default version
3356 pSort = (INT_PTR) SortVersioning::GetSortHandle(localeName, NULL);
3359 _ASSERTE(RunningOnWin8() || pSort != NULL);
3363 #endif //FEATURE_CORECLR
3366 INT_PTR COMNlsInfo::InternalInitOsSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3373 PRECONDITION(localeName != NULL);
3376 INT_PTR pSort = NULL;
3378 AppDomain* curDomain = GetAppDomain();
3380 #ifndef FEATURE_CORECLR
3381 if (RunningOnWin8() || curDomain->m_pCustomSortLibrary != NULL)
3383 if (RunningOnWin8())
3384 #endif //FEATURE_CORECLR
3389 #ifndef FEATURE_CORECLR
3390 if (curDomain->m_pCustomSortLibrary != NULL)
3392 ret = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
3393 *handleOrigin = (INT_PTR) curDomain->m_pCustomSortLibrary->pLCMapStringEx;
3396 #endif //FEATURE_CORECLR
3398 ret = NewApis::LCMapStringEx(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
3399 *handleOrigin = (INT_PTR) NewApis::LCMapStringEx;
3404 pSort = (INT_PTR) lSortHandle;
3411 #ifndef FEATURE_CORECLR
3412 BOOL QCALLTYPE COMNlsInfo::InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation)
3422 AppDomain* curDomain = GetAppDomain();
3424 if(curDomain->m_bUseOsSorting)
3426 if(curDomain->m_pCustomSortLibrary != NULL)
3428 ret = (curDomain->m_pCustomSortLibrary->pGetNLSVersionEx)(COMPARE_STRING, lpLocaleName, lpVersionInformation);
3432 ret = GetNLSVersionEx(COMPARE_STRING, lpLocaleName, lpVersionInformation);
3437 handle = EnsureValidSortHandle(handle, handleOrigin, lpLocaleName);
3439 NLSVERSIONINFO nlsVersion;
3440 nlsVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
3442 ret = SortVersioning::SortGetNLSVersion((SortVersioning::PSORTHANDLE) handle, COMPARE_STRING, &nlsVersion);
3444 lpVersionInformation->dwNLSVersion = nlsVersion.dwNLSVersion;
3445 lpVersionInformation->dwDefinedVersion = nlsVersion.dwDefinedVersion;
3446 lpVersionInformation->dwEffectiveId = 0;
3447 ZeroMemory(&(lpVersionInformation->guidCustomVersion), sizeof(GUID));
3455 DWORD QCALLTYPE COMNlsInfo::InternalGetSortVersion()
3461 DWORD version = DEFAULT_SORT_VERSION;
3465 AppDomain* curDomain = GetAppDomain();
3466 version = curDomain->m_sortVersion;
3473 #endif //FEATURE_CORECLR