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)
140 SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
145 void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
150 ////////////////////////////////////////////////////////////////////////////
154 // Returns the hash value for given sort name & version.
156 // WARNING: This must be case insensitive. Currently we're expecting only
157 // a-z, A-Z, 0-9 & -.
159 ////////////////////////////////////////////////////////////////////////////
160 __inline __range(0, SORT_HASH_TBL_SIZE-1) int GetSortHashValue(
161 __in LPCWSTR pSortName,
162 __in DWORD dwVersion)
164 int iHash = 12; // Seed hash value
165 int iMax; // Number of characters to count (prevent problems with too-bad strings)
170 for (iMax = 10; *pSortName != 0 && iMax != 0; pSortName++, iMax--)
173 iHash ^= ((*pSortName) & 0xdf); // 0x20 will make cases be the same (and other wierd stuff too, but we don't care about that)
177 // Add the version hash
178 // (the middle 2 bytes are most interesting)
179 iHash ^= dwVersion >> 8;
181 // Mix up our bits and hash it with 128
182 _ASSERT(SORT_HASH_TBL_SIZE == 128);
183 return (iHash + (iHash >> 8)) & 0x7f;
187 ////////////////////////////////////////////////////////////////////////////
189 // InsertSortHashNode
191 // Inserts a sort hash node into the global sort hash tables. It assumes
192 // that all unused hash values in the table are pointing to NULL. If
193 // there is a collision, the new node will be added LAST in the list.
194 // (Presuming that the most often used are also the first used)
196 // We do an interlocked exchange and free the pointer if we can't add it.
198 // Warning: We stick stuff in this list, but we never remove it, so it
199 // get kind of big. Removing entries would be difficult however
200 // because it would require some sort of synchronization with the
201 // reader functions (like GetLocaleInfo), or maybe an in-use flag
204 ////////////////////////////////////////////////////////////////////////////
205 PSORTHANDLE InsertSortHashNode(PSORTHANDLE pHashN)
207 __range(0, SORT_HASH_TBL_SIZE-1) UINT index;
209 PSORTHANDLE* pNextToUpdate;
212 // Insert the hash node into the list (by name/version)
215 #pragma warning(push)
216 #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
218 index = GetSortHashValue(pHashN->sortName, pHashN->dwNLSVersion);
224 pSearch = g_pSortHash[index];
226 // Remember last pointer in case we need to add it
227 pNextToUpdate = &g_pSortHash[index];
229 // We'll be the last node when added
230 pHashN->pNext = NULL;
234 while (pSearch != NULL)
236 // See if we already found a node.
237 if ((pSearch->dwNLSVersion == pHashN->dwNLSVersion) &&
238 NlsCompareInvariantNoCase( pSearch->sortName, pHashN->sortName,
239 LOCALE_NAME_MAX_LENGTH, TRUE) == 0)
241 // Its the same, which is unexpected, return the old one
245 pNextToUpdate = &pSearch->pNext;
246 pSearch = pSearch->pNext;
249 // At end, try to add our node
250 pSearch = InterlockedCompareExchangeT(pNextToUpdate, pHashN, NULL);
252 // If pNextToUpdate isn't NULL then another process snuck in and updated the list
253 // while we were getting ready.
256 // It was added, stop
260 // It wasn't added, pSearch now points to a new node that snuck in, so
261 // continue and try that one. This should be really rare, even in a busy
262 // loop, so we don't try a real lock. Either
263 // a) the snuck in node is the same as pHashN, and we'll return pSearch
264 // in the first loop, or
265 // b) the snuck in node is new, in which case we'll try to readd. Very worst
266 // case we'd collide while someone added ALL of the other locales with our
267 // hash, but eventually we'd hit case a. (And there's only a couple hundred
268 // tries, so this can't lock for long.)
271 // Return the same one we added
276 ////////////////////////////////////////////////////////////////////////////
280 // Searches for the sort hash node for the given sort name & version.
281 // The result is returned. If none are found NULL is returned.
283 // NOTE: Call GetSortNode() which calls this.
285 // Defined as inline.
287 ////////////////////////////////////////////////////////////////////////////
289 __inline PSORTHANDLE FindSortHashNode(
290 __in LPCWSTR pSortName,
291 __in DWORD dwVersion)
294 __range(0,SORT_HASH_TBL_SIZE-1) int index;
297 index = GetSortHashValue(pSortName, dwVersion);
300 pHashN = g_pSortHash[index];
302 // Look through the list to see if one matches name and user info
303 // We're sneaky here because we know our length of our hash name string is stored
304 // just before that string.
305 while ((pHashN != NULL) &&
306 ((dwVersion != pHashN->dwNLSVersion) ||
307 (NlsCompareInvariantNoCase(pSortName, pHashN->sortName, LOCALE_NAME_MAX_LENGTH, TRUE) != 0)))
309 pHashN = pHashN->pNext;
316 ////////////////////////////////////////////////////////////////////////////
320 // Builds a sort hash node and sticks it in the hash table.
322 // NOTE: Call GetSortNode() which calls this.
324 // Defined as inline.
326 ////////////////////////////////////////////////////////////////////////////
327 PSORTHANDLE MakeSortHashNode(
328 __in LPCWSTR pSortName,
329 __in DWORD dwVersion)
331 NLSVERSIONINFO sortVersion;
333 PSORTHANDLE pSort = NULL;
334 PSORTHANDLE pSortInHash;
336 // Valid locale, now we need to find out where to point this version at
337 SORTGETHANDLE pGetHandle = GetSortGetHandle(dwVersion);
338 if (pGetHandle == NULL) return NULL;
340 sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
341 sortVersion.dwNLSVersion = dwVersion;
342 sortVersion.dwDefinedVersion = dwVersion;
344 pSort = pGetHandle(pSortName, &sortVersion, NULL);
346 // If still missing, fail
349 // Invalid sort, fail
353 // Now we need to add it
354 pSortInHash = InsertSortHashNode(pSort);
356 // If we got a different one back then free the one we added
357 if (pSortInHash != pSort && pSortInHash)
359 // We got a different one from the hash (someone beat us to the cache)
360 // so use that and discard the new one.
361 DoSortCloseHandle(dwVersion, pSort);
368 ////////////////////////////////////////////////////////////////////////////
372 // Get a sort hash node for the specified sort name & version
374 ////////////////////////////////////////////////////////////////////////////
375 PSORTHANDLE GetSortNode(
376 __in LPCWSTR pSortName,
377 __in DWORD dwVersion)
379 PSORTHANDLE pSortHashN = NULL;
381 // WARNING: We don't bother doing the null/default/system checks
383 // Didn't have an obvious one, look in the hash table
384 pSortHashN = FindSortHashNode(pSortName, dwVersion);
387 // If the hash node does not exist, we may need to get make one
389 if (pSortHashN == NULL)
392 // Hash node does NOT exist, try to make it
395 pSortHashN = MakeSortHashNode(pSortName, dwVersion);
399 // If the hash node still does not exist, we may need to fallback to default
402 if (pSortHashN == NULL && dwVersion != SORT_VERSION_DEFAULT)
404 return GetSortNode(pSortName, SORT_VERSION_DEFAULT);
408 // Return pointer to hash node
409 // (null if we still don't have one)
414 ////////////////////////////////////////////////////////////////////////////
417 // Check for the DWORD "CompatSortNLSVersion" CLR config option.
419 // .Net 4.0 introduces sorting changes that can affect the behavior of any of the methods
420 // in CompareInfo. To mitigate against compatibility problems Applications can enable the
421 // legacy CompareInfo behavior by using the 'SortNLSVersion' configuration option
423 // There are three ways to use the configuration option:
425 // 1) Config file (MyApp.exe.config)
426 // <?xml version ="1.0"?>
429 // <CompatSortNLSVersion enabled="4096"/><!--0x00001000 -->
432 // 2) Environment variable
433 // set COMPlus_CompatSortNLSVersion=4096
435 // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
436 // "CompatSortNLSVersion"=dword:00001000
438 ////////////////////////////////////////////////////////////////////////////
439 DWORD SortNLSVersion()
441 return SORT_VERSION_DEFAULT;
444 ////////////////////////////////////////////////////////////////////////////
448 // Get the version from a version blob, resolving to the default version
451 ////////////////////////////////////////////////////////////////////////////
452 __inline DWORD VersionValue(__in_opt const NLSVERSIONINFO * const lpVersionInformation)
456 // If the caller passed null or zero we use the default version
458 if ((lpVersionInformation == NULL) ||
459 ((lpVersionInformation->dwNLSVersion == 0) &&
460 (lpVersionInformation->dwDefinedVersion ==0))
463 return SortNLSVersion();
466 // TODO: Will need to review this
467 if(((lpVersionInformation->dwNLSVersion == 0) &&
468 (lpVersionInformation->dwDefinedVersion != 0 )))
470 return lpVersionInformation->dwDefinedVersion;
473 return lpVersionInformation->dwNLSVersion;
476 ////////////////////////////////////////////////////////////////////////////
480 // Supposed to call the dll for the appropriate version. If the default
481 // version isn't available call the ordinal behavior (for minwin)
483 // Just get the sort hash node and call the worker function
485 ////////////////////////////////////////////////////////////////////////////
486 __success(return != 0) int WINAPI SortGetSortKey(
487 __in LPCWSTR pLocaleName,
489 __in_ecount(cchSrc) LPCWSTR pSrc,
491 __out_bcount_opt(cbDest) LPBYTE pDest,
493 __in_opt CONST NLSVERSIONINFO *lpVersionInformation,
494 __in_opt LPVOID lpReserved,
495 __in_opt LPARAM lParam
498 PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
499 return SortDllGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
502 // SortDllGetSortKey handles any modification to flags
503 // necessary before the actual call to the dll
504 __success(return != 0) int WINAPI SortDllGetSortKey(
505 __in PSORTHANDLE pSort,
507 __in_ecount(cchSrc) LPCWSTR pSrc,
509 __out_bcount_opt(cbDest) LPBYTE pDest,
511 __in_opt LPVOID lpReserved,
512 __in_opt LPARAM lParam )
516 SetLastError(ERROR_INVALID_PARAMETER);
521 // Note that GetSortKey'll have the opposite behavior for the
522 // linguistic casing flag (eg: use flag for bad behavior, linguistic
524 dwFlags ^= NORM_LINGUISTIC_CASING;
526 return pSort->pSortGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
529 __success(return != 0) int SortDllGetHashCode(
530 __in PSORTHANDLE pSort,
532 __in_ecount(cchSrc) LPCWSTR pSrc,
534 __in_opt LPVOID lpReserved,
535 __in_opt LPARAM lParam )
539 SetLastError(ERROR_INVALID_PARAMETER);
542 const int SortDllGetHashCodeApiIntroducedVersion = 2;
543 if(pSort->dwSHVersion < SortDllGetHashCodeApiIntroducedVersion)
545 SetLastError(ERROR_NOT_SUPPORTED);
550 // Note that GetSortKey'll have the opposite behavior for the
551 // linguistic casing flag (eg: use flag for bad behavior, linguistic
553 dwFlags ^= NORM_LINGUISTIC_CASING;
555 return pSort->pSortGetHashCode(pSort, dwFlags, pSrc, cchSrc, lpReserved, lParam);
559 ////////////////////////////////////////////////////////////////////////////
563 // Supposed to call the dll for the appropriate version. If the default
564 // version isn't available call the ordinal behavior (for minwin)
566 // NOTE: The linguistic casing flags are backwards (ie: set the flag to
567 // get the non-linguistic behavior.) If we expose this then we'll
568 // need to publish the no-linguistic flag.
570 // Just get the sort hash node and call the worker function
572 ////////////////////////////////////////////////////////////////////////////
573 __success(return != 0) int WINAPI SortChangeCase(
574 __in LPCWSTR pLocaleName,
576 __in_ecount(cchSrc) LPCWSTR pSrc,
578 __out_ecount_opt(cchDest) LPWSTR pDest,
580 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
581 __in_opt LPVOID lpReserved,
582 __in_opt LPARAM lParam)
584 PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
585 return SortDllChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
588 // SortDllChangeCase handles any modification to flags
589 // necessary before the actual call to the dll
590 __success(return != 0) int WINAPI SortDllChangeCase(
591 __in PSORTHANDLE pSort,
593 __in_ecount(cchSrc) LPCWSTR pSrc,
595 __out_ecount_opt(cchDest) LPWSTR pDest,
597 __in_opt LPVOID lpReserved,
598 __in_opt LPARAM lParam)
602 SetLastError(ERROR_INVALID_PARAMETER);
606 // Note that Change Case'll have the opposite behavior for the
607 // linguistic casing flag (eg: use flag for bad behavior, linguistic
609 dwFlags ^= LCMAP_LINGUISTIC_CASING;
611 #pragma warning(push)
612 #pragma warning(disable: 26036) // prefast - Possible postcondition violation due to failure to null terminate string
614 return pSort->pSortChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
620 ////////////////////////////////////////////////////////////////////////////
624 // Supposed to call the dll for the appropriate version. If the default
625 // version isn't available call the ordinal behavior (for minwin)
627 // Just get the sort hash node and call the worker function
629 ////////////////////////////////////////////////////////////////////////////
630 __success(return != 0) int WINAPI SortCompareString(
631 __in LPCWSTR lpLocaleName,
632 __in DWORD dwCmpFlags,
633 __in_ecount(cchCount1) LPCWSTR lpString1,
635 __in_ecount(cchCount2) LPCWSTR lpString2,
637 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
638 __in_opt LPVOID lpReserved,
639 __in_opt LPARAM lParam)
641 PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
642 return SortDllCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
646 // SortDllCompareString handles any modification to flags
647 // necessary before the actual call to the dll
648 __success(return != 0) int WINAPI SortDllCompareString(
649 __in PSORTHANDLE pSort,
650 __in DWORD dwCmpFlags,
651 __in_ecount(cchCount1) LPCWSTR lpString1,
653 __in_ecount(cchCount2) LPCWSTR lpString2,
655 __in_opt LPVOID lpReserved,
656 __in_opt LPARAM lParam)
660 SetLastError(ERROR_INVALID_PARAMETER);
664 // Note that the dll will have the opposite behavior of CompareStringEx for the
665 // linguistic casing flag (eg: use flag for bad behavior, linguistic
666 // by default) because we want new public APIs to have the "right"
667 // behavior by default
668 dwCmpFlags ^= NORM_LINGUISTIC_CASING;
670 return pSort->pSortCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
673 ////////////////////////////////////////////////////////////////////////////
677 // Finds lpStringValue within lpStringSource based on the rules given
678 // in dwFindNLSStringFlags.
680 // Supposed to call the dll for the appropriate version. If the default
681 // version isn't available call the ordinal behavior (for minwin)
683 // Just get the sort hash node and call the worker function
685 ////////////////////////////////////////////////////////////////////////////
686 __success(return != 0) int WINAPI SortFindString(
687 __in LPCWSTR lpLocaleName,
688 __in DWORD dwFindNLSStringFlags,
689 __in_ecount(cchSource) LPCWSTR lpStringSource,
691 __in_ecount(cchValue) LPCWSTR lpStringValue,
693 __out_opt LPINT pcchFound,
694 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
695 __in_opt LPVOID lpReserved,
696 __in_opt LPARAM lParam)
698 PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
699 return SortDllFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound, lpReserved, lParam);
702 // SortDllFindString handles any modification to flags
703 // necessary before the actual call to the dll
704 __success(return != 0) int WINAPI SortDllFindString(
705 __in PSORTHANDLE pSort,
706 __in DWORD dwFindNLSStringFlags,
707 __in_ecount(cchSource) LPCWSTR lpStringSource,
709 __in_ecount(cchValue) LPCWSTR lpStringValue,
711 __out_opt LPINT pcchFound,
712 __in_opt LPVOID lpReserved,
713 __in_opt LPARAM lParam)
717 SetLastError(ERROR_INVALID_PARAMETER);
721 // Note that the dll will have the opposite behavior of FindNlsString for the
722 // linguistic casing flag (eg: use flag for bad behavior, linguistic
723 // by default) because we want new public APIs to have the "right"
724 // behavior by default
725 dwFindNLSStringFlags ^= NORM_LINGUISTIC_CASING;
727 int cchFound; // we need to get the length even if the caller doesn't care about it (see below)
728 int result = pSort->pSortFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound, lpReserved, lParam);
729 // When searching from end with an empty pattern (either empty string or all ignored characters)
730 // a match is found (result != -1)
731 // Currently we get a result == 0 but we are hoping this will change
732 // with Win7 to be the length of the source string (thus pointing past-the-end)
733 // and the length of the match (cchFound) will be 0
734 // For compatibility, we need to return the index of the last character (or 0 if the source is empty)
735 if((dwFindNLSStringFlags & FIND_FROMEND) &&
740 result = cchSource - 1;
743 // if the caller cares about the length, give it to them
744 if(pcchFound != NULL)
746 *pcchFound = cchFound;
753 ////////////////////////////////////////////////////////////////////////////
755 // SortIsDefinedString
757 // This routine looks for code points inside a string to see if they are
758 // defined within the NSL context. If lpVersionInformation is NULL, the
759 // version is the current version. Same thing the dwDefinedVersion is equal
762 // Supposed to call the dll for the appropriate version. If the default
763 // version isn't available call the ordinal behavior (for minwin)
765 // Just get the sort hash node and call the worker function
767 ////////////////////////////////////////////////////////////////////////////
768 BOOL WINAPI SortIsDefinedString(
769 __in NLS_FUNCTION Function,
771 __in CONST NLSVERSIONINFOEX * lpVersionInformation,
772 __in_ecount(cchStr) LPCWSTR lpString,
775 // Get an invariant sort node
776 PSORTHANDLE pSort = GetSortNode(W(""), VersionValue((CONST NLSVERSIONINFO *)lpVersionInformation));
777 return SortDllIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
780 // SortDllIsDefinedString handles any modification to flags
781 // necessary before the actual call to the dll
782 BOOL WINAPI SortDllIsDefinedString(
783 __in PSORTHANDLE pSort,
784 __in NLS_FUNCTION Function,
786 __in_ecount(cchStr) LPCWSTR lpString,
789 // Fail if we couldn't find one
792 SetLastError(ERROR_INVALID_PARAMETER);
796 return pSort->pSortIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
799 BOOL SortGetNLSVersion(__in PSORTHANDLE pSort,
800 __in NLS_FUNCTION Function,
801 __inout NLSVERSIONINFO * lpVersionInformation )
803 lpVersionInformation->dwNLSVersion = pSort->dwNLSVersion;
804 lpVersionInformation->dwDefinedVersion = pSort->dwDefinedVersion;
809 // Wrapper for SortGetSortKey and SortChangeCase, which are both
810 // smushed into LCMapStringEx
811 __success(return != 0) int
812 LCMapStringEx (__in LPCWSTR lpLocaleName,
813 __in DWORD dwMapFlags,
814 __in_ecount(cchSrc) LPCWSTR lpSrcStr,
816 __out_ecount_opt(cchDest) LPWSTR lpDestStr, // really this should be __out_awcount_opt(dwMapFlags & LCMAP_SORTKEY, cchDest)
818 __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
819 __in_opt LPVOID lpReserved,
820 __in_opt LPARAM lParam )
822 // Should be either sort key...
823 if (dwMapFlags & LCMAP_SORTKEY)
826 #pragma warning(push)
827 #pragma warning(disable: 26036) // Prefast - Possible precondition violation due to failure to null terminate string lpDestStr-interpreted differently depending on flag
829 return SortGetSortKey(lpLocaleName,
830 dwMapFlags & ~(LCMAP_SORTKEY), // Don't need sort key flag
833 (LPBYTE)lpDestStr, // Sort keys are bytes not WCHARs
834 cchDest, // Sort keys are bytes not WCHARs
835 lpVersionInformation,
844 // Check for changing case conditions. This may be combined with Chinese or Japanese
845 // transliteration, but not with sort key nor ignore space/symbols
847 _ASSERT(dwMapFlags & (LCMAP_TITLECASE | LCMAP_UPPERCASE | LCMAP_LOWERCASE));
850 // Call casing wrapper, which'll either call the correct version dll
851 // or call ordinal behavior in the minwin case
853 return SortChangeCase(lpLocaleName,
854 dwMapFlags & ~(LCMAP_BYTEREV),
859 lpVersionInformation,
865 ////////////////////////////////////////////////////////////////////////////
867 // IsAvailableVersion()
869 // Get the SortGetHandle() function for the proper dll version.
871 ////////////////////////////////////////////////////////////////////////////
872 BOOL IsAvailableVersion(__in_opt CONST NLSVERSIONINFO * pVersion)
874 return GetSortGetHandle(VersionValue(pVersion)) != NULL;
878 ////////////////////////////////////////////////////////////////////////////
882 // Get the SortHandle for the given locale and version
884 ////////////////////////////////////////////////////////////////////////////
885 PSORTHANDLE GetSortHandle(__in LPCWSTR lpLocaleName, __in_opt CONST NLSVERSIONINFO * pVersion)
887 DWORD version = VersionValue(pVersion);
888 if (GetSortGetHandle(version) == NULL)
892 return GetSortNode(lpLocaleName, version);