2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 ////////////////////////////////////////////////////////////////////////////
7 // File: SortVersioning.cpp
11 // Purpose: Provides access of the sort versioning functionality on
12 // downlevel (pre-Win7) machines.
15 // This is not used on CoreCLR, where we always go to the OS
18 ////////////////////////////////////////////////////////////////////////////
21 #include "sortversioning.h"
25 #include "clrconfig.h"
27 #define SORT_VERSION_V4 0x00060101
28 #define SORT_VERSION_WHIDBEY 0x00001000
29 #define SORT_VERSION_DEFAULT SORT_VERSION_V4
30 #define SORT_DEFAULT_DLL_NAME MAKEDLLNAME(W("nlssorting"))
32 namespace SortVersioning
34 #define SORT_HASH_TBL_SIZE 128
37 // Forward Declarations
39 PSORTHANDLE MakeSortHashNode(
40 __in LPCWSTR pSortName,
41 __in DWORD dwVersion);
44 PSORTHANDLE InsertSortHashNode(
45 __in PSORTHANDLE pHashN);
47 static PSORTHANDLE g_pSortHash[SORT_HASH_TBL_SIZE]; // Sort node hash table
49 static HMODULE g_hSortDefault = (HMODULE)-1;
51 __encoded_pointer static SORTGETHANDLE g_pDefaultGetHandle;
52 __encoded_pointer static SORTCLOSEHANDLE g_pDefaultCloseHandle;
54 static HMODULE g_hSortCompatV2 = (HMODULE)-1;
56 __encoded_pointer static SORTGETHANDLE g_pV2GetHandle;
57 __encoded_pointer static SORTCLOSEHANDLE g_pV2CloseHandle;
59 static HMODULE g_hSortCompatV4 = (HMODULE)-1;
61 __encoded_pointer static SORTGETHANDLE g_pV4GetHandle;
62 __encoded_pointer static SORTCLOSEHANDLE g_pV4CloseHandle;
65 ////////////////////////////////////////////////////////////////////////////
67 // NlsCompareInvariantNoCase
69 // This routine does fast caseless comparison without needing the tables.
70 // This helps us do the comparisons we need to load the tables :-)
72 // Returns 0 if identical, <0 if pFirst if first string sorts first.
74 // This is only intended to help with our locale name comparisons,
75 // which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
78 // WARNING: [\]^_` will be less than A-Z because we make everything lower
79 // case before comparing them.
81 // When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
82 // When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
83 // or when null terminators are reached, whichever happens first (strncmp-like behavior)
85 ////////////////////////////////////////////////////////////////////////////
86 int NlsCompareInvariantNoCase(
97 size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
98 size--, pFirst++, pSecond++)
100 // Make them lower case
101 if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
102 if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
105 i = (first - second);
107 // Are they the same?
111 // Otherwise the difference. Remember we made A-Z into lower case, so
112 // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the
113 // characters between A-Z and a-Z in ascii)
117 // When we are here, one of these holds:
119 // or one of the strings has a null terminator
120 // or both of the string reaches null terminator
122 if (bNullEnd || size != 0)
124 // If bNullEnd is TRUE, always check for null terminator.
125 // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
126 // than another (hense the size != 0 check).
128 // See if one string ended first
129 if (*pFirst != 0 || *pSecond != 0)
132 return *pFirst == 0 ? -1 : 1;
136 // Return our difference (0)
140 #if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
141 ////////////////////////////////////////////////////////////////////////////
143 // LoadSortModuleAndInvariant()
145 // Attempts to load the given dll. If that is successful, attempts to
146 // load the invariant data. If that is successful, returns the module handle
147 // and the addresses of the SortGetHandle function and SortCloseHandle function
149 // failure is indicated by returning NULL for the module handle and the
150 // function addresses
152 ////////////////////////////////////////////////////////////////////////////
154 HMODULE LoadSortModuleAndInvariant(
155 __in LPCWSTR sDllName,
156 __in DWORD dwVersion,
157 __out SORTGETHANDLE* ppGetHandle,
158 __out SORTCLOSEHANDLE* ppCloseHandle
162 *ppCloseHandle = NULL;
165 if(FAILED(UtilCode::LoadLibraryShim(sDllName, NULL, NULL, &hSort)))
170 SORTGETHANDLE pGetHandle = (SORTGETHANDLE)GetProcAddress(hSort, "SortGetHandle");
171 SORTCLOSEHANDLE pCloseHandle = (SORTCLOSEHANDLE)GetProcAddress(hSort, "SortCloseHandle");
173 // If we didn't load the procs, then remember that
174 if (pCloseHandle == NULL || pGetHandle == NULL)
176 ::FreeLibrary(hSort);
180 // Verify that the data file's available
181 NLSVERSIONINFO sortVersion;
182 sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
183 sortVersion.dwNLSVersion = dwVersion;
184 sortVersion.dwDefinedVersion = dwVersion;
186 // Invariant must be there and is kinda common, so we'll just get it
187 PSORTHANDLE pSort = pGetHandle(W(""), &sortVersion, NULL);
190 // Yikes, invariant failed, forget about it.
191 ::FreeLibrary(hSort);
195 // Since we found it, may as well remember it.
196 const SORTHANDLE * const pSortInHash = InsertSortHashNode(pSort);
198 // If we got a different one back then free the one we added
199 if (pSortInHash != pSort && pSortInHash)
201 // We got a different one from the hash (someone beat us to the cache)
202 // so use that and discard the new one.
206 *ppGetHandle = pGetHandle;
207 *ppCloseHandle = pCloseHandle;
211 // Attempts to load a Sort DLL. If this fails, the values of the __out parameters are unchanged.
213 BOOL LoadSortDllAndPublish(
214 __in LPCWSTR sDllName,
215 __in DWORD dwVersion,
216 __out __encoded_pointer SORTGETHANDLE* ppGetHandle,
217 __out __encoded_pointer SORTCLOSEHANDLE* ppCloseHandle,
218 __out HMODULE* phSortDll)
221 SORTGETHANDLE pGetHandle;
222 SORTCLOSEHANDLE pCloseHandle;
224 hSortDll = LoadSortModuleAndInvariant(sDllName, dwVersion, &pGetHandle, &pCloseHandle);
226 if(hSortDll != NULL) {
227 *phSortDll = hSortDll;
228 *ppGetHandle = (SORTGETHANDLE)EncodePointer(pGetHandle);
229 *ppCloseHandle = (SORTCLOSEHANDLE)EncodePointer(pCloseHandle);
236 ////////////////////////////////////////////////////////////////////////////
238 // GetSortGetHandle()
240 // Get the SortGetHandle() function for the proper dll version.
242 ////////////////////////////////////////////////////////////////////////////
243 SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
245 if(dwVersion == SORT_VERSION_DEFAULT)
247 // If we haven't tried to load the module/proc before do so now
248 if (g_hSortDefault == (HMODULE)-1)
250 LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault);
253 // This check is necessary because the LoadSortDllAndPublish call may have failed since some platforms
254 // won't have nlssorting.dll (e.g. Windows 8 and above).
255 if (g_hSortDefault != (HMODULE)-1)
257 return (SORTGETHANDLE)DecodePointer(g_pDefaultGetHandle);
261 HMODULE* pHSortModule;
262 SORTGETHANDLE* ppGetHandle;
263 SORTCLOSEHANDLE* ppCloseHandle;
265 if(dwVersion == SORT_VERSION_V4)
267 ppGetHandle = &g_pV4GetHandle;
268 ppCloseHandle = &g_pV4CloseHandle;
269 pHSortModule = &g_hSortCompatV4;
271 else if(dwVersion == SORT_VERSION_WHIDBEY)
273 ppGetHandle = &g_pV2GetHandle;
274 ppCloseHandle = &g_pV2CloseHandle;
275 pHSortModule = &g_hSortCompatV2;
279 // Unsupported sorting version.
283 if(*pHSortModule == (HMODULE) -1)
285 // get module name - the module name should be "Sort"+dwVersion.ToString("x8")+".dll"
286 WCHAR moduleName[] = W("Sort00000000.dll");
287 // replace the "00000000" with the hexadecimal of dwVersion
288 LPCWSTR hex = W("0123456789abcdef");
289 WCHAR* p = &moduleName[4+8]; // position at end of number part of dll name
291 unsigned int value = dwVersion;
293 while (value != 0 && p != moduleName) {
294 int digit = value & 0xF;
299 if(!LoadSortDllAndPublish(&moduleName[0], dwVersion, ppGetHandle, ppCloseHandle, pHSortModule))
301 // We failed to load a versioned sort dll, try to fall back to the current version.
302 // If we haven't tried to load the module/proc before do so now
303 if (g_hSortDefault == (HMODULE)-1)
305 LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault);
308 *pHSortModule = g_hSortDefault;
309 *ppCloseHandle = g_pDefaultCloseHandle;
310 *ppGetHandle = g_pDefaultGetHandle;
314 // At this point, we've either loaded a sorting dll or we've exausted all options.
315 if(*pHSortModule == (HMODULE) -1)
317 // Couldn't find anything, give up.
322 return (SORTGETHANDLE)DecodePointer(*ppGetHandle);
325 // Unknown version requested
329 void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
331 if (dwVersion == SORT_VERSION_DEFAULT)
333 SORTCLOSEHANDLE pDefaultCloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pDefaultCloseHandle);
334 if(pDefaultCloseHandle != NULL)
336 (pDefaultCloseHandle)(pSort);
341 if (dwVersion == SORT_VERSION_V4)
343 SORTCLOSEHANDLE pV4CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV4CloseHandle);
344 if(pV4CloseHandle != NULL)
346 (pV4CloseHandle)(pSort);
352 if (dwVersion == SORT_VERSION_WHIDBEY)
354 SORTCLOSEHANDLE pV2CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV2CloseHandle);
355 if(pV2CloseHandle != NULL)
357 (pV2CloseHandle)(pSort);
363 #else // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
365 SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
370 void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
374 #endif // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
376 ////////////////////////////////////////////////////////////////////////////
380 // Returns the hash value for given sort name & version.
382 // WARNING: This must be case insensitive. Currently we're expecting only
383 // a-z, A-Z, 0-9 & -.
385 ////////////////////////////////////////////////////////////////////////////
386 __inline __range(0, SORT_HASH_TBL_SIZE-1) int GetSortHashValue(
387 __in LPCWSTR pSortName,
388 __in DWORD dwVersion)
390 int iHash = 12; // Seed hash value
391 int iMax; // Number of characters to count (prevent problems with too-bad strings)
396 for (iMax = 10; *pSortName != 0 && iMax != 0; pSortName++, iMax--)
399 iHash ^= ((*pSortName) & 0xdf); // 0x20 will make cases be the same (and other wierd stuff too, but we don't care about that)
403 // Add the version hash
404 // (the middle 2 bytes are most interesting)
405 iHash ^= dwVersion >> 8;
407 // Mix up our bits and hash it with 128
408 _ASSERT(SORT_HASH_TBL_SIZE == 128);
409 return (iHash + (iHash >> 8)) & 0x7f;
413 ////////////////////////////////////////////////////////////////////////////
415 // InsertSortHashNode
417 // Inserts a sort hash node into the global sort hash tables. It assumes
418 // that all unused hash values in the table are pointing to NULL. If
419 // there is a collision, the new node will be added LAST in the list.
420 // (Presuming that the most often used are also the first used)
422 // We do an interlocked exchange and free the pointer if we can't add it.
424 // Warning: We stick stuff in this list, but we never remove it, so it
425 // get kind of big. Removing entries would be difficult however
426 // because it would require some sort of synchronization with the
427 // reader functions (like GetLocaleInfo), or maybe an in-use flag
430 ////////////////////////////////////////////////////////////////////////////
431 PSORTHANDLE InsertSortHashNode(PSORTHANDLE pHashN)
433 __range(0, SORT_HASH_TBL_SIZE-1) UINT index;
435 PSORTHANDLE* pNextToUpdate;
438 // Insert the hash node into the list (by name/version)
441 #pragma warning(push)
442 #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
444 index = GetSortHashValue(pHashN->sortName, pHashN->dwNLSVersion);
450 pSearch = g_pSortHash[index];
452 // Remember last pointer in case we need to add it
453 pNextToUpdate = &g_pSortHash[index];
455 // We'll be the last node when added
456 pHashN->pNext = NULL;
460 while (pSearch != NULL)
462 // See if we already found a node.
463 if ((pSearch->dwNLSVersion == pHashN->dwNLSVersion) &&
464 NlsCompareInvariantNoCase( pSearch->sortName, pHashN->sortName,
465 LOCALE_NAME_MAX_LENGTH, TRUE) == 0)
467 // Its the same, which is unexpected, return the old one
471 pNextToUpdate = &pSearch->pNext;
472 pSearch = pSearch->pNext;
475 // At end, try to add our node
476 pSearch = InterlockedCompareExchangeT(pNextToUpdate, pHashN, NULL);
478 // If pNextToUpdate isn't NULL then another process snuck in and updated the list
479 // while we were getting ready.
482 // It was added, stop
486 // It wasn't added, pSearch now points to a new node that snuck in, so
487 // continue and try that one. This should be really rare, even in a busy
488 // loop, so we don't try a real lock. Either
489 // a) the snuck in node is the same as pHashN, and we'll return pSearch
490 // in the first loop, or
491 // b) the snuck in node is new, in which case we'll try to readd. Very worst
492 // case we'd collide while someone added ALL of the other locales with our
493 // hash, but eventually we'd hit case a. (And there's only a couple hundred
494 // tries, so this can't lock for long.)
497 // Return the same one we added
502 ////////////////////////////////////////////////////////////////////////////
506 // Searches for the sort hash node for the given sort name & version.
507 // The result is returned. If none are found NULL is returned.
509 // NOTE: Call GetSortNode() which calls this.
511 // Defined as inline.
513 ////////////////////////////////////////////////////////////////////////////
515 __inline PSORTHANDLE FindSortHashNode(
516 __in LPCWSTR pSortName,
517 __in DWORD dwVersion)
520 __range(0,SORT_HASH_TBL_SIZE-1) int index;
523 index = GetSortHashValue(pSortName, dwVersion);
526 pHashN = g_pSortHash[index];
528 // Look through the list to see if one matches name and user info
529 // We're sneaky here because we know our length of our hash name string is stored
530 // just before that string.
531 while ((pHashN != NULL) &&
532 ((dwVersion != pHashN->dwNLSVersion) ||
533 (NlsCompareInvariantNoCase(pSortName, pHashN->sortName, LOCALE_NAME_MAX_LENGTH, TRUE) != 0)))
535 pHashN = pHashN->pNext;
542 ////////////////////////////////////////////////////////////////////////////
546 // Builds a sort hash node and sticks it in the hash table.
548 // NOTE: Call GetSortNode() which calls this.
550 // Defined as inline.
552 ////////////////////////////////////////////////////////////////////////////
553 PSORTHANDLE MakeSortHashNode(
554 __in LPCWSTR pSortName,
555 __in DWORD dwVersion)
557 NLSVERSIONINFO sortVersion;
559 PSORTHANDLE pSort = NULL;
560 PSORTHANDLE pSortInHash;
562 // Valid locale, now we need to find out where to point this version at
563 SORTGETHANDLE pGetHandle = GetSortGetHandle(dwVersion);
564 if (pGetHandle == NULL) return NULL;
566 sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
567 sortVersion.dwNLSVersion = dwVersion;
568 sortVersion.dwDefinedVersion = dwVersion;
570 pSort = pGetHandle(pSortName, &sortVersion, NULL);
572 // If still missing, fail
575 // Invalid sort, fail
579 // Now we need to add it
580 pSortInHash = InsertSortHashNode(pSort);
582 // If we got a different one back then free the one we added
583 if (pSortInHash != pSort && pSortInHash)
585 // We got a different one from the hash (someone beat us to the cache)
586 // so use that and discard the new one.
587 DoSortCloseHandle(dwVersion, pSort);
594 ////////////////////////////////////////////////////////////////////////////
598 // Get a sort hash node for the specified sort name & version
600 ////////////////////////////////////////////////////////////////////////////
601 PSORTHANDLE GetSortNode(
602 __in LPCWSTR pSortName,
603 __in DWORD dwVersion)
605 PSORTHANDLE pSortHashN = NULL;
607 // WARNING: We don't bother doing the null/default/system checks
609 // Didn't have an obvious one, look in the hash table
610 pSortHashN = FindSortHashNode(pSortName, dwVersion);
613 // If the hash node does not exist, we may need to get make one
615 if (pSortHashN == NULL)
618 // Hash node does NOT exist, try to make it
621 pSortHashN = MakeSortHashNode(pSortName, dwVersion);
625 // If the hash node still does not exist, we may need to fallback to default
628 if (pSortHashN == NULL && dwVersion != SORT_VERSION_DEFAULT)
630 return GetSortNode(pSortName, SORT_VERSION_DEFAULT);
634 // Return pointer to hash node
635 // (null if we still don't have one)
640 ////////////////////////////////////////////////////////////////////////////
643 // Check for the DWORD "CompatSortNLSVersion" CLR config option.
645 // .Net 4.0 introduces sorting changes that can affect the behavior of any of the methods
646 // in CompareInfo. To mitigate against compatibility problems Applications can enable the
647 // legacy CompareInfo behavior by using the 'SortNLSVersion' configuration option
649 // There are three ways to use the configuration option:
651 // 1) Config file (MyApp.exe.config)
652 // <?xml version ="1.0"?>
655 // <CompatSortNLSVersion enabled="4096"/><!--0x00001000 -->
658 // 2) Environment variable
659 // set COMPLUS_CompatSortNLSVersion=4096
661 // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
662 // "CompatSortNLSVersion"=dword:00001000
664 ////////////////////////////////////////////////////////////////////////////
665 DWORD SortNLSVersion()
667 #ifdef FEATURE_CORECLR
668 return SORT_VERSION_DEFAULT;
670 static bool sortNLSVersionConfigChecked = false;
671 static DWORD sortNLSVersion = SORT_VERSION_DEFAULT;
673 if(!sortNLSVersionConfigChecked)
675 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
676 sortNLSVersion = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CompatSortNLSVersion);
677 if(sortNLSVersion == 0)
679 sortNLSVersion = SORT_VERSION_DEFAULT;
681 END_SO_INTOLERANT_CODE;
683 sortNLSVersionConfigChecked = true;
685 return sortNLSVersion;
686 #endif // !FEATURE_CORECLR
689 ////////////////////////////////////////////////////////////////////////////
693 // Get the version from a version blob, resolving to the default version
696 ////////////////////////////////////////////////////////////////////////////
697 __inline DWORD VersionValue(__in_opt const NLSVERSIONINFO * const lpVersionInformation)
701 // If the caller passed null or zero we use the default version
703 if ((lpVersionInformation == NULL) ||
704 ((lpVersionInformation->dwNLSVersion == 0) &&
705 (lpVersionInformation->dwDefinedVersion ==0))
708 return SortNLSVersion();
711 // TODO: Will need to review this
712 if(((lpVersionInformation->dwNLSVersion == 0) &&
713 (lpVersionInformation->dwDefinedVersion != 0 )))
715 return lpVersionInformation->dwDefinedVersion;
718 return lpVersionInformation->dwNLSVersion;
721 ////////////////////////////////////////////////////////////////////////////
725 // Supposed to call the dll for the appropriate version. If the default
726 // version isn't available call the ordinal behavior (for minwin)
728 // Just get the sort hash node and call the worker function
730 ////////////////////////////////////////////////////////////////////////////
731 __success(return != 0) int WINAPI SortGetSortKey(
732 __in LPCWSTR pLocaleName,
734 __in_ecount(cchSrc) LPCWSTR pSrc,
736 __out_bcount_opt(cbDest) LPBYTE pDest,
738 __in_opt CONST NLSVERSIONINFO *lpVersionInformation,
739 __in_opt LPVOID lpReserved,
740 __in_opt LPARAM lParam
743 PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
744 return SortDllGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
747 // SortDllGetSortKey handles any modification to flags
748 // necessary before the actual call to the dll
749 __success(return != 0) int WINAPI SortDllGetSortKey(
750 __in PSORTHANDLE pSort,
752 __in_ecount(cchSrc) LPCWSTR pSrc,
754 __out_bcount_opt(cbDest) LPBYTE pDest,
756 __in_opt LPVOID lpReserved,
757 __in_opt LPARAM lParam )
761 SetLastError(ERROR_INVALID_PARAMETER);
766 // Note that GetSortKey'll have the opposite behavior for the
767 // linguistic casing flag (eg: use flag for bad behavior, linguistic
769 dwFlags ^= NORM_LINGUISTIC_CASING;
771 return pSort->pSortGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
774 __success(return != 0) int SortDllGetHashCode(
775 __in PSORTHANDLE pSort,
777 __in_ecount(cchSrc) LPCWSTR pSrc,
779 __in_opt LPVOID lpReserved,
780 __in_opt LPARAM lParam )
784 SetLastError(ERROR_INVALID_PARAMETER);
787 const int SortDllGetHashCodeApiIntroducedVersion = 2;
788 if(pSort->dwSHVersion < SortDllGetHashCodeApiIntroducedVersion)
790 SetLastError(ERROR_NOT_SUPPORTED);
795 // Note that GetSortKey'll have the opposite behavior for the
796 // linguistic casing flag (eg: use flag for bad behavior, linguistic
798 dwFlags ^= NORM_LINGUISTIC_CASING;
800 return pSort->pSortGetHashCode(pSort, dwFlags, pSrc, cchSrc, lpReserved, lParam);
804 ////////////////////////////////////////////////////////////////////////////
808 // Supposed to call the dll for the appropriate version. If the default
809 // version isn't available call the ordinal behavior (for minwin)
811 // NOTE: The linguistic casing flags are backwards (ie: set the flag to
812 // get the non-linguistic behavior.) If we expose this then we'll
813 // need to publish the no-linguistic flag.
815 // Just get the sort hash node and call the worker function
817 ////////////////////////////////////////////////////////////////////////////
818 __success(return != 0) int WINAPI SortChangeCase(
819 __in LPCWSTR pLocaleName,
821 __in_ecount(cchSrc) LPCWSTR pSrc,
823 __out_ecount_opt(cchDest) LPWSTR pDest,
825 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
826 __in_opt LPVOID lpReserved,
827 __in_opt LPARAM lParam)
829 PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
830 return SortDllChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
833 // SortDllChangeCase handles any modification to flags
834 // necessary before the actual call to the dll
835 __success(return != 0) int WINAPI SortDllChangeCase(
836 __in PSORTHANDLE pSort,
838 __in_ecount(cchSrc) LPCWSTR pSrc,
840 __out_ecount_opt(cchDest) LPWSTR pDest,
842 __in_opt LPVOID lpReserved,
843 __in_opt LPARAM lParam)
847 SetLastError(ERROR_INVALID_PARAMETER);
851 // Note that Change Case'll have the opposite behavior for the
852 // linguistic casing flag (eg: use flag for bad behavior, linguistic
854 dwFlags ^= LCMAP_LINGUISTIC_CASING;
856 #pragma warning(push)
857 #pragma warning(disable: 26036) // prefast - Possible postcondition violation due to failure to null terminate string
859 return pSort->pSortChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
865 ////////////////////////////////////////////////////////////////////////////
869 // Supposed to call the dll for the appropriate version. If the default
870 // version isn't available call the ordinal behavior (for minwin)
872 // Just get the sort hash node and call the worker function
874 ////////////////////////////////////////////////////////////////////////////
875 __success(return != 0) int WINAPI SortCompareString(
876 __in LPCWSTR lpLocaleName,
877 __in DWORD dwCmpFlags,
878 __in_ecount(cchCount1) LPCWSTR lpString1,
880 __in_ecount(cchCount2) LPCWSTR lpString2,
882 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
883 __in_opt LPVOID lpReserved,
884 __in_opt LPARAM lParam)
886 PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
887 return SortDllCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
891 // SortDllCompareString handles any modification to flags
892 // necessary before the actual call to the dll
893 __success(return != 0) int WINAPI SortDllCompareString(
894 __in PSORTHANDLE pSort,
895 __in DWORD dwCmpFlags,
896 __in_ecount(cchCount1) LPCWSTR lpString1,
898 __in_ecount(cchCount2) LPCWSTR lpString2,
900 __in_opt LPVOID lpReserved,
901 __in_opt LPARAM lParam)
905 SetLastError(ERROR_INVALID_PARAMETER);
909 // Note that the dll will have the opposite behavior of CompareStringEx for the
910 // linguistic casing flag (eg: use flag for bad behavior, linguistic
911 // by default) because we want new public APIs to have the "right"
912 // behavior by default
913 dwCmpFlags ^= NORM_LINGUISTIC_CASING;
915 return pSort->pSortCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
918 ////////////////////////////////////////////////////////////////////////////
922 // Finds lpStringValue within lpStringSource based on the rules given
923 // in dwFindNLSStringFlags.
925 // Supposed to call the dll for the appropriate version. If the default
926 // version isn't available call the ordinal behavior (for minwin)
928 // Just get the sort hash node and call the worker function
930 ////////////////////////////////////////////////////////////////////////////
931 __success(return != 0) int WINAPI SortFindString(
932 __in LPCWSTR lpLocaleName,
933 __in DWORD dwFindNLSStringFlags,
934 __in_ecount(cchSource) LPCWSTR lpStringSource,
936 __in_ecount(cchValue) LPCWSTR lpStringValue,
938 __out_opt LPINT pcchFound,
939 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
940 __in_opt LPVOID lpReserved,
941 __in_opt LPARAM lParam)
943 PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
944 return SortDllFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound, lpReserved, lParam);
947 // SortDllFindString handles any modification to flags
948 // necessary before the actual call to the dll
949 __success(return != 0) int WINAPI SortDllFindString(
950 __in PSORTHANDLE pSort,
951 __in DWORD dwFindNLSStringFlags,
952 __in_ecount(cchSource) LPCWSTR lpStringSource,
954 __in_ecount(cchValue) LPCWSTR lpStringValue,
956 __out_opt LPINT pcchFound,
957 __in_opt LPVOID lpReserved,
958 __in_opt LPARAM lParam)
962 SetLastError(ERROR_INVALID_PARAMETER);
966 // Note that the dll will have the opposite behavior of FindNlsString for the
967 // linguistic casing flag (eg: use flag for bad behavior, linguistic
968 // by default) because we want new public APIs to have the "right"
969 // behavior by default
970 dwFindNLSStringFlags ^= NORM_LINGUISTIC_CASING;
972 int cchFound; // we need to get the length even if the caller doesn't care about it (see below)
973 int result = pSort->pSortFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound, lpReserved, lParam);
974 // When searching from end with an empty pattern (either empty string or all ignored characters)
975 // a match is found (result != -1)
976 // Currently we get a result == 0 but we are hoping this will change
977 // with Win7 to be the length of the source string (thus pointing past-the-end)
978 // and the length of the match (cchFound) will be 0
979 // For compatibility, we need to return the index of the last character (or 0 if the source is empty)
980 if((dwFindNLSStringFlags & FIND_FROMEND) &&
985 result = cchSource - 1;
988 // if the caller cares about the length, give it to them
989 if(pcchFound != NULL)
991 *pcchFound = cchFound;
998 ////////////////////////////////////////////////////////////////////////////
1000 // SortIsDefinedString
1002 // This routine looks for code points inside a string to see if they are
1003 // defined within the NSL context. If lpVersionInformation is NULL, the
1004 // version is the current version. Same thing the dwDefinedVersion is equal
1007 // Supposed to call the dll for the appropriate version. If the default
1008 // version isn't available call the ordinal behavior (for minwin)
1010 // Just get the sort hash node and call the worker function
1012 ////////////////////////////////////////////////////////////////////////////
1013 BOOL WINAPI SortIsDefinedString(
1014 __in NLS_FUNCTION Function,
1016 __in CONST NLSVERSIONINFOEX * lpVersionInformation,
1017 __in_ecount(cchStr) LPCWSTR lpString,
1020 // Get an invariant sort node
1021 PSORTHANDLE pSort = GetSortNode(W(""), VersionValue((CONST NLSVERSIONINFO *)lpVersionInformation));
1022 return SortDllIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
1025 // SortDllIsDefinedString handles any modification to flags
1026 // necessary before the actual call to the dll
1027 BOOL WINAPI SortDllIsDefinedString(
1028 __in PSORTHANDLE pSort,
1029 __in NLS_FUNCTION Function,
1031 __in_ecount(cchStr) LPCWSTR lpString,
1034 // Fail if we couldn't find one
1037 SetLastError(ERROR_INVALID_PARAMETER);
1041 return pSort->pSortIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
1044 BOOL SortGetNLSVersion(__in PSORTHANDLE pSort,
1045 __in NLS_FUNCTION Function,
1046 __inout NLSVERSIONINFO * lpVersionInformation )
1048 lpVersionInformation->dwNLSVersion = pSort->dwNLSVersion;
1049 lpVersionInformation->dwDefinedVersion = pSort->dwDefinedVersion;
1054 // Wrapper for SortGetSortKey and SortChangeCase, which are both
1055 // smushed into LCMapStringEx
1056 __success(return != 0) int
1057 LCMapStringEx (__in LPCWSTR lpLocaleName,
1058 __in DWORD dwMapFlags,
1059 __in_ecount(cchSrc) LPCWSTR lpSrcStr,
1061 __out_ecount_opt(cchDest) LPWSTR lpDestStr, // really this should be __out_awcount_opt(dwMapFlags & LCMAP_SORTKEY, cchDest)
1063 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
1064 __in_opt LPVOID lpReserved,
1065 __in_opt LPARAM lParam )
1067 // Should be either sort key...
1068 if (dwMapFlags & LCMAP_SORTKEY)
1071 #pragma warning(push)
1072 #pragma warning(disable: 26036) // Prefast - Possible precondition violation due to failure to null terminate string lpDestStr-interpreted differently depending on flag
1074 return SortGetSortKey(lpLocaleName,
1075 dwMapFlags & ~(LCMAP_SORTKEY), // Don't need sort key flag
1078 (LPBYTE)lpDestStr, // Sort keys are bytes not WCHARs
1079 cchDest, // Sort keys are bytes not WCHARs
1080 lpVersionInformation,
1084 #pragma warning(pop)
1089 // Check for changing case conditions. This may be combined with Chinese or Japanese
1090 // transliteration, but not with sort key nor ignore space/symbols
1092 _ASSERT(dwMapFlags & (LCMAP_TITLECASE | LCMAP_UPPERCASE | LCMAP_LOWERCASE));
1095 // Call casing wrapper, which'll either call the correct version dll
1096 // or call ordinal behavior in the minwin case
1098 return SortChangeCase(lpLocaleName,
1099 dwMapFlags & ~(LCMAP_BYTEREV),
1104 lpVersionInformation,
1110 ////////////////////////////////////////////////////////////////////////////
1112 // IsAvailableVersion()
1114 // Get the SortGetHandle() function for the proper dll version.
1116 ////////////////////////////////////////////////////////////////////////////
1117 BOOL IsAvailableVersion(__in_opt CONST NLSVERSIONINFO * pVersion)
1119 return GetSortGetHandle(VersionValue(pVersion)) != NULL;
1123 ////////////////////////////////////////////////////////////////////////////
1127 // Get the SortHandle for the given locale and version
1129 ////////////////////////////////////////////////////////////////////////////
1130 PSORTHANDLE GetSortHandle(__in LPCWSTR lpLocaleName, __in_opt CONST NLSVERSIONINFO * pVersion)
1132 DWORD version = VersionValue(pVersion);
1133 if (GetSortGetHandle(version) == NULL)
1137 return GetSortNode(lpLocaleName, version);