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 //*****************************************************************************
5 // Util_NoDependencies.cpp
9 // This contains a bunch of C++ utility classes needed also for UtilCode without dependencies
10 // (standalone version without CLR/clr.dll/mscoree.dll dependencies).
12 //*****************************************************************************
18 #if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG)
20 RunningOnStatusEnum gRunningOnStatus = RUNNING_ON_STATUS_UNINITED;
22 #define NON_SUPPORTED_PLATFORM_MSGBOX_TITLE W("Platform not supported")
23 #define NON_SUPPORTED_PLATFORM_MSGBOX_TEXT W("The minimum supported platform is Windows 7")
24 #define NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE 0xBAD1BAD1
26 //*****************************************************************************
27 // One time initialization of the OS version
28 //*****************************************************************************
29 void InitRunningOnVersionStatus ()
32 STATIC_CONTRACT_NOTHROW;
33 STATIC_CONTRACT_GC_NOTRIGGER;
34 STATIC_CONTRACT_CANNOT_TAKE_LOCK;
36 BOOL fSupportedPlatform = FALSE;
38 DWORDLONG dwlConditionMask;
40 ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
41 sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
43 sVer.dwMajorVersion = 6;
44 sVer.dwMinorVersion = 2;
45 sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
49 dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
50 dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
51 dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
53 if(VerifyVersionInfo(&sVer, VER_MAJORVERSION | VER_PLATFORMID | VER_MINORVERSION, dwlConditionMask))
55 gRunningOnStatus = RUNNING_ON_WIN8;
56 fSupportedPlatform = TRUE;
61 ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
62 sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
64 sVer.dwMajorVersion = 6;
65 sVer.dwMinorVersion = 1;
66 sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
70 dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
71 dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
72 dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
74 if(VerifyVersionInfo(&sVer, VER_MAJORVERSION | VER_PLATFORMID | VER_MINORVERSION, dwlConditionMask))
76 gRunningOnStatus = RUNNING_ON_WIN7;
77 fSupportedPlatform = TRUE;
83 if (!fSupportedPlatform)
85 // The current platform isn't supported. Display a message box to this effect and exit.
86 // Note that this should never happen since the .NET Fx setup should not install on
87 // non supported platforms (which is why the message box text isn't localized).
88 UtilMessageBoxCatastrophicNonLocalized(NON_SUPPORTED_PLATFORM_MSGBOX_TITLE, NON_SUPPORTED_PLATFORM_MSGBOX_TEXT, MB_OK | MB_ICONERROR, TRUE);
89 TerminateProcess(GetCurrentProcess(), NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE);
92 } // InitRunningOnVersionStatus
95 //------------------------------------------------------------------------------
96 // Returns TRUE if we are running on a 64-bit OS in WoW, FALSE otherwise.
102 static int s_Wow64Process;
104 if (s_Wow64Process == 0)
106 BOOL fWow64Process = FALSE;
108 if (!IsWow64Process(GetCurrentProcess(), &fWow64Process))
109 fWow64Process = FALSE;
111 s_Wow64Process = fWow64Process ? 1 : -1;
114 return (s_Wow64Process == 1) ? TRUE : FALSE;
120 //------------------------------------------------------------------------------
122 // GetRegistryLongValue - Reads a configuration LONG value from the registry.
125 // hKeyParent -- Parent key
126 // szKey -- key to open
127 // szName -- name of the value
128 // pValue -- put value here, if found
129 // fReadNonVirtualizedKey -- whether to read 64-bit hive on WOW64
132 // TRUE -- If the value was found and read
133 // FALSE -- The value was not found, could not be read, or was not DWORD
137 //------------------------------------------------------------------------------
138 BOOL GetRegistryLongValue(HKEY hKeyParent,
142 BOOL fReadNonVirtualizedKey)
151 DWORD ret; // Return value from registry operation.
152 HKEYHolder hkey; // Registry key.
153 long iValue; // The value to read.
154 DWORD iType; // Type of value to get.
155 DWORD iSize; // Size of buffer.
156 REGSAM samDesired = KEY_READ; // Desired access rights to the key
158 if (fReadNonVirtualizedKey)
160 if (RunningInWow64())
162 samDesired |= KEY_WOW64_64KEY;
166 ret = WszRegOpenKeyEx(hKeyParent, szKey, 0, samDesired, &hkey);
168 // If we opened the key, see if there is a value.
169 if (ret == ERROR_SUCCESS)
172 iSize = sizeof(long);
173 ret = WszRegQueryValueEx(hkey, szName, NULL, &iType, reinterpret_cast<BYTE*>(&iValue), &iSize);
175 if (ret == ERROR_SUCCESS && iType == REG_DWORD && iSize == sizeof(long))
176 { // We successfully read a DWORD value.
183 } // GetRegistryLongValue
185 //----------------------------------------------------------------------------
187 // GetCurrentModuleFileName - Retrieve the current module's filename
190 // pBuffer - output string buffer
193 // S_OK on success, else detailed error code.
197 //----------------------------------------------------------------------------
198 HRESULT GetCurrentModuleFileName(SString& pBuffer)
200 LIMITED_METHOD_CONTRACT;
203 DWORD ret = WszGetModuleFileName(NULL, pBuffer);
214 //----------------------------------------------------------------------------
216 // IsCurrentModuleFileNameInAutoExclusionList - decide if the current module's filename
217 // is in the AutoExclusionList list
226 // This function cannot be used in out of process scenarios like DAC because it
227 // looks at current module's filename. In OOP we want to use target process's
228 // module's filename.
230 //----------------------------------------------------------------------------
231 BOOL IsCurrentModuleFileNameInAutoExclusionList()
240 HKEYHolder hKeyHolder;
242 // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList"
243 DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerAutoExclusionListKey, 0, KEY_READ, &hKeyHolder);
245 if (ret != ERROR_SUCCESS)
247 // there's not even an AutoExclusionList hive
251 PathString wszAppName;
253 // Get the appname to look up in the exclusion or inclusion list.
254 if (GetCurrentModuleFileName(wszAppName) != S_OK)
256 // Assume it is not on the exclusion list if we cannot find the module's filename.
260 // Look in AutoExclusionList key for appName get the size of any value stored there.
261 DWORD value, valueType, valueSize = sizeof(value);
262 ret = WszRegQueryValueEx(hKeyHolder, wszAppName, 0, &valueType, reinterpret_cast<BYTE*>(&value), &valueSize);
263 if ((ret == ERROR_SUCCESS) && (valueType == REG_DWORD) && (value == 1))
269 } // IsCurrentModuleFileNameInAutoExclusionList
271 //*****************************************************************************
272 // Retrieve information regarding what registered default debugger
273 //*****************************************************************************
274 void GetDebuggerSettingInfo(SString &ssDebuggerString, BOOL *pfAuto)
285 DWORD cchDebuggerString = MAX_LONGPATH;
286 INDEBUG(DWORD cchOldDebuggerString = cchDebuggerString);
288 WCHAR * buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString);
289 HRESULT hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto);
290 ssDebuggerString.CloseBuffer(cchDebuggerString);
292 while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
294 _ASSERTE(cchDebuggerString > cchOldDebuggerString);
295 INDEBUG(cchOldDebuggerString = cchDebuggerString);
297 buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString);
298 hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto);
299 ssDebuggerString.CloseBuffer(cchDebuggerString);
302 if (*ssDebuggerString.GetUnicode() == W('\0'))
304 ssDebuggerString.Clear();
309 ssDebuggerString.Clear();
318 ssDebuggerString.Clear();
324 EX_END_CATCH(SwallowAllExceptions);
325 } // GetDebuggerSettingInfo
327 //---------------------------------------------------------------------------------------
329 // GetDebuggerSettingInfoWorker - retrieve information regarding what registered default debugger
332 // * wszDebuggerString - [out] the string buffer to store the registered debugger launch
334 // * pcchDebuggerString - [in, out] the size of string buffer in characters
335 // * pfAuto - [in] the flag to indicate whether the debugger neeeds to be launched
339 // HRESULT indicating success or failure.
342 // * wszDebuggerString can be NULL. When wszDebuggerString is NULL, pcchDebuggerString should
343 // * point to a DWORD of zero. pcchDebuggerString cannot be NULL, and the DWORD pointed by
344 // * pcchDebuggerString will store the used or required string buffer size in characters.
345 HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto)
351 PRECONDITION(pcchDebuggerString != NULL);
355 if ((pcchDebuggerString == NULL) || ((wszDebuggerString == NULL) && (*pcchDebuggerString != 0)))
360 // Initialize the output values before we start.
361 if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
363 *wszDebuggerString = W('\0');
371 HKEYHolder hKeyHolder;
373 // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
374 DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerKey, 0, KEY_READ, &hKeyHolder);
376 if (ret != ERROR_SUCCESS)
377 { // Wow, there's not even an AeDebug hive, so no native debugger, no auto.
381 // Look in AeDebug key for "Debugger"; get the size of any value stored there.
382 DWORD valueType, valueSize = 0;
383 ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, 0, &valueType, 0, &valueSize);
385 _ASSERTE(pcchDebuggerString != NULL);
386 if ((wszDebuggerString == NULL) || (*pcchDebuggerString < valueSize / sizeof(WCHAR)))
388 *pcchDebuggerString = valueSize / sizeof(WCHAR) + 1;
389 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
392 *pcchDebuggerString = valueSize / sizeof(WCHAR);
394 // The size of an empty string with the null terminator is 2.
395 BOOL fIsDebuggerStringEmptry = valueSize <= 2 ? TRUE : FALSE;
397 if ((ret != ERROR_SUCCESS) || (valueType != REG_SZ) || fIsDebuggerStringEmptry)
402 _ASSERTE(wszDebuggerString != NULL);
403 ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, NULL, NULL, reinterpret_cast< LPBYTE >(wszDebuggerString), &valueSize);
404 if (ret != ERROR_SUCCESS)
406 *wszDebuggerString = W('\0');
410 // The callers are in nothrow scope, so we must swallow exceptions and reset the output parameters to the
411 // default values if exceptions like OOM ever happen.
418 // Get the appname to look up in DebugApplications key.
419 PathString wzAppName;
422 // Check DebugApplications setting
423 if ((SUCCEEDED(GetCurrentModuleFileName(wzAppName))) &&
425 GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
426 GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsKey, wzAppName, &iValue, TRUE) ||
427 GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
428 GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsKey, wzAppName, &iValue, TRUE)
436 // Look in AeDebug key for "Auto"; get the size of any value stored there.
437 ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, 0, &valueType, 0, &valueSize);
438 if ((ret == ERROR_SUCCESS) && (valueType == REG_SZ) && (valueSize / sizeof(WCHAR) < MAX_LONGPATH))
440 WCHAR wzAutoKey[MAX_LONGPATH];
441 valueSize = NumItems(wzAutoKey) * sizeof(WCHAR);
442 WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, NULL, NULL, reinterpret_cast< LPBYTE >(wzAutoKey), &valueSize);
444 // The OS's behavior is to consider Auto to be FALSE unless the first character is set
445 // to 1. They don't take into consideration the following characters. Also if the value
446 // isn't present they assume an Auto value of FALSE.
447 if ((wzAutoKey[0] == W('1')) && !IsCurrentModuleFileNameInAutoExclusionList())
459 if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
461 *wszDebuggerString = W('\0');
469 EX_END_CATCH(SwallowAllExceptions);
472 } // GetDebuggerSettingInfoWorker
473 #endif // FEATURE_PAL
475 #endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG)
477 //*****************************************************************************
478 // Convert hex value into a wide string of hex digits
479 //*****************************************************************************
482 __out_ecount((cbHexNum * 2)) LPWSTR szHexNum,
492 cbHexNum *= 2; // each nibble is a char
493 while (cbHexNum != 0)
495 DWORD thisHexDigit = hHexNum % 16;
498 if (thisHexDigit < 10)
500 *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit + W('0'));
504 *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit - 10 + W('A'));
510 //*****************************************************************************
511 // Convert a GUID into a pointer to a Wide char string
512 //*****************************************************************************
515 GUID Guid, // The GUID to convert.
516 __out_ecount(cchGuid) LPWSTR szGuid, // String into which the GUID is stored
517 DWORD cchGuid) // Count in wchars
527 // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD
528 // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.
530 if (cchGuid < 39) // 38 chars + 1 null terminating.
533 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
537 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
539 if (FAILED (GetStr(Guid.Data1, szGuid+1 , 4))) return 0;
543 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
545 if (FAILED (GetStr(Guid.Data2, szGuid+10, 2))) return 0;
549 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
551 if (FAILED (GetStr(Guid.Data3, szGuid+15, 2))) return 0;
555 // Get the last two fields (which are byte arrays).
556 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
558 for (i=0; i < 2; ++i)
559 if (FAILED(GetStr(Guid.Data4[i], szGuid + 20 + (i * 2), 1)))
564 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
566 for (i=0; i < 6; ++i)
567 if (FAILED(GetStr(Guid.Data4[i+2], szGuid + 25 + (i * 2), 1)))
570 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
573 szGuid[38] = W('\0');
578 //*****************************************************************************
579 // Convert wide string of (at most eight) hex digits into a hex value
580 //*****************************************************************************
583 __in_ecount((cbHexNum * 2)) LPCWSTR szHexNum,
592 _ASSERTE (szHexNum && phHexNum);
593 _ASSERTE(cbHexNum == 1 || cbHexNum == 2 || cbHexNum == 4);
595 cbHexNum *= 2; // each nibble is a char
597 for (DWORD i = 0; i < cbHexNum; ++i)
600 if (szHexNum[i] >= W('0') && szHexNum[i] <= W('9'))
602 nibble = szHexNum[i] - '0';
604 else if (szHexNum[i] >= W('A') && szHexNum[i] <= W('F'))
606 nibble = 10 + szHexNum[i] - 'A';
608 else if (szHexNum[i] >= W('a') && szHexNum[i] <= W('f'))
610 nibble = 10 + szHexNum[i] - 'a';
616 val = (val << 4) + nibble;
622 //*****************************************************************************
623 // Parse a Wide char string into a GUID
624 //*****************************************************************************
627 GUID * Guid, // [OUT] The GUID to fill in
628 __in_ecount(cchGuid) LPCWSTR szGuid, // [IN] String to parse
629 DWORD cchGuid) // [IN] Count in wchars in string
640 // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD
641 // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.
643 if (cchGuid < 38) // 38 chars + 1 null terminating.
646 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
648 if (szGuid[0] != W('{')) return FALSE;
650 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
652 if (FAILED (GetHex(&dw, szGuid+1 , 4))) return FALSE;
655 if (szGuid[9] != W('-')) return FALSE;
657 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
659 if (FAILED (GetHex(&dw, szGuid+10, 2))) return FALSE;
660 Guid->Data2 = (WORD)dw;
662 if (szGuid[14] != W('-')) return FALSE;
664 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
666 if (FAILED (GetHex(&dw, szGuid+15, 2))) return FALSE;
667 Guid->Data3 = (WORD)dw;
669 if (szGuid[19] != W('-')) return FALSE;
671 // Get the last two fields (which are byte arrays).
672 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
674 for (i=0; i < 2; ++i)
676 if (FAILED(GetHex(&dw, szGuid + 20 + (i * 2), 1))) return FALSE;
677 Guid->Data4[i] = (BYTE)dw;
680 if (szGuid[24] != W('-')) return FALSE;
682 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
684 for (i=0; i < 6; ++i)
686 if (FAILED(GetHex(&dw, szGuid + 25 + (i * 2), 1))) return FALSE;
687 Guid->Data4[i+2] = (BYTE)dw;
690 // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
692 if (szGuid[37] != W('}')) return FALSE;
699 // Always write regardless of registry.
700 void _cdecl DbgWriteEx(LPCTSTR szFmt, ...)
711 va_start(marker, szFmt);
712 _vsnwprintf_s(rcBuff, _countof(rcBuff), _TRUNCATE, szFmt, marker);
714 WszOutputDebugString(rcBuff);
718 /**************************************************************************/
719 void ConfigDWORD::init(const CLRConfig::ConfigDWORDInfo & info)
727 // make sure that the memory was zero initialized
728 _ASSERTE(m_inited == 0 || m_inited == 1);
730 m_value = CLRConfig::GetConfigValue(info);
734 //---------------------------------------------------------------------------------------
736 // Takes a const input string, and returns the start & size of the substring that has all
737 // leading and trailing whitespace removed. The original string is not modified.
740 // * pwsz - [in] points to const string we want to trim; [out] points to beginning
741 // of trimmed substring of input string
742 // * pcch - [in] Points to length in chars of input string (not counting null
743 // terminator); [out] Points to length in chars of trimmed substring (not
744 // counting null terminator)
746 void TrimWhiteSpace(__deref_inout_ecount(*pcch) LPCWSTR *pwsz, __inout LPDWORD pcch)
748 LIMITED_METHOD_DAC_CONTRACT;
750 _ASSERTE (pwsz != NULL);
751 _ASSERTE (*pwsz != NULL);
752 _ASSERTE (pcch != NULL);
755 LPCWSTR wszBeginning = *pwsz;
756 LPCWSTR wszEnd = wszBeginning + (cch - 1);
758 while ((cch != 0) && iswspace(*wszBeginning))
764 while ((cch != 0) && iswspace(*wszEnd))
770 *pwsz = wszBeginning;
774 BOOL ThreadWillCreateGuardPage(SIZE_T sizeReservedStack, SIZE_T sizeCommitedStack)
776 // We need to make sure there will be a reserved but never committed page at the end
777 // of the stack. We do here the check NT does when it creates the user stack to decide
778 // if there is going to be a guard page. However, that is not enough, as if we only
779 // have a guard page, we have nothing to protect us from going pass it. Well, in
780 // fact, there is something that we will protect you, there are certain places
781 // (RTLUnwind) in NT that will check that the current frame is within stack limits.
782 // If we are not it will bomb out. We will also bomb out if we touch the hard guard
785 // For situation B, teb->StackLimit is at the beggining of the user stack (ie
786 // before updating StackLimit it checks if it was able to create a new guard page,
787 // in this case, it can't), which makes the check fail in RtlUnwind.
789 // Situation A [ Hard guard page | Guard page | user stack]
791 // Situation B [ Guard page | User stack ]
793 // Situation C [ User stack ( no room for guard page) ]
795 // Situation D (W9x) : Guard page or not, w9x has a 64k reserved region below
796 // the stack, we don't need any checks at all
798 // We really want to be in situation A all the time, so we add one more page
799 // to our requirements (we require guard page + hard guard)
802 ::GetSystemInfo(&sysInfo);
804 // OS rounds up sizes the following way to decide if it marks a guard page
805 sizeReservedStack = ALIGN(sizeReservedStack, ((size_t)sysInfo.dwAllocationGranularity)); // Allocation granularity
806 sizeCommitedStack = ALIGN(sizeCommitedStack, ((size_t)sysInfo.dwPageSize)); // Page Size
808 // OS wont create guard page, we can't execute managed code safely.
809 // We also have to make sure we have a 'hard' guard, thus we add another
810 // page to the memory we would need comitted.
811 // That is, the following code will check if sizeReservedStack is at least 2 pages
812 // more than sizeCommitedStack.
813 return (sizeReservedStack > sizeCommitedStack + ((size_t)sysInfo.dwPageSize));
814 } // ThreadWillCreateGuardPage
816 //The following characters have special sorting weights when combined with other
817 //characters, which means we can't use our fast sorting algorithm on them.
818 //Most of these are pretty rare control characters, but apostrophe and hyphen
819 //are fairly common and force us down the slower path. This is because we want
820 //"word sorting", which means that "coop" and "co-op" sort together, instead of
821 //separately as they would if we were doing a string sort.
822 // 0x0001 6 3 2 2 0 ;Start Of Heading
823 // 0x0002 6 4 2 2 0 ;Start Of Text
824 // 0x0003 6 5 2 2 0 ;End Of Text
825 // 0x0004 6 6 2 2 0 ;End Of Transmission
826 // 0x0005 6 7 2 2 0 ;Enquiry
827 // 0x0006 6 8 2 2 0 ;Acknowledge
828 // 0x0007 6 9 2 2 0 ;Bell
829 // 0x0008 6 10 2 2 0 ;Backspace
831 // 0x000e 6 11 2 2 0 ;Shift Out
832 // 0x000f 6 12 2 2 0 ;Shift In
833 // 0x0010 6 13 2 2 0 ;Data Link Escape
834 // 0x0011 6 14 2 2 0 ;Device Control One
835 // 0x0012 6 15 2 2 0 ;Device Control Two
836 // 0x0013 6 16 2 2 0 ;Device Control Three
837 // 0x0014 6 17 2 2 0 ;Device Control Four
838 // 0x0015 6 18 2 2 0 ;Negative Acknowledge
839 // 0x0016 6 19 2 2 0 ;Synchronous Idle
840 // 0x0017 6 20 2 2 0 ;End Of Transmission Block
841 // 0x0018 6 21 2 2 0 ;Cancel
842 // 0x0019 6 22 2 2 0 ;End Of Medium
843 // 0x001a 6 23 2 2 0 ;Substitute
844 // 0x001b 6 24 2 2 0 ;Escape
845 // 0x001c 6 25 2 2 0 ;File Separator
846 // 0x001d 6 26 2 2 0 ;Group Separator
847 // 0x001e 6 27 2 2 0 ;Record Separator
848 // 0x001f 6 28 2 2 0 ;Unit Separator
850 // 0x0027 6 128 2 2 0 ;Apostrophe-Quote
851 // 0x002d 6 130 2 2 0 ;Hyphen-Minus
853 // 0x007f 6 29 2 2 0 ;Delete
856 HighCharHelper::HighCharTable[]= {
871 #endif // PLATFORM_UNIX
878 #endif // PLATFORM_UNIX
993 }; // HighCharHelper::HighCharTable