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 ////////////////////////////////////////////////////////////////////////////
6 // File: SortVersioning.cpp
10 // Purpose: Provides access of the sort versioning functionality on
11 // downlevel (pre-Win7) machines.
14 // This is not used on CoreCLR, where we always go to the OS
17 ////////////////////////////////////////////////////////////////////////////
20 #include "sortversioning.h"
24 #include "clrconfig.h"
26 #define SORT_VERSION_V4 0x00060101
27 #define SORT_VERSION_WHIDBEY 0x00001000
28 #define SORT_VERSION_DEFAULT SORT_VERSION_V4
29 #define SORT_DEFAULT_DLL_NAME MAKEDLLNAME(W("nlssorting"))
31 namespace SortVersioning
33 #define SORT_HASH_TBL_SIZE 128
36 // Forward Declarations
38 PSORTHANDLE MakeSortHashNode(
39 __in LPCWSTR pSortName,
40 __in DWORD dwVersion);
43 PSORTHANDLE InsertSortHashNode(
44 __in PSORTHANDLE pHashN);
46 static PSORTHANDLE g_pSortHash[SORT_HASH_TBL_SIZE]; // Sort node hash table
48 static HMODULE g_hSortDefault = (HMODULE)-1;
50 __encoded_pointer static SORTGETHANDLE g_pDefaultGetHandle;
51 __encoded_pointer static SORTCLOSEHANDLE g_pDefaultCloseHandle;
53 static HMODULE g_hSortCompatV2 = (HMODULE)-1;
55 __encoded_pointer static SORTGETHANDLE g_pV2GetHandle;
56 __encoded_pointer static SORTCLOSEHANDLE g_pV2CloseHandle;
58 static HMODULE g_hSortCompatV4 = (HMODULE)-1;
60 __encoded_pointer static SORTGETHANDLE g_pV4GetHandle;
61 __encoded_pointer static SORTCLOSEHANDLE g_pV4CloseHandle;
64 ////////////////////////////////////////////////////////////////////////////
66 // NlsCompareInvariantNoCase
68 // This routine does fast caseless comparison without needing the tables.
69 // This helps us do the comparisons we need to load the tables :-)
71 // Returns 0 if identical, <0 if pFirst if first string sorts first.
73 // This is only intended to help with our locale name comparisons,
74 // which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
77 // WARNING: [\]^_` will be less than A-Z because we make everything lower
78 // case before comparing them.
80 // When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
81 // When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
82 // or when null terminators are reached, whichever happens first (strncmp-like behavior)
84 ////////////////////////////////////////////////////////////////////////////
85 int NlsCompareInvariantNoCase(
96 size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
97 size--, pFirst++, pSecond++)
99 // Make them lower case
100 if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
101 if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
104 i = (first - second);
106 // Are they the same?
110 // Otherwise the difference. Remember we made A-Z into lower case, so
111 // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the
112 // characters between A-Z and a-Z in ascii)
116 // When we are here, one of these holds:
118 // or one of the strings has a null terminator
119 // or both of the string reaches null terminator
121 if (bNullEnd || size != 0)
123 // If bNullEnd is TRUE, always check for null terminator.
124 // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
125 // than another (hense the size != 0 check).
127 // See if one string ended first
128 if (*pFirst != 0 || *pSecond != 0)
131 return *pFirst == 0 ? -1 : 1;
135 // Return our difference (0)
139 #if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
140 ////////////////////////////////////////////////////////////////////////////
142 // LoadSortModuleAndInvariant()
144 // Attempts to load the given dll. If that is successful, attempts to
145 // load the invariant data. If that is successful, returns the module handle
146 // and the addresses of the SortGetHandle function and SortCloseHandle function
148 // failure is indicated by returning NULL for the module handle and the
149 // function addresses
151 ////////////////////////////////////////////////////////////////////////////
153 HMODULE LoadSortModuleAndInvariant(
154 __in LPCWSTR sDllName,
155 __in DWORD dwVersion,
156 __out SORTGETHANDLE* ppGetHandle,
157 __out SORTCLOSEHANDLE* ppCloseHandle
161 *ppCloseHandle = NULL;
164 if(FAILED(UtilCode::LoadLibraryShim(sDllName, NULL, NULL, &hSort)))
169 SORTGETHANDLE pGetHandle = (SORTGETHANDLE)GetProcAddress(hSort, "SortGetHandle");
170 SORTCLOSEHANDLE pCloseHandle = (SORTCLOSEHANDLE)GetProcAddress(hSort, "SortCloseHandle");
172 // If we didn't load the procs, then remember that
173 if (pCloseHandle == NULL || pGetHandle == NULL)
175 ::FreeLibrary(hSort);
179 // Verify that the data file's available
180 NLSVERSIONINFO sortVersion;
181 sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
182 sortVersion.dwNLSVersion = dwVersion;
183 sortVersion.dwDefinedVersion = dwVersion;
185 // Invariant must be there and is kinda common, so we'll just get it
186 PSORTHANDLE pSort = pGetHandle(W(""), &sortVersion, NULL);
189 // Yikes, invariant failed, forget about it.
190 ::FreeLibrary(hSort);
194 // Since we found it, may as well remember it.
195 const SORTHANDLE * const pSortInHash = InsertSortHashNode(pSort);
197 // If we got a different one back then free the one we added
198 if (pSortInHash != pSort && pSortInHash)
200 // We got a different one from the hash (someone beat us to the cache)
201 // so use that and discard the new one.
205 *ppGetHandle = pGetHandle;
206 *ppCloseHandle = pCloseHandle;
210 // Attempts to load a Sort DLL. If this fails, the values of the __out parameters are unchanged.
212 BOOL LoadSortDllAndPublish(
213 __in LPCWSTR sDllName,
214 __in DWORD dwVersion,
215 __out __encoded_pointer SORTGETHANDLE* ppGetHandle,
216 __out __encoded_pointer SORTCLOSEHANDLE* ppCloseHandle,
217 __out HMODULE* phSortDll)
220 SORTGETHANDLE pGetHandle;
221 SORTCLOSEHANDLE pCloseHandle;
223 hSortDll = LoadSortModuleAndInvariant(sDllName, dwVersion, &pGetHandle, &pCloseHandle);
225 if(hSortDll != NULL) {
226 *phSortDll = hSortDll;
227 *ppGetHandle = (SORTGETHANDLE)EncodePointer(pGetHandle);
228 *ppCloseHandle = (SORTCLOSEHANDLE)EncodePointer(pCloseHandle);
235 ////////////////////////////////////////////////////////////////////////////
237 // GetSortGetHandle()
239 // Get the SortGetHandle() function for the proper dll version.
241 ////////////////////////////////////////////////////////////////////////////
242 SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
244 if(dwVersion == SORT_VERSION_DEFAULT)
246 // If we haven't tried to load the module/proc before do so now
247 if (g_hSortDefault == (HMODULE)-1)
249 LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault);
252 // This check is necessary because the LoadSortDllAndPublish call may have failed since some platforms
253 // won't have nlssorting.dll (e.g. Windows 8 and above).
254 if (g_hSortDefault != (HMODULE)-1)
256 return (SORTGETHANDLE)DecodePointer(g_pDefaultGetHandle);
260 HMODULE* pHSortModule;
261 SORTGETHANDLE* ppGetHandle;
262 SORTCLOSEHANDLE* ppCloseHandle;
264 if(dwVersion == SORT_VERSION_V4)
266 ppGetHandle = &g_pV4GetHandle;
267 ppCloseHandle = &g_pV4CloseHandle;
268 pHSortModule = &g_hSortCompatV4;
270 else if(dwVersion == SORT_VERSION_WHIDBEY)
272 ppGetHandle = &g_pV2GetHandle;
273 ppCloseHandle = &g_pV2CloseHandle;
274 pHSortModule = &g_hSortCompatV2;
278 // Unsupported sorting version.
282 if(*pHSortModule == (HMODULE) -1)
284 // get module name - the module name should be "Sort"+dwVersion.ToString("x8")+".dll"
285 WCHAR moduleName[] = W("Sort00000000.dll");
286 // replace the "00000000" with the hexadecimal of dwVersion
287 LPCWSTR hex = W("0123456789abcdef");
288 WCHAR* p = &moduleName[4+8]; // position at end of number part of dll name
290 unsigned int value = dwVersion;
292 while (value != 0 && p != moduleName) {
293 int digit = value & 0xF;
298 if(!LoadSortDllAndPublish(&moduleName[0], dwVersion, ppGetHandle, ppCloseHandle, pHSortModule))
300 // We failed to load a versioned sort dll, try to fall back to the current version.
301 // If we haven't tried to load the module/proc before do so now
302 if (g_hSortDefault == (HMODULE)-1)
304 LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault);
307 *pHSortModule = g_hSortDefault;
308 *ppCloseHandle = g_pDefaultCloseHandle;
309 *ppGetHandle = g_pDefaultGetHandle;
313 // At this point, we've either loaded a sorting dll or we've exausted all options.
314 if(*pHSortModule == (HMODULE) -1)
316 // Couldn't find anything, give up.
321 return (SORTGETHANDLE)DecodePointer(*ppGetHandle);
324 // Unknown version requested
328 void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
330 if (dwVersion == SORT_VERSION_DEFAULT)
332 SORTCLOSEHANDLE pDefaultCloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pDefaultCloseHandle);
333 if(pDefaultCloseHandle != NULL)
335 (pDefaultCloseHandle)(pSort);
340 if (dwVersion == SORT_VERSION_V4)
342 SORTCLOSEHANDLE pV4CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV4CloseHandle);
343 if(pV4CloseHandle != NULL)
345 (pV4CloseHandle)(pSort);
351 if (dwVersion == SORT_VERSION_WHIDBEY)
353 SORTCLOSEHANDLE pV2CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV2CloseHandle);
354 if(pV2CloseHandle != NULL)
356 (pV2CloseHandle)(pSort);
362 #else // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
364 SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
369 void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
373 #endif // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
375 ////////////////////////////////////////////////////////////////////////////
379 // Returns the hash value for given sort name & version.
381 // WARNING: This must be case insensitive. Currently we're expecting only
382 // a-z, A-Z, 0-9 & -.
384 ////////////////////////////////////////////////////////////////////////////
385 __inline __range(0, SORT_HASH_TBL_SIZE-1) int GetSortHashValue(
386 __in LPCWSTR pSortName,
387 __in DWORD dwVersion)
389 int iHash = 12; // Seed hash value
390 int iMax; // Number of characters to count (prevent problems with too-bad strings)
395 for (iMax = 10; *pSortName != 0 && iMax != 0; pSortName++, iMax--)
398 iHash ^= ((*pSortName) & 0xdf); // 0x20 will make cases be the same (and other wierd stuff too, but we don't care about that)
402 // Add the version hash
403 // (the middle 2 bytes are most interesting)
404 iHash ^= dwVersion >> 8;
406 // Mix up our bits and hash it with 128
407 _ASSERT(SORT_HASH_TBL_SIZE == 128);
408 return (iHash + (iHash >> 8)) & 0x7f;
412 ////////////////////////////////////////////////////////////////////////////
414 // InsertSortHashNode
416 // Inserts a sort hash node into the global sort hash tables. It assumes
417 // that all unused hash values in the table are pointing to NULL. If
418 // there is a collision, the new node will be added LAST in the list.
419 // (Presuming that the most often used are also the first used)
421 // We do an interlocked exchange and free the pointer if we can't add it.
423 // Warning: We stick stuff in this list, but we never remove it, so it
424 // get kind of big. Removing entries would be difficult however
425 // because it would require some sort of synchronization with the
426 // reader functions (like GetLocaleInfo), or maybe an in-use flag
429 ////////////////////////////////////////////////////////////////////////////
430 PSORTHANDLE InsertSortHashNode(PSORTHANDLE pHashN)
432 __range(0, SORT_HASH_TBL_SIZE-1) UINT index;
434 PSORTHANDLE* pNextToUpdate;
437 // Insert the hash node into the list (by name/version)
440 #pragma warning(push)
441 #pragma warning(disable: 26037) // Prefast warning - Possible precondition violation due to failure to null terminate string - GetSortHashValue only uses first 10 characters and sortName is null terminated
443 index = GetSortHashValue(pHashN->sortName, pHashN->dwNLSVersion);
449 pSearch = g_pSortHash[index];
451 // Remember last pointer in case we need to add it
452 pNextToUpdate = &g_pSortHash[index];
454 // We'll be the last node when added
455 pHashN->pNext = NULL;
459 while (pSearch != NULL)
461 // See if we already found a node.
462 if ((pSearch->dwNLSVersion == pHashN->dwNLSVersion) &&
463 NlsCompareInvariantNoCase( pSearch->sortName, pHashN->sortName,
464 LOCALE_NAME_MAX_LENGTH, TRUE) == 0)
466 // Its the same, which is unexpected, return the old one
470 pNextToUpdate = &pSearch->pNext;
471 pSearch = pSearch->pNext;
474 // At end, try to add our node
475 pSearch = InterlockedCompareExchangeT(pNextToUpdate, pHashN, NULL);
477 // If pNextToUpdate isn't NULL then another process snuck in and updated the list
478 // while we were getting ready.
481 // It was added, stop
485 // It wasn't added, pSearch now points to a new node that snuck in, so
486 // continue and try that one. This should be really rare, even in a busy
487 // loop, so we don't try a real lock. Either
488 // a) the snuck in node is the same as pHashN, and we'll return pSearch
489 // in the first loop, or
490 // b) the snuck in node is new, in which case we'll try to readd. Very worst
491 // case we'd collide while someone added ALL of the other locales with our
492 // hash, but eventually we'd hit case a. (And there's only a couple hundred
493 // tries, so this can't lock for long.)
496 // Return the same one we added
501 ////////////////////////////////////////////////////////////////////////////
505 // Searches for the sort hash node for the given sort name & version.
506 // The result is returned. If none are found NULL is returned.
508 // NOTE: Call GetSortNode() which calls this.
510 // Defined as inline.
512 ////////////////////////////////////////////////////////////////////////////
514 __inline PSORTHANDLE FindSortHashNode(
515 __in LPCWSTR pSortName,
516 __in DWORD dwVersion)
519 __range(0,SORT_HASH_TBL_SIZE-1) int index;
522 index = GetSortHashValue(pSortName, dwVersion);
525 pHashN = g_pSortHash[index];
527 // Look through the list to see if one matches name and user info
528 // We're sneaky here because we know our length of our hash name string is stored
529 // just before that string.
530 while ((pHashN != NULL) &&
531 ((dwVersion != pHashN->dwNLSVersion) ||
532 (NlsCompareInvariantNoCase(pSortName, pHashN->sortName, LOCALE_NAME_MAX_LENGTH, TRUE) != 0)))
534 pHashN = pHashN->pNext;
541 ////////////////////////////////////////////////////////////////////////////
545 // Builds a sort hash node and sticks it in the hash table.
547 // NOTE: Call GetSortNode() which calls this.
549 // Defined as inline.
551 ////////////////////////////////////////////////////////////////////////////
552 PSORTHANDLE MakeSortHashNode(
553 __in LPCWSTR pSortName,
554 __in DWORD dwVersion)
556 NLSVERSIONINFO sortVersion;
558 PSORTHANDLE pSort = NULL;
559 PSORTHANDLE pSortInHash;
561 // Valid locale, now we need to find out where to point this version at
562 SORTGETHANDLE pGetHandle = GetSortGetHandle(dwVersion);
563 if (pGetHandle == NULL) return NULL;
565 sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
566 sortVersion.dwNLSVersion = dwVersion;
567 sortVersion.dwDefinedVersion = dwVersion;
569 pSort = pGetHandle(pSortName, &sortVersion, NULL);
571 // If still missing, fail
574 // Invalid sort, fail
578 // Now we need to add it
579 pSortInHash = InsertSortHashNode(pSort);
581 // If we got a different one back then free the one we added
582 if (pSortInHash != pSort && pSortInHash)
584 // We got a different one from the hash (someone beat us to the cache)
585 // so use that and discard the new one.
586 DoSortCloseHandle(dwVersion, pSort);
593 ////////////////////////////////////////////////////////////////////////////
597 // Get a sort hash node for the specified sort name & version
599 ////////////////////////////////////////////////////////////////////////////
600 PSORTHANDLE GetSortNode(
601 __in LPCWSTR pSortName,
602 __in DWORD dwVersion)
604 PSORTHANDLE pSortHashN = NULL;
606 // WARNING: We don't bother doing the null/default/system checks
608 // Didn't have an obvious one, look in the hash table
609 pSortHashN = FindSortHashNode(pSortName, dwVersion);
612 // If the hash node does not exist, we may need to get make one
614 if (pSortHashN == NULL)
617 // Hash node does NOT exist, try to make it
620 pSortHashN = MakeSortHashNode(pSortName, dwVersion);
624 // If the hash node still does not exist, we may need to fallback to default
627 if (pSortHashN == NULL && dwVersion != SORT_VERSION_DEFAULT)
629 return GetSortNode(pSortName, SORT_VERSION_DEFAULT);
633 // Return pointer to hash node
634 // (null if we still don't have one)
639 ////////////////////////////////////////////////////////////////////////////
642 // Check for the DWORD "CompatSortNLSVersion" CLR config option.
644 // .Net 4.0 introduces sorting changes that can affect the behavior of any of the methods
645 // in CompareInfo. To mitigate against compatibility problems Applications can enable the
646 // legacy CompareInfo behavior by using the 'SortNLSVersion' configuration option
648 // There are three ways to use the configuration option:
650 // 1) Config file (MyApp.exe.config)
651 // <?xml version ="1.0"?>
654 // <CompatSortNLSVersion enabled="4096"/><!--0x00001000 -->
657 // 2) Environment variable
658 // set COMPLUS_CompatSortNLSVersion=4096
660 // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
661 // "CompatSortNLSVersion"=dword:00001000
663 ////////////////////////////////////////////////////////////////////////////
664 DWORD SortNLSVersion()
666 #ifdef FEATURE_CORECLR
667 return SORT_VERSION_DEFAULT;
669 static bool sortNLSVersionConfigChecked = false;
670 static DWORD sortNLSVersion = SORT_VERSION_DEFAULT;
672 if(!sortNLSVersionConfigChecked)
674 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
675 sortNLSVersion = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CompatSortNLSVersion);
676 if(sortNLSVersion == 0)
678 sortNLSVersion = SORT_VERSION_DEFAULT;
680 END_SO_INTOLERANT_CODE;
682 sortNLSVersionConfigChecked = true;
684 return sortNLSVersion;
685 #endif // !FEATURE_CORECLR
688 ////////////////////////////////////////////////////////////////////////////
692 // Get the version from a version blob, resolving to the default version
695 ////////////////////////////////////////////////////////////////////////////
696 __inline DWORD VersionValue(__in_opt const NLSVERSIONINFO * const lpVersionInformation)
700 // If the caller passed null or zero we use the default version
702 if ((lpVersionInformation == NULL) ||
703 ((lpVersionInformation->dwNLSVersion == 0) &&
704 (lpVersionInformation->dwDefinedVersion ==0))
707 return SortNLSVersion();
710 // TODO: Will need to review this
711 if(((lpVersionInformation->dwNLSVersion == 0) &&
712 (lpVersionInformation->dwDefinedVersion != 0 )))
714 return lpVersionInformation->dwDefinedVersion;
717 return lpVersionInformation->dwNLSVersion;
720 ////////////////////////////////////////////////////////////////////////////
724 // Supposed to call the dll for the appropriate version. If the default
725 // version isn't available call the ordinal behavior (for minwin)
727 // Just get the sort hash node and call the worker function
729 ////////////////////////////////////////////////////////////////////////////
730 __success(return != 0) int WINAPI SortGetSortKey(
731 __in LPCWSTR pLocaleName,
733 __in_ecount(cchSrc) LPCWSTR pSrc,
735 __out_bcount_opt(cbDest) LPBYTE pDest,
737 __in_opt CONST NLSVERSIONINFO *lpVersionInformation,
738 __in_opt LPVOID lpReserved,
739 __in_opt LPARAM lParam
742 PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
743 return SortDllGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
746 // SortDllGetSortKey handles any modification to flags
747 // necessary before the actual call to the dll
748 __success(return != 0) int WINAPI SortDllGetSortKey(
749 __in PSORTHANDLE pSort,
751 __in_ecount(cchSrc) LPCWSTR pSrc,
753 __out_bcount_opt(cbDest) LPBYTE pDest,
755 __in_opt LPVOID lpReserved,
756 __in_opt LPARAM lParam )
760 SetLastError(ERROR_INVALID_PARAMETER);
765 // Note that GetSortKey'll have the opposite behavior for the
766 // linguistic casing flag (eg: use flag for bad behavior, linguistic
768 dwFlags ^= NORM_LINGUISTIC_CASING;
770 return pSort->pSortGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
773 __success(return != 0) int SortDllGetHashCode(
774 __in PSORTHANDLE pSort,
776 __in_ecount(cchSrc) LPCWSTR pSrc,
778 __in_opt LPVOID lpReserved,
779 __in_opt LPARAM lParam )
783 SetLastError(ERROR_INVALID_PARAMETER);
786 const int SortDllGetHashCodeApiIntroducedVersion = 2;
787 if(pSort->dwSHVersion < SortDllGetHashCodeApiIntroducedVersion)
789 SetLastError(ERROR_NOT_SUPPORTED);
794 // Note that GetSortKey'll have the opposite behavior for the
795 // linguistic casing flag (eg: use flag for bad behavior, linguistic
797 dwFlags ^= NORM_LINGUISTIC_CASING;
799 return pSort->pSortGetHashCode(pSort, dwFlags, pSrc, cchSrc, lpReserved, lParam);
803 ////////////////////////////////////////////////////////////////////////////
807 // Supposed to call the dll for the appropriate version. If the default
808 // version isn't available call the ordinal behavior (for minwin)
810 // NOTE: The linguistic casing flags are backwards (ie: set the flag to
811 // get the non-linguistic behavior.) If we expose this then we'll
812 // need to publish the no-linguistic flag.
814 // Just get the sort hash node and call the worker function
816 ////////////////////////////////////////////////////////////////////////////
817 __success(return != 0) int WINAPI SortChangeCase(
818 __in LPCWSTR pLocaleName,
820 __in_ecount(cchSrc) LPCWSTR pSrc,
822 __out_ecount_opt(cchDest) LPWSTR pDest,
824 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
825 __in_opt LPVOID lpReserved,
826 __in_opt LPARAM lParam)
828 PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
829 return SortDllChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
832 // SortDllChangeCase handles any modification to flags
833 // necessary before the actual call to the dll
834 __success(return != 0) int WINAPI SortDllChangeCase(
835 __in PSORTHANDLE pSort,
837 __in_ecount(cchSrc) LPCWSTR pSrc,
839 __out_ecount_opt(cchDest) LPWSTR pDest,
841 __in_opt LPVOID lpReserved,
842 __in_opt LPARAM lParam)
846 SetLastError(ERROR_INVALID_PARAMETER);
850 // Note that Change Case'll have the opposite behavior for the
851 // linguistic casing flag (eg: use flag for bad behavior, linguistic
853 dwFlags ^= LCMAP_LINGUISTIC_CASING;
855 #pragma warning(push)
856 #pragma warning(disable: 26036) // prefast - Possible postcondition violation due to failure to null terminate string
858 return pSort->pSortChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
864 ////////////////////////////////////////////////////////////////////////////
868 // Supposed to call the dll for the appropriate version. If the default
869 // version isn't available call the ordinal behavior (for minwin)
871 // Just get the sort hash node and call the worker function
873 ////////////////////////////////////////////////////////////////////////////
874 __success(return != 0) int WINAPI SortCompareString(
875 __in LPCWSTR lpLocaleName,
876 __in DWORD dwCmpFlags,
877 __in_ecount(cchCount1) LPCWSTR lpString1,
879 __in_ecount(cchCount2) LPCWSTR lpString2,
881 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
882 __in_opt LPVOID lpReserved,
883 __in_opt LPARAM lParam)
885 PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
886 return SortDllCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
890 // SortDllCompareString handles any modification to flags
891 // necessary before the actual call to the dll
892 __success(return != 0) int WINAPI SortDllCompareString(
893 __in PSORTHANDLE pSort,
894 __in DWORD dwCmpFlags,
895 __in_ecount(cchCount1) LPCWSTR lpString1,
897 __in_ecount(cchCount2) LPCWSTR lpString2,
899 __in_opt LPVOID lpReserved,
900 __in_opt LPARAM lParam)
904 SetLastError(ERROR_INVALID_PARAMETER);
908 // Note that the dll will have the opposite behavior of CompareStringEx for the
909 // linguistic casing flag (eg: use flag for bad behavior, linguistic
910 // by default) because we want new public APIs to have the "right"
911 // behavior by default
912 dwCmpFlags ^= NORM_LINGUISTIC_CASING;
914 return pSort->pSortCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
917 ////////////////////////////////////////////////////////////////////////////
921 // Finds lpStringValue within lpStringSource based on the rules given
922 // in dwFindNLSStringFlags.
924 // Supposed to call the dll for the appropriate version. If the default
925 // version isn't available call the ordinal behavior (for minwin)
927 // Just get the sort hash node and call the worker function
929 ////////////////////////////////////////////////////////////////////////////
930 __success(return != 0) int WINAPI SortFindString(
931 __in LPCWSTR lpLocaleName,
932 __in DWORD dwFindNLSStringFlags,
933 __in_ecount(cchSource) LPCWSTR lpStringSource,
935 __in_ecount(cchValue) LPCWSTR lpStringValue,
937 __out_opt LPINT pcchFound,
938 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
939 __in_opt LPVOID lpReserved,
940 __in_opt LPARAM lParam)
942 PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
943 return SortDllFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound, lpReserved, lParam);
946 // SortDllFindString handles any modification to flags
947 // necessary before the actual call to the dll
948 __success(return != 0) int WINAPI SortDllFindString(
949 __in PSORTHANDLE pSort,
950 __in DWORD dwFindNLSStringFlags,
951 __in_ecount(cchSource) LPCWSTR lpStringSource,
953 __in_ecount(cchValue) LPCWSTR lpStringValue,
955 __out_opt LPINT pcchFound,
956 __in_opt LPVOID lpReserved,
957 __in_opt LPARAM lParam)
961 SetLastError(ERROR_INVALID_PARAMETER);
965 // Note that the dll will have the opposite behavior of FindNlsString for the
966 // linguistic casing flag (eg: use flag for bad behavior, linguistic
967 // by default) because we want new public APIs to have the "right"
968 // behavior by default
969 dwFindNLSStringFlags ^= NORM_LINGUISTIC_CASING;
971 int cchFound; // we need to get the length even if the caller doesn't care about it (see below)
972 int result = pSort->pSortFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound, lpReserved, lParam);
973 // When searching from end with an empty pattern (either empty string or all ignored characters)
974 // a match is found (result != -1)
975 // Currently we get a result == 0 but we are hoping this will change
976 // with Win7 to be the length of the source string (thus pointing past-the-end)
977 // and the length of the match (cchFound) will be 0
978 // For compatibility, we need to return the index of the last character (or 0 if the source is empty)
979 if((dwFindNLSStringFlags & FIND_FROMEND) &&
984 result = cchSource - 1;
987 // if the caller cares about the length, give it to them
988 if(pcchFound != NULL)
990 *pcchFound = cchFound;
997 ////////////////////////////////////////////////////////////////////////////
999 // SortIsDefinedString
1001 // This routine looks for code points inside a string to see if they are
1002 // defined within the NSL context. If lpVersionInformation is NULL, the
1003 // version is the current version. Same thing the dwDefinedVersion is equal
1006 // Supposed to call the dll for the appropriate version. If the default
1007 // version isn't available call the ordinal behavior (for minwin)
1009 // Just get the sort hash node and call the worker function
1011 ////////////////////////////////////////////////////////////////////////////
1012 BOOL WINAPI SortIsDefinedString(
1013 __in NLS_FUNCTION Function,
1015 __in CONST NLSVERSIONINFOEX * lpVersionInformation,
1016 __in_ecount(cchStr) LPCWSTR lpString,
1019 // Get an invariant sort node
1020 PSORTHANDLE pSort = GetSortNode(W(""), VersionValue((CONST NLSVERSIONINFO *)lpVersionInformation));
1021 return SortDllIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
1024 // SortDllIsDefinedString handles any modification to flags
1025 // necessary before the actual call to the dll
1026 BOOL WINAPI SortDllIsDefinedString(
1027 __in PSORTHANDLE pSort,
1028 __in NLS_FUNCTION Function,
1030 __in_ecount(cchStr) LPCWSTR lpString,
1033 // Fail if we couldn't find one
1036 SetLastError(ERROR_INVALID_PARAMETER);
1040 return pSort->pSortIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
1043 BOOL SortGetNLSVersion(__in PSORTHANDLE pSort,
1044 __in NLS_FUNCTION Function,
1045 __inout NLSVERSIONINFO * lpVersionInformation )
1047 lpVersionInformation->dwNLSVersion = pSort->dwNLSVersion;
1048 lpVersionInformation->dwDefinedVersion = pSort->dwDefinedVersion;
1053 // Wrapper for SortGetSortKey and SortChangeCase, which are both
1054 // smushed into LCMapStringEx
1055 __success(return != 0) int
1056 LCMapStringEx (__in LPCWSTR lpLocaleName,
1057 __in DWORD dwMapFlags,
1058 __in_ecount(cchSrc) LPCWSTR lpSrcStr,
1060 __out_ecount_opt(cchDest) LPWSTR lpDestStr, // really this should be __out_awcount_opt(dwMapFlags & LCMAP_SORTKEY, cchDest)
1062 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
1063 __in_opt LPVOID lpReserved,
1064 __in_opt LPARAM lParam )
1066 // Should be either sort key...
1067 if (dwMapFlags & LCMAP_SORTKEY)
1070 #pragma warning(push)
1071 #pragma warning(disable: 26036) // Prefast - Possible precondition violation due to failure to null terminate string lpDestStr-interpreted differently depending on flag
1073 return SortGetSortKey(lpLocaleName,
1074 dwMapFlags & ~(LCMAP_SORTKEY), // Don't need sort key flag
1077 (LPBYTE)lpDestStr, // Sort keys are bytes not WCHARs
1078 cchDest, // Sort keys are bytes not WCHARs
1079 lpVersionInformation,
1083 #pragma warning(pop)
1088 // Check for changing case conditions. This may be combined with Chinese or Japanese
1089 // transliteration, but not with sort key nor ignore space/symbols
1091 _ASSERT(dwMapFlags & (LCMAP_TITLECASE | LCMAP_UPPERCASE | LCMAP_LOWERCASE));
1094 // Call casing wrapper, which'll either call the correct version dll
1095 // or call ordinal behavior in the minwin case
1097 return SortChangeCase(lpLocaleName,
1098 dwMapFlags & ~(LCMAP_BYTEREV),
1103 lpVersionInformation,
1109 ////////////////////////////////////////////////////////////////////////////
1111 // IsAvailableVersion()
1113 // Get the SortGetHandle() function for the proper dll version.
1115 ////////////////////////////////////////////////////////////////////////////
1116 BOOL IsAvailableVersion(__in_opt CONST NLSVERSIONINFO * pVersion)
1118 return GetSortGetHandle(VersionValue(pVersion)) != NULL;
1122 ////////////////////////////////////////////////////////////////////////////
1126 // Get the SortHandle for the given locale and version
1128 ////////////////////////////////////////////////////////////////////////////
1129 PSORTHANDLE GetSortHandle(__in LPCWSTR lpLocaleName, __in_opt CONST NLSVERSIONINFO * pVersion)
1131 DWORD version = VersionValue(pVersion);
1132 if (GetSortGetHandle(version) == NULL)
1136 return GetSortNode(lpLocaleName, version);