summaryrefslogtreecommitdiff
path: root/src/utilcode
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/utilcode
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/utilcode')
-rw-r--r--src/utilcode/.gitmirror1
-rw-r--r--src/utilcode/CMakeLists.txt100
-rw-r--r--src/utilcode/UtilCode.vcproj606
-rw-r--r--src/utilcode/UtilCode.vcxproj447
-rw-r--r--src/utilcode/UtilCode.vcxproj.vspscc10
-rw-r--r--src/utilcode/apithreadstress.cpp181
-rw-r--r--src/utilcode/appxutil.cpp866
-rw-r--r--src/utilcode/arraylist.cpp311
-rw-r--r--src/utilcode/assemblyfilehash.cpp170
-rw-r--r--src/utilcode/bitvector.cpp406
-rw-r--r--src/utilcode/ccomprc.cpp1134
-rw-r--r--src/utilcode/check.cpp309
-rw-r--r--src/utilcode/circularlog.cpp312
-rw-r--r--src/utilcode/clrconfig.cpp777
-rw-r--r--src/utilcode/clrhelpers.cpp14
-rw-r--r--src/utilcode/clrhost.cpp460
-rw-r--r--src/utilcode/clrhost_nodependencies.cpp958
-rw-r--r--src/utilcode/collections.cpp973
-rw-r--r--src/utilcode/comex.cpp52
-rw-r--r--src/utilcode/corimage.cpp261
-rw-r--r--src/utilcode/crossgen/.gitmirror1
-rw-r--r--src/utilcode/crossgen/utilcode_crossgen.nativeproj21
-rw-r--r--src/utilcode/cycletimer.cpp85
-rw-r--r--src/utilcode/dac/.gitmirror1
-rw-r--r--src/utilcode/dac/CMakeLists.txt10
-rw-r--r--src/utilcode/dac/dirs.proj18
-rw-r--r--src/utilcode/dacutil.cpp251
-rw-r--r--src/utilcode/debug.cpp949
-rw-r--r--src/utilcode/delayloadhelpers.cpp116
-rw-r--r--src/utilcode/dirs.proj27
-rw-r--r--src/utilcode/dlwrap.cpp231
-rw-r--r--src/utilcode/downlevel.cpp2736
-rw-r--r--src/utilcode/dyncrt/.gitmirror1
-rw-r--r--src/utilcode/dyncrt/CMakeLists.txt7
-rw-r--r--src/utilcode/dyncrt/dyncrt.nativeproj25
-rw-r--r--src/utilcode/ex.cpp2121
-rw-r--r--src/utilcode/format1.cpp121
-rw-r--r--src/utilcode/fstream.cpp296
-rw-r--r--src/utilcode/fstring.cpp317
-rw-r--r--src/utilcode/genericstackprobe.cpp511
-rw-r--r--src/utilcode/guidfromname.cpp210
-rw-r--r--src/utilcode/hostimpl.cpp426
-rw-r--r--src/utilcode/hostimpl.h112
-rw-r--r--src/utilcode/iallocator.cpp15
-rw-r--r--src/utilcode/ilformatter.cpp822
-rw-r--r--src/utilcode/jitperf.cpp132
-rw-r--r--src/utilcode/lazycow.cpp315
-rw-r--r--src/utilcode/loaderheap.cpp2262
-rw-r--r--src/utilcode/loadrc-impl.cpp322
-rw-r--r--src/utilcode/loadrc.cpp17
-rw-r--r--src/utilcode/log.cpp447
-rw-r--r--src/utilcode/makepath.cpp208
-rw-r--r--src/utilcode/md5.cpp293
-rw-r--r--src/utilcode/memorypool.cpp318
-rw-r--r--src/utilcode/namespaceutil.cpp680
-rw-r--r--src/utilcode/newapis.cpp1429
-rw-r--r--src/utilcode/opinfo.cpp129
-rw-r--r--src/utilcode/outstring.cpp182
-rw-r--r--src/utilcode/pedecoder.cpp2938
-rw-r--r--src/utilcode/peinformation.cpp194
-rw-r--r--src/utilcode/perflog.cpp327
-rw-r--r--src/utilcode/posterror.cpp404
-rw-r--r--src/utilcode/prettyprintsig.cpp1004
-rw-r--r--src/utilcode/rangetree.cpp520
-rw-r--r--src/utilcode/registrywrapper.cpp360
-rw-r--r--src/utilcode/regutil.cpp1496
-rw-r--r--src/utilcode/regutilformac.cpp13
-rw-r--r--src/utilcode/safewrap.cpp415
-rw-r--r--src/utilcode/sbuffer.cpp147
-rw-r--r--src/utilcode/securityutil.cpp509
-rw-r--r--src/utilcode/securitywrapper.cpp842
-rw-r--r--src/utilcode/sigbuilder.cpp166
-rw-r--r--src/utilcode/sigparser.cpp197
-rw-r--r--src/utilcode/sortversioning.cpp1139
-rw-r--r--src/utilcode/splitpath.cpp281
-rw-r--r--src/utilcode/sstring.cpp2871
-rw-r--r--src/utilcode/sstring_com.cpp103
-rw-r--r--src/utilcode/stacktrace.cpp978
-rw-r--r--src/utilcode/staticnohost/.gitmirror1
-rw-r--r--src/utilcode/staticnohost/CMakeLists.txt6
-rw-r--r--src/utilcode/staticnohost/staticnohost.nativeproj15
-rw-r--r--src/utilcode/staticnohost/staticnohost.targets15
-rw-r--r--src/utilcode/stdafx.cpp13
-rw-r--r--src/utilcode/stdafx.h23
-rw-r--r--src/utilcode/stgpool.cpp2451
-rw-r--r--src/utilcode/stgpooli.cpp349
-rw-r--r--src/utilcode/stgpoolreadonly.cpp212
-rw-r--r--src/utilcode/stresslog.cpp720
-rw-r--r--src/utilcode/sxshelpers.cpp1509
-rw-r--r--src/utilcode/tlbutils.cpp222
-rw-r--r--src/utilcode/tls.cpp272
-rw-r--r--src/utilcode/util.cpp4091
-rw-r--r--src/utilcode/util_nodependencies.cpp1119
-rw-r--r--src/utilcode/utilcode.settings.targets128
-rw-r--r--src/utilcode/utilmessagebox.cpp599
-rw-r--r--src/utilcode/utsem.cpp553
-rw-r--r--src/utilcode/winfix.cpp515
97 files changed, 52229 insertions, 0 deletions
diff --git a/src/utilcode/.gitmirror b/src/utilcode/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/utilcode/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/utilcode/CMakeLists.txt b/src/utilcode/CMakeLists.txt
new file mode 100644
index 0000000000..bf7317b526
--- /dev/null
+++ b/src/utilcode/CMakeLists.txt
@@ -0,0 +1,100 @@
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_definitions(-DNO_CRT)
+
+if(WIN32)
+ add_compile_options(/wd4996)
+endif(WIN32)
+
+set(UTILCODE_SOURCES
+ clrhost_nodependencies.cpp
+ ccomprc.cpp
+ ex.cpp
+ sbuffer.cpp
+ sstring_com.cpp
+ fstring.cpp
+ namespaceutil.cpp
+ makepath.cpp
+ splitpath.cpp
+ clrconfig.cpp
+ registrywrapper.cpp
+ collections.cpp
+ genericstackprobe.cpp
+ posterror.cpp
+ fstream.cpp
+ clrhelpers.cpp
+ stgpool.cpp
+ stgpooli.cpp
+ stgpoolreadonly.cpp
+ lazycow.cpp
+ utsem.cpp
+ peinformation.cpp
+ check.cpp
+ log.cpp
+ apithreadstress.cpp
+ arraylist.cpp
+ bitvector.cpp
+ comex.cpp
+ delayloadhelpers.cpp
+ guidfromname.cpp
+ jitperf.cpp
+ memorypool.cpp
+ rangetree.cpp
+ iallocator.cpp
+ loaderheap.cpp
+ perflog.cpp
+ outstring.cpp
+ ilformatter.cpp
+ opinfo.cpp
+ dacutil.cpp
+ sortversioning.cpp
+ corimage.cpp
+ format1.cpp
+ prettyprintsig.cpp
+ regutil.cpp
+ sigbuilder.cpp
+ sigparser.cpp
+ sstring.cpp
+ util_nodependencies.cpp
+ utilmessagebox.cpp
+ safewrap.cpp
+ clrhost.cpp
+ cycletimer.cpp
+ md5.cpp
+ util.cpp
+ stresslog.cpp
+ debug.cpp
+ pedecoder.cpp
+)
+
+# These source file do not yet compile on Linux.
+# They should be moved out from here into the declaration
+# of UTILCODE_SOURCES above after fixing compiler errors.
+if(WIN32)
+ set(UTILCODE_SOURCES
+ ${UTILCODE_SOURCES}
+ appxutil.cpp
+ dlwrap.cpp
+ downlevel.cpp
+ loadrc.cpp
+ newapis.cpp
+ tls.cpp
+ securitywrapper.cpp
+ securityutil.cpp
+ stacktrace.cpp
+ winfix.cpp
+ )
+endif(WIN32)
+
+convert_to_absolute_path(UTILCODE_SOURCES ${UTILCODE_SOURCES})
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_compile_options(-fPIC)
+endif(CLR_CMAKE_PLATFORM_UNIX)
+
+add_subdirectory(dac)
+add_subdirectory(dyncrt)
+if(WIN32)
+ add_subdirectory(staticnohost)
+endif(WIN32) \ No newline at end of file
diff --git a/src/utilcode/UtilCode.vcproj b/src/utilcode/UtilCode.vcproj
new file mode 100644
index 0000000000..3f147725b8
--- /dev/null
+++ b/src/utilcode/UtilCode.vcproj
@@ -0,0 +1,606 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="utilcode"
+ ProjectGUID="{82B95BDA-224E-49B4-8BB7-03E7B9EEFF3B}"
+ RootNamespace="utilcode"
+ SccProjectName="SAK"
+ SccAuxPath="SAK"
+ SccLocalPath="SAK"
+ SccProvider="SAK"
+ Keyword="MakeFileProj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="x86dbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:dbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:dbg"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86chk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:chk"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:chk"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86ret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:ret"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:ret"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64dbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:dbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:dbg"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1;"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64chk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:chk"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:chk"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1;"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64ret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:ret"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:ret"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1;"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86corechk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:corechk"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:corechk"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86coredbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="x86coreret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coreret"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coreret"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Mac.inteldbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_X86_=1;i386=1"
+ IncludeSearchPath="..\inc\;&quot;$(SolutionDir)\..\..\..\rotor\palrt\inc&quot;;&quot;$(SolutionDir)\..\..\..\rotor\pal\inc&quot;"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64corechk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:corechk"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:corechk"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64coredbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coredbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coredbg"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="amd64coreret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coreret"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coreret"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_AMD64_=1;_WIN64=1"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="armcoredbg|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coredbg"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coredbg"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_ARM_;ARM"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="armcorechk|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:corechk"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:corechk"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_ARM_;ARM"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="armcoreret|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coreret"
+ ReBuildCommandLine="$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coreret"
+ CleanCommandLine=""
+ Output="Example.exe"
+ PreprocessorDefinitions="_ARM_;ARM"
+ IncludeSearchPath="..\inc\"
+ ForcedIncludes="$(ProjectDir)..\defines\cache\defines.$(ConfigurationName).h"
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\apithreadstress.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\arraylist.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\assemblyfilehash.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\bitvector.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\ccomprc.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\check.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\circularlog.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\CLRConfig.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\clrhost.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\COMEx.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\corimage.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\corimage.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\CycleTimer.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\dacutil.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\debug.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\dlwrap.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\downlevel.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\ex.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\format1.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\genericstackprobe.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\GuidFromName.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hostimpl.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hostimpl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\IAllocator.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\ilFormatter.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\jitperf.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\loaderheap.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\lazycow.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\log.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\makepath.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\md5.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\memorypool.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\namespaceutil.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\newapis.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\pedecoder.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\PerfLog.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\posterror.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\PrettyPrintSig.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\rangetree.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\regutil.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\safewrap.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sbuffer.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\SecurityUtil.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\securitywrapper.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sigbuilder.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sigparser.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\SortVersioning.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\SortVersioning.h"
+ >
+ </File>
+ <File
+ RelativePath=".\splitpat.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sstring.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sstring_com.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stacktrace.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stgpool.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stgpooli.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stgpoolreadonly.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stresslog.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\TlbUtils.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\tls.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\util.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\utilmessagebox.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\UTSEM.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\winfix.cpp"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/utilcode/UtilCode.vcxproj b/src/utilcode/UtilCode.vcxproj
new file mode 100644
index 0000000000..d0926fd8c6
--- /dev/null
+++ b/src/utilcode/UtilCode.vcxproj
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="amd64chk|Win32">
+ <Configuration>amd64chk</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="amd64corechk|Win32">
+ <Configuration>amd64corechk</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="amd64coredbg|Win32">
+ <Configuration>amd64coredbg</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="amd64coreret|Win32">
+ <Configuration>amd64coreret</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="amd64dbg|Win32">
+ <Configuration>amd64dbg</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="amd64ret|Win32">
+ <Configuration>amd64ret</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="armcorechk|Win32">
+ <Configuration>armcorechk</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="armcoredbg|Win32">
+ <Configuration>armcoredbg</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="armcoreret|Win32">
+ <Configuration>armcoreret</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Mac.inteldbg|Win32">
+ <Configuration>Mac.inteldbg</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="x86chk|Win32">
+ <Configuration>x86chk</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="x86corechk|Win32">
+ <Configuration>x86corechk</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="x86coredbg|Win32">
+ <Configuration>x86coredbg</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="x86coreret|Win32">
+ <Configuration>x86coreret</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="x86dbg|Win32">
+ <Configuration>x86dbg</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="x86ret|Win32">
+ <Configuration>x86ret</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{82B95BDA-224E-49B4-8BB7-03E7B9EEFF3B}</ProjectGuid>
+ <RootNamespace>utilcode</RootNamespace>
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ <Keyword>MakeFileProj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>12.0.20617.1</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86dbg|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:dbg</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:dbg</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86chk|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:chk</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:chk</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86ret|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:ret</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:ret</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64dbg|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:dbg</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:dbg</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64chk|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:chk</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:chk</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64ret|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x64/buildType:ret</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x64/buildType:ret</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86corechk|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:corechk</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:corechk</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coredbg|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86coreret|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coreret</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coreret</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Mac.inteldbg|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:x86/buildType:coredbg</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:x86/buildType:coredbg</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_X86_=1;i386=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(SolutionDir)\..\..\..\rotor\palrt\inc;$(SolutionDir)\..\..\..\rotor\pal\inc;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64corechk|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:corechk</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:corechk</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coredbg|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coredbg</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coredbg</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='amd64coreret|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:amd64/buildType:coreret</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:amd64/buildType:coreret</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_AMD64_=1;_WIN64=1;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoredbg|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coredbg</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coredbg</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_ARM_;ARM;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcorechk|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:corechk</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:corechk</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_ARM_;ARM;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='armcoreret|Win32'">
+ <OutDir>$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <NMakeBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArch:arm/buildType:coreret</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>$(ProjectDir)..\..\bin\runjs userBuild ndp\clr\src\utilcode /buildArgs:-c/buildArch:arm/buildType:coreret</NMakeReBuildCommandLine>
+ <NMakeCleanCommandLine />
+ <NMakeOutput>Example.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>_ARM_;ARM;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeIncludeSearchPath>..\inc\;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
+ <NMakeForcedIncludes>$(ProjectDir)..\defines\cache\defines.$(Configuration).h;$(NMakeForcedIncludes)</NMakeForcedIncludes>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="apithreadstress.cpp" />
+ <ClCompile Include="arraylist.cpp" />
+ <ClCompile Include="assemblyfilehash.cpp" />
+ <ClCompile Include="bitvector.cpp" />
+ <ClCompile Include="ccomprc.cpp" />
+ <ClCompile Include="check.cpp" />
+ <ClCompile Include="circularlog.cpp" />
+ <ClCompile Include="CLRConfig.cpp" />
+ <ClCompile Include="clrhost.cpp" />
+ <ClCompile Include="COMEx.cpp" />
+ <ClCompile Include="corimage.cpp" />
+ <ClCompile Include="CycleTimer.cpp" />
+ <ClCompile Include="dacutil.cpp" />
+ <ClCompile Include="debug.cpp" />
+ <ClCompile Include="dlwrap.cpp" />
+ <ClCompile Include="downlevel.cpp" />
+ <ClCompile Include="ex.cpp" />
+ <ClCompile Include="format1.cpp" />
+ <ClCompile Include="genericstackprobe.cpp" />
+ <ClCompile Include="GuidFromName.cpp" />
+ <ClCompile Include="hostimpl.cpp" />
+ <ClCompile Include="IAllocator.cpp" />
+ <ClCompile Include="ilFormatter.cpp" />
+ <ClCompile Include="jitperf.cpp" />
+ <ClCompile Include="loaderheap.cpp" />
+ <ClCompile Include="lazycow.cpp" />
+ <ClCompile Include="log.cpp" />
+ <ClCompile Include="makepath.cpp" />
+ <ClCompile Include="md5.cpp" />
+ <ClCompile Include="memorypool.cpp" />
+ <ClCompile Include="namespaceutil.cpp" />
+ <ClCompile Include="newapis.cpp" />
+ <ClCompile Include="pedecoder.cpp" />
+ <ClCompile Include="PerfLog.cpp" />
+ <ClCompile Include="posterror.cpp" />
+ <ClCompile Include="PrettyPrintSig.cpp" />
+ <ClCompile Include="rangetree.cpp" />
+ <ClCompile Include="regutil.cpp" />
+ <ClCompile Include="safewrap.cpp" />
+ <ClCompile Include="sbuffer.cpp" />
+ <ClCompile Include="SecurityUtil.cpp" />
+ <ClCompile Include="securitywrapper.cpp" />
+ <ClCompile Include="sigbuilder.cpp" />
+ <ClCompile Include="sigparser.cpp" />
+ <ClCompile Include="SortVersioning.cpp" />
+ <ClCompile Include="splitpat.cpp" />
+ <ClCompile Include="sstring.cpp" />
+ <ClCompile Include="sstring_com.cpp" />
+ <ClCompile Include="stacktrace.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ <ClCompile Include="stgpool.cpp" />
+ <ClCompile Include="stgpooli.cpp" />
+ <ClCompile Include="stgpoolreadonly.cpp" />
+ <ClCompile Include="stresslog.cpp" />
+ <ClCompile Include="tick.cpp" />
+ <ClCompile Include="TlbUtils.cpp" />
+ <ClCompile Include="tls.cpp" />
+ <ClCompile Include="util.cpp" />
+ <ClCompile Include="utilmessagebox.cpp" />
+ <ClCompile Include="UTSEM.cpp" />
+ <ClCompile Include="winfix.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="hostimpl.h" />
+ <ClInclude Include="SortVersioning.h" />
+ <ClInclude Include="stdafx.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/src/utilcode/UtilCode.vcxproj.vspscc b/src/utilcode/UtilCode.vcxproj.vspscc
new file mode 100644
index 0000000000..b6d32892fd
--- /dev/null
+++ b/src/utilcode/UtilCode.vcxproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = ""
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
+}
diff --git a/src/utilcode/apithreadstress.cpp b/src/utilcode/apithreadstress.cpp
new file mode 100644
index 0000000000..1dba1190f0
--- /dev/null
+++ b/src/utilcode/apithreadstress.cpp
@@ -0,0 +1,181 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ---------------------------------------------------------------------------
+// APIThreadStress.cpp (API thread stresser)
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "apithreadstress.h"
+#include "clrhost.h"
+#include "ex.h"
+#include "log.h"
+
+
+
+// For now, thread stress is incompatible with hosting. We need a host CreateThread
+// to fix this.
+#undef SetEvent
+#undef ResetEvent
+
+int APIThreadStress::s_threadStressCount = 0;
+
+APIThreadStress::APIThreadStress()
+{
+ WRAPPER_NO_CONTRACT;
+
+ m_threadCount = 0;
+
+ // Don't "fork" stress threads
+ if (ClrFlsGetValue(TlsIdx_StressThread) == NULL)
+ m_threadCount = s_threadStressCount;
+
+ if (m_threadCount != 0)
+ {
+ m_setupOK = TRUE;
+
+ m_hThreadArray = new (nothrow) HANDLE [ m_threadCount ];
+ if (m_hThreadArray == NULL)
+ m_setupOK = FALSE;
+ else
+ {
+ HANDLE *p = m_hThreadArray;
+ HANDLE *pEnd = p + m_threadCount;
+
+ while (p < pEnd)
+ {
+ DWORD id;
+ *p = ::CreateThread(NULL, 0, StartThread, this, 0, &id);
+ if (*p == NULL)
+ m_setupOK = FALSE;
+ p++;
+ }
+ }
+
+ m_syncEvent = ClrCreateManualEvent(FALSE);
+ if (m_syncEvent == INVALID_HANDLE_VALUE)
+ m_setupOK = FALSE;
+ }
+}
+
+APIThreadStress::~APIThreadStress()
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_threadCount > 0)
+ {
+ HANDLE *p = m_hThreadArray;
+ HANDLE *pEnd = p + m_threadCount;
+
+ if (p != NULL)
+ {
+ while (p < pEnd)
+ {
+ if (*p != NULL)
+ {
+ if (m_threadCount > 0 && m_setupOK)
+ WaitForSingleObjectEx(*p, INFINITE, FALSE);
+
+ ::CloseHandle(*p);
+ }
+ p++;
+ }
+ delete [] m_hThreadArray;
+ }
+
+ if (m_syncEvent != INVALID_HANDLE_VALUE)
+ CloseHandle(m_syncEvent);
+ }
+}
+
+void APIThreadStress::SetThreadStressCount(int threadCount)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ s_threadStressCount = threadCount;
+}
+
+
+DWORD WINAPI APIThreadStress::StartThread(void *arg)
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_SO_NOT_MAINLINE; // not mainline so scenario
+
+ APIThreadStress *pThis = (APIThreadStress *) arg;
+
+ ClrFlsSetValue(TlsIdx_StressThread, pThis);
+
+ EX_TRY
+ {
+ // Perform initial synchronization
+ WaitForSingleObjectEx(pThis->m_syncEvent, INFINITE, FALSE);
+ InterlockedIncrement(&pThis->m_runCount);
+
+ LOG((LF_ALL, LL_INFO100, "Stressing operation on thread %d\n", GetCurrentThreadId()));
+ ((APIThreadStress *)arg)->Invoke();
+ LOG((LF_ALL, LL_INFO100, "End stress operation on thread %d\n", GetCurrentThreadId()));
+
+ if (InterlockedDecrement(&pThis->m_runCount) == 0)
+ ::SetEvent(pThis->m_syncEvent);
+ }
+ EX_CATCH
+ {
+ LOG((LF_ALL, LL_ERROR, "Exception during stress operation on thread %d\n", GetCurrentThreadId()));
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return 0;
+}
+
+BOOL APIThreadStress::DoThreadStress()
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_threadCount > 0 && m_setupOK)
+ {
+ HANDLE *p = m_hThreadArray;
+ HANDLE *pEnd = p + m_threadCount;
+
+ while (p < pEnd)
+ {
+ ::ResumeThread(*p);
+ p++;
+ }
+
+ // Start the threads at the same time
+ ::SetEvent(m_syncEvent);
+
+ return TRUE;
+ }
+ else
+ {
+ SyncThreadStress();
+ return FALSE;
+ }
+}
+
+void APIThreadStress::SyncThreadStress()
+{
+ WRAPPER_NO_CONTRACT;
+
+ APIThreadStress *pThis = (APIThreadStress *) ClrFlsGetValue(TlsIdx_StressThread);
+
+ if (pThis != NULL)
+ {
+ LOG((LF_ALL, LL_INFO1000, "Syncing stress operation on thread %d\n", GetCurrentThreadId()));
+
+ ::ResetEvent(pThis->m_syncEvent);
+
+ if (InterlockedDecrement(&pThis->m_runCount) == 0)
+ ::SetEvent(pThis->m_syncEvent);
+ else
+ WaitForSingleObjectEx(pThis->m_syncEvent, INFINITE, FALSE);
+ InterlockedIncrement(&pThis->m_runCount);
+
+ LOG((LF_ALL, LL_INFO1000, "Resuming stress operation on thread %d\n", GetCurrentThreadId()));
+ }
+}
+
diff --git a/src/utilcode/appxutil.cpp b/src/utilcode/appxutil.cpp
new file mode 100644
index 0000000000..538fa38637
--- /dev/null
+++ b/src/utilcode/appxutil.cpp
@@ -0,0 +1,866 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+
+#include "stdafx.h"
+
+#include <strsafe.h>
+
+#include "utilcode.h"
+#include "holder.h"
+#include "volatile.h"
+#include "clr/fs.h"
+#include "clr/str.h"
+
+#include "appxutil.h"
+#include "ex.h"
+
+#include "shlwapi.h" // Path manipulation APIs
+
+#ifdef FEATURE_CORECLR
+
+GVAL_IMPL(bool, g_fAppX);
+INDEBUG(bool g_fIsAppXAsked;)
+
+namespace AppX
+{
+#ifdef DACCESS_COMPILE
+ bool DacIsAppXProcess()
+ {
+ return g_fAppX;
+ }
+#else
+
+ // Returns true if host has deemed the process to be appx
+ bool IsAppXProcess()
+ {
+ INDEBUG(g_fIsAppXAsked = true;)
+ return g_fAppX;
+ }
+
+
+ void SetIsAppXProcess(bool value)
+ {
+ _ASSERTE(!g_fIsAppXAsked);
+ g_fAppX = value;
+ }
+#endif
+};
+
+#else // FEATURE_CORECLR
+
+//---------------------------------------------------------------------------------------------
+// Convenience values
+
+#ifndef E_INSUFFICIENT_BUFFER
+ #define E_INSUFFICIENT_BUFFER (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+#endif
+
+#ifndef E_FILE_NOT_FOUND
+ #define E_FILE_NOT_FOUND (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
+#endif
+
+//---------------------------------------------------------------------------------------------
+using clr::str::IsNullOrEmpty;
+
+//---------------------------------------------------------------------------------------------
+typedef decltype(GetCurrentPackageId) GetCurrentPackageId_t;
+typedef decltype(GetCurrentPackageInfo) GetCurrentPackageInfo_t;
+typedef decltype(GetCurrentPackagePath) GetCurrentPackagePath_t;
+typedef decltype(OpenPackageInfoByFullName) OpenPackageInfoByFullName_t;
+typedef decltype(ClosePackageInfo) ClosePackageInfo_t;
+typedef decltype(GetPackageInfo) GetPackageInfo_t;
+
+//---------------------------------------------------------------------------------------------
+// Caches AppX ARI API-related information.
+struct AppXRTInfo
+{
+ HMODULE m_hAppXRTMod;
+ bool m_fIsAppXProcess;
+ bool m_fIsAppXAdaptiveApp;
+ bool m_fIsAppXNGen;
+
+ GetCurrentPackageId_t * m_pfnGetCurrentPackageId;
+ GetCurrentPackageInfo_t * m_pfnGetCurrentPackageInfo;
+ GetCurrentPackagePath_t * m_pfnGetCurrentPackagePath;
+
+ NewArrayHolder<BYTE> m_pbAppContainerInfo;
+ DWORD m_cbAppContainerInfo;
+
+ struct CurrentPackageInfo
+ {
+ UINT32 m_cbCurrentPackageInfo;
+ PBYTE m_pbCurrentPackageInfo;
+ UINT32 m_nCount;
+
+ CurrentPackageInfo(UINT32 cbPkgInfo, PBYTE pbPkgInfo, UINT32 nCount)
+ : m_cbCurrentPackageInfo(cbPkgInfo)
+ , m_pbCurrentPackageInfo(pbPkgInfo)
+ , m_nCount(nCount)
+ { LIMITED_METHOD_CONTRACT; }
+
+ ~CurrentPackageInfo()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (m_pbCurrentPackageInfo != nullptr)
+ {
+ delete [] m_pbCurrentPackageInfo;
+ m_pbCurrentPackageInfo = nullptr;
+ }
+ }
+ };
+
+ CurrentPackageInfo * m_pCurrentPackageInfo;
+
+ NewArrayHolder<WCHAR> m_AdaptiveAppWinmetadataDir;
+
+ AppXRTInfo() :
+ m_hAppXRTMod(nullptr),
+ m_fIsAppXProcess(false),
+ m_fIsAppXAdaptiveApp(false),
+ m_fIsAppXNGen(false),
+ m_pfnGetCurrentPackageId(nullptr),
+ m_pfnGetCurrentPackageInfo(nullptr),
+ m_pfnGetCurrentPackagePath(nullptr),
+ m_pbAppContainerInfo(nullptr),
+ m_pCurrentPackageInfo(nullptr),
+ m_AdaptiveAppWinmetadataDir(nullptr)
+ { LIMITED_METHOD_CONTRACT; }
+
+ ~AppXRTInfo()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (m_pCurrentPackageInfo != nullptr)
+ {
+ delete m_pCurrentPackageInfo;
+ m_pCurrentPackageInfo = nullptr;
+ }
+
+ if (m_hAppXRTMod != nullptr)
+ {
+ FreeLibrary(m_hAppXRTMod);
+ m_hAppXRTMod = nullptr;
+ }
+ }
+}; // struct AppXRTInfo
+
+GPTR_IMPL(AppXRTInfo, g_pAppXRTInfo); // Relies on zero init static memory.
+
+#ifndef DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------------
+static
+HRESULT GetAppContainerTokenInfoForProcess(
+ DWORD pid,
+ NewArrayHolder<BYTE>& pbAppContainerTokenInfo,
+ DWORD* pcbAppContainerTokenInfo)
+{
+ PRECONDITION(CheckPointer(pcbAppContainerTokenInfo, NULL_OK));
+
+ HRESULT hr = S_OK;
+
+ pbAppContainerTokenInfo = nullptr;
+
+ // In order to get the AppContainer SID we need to open the process token
+ HandleHolder hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (hProcess == NULL)
+ return HRESULT_FROM_GetLastError();
+
+ HandleHolder hToken;
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ return HRESULT_FROM_GetLastError();
+
+ // Query the process to see if it's inside an AppContainer
+ ULONG isAppContainer = 0;
+ DWORD actualLength = 0;
+ if (!GetTokenInformation(hToken, static_cast<TOKEN_INFORMATION_CLASS>(TokenIsAppContainer), &isAppContainer, sizeof(isAppContainer), &actualLength))
+ return HRESULT_FROM_GetLastError();
+
+ _ASSERTE(actualLength > 0);
+
+ // Not an AppContainer so bail
+ if (!isAppContainer)
+ {
+ return S_FALSE;
+ }
+
+ // Now we need the AppContainer SID so first get the required buffer length
+ actualLength = 0;
+ VERIFY(!GetTokenInformation(hToken, static_cast<TOKEN_INFORMATION_CLASS>(TokenAppContainerSid), NULL, 0, &actualLength));
+ hr = HRESULT_FROM_GetLastError();
+ _ASSERTE(hr == E_INSUFFICIENT_BUFFER);
+
+ // Something unexpected happened
+ if (hr != E_INSUFFICIENT_BUFFER)
+ return hr;
+
+ // Now we know the length of the AppContainer SID so create a buffer and retrieve it
+ pbAppContainerTokenInfo = new (nothrow) BYTE[actualLength];
+ IfNullRet(pbAppContainerTokenInfo);
+
+ if (!GetTokenInformation(hToken, static_cast<TOKEN_INFORMATION_CLASS>(TokenAppContainerSid), pbAppContainerTokenInfo.GetValue(), actualLength, &actualLength))
+ return HRESULT_FROM_GetLastError();
+
+ if (pcbAppContainerTokenInfo != nullptr)
+ *pcbAppContainerTokenInfo = actualLength;
+
+ return S_OK;
+}
+
+//---------------------------------------------------------------------------------------------
+// Initializes a global AppXRTInfo structure if it has not already been initialized. Returns
+// false only in the event of OOM; otherwise caches the result of ARI information in
+// g_pAppXRTInfo. Thread safe.
+static
+HRESULT InitAppXRT()
+{
+ // This will be used for catastrophic errors.
+ HRESULT hr = S_OK;
+
+ if (VolatileLoad(&g_pAppXRTInfo) == nullptr)
+ {
+ NewHolder<AppXRTInfo> pAppXRTInfo = new (nothrow) AppXRTInfo();
+ IfNullRet(pAppXRTInfo); // Catastrophic error.
+
+ pAppXRTInfo->m_fIsAppXProcess = false;
+
+ do
+ {
+ LPCWSTR wzAppXRTDll = W("api-ms-win-appmodel-runtime-l1-1-0.dll");
+ // Does not use GetLoadWithAlteredSearchPathFlag() because that would cause infinite recursion.
+ pAppXRTInfo->m_hAppXRTMod = WszLoadLibrary(wzAppXRTDll);
+ if (pAppXRTInfo->m_hAppXRTMod == nullptr)
+ { // Error is catastrophic: can't find kernel32.dll?
+ hr = HRESULT_FROM_GetLastError();
+ break;
+ }
+
+ pAppXRTInfo->m_pfnGetCurrentPackageId = reinterpret_cast<GetCurrentPackageId_t *>(
+ GetProcAddress(pAppXRTInfo->m_hAppXRTMod, "GetCurrentPackageId"));
+ if (pAppXRTInfo->m_pfnGetCurrentPackageId == nullptr)
+ { // Error is non-catastrophic: could be running downlevel
+ break;
+ }
+
+ pAppXRTInfo->m_pfnGetCurrentPackageInfo = reinterpret_cast<GetCurrentPackageInfo_t *>(
+ GetProcAddress(pAppXRTInfo->m_hAppXRTMod, "GetCurrentPackageInfo"));
+ if (pAppXRTInfo->m_pfnGetCurrentPackageInfo == nullptr)
+ { // Error is catastrophic: GetCurrentPackageId is available but not GetCurrentPackageInfo?
+ hr = HRESULT_FROM_GetLastError();
+ break;
+ }
+
+ pAppXRTInfo->m_pfnGetCurrentPackagePath = reinterpret_cast<GetCurrentPackagePath_t *>(
+ GetProcAddress(pAppXRTInfo->m_hAppXRTMod, "GetCurrentPackagePath"));
+ if (pAppXRTInfo->m_pfnGetCurrentPackagePath == nullptr)
+ { // Error is catastrophic: GetCurrentPackageInfo is available but not GetCurrentPackagePath?
+ hr = HRESULT_FROM_GetLastError();
+ break;
+ }
+
+ // Determine if this is an AppX process
+ UINT32 cbBuffer = 0;
+ LONG lRes = (*pAppXRTInfo->m_pfnGetCurrentPackageId)(&cbBuffer, nullptr);
+ pAppXRTInfo->m_fIsAppXProcess = (lRes == ERROR_INSUFFICIENT_BUFFER);
+
+ _ASSERTE(AppX::IsAppXSupported());
+
+ hr = GetAppContainerTokenInfoForProcess(
+ GetCurrentProcessId(),
+ pAppXRTInfo->m_pbAppContainerInfo,
+ &pAppXRTInfo->m_cbAppContainerInfo);
+
+ if (FAILED(hr))
+ {
+ if (pAppXRTInfo->m_fIsAppXProcess)
+ { // Error is catastrophic: running in true immersive process but no token info?
+ }
+ else
+ { // Error is non-catastrophic: reset HRESULT to S_OK.
+ hr = S_OK;
+ }
+ break;
+ }
+ }
+ while (false);
+
+ if (InterlockedCompareExchangeT<AppXRTInfo>(&g_pAppXRTInfo, pAppXRTInfo, nullptr) == nullptr)
+ {
+ pAppXRTInfo.SuppressRelease();
+ }
+ }
+
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------------
+// Inline helper to check first an only init when required.
+static inline HRESULT CheckInitAppXRT()
+{
+ return (VolatileLoad(&g_pAppXRTInfo) != nullptr) ? S_OK : InitAppXRT();
+}
+
+#endif // !DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------------
+// Contains general helper methods for interacting with AppX functionality. This code will
+// gracefully fail on downlevel OS by returning false from AppX::IsAppXProcess, so always
+// call this API first to check before calling any of the others defined in this namespace.
+//
+// See http://windows/windows8/docs/Windows%208%20Feature%20Documents/Developer%20Experience%20(DEVX)/Apps%20Experience%20(APPX)/Modern%20Client/App%20Runtime%20Improvements%20API%20Developer%20Platform%20Spec.docm
+// for more information.
+
+namespace AppX
+{
+#ifdef DACCESS_COMPILE
+
+ //-----------------------------------------------------------------------------------------
+ // DAC-only IsAppXProcess. Returns false if g_pAppXRTInfo has not been initialized.
+ bool DacIsAppXProcess()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (g_pAppXRTInfo != nullptr && g_pAppXRTInfo->m_fIsAppXProcess);
+ }
+
+#else // DACCESS_COMPILE
+
+ //---------------------------------------------------------------------------------------------
+ // cleans up resources allocated in InitAppXRT()
+ void ShutDown()
+ {
+ if (VolatileLoad(&g_pAppXRTInfo) != nullptr)
+ {
+ delete g_pAppXRTInfo;
+ g_pAppXRTInfo = nullptr;
+ }
+ }
+
+ //-----------------------------------------------------------------------------------------
+ // Returns true if the current process is immersive.
+ // NOTE: a return value of true doesn't necessarily indicate that the process is a
+ // real Metro app, e.g. it could be an ngen process compiling an AppX assembly.
+ bool IsAppXProcess()
+ {
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+
+ if (FAILED(hr = CheckInitAppXRT()))
+ {
+ SetLastError(hr); // HRESULT_FROM_WIN32 is idempotent when error value is HRESULT.
+ return false;
+ }
+
+ return g_pAppXRTInfo->m_fIsAppXProcess;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ // Returns true if the current process is immersive.
+ // Only produces reliable results after IsAppXProcess is inititalized
+ bool IsAppXProcess_Initialized_NoFault()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (VolatileLoad(&g_pAppXRTInfo) == nullptr)
+ {
+ return false;
+ }
+ return g_pAppXRTInfo->m_fIsAppXProcess;
+ }
+
+ bool IsAppXNGen()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return VolatileLoad(&g_pAppXRTInfo) != nullptr && g_pAppXRTInfo->m_fIsAppXNGen;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ HRESULT InitCurrentPackageInfoCache()
+ {
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+
+ UINT32 cbBuffer = 0;
+ hr = HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageInfo)(PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, nullptr, nullptr));
+ if (hr != E_INSUFFICIENT_BUFFER)
+ return hr;
+
+ NewArrayHolder<BYTE> pbBuffer(new (nothrow) BYTE[cbBuffer]);
+ IfNullRet(pbBuffer);
+
+ UINT32 nCount = 0;
+ IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageInfo)(PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, pbBuffer, &nCount)));
+
+ NewHolder<AppXRTInfo::CurrentPackageInfo> pPkgInfo(
+ new (nothrow) AppXRTInfo::CurrentPackageInfo(cbBuffer, pbBuffer.Extract(), nCount));
+ IfNullRet(pPkgInfo);
+
+ if (InterlockedCompareExchangeT<AppXRTInfo::CurrentPackageInfo>(
+ &g_pAppXRTInfo->m_pCurrentPackageInfo, pPkgInfo, nullptr) == nullptr)
+ {
+ pPkgInfo.SuppressRelease();
+ }
+
+ return S_OK;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ FORCEINLINE HRESULT CheckInitCurrentPackageInfoCache()
+ {
+ WRAPPER_NO_CONTRACT;
+ PRECONDITION(IsAppXProcess());
+
+ if (!IsAppXProcess())
+ return E_UNEXPECTED;
+
+ if (g_pAppXRTInfo->m_pCurrentPackageInfo == nullptr)
+ return InitCurrentPackageInfoCache();
+ else
+ return S_OK;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ LPCWSTR GetHeadPackageMoniker()
+ {
+ STANDARD_VM_CONTRACT;
+
+ IfFailThrow(CheckInitCurrentPackageInfoCache());
+ return reinterpret_cast<PPACKAGE_INFO>(
+ g_pAppXRTInfo->m_pCurrentPackageInfo->m_pbCurrentPackageInfo)->packageFullName;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ // Returns the current process' PACKAGE_ID in the provided buffer. See the ARI spec (above)
+ // for more information.
+ HRESULT GetCurrentPackageId(
+ PUINT32 pBufferLength,
+ PBYTE pBuffer)
+ {
+ LIMITED_METHOD_CONTRACT;
+ HRESULT hr = S_OK;
+
+ IfFailRet(CheckInitAppXRT());
+ IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageId)(pBufferLength, pBuffer)));
+
+ return S_OK;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ // Returns the current process' PACKAGE_INFO in the provided buffer. See the ARI spec
+ // (above) for more information.
+ HRESULT GetCurrentPackageInfo(
+ UINT32 uiFlags,
+ PUINT32 pcbBuffer,
+ PBYTE pbBuffer,
+ PUINT32 pnCount)
+ {
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(IsAppXProcess());
+ PRECONDITION(CheckPointer(pcbBuffer));
+
+ HRESULT hr = S_OK;
+
+ IfFailRet(CheckInitAppXRT());
+
+ if (pcbBuffer == nullptr)
+ return E_INVALIDARG;
+
+ if (uiFlags == PACKAGE_FILTER_CLR_DEFAULT)
+ {
+ IfFailRet(CheckInitCurrentPackageInfoCache());
+
+ DWORD cbBuffer = *pcbBuffer;
+ *pcbBuffer = g_pAppXRTInfo->m_pCurrentPackageInfo->m_cbCurrentPackageInfo;
+ if (pnCount != nullptr)
+ {
+ *pnCount = g_pAppXRTInfo->m_pCurrentPackageInfo->m_nCount;
+ }
+
+ if (pbBuffer == nullptr || cbBuffer < g_pAppXRTInfo->m_pCurrentPackageInfo->m_cbCurrentPackageInfo)
+ {
+ return E_INSUFFICIENT_BUFFER;
+ }
+ memcpy(pbBuffer, g_pAppXRTInfo->m_pCurrentPackageInfo->m_pbCurrentPackageInfo, g_pAppXRTInfo->m_pCurrentPackageInfo->m_cbCurrentPackageInfo);
+ }
+ else
+ {
+ IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackageInfo)(uiFlags, pcbBuffer, pbBuffer, pnCount)));
+ }
+
+ return S_OK;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ bool IsAdaptiveApp()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ HRESULT hr = S_OK;
+ static bool cachedIsAdaptiveApp = false;
+
+ if (!IsAppXProcess())
+ {
+ return false;
+ }
+
+ if (!cachedIsAdaptiveApp)
+ {
+ cachedIsAdaptiveApp = true;
+ LPWSTR adaptiveAppWinmetaDataDir = NULL;
+
+ if (SUCCEEDED(hr = AppX::GetWinMetadataDirForAdaptiveApps(&adaptiveAppWinmetaDataDir)))
+ {
+ g_pAppXRTInfo->m_fIsAppXAdaptiveApp = clr::fs::Dir::Exists(adaptiveAppWinmetaDataDir);
+
+ }
+ else
+ {
+ SetLastError(hr);
+ g_pAppXRTInfo->m_fIsAppXAdaptiveApp = false;
+ }
+
+ }
+
+ return g_pAppXRTInfo->m_fIsAppXAdaptiveApp;
+ }
+
+
+ //-----------------------------------------------------------------------------------------
+ // length : Upon success, contains the the length of packagePath
+ // [in/out]
+ //
+ // packageRoot : Upon success, contains the full packagePath
+ // [out] [ Note: The memory has to be preallocated for the above length]
+ //
+ HRESULT GetCurrentPackagePath(_Inout_ UINT32* length, _Out_opt_ PWSTR packageRoot)
+ {
+ PRECONDITION(IsAppXProcess());
+ PRECONDITION(CheckPointer(length));
+
+ HRESULT hr;
+ IfFailRet(CheckInitAppXRT());
+
+ IfFailRet(HRESULT_FROM_WIN32((*g_pAppXRTInfo->m_pfnGetCurrentPackagePath)(length, packageRoot)));
+ return S_OK;
+ }
+
+ //-----------------------------------------------------------------------------------------
+ // winMetadDataDir : Upon success, contains the absolute path for the winmetadata directory for the adaptive app
+ // [out] NOTE: The string the pointer points to is global memory and never should be modified
+ //
+ HRESULT GetWinMetadataDirForAdaptiveApps(_Out_ LPWSTR* winMetadDataDir)
+ {
+
+ PRECONDITION(IsAppXProcess());
+
+ HRESULT hr;
+
+ IfFailRet(CheckInitAppXRT());
+
+ if (g_pAppXRTInfo->m_AdaptiveAppWinmetadataDir == nullptr)
+ {
+ LPCWSTR wzWinMetadataFolder=W("\\WinMetadata");
+ NewArrayHolder<WCHAR> wzCompletePath;
+ UINT32 length=0;
+
+ hr = GetCurrentPackagePath(&length, NULL);
+ if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ return hr;
+
+ NewArrayHolder<WCHAR> wzPath_holder = new (nothrow) WCHAR[length];
+
+ IfNullRet(wzPath_holder);
+ IfFailRet(GetCurrentPackagePath(&length, wzPath_holder.GetValue()));
+
+ DWORD cchFullPathBuf = length + (DWORD)wcslen(wzWinMetadataFolder) + 1;
+ IfNullRet(wzCompletePath = new (nothrow) WCHAR[cchFullPathBuf]);
+ IfFailRet(clr::fs::Path::Combine(wzPath_holder.GetValue(), wzWinMetadataFolder, &cchFullPathBuf, wzCompletePath.GetValue()));
+ g_pAppXRTInfo->m_AdaptiveAppWinmetadataDir = wzCompletePath.Extract();
+ }
+
+ *winMetadDataDir = g_pAppXRTInfo->m_AdaptiveAppWinmetadataDir;
+
+ return S_OK;
+ }
+
+#if defined(FEATURE_APPX_BINDER)
+ //-----------------------------------------------------------------------------------------
+ // Iterates the current process' packages and returns the first package that contains a
+ // file matching wzFileName.
+ //
+ // wzFileName : The file to look for in the current process' packages. If this is a
+ // [in] relative path, then it appends the path to the root of each package in
+ // sequence to see if the resulting path points to an existing file. If this
+ // is an absolute path, then for each package it determines if the package
+ // root is a prefix of wzFileName.
+ // pcchPathName : Upon entry contains the length of the buffer pwzPathName, and upon return
+ // [in/out] contains either the number of characters written or the buffer size
+ // required.
+ // pwzPathName : Upon success, contains the absolute path for the first matching file.
+ // [out]
+ HRESULT FindFileInCurrentPackage(
+ PCWSTR wzFileName,
+ PUINT32 pcchPathName,
+ PWSTR pwzPathName,
+ UINT32 uiFlags,
+ __in PCWSTR *rgwzAltPaths,
+ __in UINT32 cAltPaths,
+ FindFindInPackageFlags findInCurrentPackageFlags)
+ {
+ LIMITED_METHOD_CONTRACT;
+ PRECONDITION(IsAppXProcess());
+ PRECONDITION(CheckPointer(wzFileName));
+ PRECONDITION(CheckPointer(pcchPathName));
+ PRECONDITION(CheckPointer(pwzPathName));
+
+ HRESULT hr = S_OK;
+
+ if (!IsAppXProcess())
+ return E_UNEXPECTED;
+
+ // PACKAGE_FILTER_ALL_LOADED is obsolete, and shouldn't be used.
+ // We also don't currently handle the case where PACKAGE_FILTER_HEAD isn't set.
+ _ASSERTE(uiFlags != PACKAGE_FILTER_ALL_LOADED && (uiFlags & PACKAGE_FILTER_HEAD) != 0);
+ if (uiFlags == PACKAGE_FILTER_ALL_LOADED || (uiFlags & PACKAGE_FILTER_HEAD) == 0)
+ return E_NOTIMPL;
+
+ // File name must be non-null and relative
+ if (IsNullOrEmpty(wzFileName) || pcchPathName == nullptr || pwzPathName == nullptr)
+ return E_INVALIDARG;
+
+ // If we've been provided a full path and the file doesn't actually exist
+ // then we can immediately say that this function will fail with "file not found".
+ bool fIsRelative = clr::fs::Path::IsRelative(wzFileName);
+ if (!fIsRelative && !clr::fs::File::Exists(wzFileName))
+ return E_FILE_NOT_FOUND;
+
+ IfFailRet(CheckInitCurrentPackageInfoCache());
+
+ DWORD const cchFileName = static_cast<DWORD>(wcslen(wzFileName));
+ DWORD cchFullPathBuf = _MAX_PATH;
+ NewArrayHolder<WCHAR> wzFullPathBuf = new (nothrow) WCHAR[cchFullPathBuf];
+ IfNullRet(wzFullPathBuf);
+
+ auto FindFileInCurrentPackageHelper = [&](LPCWSTR wzPath) -> HRESULT
+ {
+ HRESULT hr = S_OK;
+
+ if (!(findInCurrentPackageFlags & FindFindInPackageFlags_AllowLongFormatPath) && clr::fs::Path::HasLongFormatPrefix(wzPath))
+ return COR_E_BAD_PATHNAME; // We can't handle long format paths.
+
+ // If the path is relative, concatenate the package root and the file name and see
+ // if the file exists.
+ if (fIsRelative)
+ {
+ DWORD cchFullPath = cchFullPathBuf;
+ hr = clr::fs::Path::Combine(wzPath, wzFileName, &cchFullPath, wzFullPathBuf);
+ if (hr == E_INSUFFICIENT_BUFFER)
+ {
+ IfNullRet(wzFullPathBuf = new (nothrow) WCHAR[cchFullPathBuf = (cchFullPath + 1)]);
+ hr = clr::fs::Path::Combine(wzPath, wzFileName, &cchFullPath, wzFullPathBuf);
+ }
+ IfFailRet(hr);
+
+ if (!clr::fs::Path::IsValid(wzFullPathBuf, cchFullPath, !!(findInCurrentPackageFlags & FindFindInPackageFlags_AllowLongFormatPath)))
+ return COR_E_BAD_PATHNAME;
+
+ if (clr::fs::File::Exists(wzFullPathBuf))
+ {
+ DWORD cchPathName = *pcchPathName;
+ *pcchPathName = cchFullPath;
+ return StringCchCopy(pwzPathName, cchPathName, wzFullPathBuf);
+ }
+ }
+ // If the path is absolute, see if the file name contains the pacakge root as a prefix
+ // and if the file exists.
+ else
+ {
+ DWORD cchPath = static_cast<DWORD>(wcslen(wzPath));
+
+ // Determine if wzPath is a path prefix of wzFileName
+ if (cchPath < cchFileName &&
+ _wcsnicmp(wzPath, wzFileName, cchPath) == 0 &&
+ (wzFileName[cchPath] == W('\\') || wzPath[cchPath-1] == W('\\'))) // Ensure wzPath is not just a prefix, but a path prefix
+ {
+ if (clr::fs::File::Exists(wzFileName))
+ {
+ DWORD cchPathName = *pcchPathName;
+ *pcchPathName = cchFileName;
+ return StringCchCopy(pwzPathName, cchPathName, wzFileName);
+ }
+ }
+ }
+
+ return S_FALSE;
+ }; // FindFileInCurrentPackageHelper
+
+ if (!(findInCurrentPackageFlags & FindFindInPackageFlags_SkipCurrentPackageGraph))
+ {
+ PCPACKAGE_INFO pCurNode = reinterpret_cast<PPACKAGE_INFO>(
+ g_pAppXRTInfo->m_pCurrentPackageInfo->m_pbCurrentPackageInfo);
+ PCPACKAGE_INFO pEndNode = pCurNode + g_pAppXRTInfo->m_pCurrentPackageInfo->m_nCount;
+ for (; pCurNode != pEndNode; ++pCurNode)
+ {
+ IfFailRet(FindFileInCurrentPackageHelper(pCurNode->path));
+
+ if (hr == S_OK)
+ {
+ return hr;
+ }
+
+ // End search if dependent packages should not be checked.
+ if ((uiFlags & PACKAGE_FILTER_DIRECT) == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ // Process alternative paths
+ for (UINT iAltPath = 0; iAltPath < cAltPaths; iAltPath++)
+ {
+ IfFailRet(FindFileInCurrentPackageHelper(rgwzAltPaths[iAltPath]));
+
+ if (hr == S_OK)
+ {
+ return hr;
+ }
+ }
+
+ return E_FILE_NOT_FOUND;
+ }
+#endif // FEATURE_APPX_BINDER
+
+ //-----------------------------------------------------------------------------------------
+ HRESULT GetAppContainerTokenInfoForProcess(
+ DWORD dwPid,
+ NewArrayHolder<BYTE>& pbAppContainerTokenInfo,
+ DWORD* pcbAppContainerTokenInfo)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ pbAppContainerTokenInfo = nullptr;
+
+ if (!IsAppXSupported())
+ {
+ return S_FALSE;
+ }
+
+ if (dwPid == GetCurrentProcessId())
+ {
+ if (FAILED(hr = CheckInitAppXRT()))
+ {
+ SetLastError(hr);
+ return S_FALSE;
+ }
+
+ if (g_pAppXRTInfo->m_pbAppContainerInfo == nullptr)
+ {
+ return S_FALSE;
+ }
+ else
+ {
+ pbAppContainerTokenInfo = g_pAppXRTInfo->m_pbAppContainerInfo.GetValue();
+ pbAppContainerTokenInfo.SuppressRelease(); // Caller does not need to free mem.
+ if (pcbAppContainerTokenInfo != nullptr)
+ {
+ *pcbAppContainerTokenInfo = g_pAppXRTInfo->m_cbAppContainerInfo;
+ }
+ return S_OK;
+ }
+ }
+ else
+ {
+ return ::GetAppContainerTokenInfoForProcess(dwPid, pbAppContainerTokenInfo, pcbAppContainerTokenInfo);
+ }
+ }
+
+ // Called during NGen to pretend that we're in a certain package. Due to Windows restriction, we can't
+ // start NGen worker processes in the right package environment (only in the right AppContainer). So
+ // NGen calls this function with a package name, to indicate that all AppX-related code should behave as
+ // if the current process is running in that package.
+ HRESULT SetCurrentPackageForNGen(__in PCWSTR pszPackageFullName)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ IfFailRet(CheckInitAppXRT());
+
+ _ASSERTE(IsAppXSupported());
+ _ASSERTE(g_pAppXRTInfo->m_hAppXRTMod != nullptr);
+
+ HMODULE hAppXRTMod = g_pAppXRTInfo->m_hAppXRTMod;
+ OpenPackageInfoByFullName_t *pfnOpenPackageInfoByFullName
+ = reinterpret_cast<OpenPackageInfoByFullName_t*>(GetProcAddress(hAppXRTMod, "OpenPackageInfoByFullName"));
+ _ASSERTE(pfnOpenPackageInfoByFullName != nullptr);
+ if (pfnOpenPackageInfoByFullName == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ ClosePackageInfo_t *pfnClosePackageInfo
+ = reinterpret_cast<ClosePackageInfo_t*>(GetProcAddress(hAppXRTMod, "ClosePackageInfo"));
+ _ASSERTE(pfnClosePackageInfo != nullptr);
+ if (pfnClosePackageInfo == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ GetPackageInfo_t *pfnGetPackageInfo
+ = reinterpret_cast<GetPackageInfo_t*>(GetProcAddress(hAppXRTMod, "GetPackageInfo"));
+ _ASSERTE(pfnGetPackageInfo != nullptr);
+ if (pfnGetPackageInfo == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ PACKAGE_INFO_REFERENCE packageInfoReference;
+ hr = HRESULT_FROM_WIN32(pfnOpenPackageInfoByFullName(pszPackageFullName, 0, &packageInfoReference));
+ if (FAILED(hr))
+ return hr;
+
+ // Automatically close packageInfoReference before we return.
+ class PackageInfoReferenceHolder
+ {
+ public:
+ PackageInfoReferenceHolder(ClosePackageInfo_t *pfnClosePackageInfo, PACKAGE_INFO_REFERENCE &packageInfoReference)
+ :m_pfnClosePackageInfo(pfnClosePackageInfo),
+ m_packageInfoReference(packageInfoReference)
+ {
+ }
+ ~PackageInfoReferenceHolder() { m_pfnClosePackageInfo(m_packageInfoReference); }
+ private:
+ ClosePackageInfo_t *m_pfnClosePackageInfo;
+ PACKAGE_INFO_REFERENCE &m_packageInfoReference;
+ } pirh(pfnClosePackageInfo, packageInfoReference);
+
+ UINT32 cbBuffer = 0;
+ hr = HRESULT_FROM_WIN32(pfnGetPackageInfo(packageInfoReference, PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, nullptr, nullptr));
+ if (hr != E_INSUFFICIENT_BUFFER)
+ return hr;
+
+ NewArrayHolder<BYTE> pbBuffer(new (nothrow) BYTE[cbBuffer]);
+ IfNullRet(pbBuffer);
+
+ UINT32 nCount = 0;
+ IfFailRet(HRESULT_FROM_WIN32(pfnGetPackageInfo(packageInfoReference, PACKAGE_FILTER_CLR_DEFAULT, &cbBuffer, pbBuffer, &nCount)));
+
+ NewHolder<AppXRTInfo::CurrentPackageInfo> pPkgInfo(
+ new (nothrow) AppXRTInfo::CurrentPackageInfo(cbBuffer, pbBuffer.Extract(), nCount));
+ IfNullRet(pPkgInfo);
+
+ if (InterlockedCompareExchangeT<AppXRTInfo::CurrentPackageInfo>(
+ &g_pAppXRTInfo->m_pCurrentPackageInfo, pPkgInfo, nullptr) == nullptr)
+ {
+ pPkgInfo.SuppressRelease();
+ }
+
+ g_pAppXRTInfo->m_fIsAppXProcess = true;
+ g_pAppXRTInfo->m_fIsAppXNGen = true;
+
+ return hr;
+ }
+
+#endif // DACCESS_COMPILE
+} // namespace AppX
+
+
+#endif // FEATURE_CORECLR
diff --git a/src/utilcode/arraylist.cpp b/src/utilcode/arraylist.cpp
new file mode 100644
index 0000000000..801ab98f54
--- /dev/null
+++ b/src/utilcode/arraylist.cpp
@@ -0,0 +1,311 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h"
+
+#include "arraylist.h"
+#include "utilcode.h"
+#include "ex.h"
+
+//
+// ArrayList is a simple class which is used to contain a growable
+// list of pointers, stored in chunks. Modification is by appending
+// only currently. Access is by index (efficient if the number of
+// elements stays small) and iteration (efficient in all cases).
+//
+// An important property of an ArrayList is that the list remains
+// coherent while it is being modified (appended to). This means that readers
+// never need to lock when accessing it. (Locking is necessary among multiple
+// writers, however.)
+//
+
+void ArrayListBase::Clear()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ ArrayListBlock *block = m_firstBlock.m_next;
+ while (block != NULL)
+ {
+ ArrayListBlock *next = block->m_next;
+ delete [] block;
+ block = next;
+ }
+ m_firstBlock.m_next = 0;
+ m_count = 0;
+}
+
+PTR_VOID * ArrayListBase::GetPtr(DWORD index) const
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+
+ _ASSERTE(index < m_count);
+
+ ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock;
+ while (index >= b->m_blockSize)
+ {
+ PREFIX_ASSUME(b->m_next != NULL);
+ index -= b->m_blockSize;
+ b = b->m_next;
+ }
+
+ return b->m_array + index;
+}
+
+#ifndef DACCESS_COMPILE
+
+HRESULT ArrayListBase::Append(void *element)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock;
+ DWORD count = m_count;
+
+ while (count >= b->m_blockSize)
+ {
+ count -= b->m_blockSize;
+
+ if (b->m_next == NULL)
+ {
+ _ASSERTE(count == 0);
+
+ DWORD nextSize = b->m_blockSize * 2;
+
+ ArrayListBlock *bNew = (ArrayListBlock *)
+ new (nothrow) BYTE [sizeof(ArrayListBlock) + nextSize * sizeof(void*)];
+
+ if (bNew == NULL)
+ return E_OUTOFMEMORY;
+
+ bNew->m_next = NULL;
+ bNew->m_blockSize = nextSize;
+
+ b->m_next = bNew;
+ }
+
+ b = b->m_next;
+ }
+
+ b->m_array[count] = element;
+
+ m_count++;
+
+ return S_OK;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+DWORD ArrayListBase::FindElement(DWORD start, PTR_VOID element) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ DWORD index = start;
+
+ _ASSERTE(index <= m_count);
+
+ ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock;
+
+ //
+ // Skip to the block containing start.
+ // index should be the index of start in the block.
+ //
+
+ while (b != NULL && index >= b->m_blockSize)
+ {
+ index -= b->m_blockSize;
+ b = b->m_next;
+ }
+
+ //
+ // Adjust start to be the index of the start of the block
+ //
+
+ start -= index;
+
+ //
+ // Compute max number of entries from the start of the block
+ //
+
+ DWORD max = m_count - start;
+
+ while (b != NULL)
+ {
+ //
+ // Compute end of search in this block - either end of the block
+ // or end of the array
+ //
+
+ DWORD blockMax;
+ if (max < b->m_blockSize)
+ blockMax = max;
+ else
+ blockMax = b->m_blockSize;
+
+ //
+ // Scan for element, until the end.
+ //
+
+ while (index < blockMax)
+ {
+ if (b->m_array[index] == element)
+ return start + index;
+ index++;
+ }
+
+ //
+ // Otherwise, increment block start index, decrement max count,
+ // reset index, and go to the next block (if any)
+ //
+
+ start += b->m_blockSize;
+ max -= b->m_blockSize;
+ index = 0;
+ b = b->m_next;
+ }
+
+ return (DWORD) NOT_FOUND;
+}
+
+BOOL ArrayListBase::Iterator::Next()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ ++m_index;
+
+ if (m_index >= m_remaining)
+ return FALSE;
+
+ if (m_index >= m_block->m_blockSize)
+ {
+ m_remaining -= m_block->m_blockSize;
+ m_index -= m_block->m_blockSize;
+ m_total += m_block->m_blockSize;
+ m_block = m_block->m_next;
+ }
+
+ return TRUE;
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+ArrayListBase::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+
+ // Assume that 'this' is enumerated, either explicitly
+ // or because this class is embedded in another.
+
+ PTR_ArrayListBlock block = m_firstBlock.m_next;
+ while (block.IsValid())
+ {
+ block.EnumMem();
+ block = block->m_next;
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+
+void StructArrayListBase::Destruct (FreeProc *pfnFree)
+{
+ WRAPPER_NO_CONTRACT;
+
+ StructArrayListEntryBase *pList = m_pChunkListHead;
+ while (pList)
+ {
+ StructArrayListEntryBase *pTrash = pList;
+ pList = pList->pNext;
+ pfnFree(this, pTrash);
+ }
+}
+
+// Copied from jit.h
+inline
+size_t roundUp(size_t size, size_t mult = sizeof(size_t))
+{
+ assert(mult && ((mult & (mult-1)) == 0)); // power of two test
+
+ return (size + (mult - 1)) & ~(mult - 1);
+}
+
+void StructArrayListBase::CreateNewChunk (SIZE_T InitialChunkLength, SIZE_T ChunkLengthGrowthFactor, SIZE_T cbElement, AllocProc *pfnAlloc, SIZE_T alignment)
+{
+ CONTRACTL {
+ THROWS;
+ PRECONDITION(!m_pChunkListHead || m_nItemsInLastChunk == m_nLastChunkCapacity);
+ } CONTRACTL_END;
+
+ SIZE_T nChunkCapacity;
+ if (!m_pChunkListHead)
+ nChunkCapacity = InitialChunkLength;
+ else
+ nChunkCapacity = m_nLastChunkCapacity * ChunkLengthGrowthFactor;
+
+ S_SIZE_T cbBaseSize = S_SIZE_T(roundUp(sizeof(StructArrayListEntryBase), alignment));
+ S_SIZE_T cbChunk = cbBaseSize +
+ S_SIZE_T(cbElement) * S_SIZE_T(nChunkCapacity);
+ _ASSERTE(!cbChunk.IsOverflow());
+ if(cbChunk.IsOverflow())
+ {
+ ThrowWin32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+
+ StructArrayListEntryBase *pNewChunk = (StructArrayListEntryBase*)pfnAlloc(this, cbChunk.Value());
+
+ if (m_pChunkListTail)
+ {
+ _ASSERTE(m_pChunkListHead);
+ m_pChunkListTail->pNext = pNewChunk;
+ }
+ else
+ {
+ _ASSERTE(!m_pChunkListHead);
+ m_pChunkListHead = pNewChunk;
+ }
+
+ pNewChunk->pNext = NULL;
+ m_pChunkListTail = pNewChunk;
+
+ m_nItemsInLastChunk = 0;
+ m_nLastChunkCapacity = nChunkCapacity;
+}
+
+
+void StructArrayListBase::ArrayIteratorBase::SetCurrentChunk (StructArrayListEntryBase *pChunk, SIZE_T nChunkCapacity)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pCurrentChunk = pChunk;
+
+ if (pChunk)
+ {
+ if (pChunk == m_pArrayList->m_pChunkListTail)
+ m_nItemsInCurrentChunk = m_pArrayList->m_nItemsInLastChunk;
+ else
+ m_nItemsInCurrentChunk = nChunkCapacity;
+
+ m_nCurrentChunkCapacity = nChunkCapacity;
+ }
+}
+
diff --git a/src/utilcode/assemblyfilehash.cpp b/src/utilcode/assemblyfilehash.cpp
new file mode 100644
index 0000000000..be6d985e8f
--- /dev/null
+++ b/src/utilcode/assemblyfilehash.cpp
@@ -0,0 +1,170 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "stdafx.h"
+#include <stdlib.h>
+#include "utilcode.h"
+#include "strongname.h"
+#include "assemblyfilehash.h"
+#include "ex.h"
+#include "corperm.h"
+
+#include <wincrypt.h>
+
+HRESULT AssemblyFileHash::ReadData()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+ NewArrayHolder<BYTE> pBuffer;
+
+ DWORD dwFileSize = SafeGetFileSize( m_hFile, NULL );
+
+ if (dwFileSize == 0xffffffff)
+ return HRESULT_FROM_GetLastError();
+
+ pBuffer = new (nothrow)BYTE[dwFileSize];
+ if(pBuffer==NULL)
+ return E_OUTOFMEMORY;
+
+ DWORD cbBuffer = dwFileSize;
+ DWORD cbRead;
+
+ if (!ReadFile( m_hFile, pBuffer, cbBuffer, &cbRead, NULL ) ||
+ cbRead != cbBuffer)
+ return HRESULT_FROM_GetLastError();
+
+ pBuffer.SuppressRelease();
+ this->m_pbData = pBuffer;
+ this->m_cbData = cbBuffer;
+ this->m_bDataOwner = TRUE;
+ this->m_NeedToReadData=FALSE;
+
+ return S_OK;
+
+}
+
+HRESULT AssemblyFileHash::SetFileName(LPCWSTR wszFileName)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HandleHolder hFile;
+
+ hFile = WszCreateFile(wszFileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return HRESULT_FROM_GetLastError();
+
+ IfFailRet(SetFileHandle(hFile));
+ hFile.SuppressRelease();
+ return S_OK;
+}
+
+
+HRESULT AssemblyFileHash::HashData(HCRYPTHASH hHash)
+{
+ WRAPPER_NO_CONTRACT;
+ if(!CryptHashData(hHash, m_pbData, m_cbData, 0))
+ return HRESULT_FROM_GetLastError();
+ return S_OK;
+}
+
+HRESULT AssemblyFileHash::CalculateHash(DWORD algid)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ if(m_NeedToReadData)
+ IfFailRet(ReadData());
+
+ _ASSERTE(!m_NeedToReadData);
+
+ HCRYPTPROV pProvider = NULL;
+ HCRYPTHASH hHash = NULL;
+ DWORD count;
+
+ if(!WszCryptAcquireContext(&pProvider,
+ NULL,
+ NULL,
+ //PROV_RSA_SIG,
+ PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT))
+ IfFailGo(HRESULT_FROM_GetLastError());
+
+
+ if(!CryptCreateHash(pProvider,
+ algid,
+ 0,
+ 0,
+ &hHash))
+ IfFailGo(HRESULT_FROM_GetLastError());
+
+ IfFailGo(HashData(hHash));
+
+ count = sizeof(m_cbHash);
+ if(!CryptGetHashParam(hHash,
+ HP_HASHSIZE,
+ (PBYTE) &m_cbHash,
+ &count,
+ 0))
+ IfFailGo(HRESULT_FROM_GetLastError());
+
+ if(m_cbHash > 0) {
+ m_pbHash = new (nothrow) BYTE[m_cbHash];
+ if (!m_pbHash)
+ IfFailGo(E_OUTOFMEMORY);
+
+ if(!CryptGetHashParam(hHash,
+ HP_HASHVAL,
+ m_pbHash,
+ &m_cbHash,
+ 0))
+ IfFailGo(HRESULT_FROM_GetLastError());
+ }
+
+ ErrExit:
+
+ if(hHash)
+ CryptDestroyHash(hHash);
+ if(pProvider)
+ CryptReleaseContext(pProvider, 0);
+ return hr;
+}
+
+
+
+
diff --git a/src/utilcode/bitvector.cpp b/src/utilcode/bitvector.cpp
new file mode 100644
index 0000000000..63af3acd93
--- /dev/null
+++ b/src/utilcode/bitvector.cpp
@@ -0,0 +1,406 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/***************************************************************************/
+/* BitVector.cpp */
+/***************************************************************************/
+// Routines to support a growable bitvector
+/***************************************************************************/
+
+#include "stdafx.h"
+#include <memory.h>
+
+#include "contract.h"
+#include "bitvector.h"
+
+#if USE_BITVECTOR
+
+int BitVector::NumBits() const
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ int count = 0;
+ ChunkType hiChunk;
+
+ if (isBig())
+ {
+ unsigned maxNonZero = 0;
+ for (unsigned i=1; (i < m_vals.GetLength()); i++)
+ {
+ if (m_vals.m_chunks[i] != 0)
+ {
+ maxNonZero = i;
+ }
+ }
+ count = (maxNonZero * CHUNK_BITS) - 1;
+ hiChunk = m_vals.m_chunks[maxNonZero];
+ }
+ else
+ {
+ hiChunk = m_val;
+ }
+
+ while (hiChunk > 0)
+ {
+ hiChunk <<= 1;
+ count++;
+ }
+
+ _ASSERTE(count >= 0);
+ return count;
+}
+
+void BitVector::doBigInit(ChunkType arg)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ m_vals.m_chunks[0] = arg;
+ m_vals.SetLength(1);
+}
+
+void BitVector::doBigInit(const BitVector& arg)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ if (arg.isBig())
+ {
+ memcpy(m_vals.m_chunks, arg.m_vals.m_chunks, (sizeof(ChunkType) * arg.m_vals.GetLength()));
+ m_vals.SetLength(arg.m_vals.GetLength());
+ }
+ else
+ {
+ m_val = arg.m_val;
+ }
+}
+
+void BitVector::doBigLeftShiftAssign(unsigned shift)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything
+ return;
+
+ unsigned numWords = shift / CHUNK_BITS;
+ unsigned numBits = shift % CHUNK_BITS;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ int from = m_vals.GetLength()-1;
+ int to = from + numWords;
+ unsigned newlen = to + 1;
+
+ ChunkType topBits = 0;
+ if (numBits > 0)
+ {
+ topBits = m_vals.m_chunks[from] >> (CHUNK_BITS - numBits);
+ }
+
+ if (topBits != 0 || numWords != 0)
+ {
+ if (topBits != 0)
+ {
+ m_vals.m_chunks[newlen] = topBits;
+ newlen++;
+ }
+ m_vals.SetLength(newlen);
+ }
+
+ while (to >= 0)
+ {
+ m_vals.m_chunks[to] = (from >= 0) ? (m_vals.m_chunks[from] << numBits) : 0;
+ from--;
+
+ if ((from >= 0) && (numBits > 0))
+ {
+ m_vals.m_chunks[to] |= m_vals.m_chunks[from] >> (CHUNK_BITS - numBits);
+ }
+ to--;
+ }
+
+ // Convert back to small format if necessary
+ if ((newlen == 1) && (m_vals.m_chunks[0] <= MaxVal))
+ {
+ m_val = ChunkType(m_vals.m_chunks[0] << 1);
+ }
+}
+
+void BitVector::doBigRightShiftAssign(unsigned shift)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything
+ return;
+
+ unsigned numWords = shift / CHUNK_BITS;
+ unsigned numBits = shift % CHUNK_BITS;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ unsigned from = numWords;
+ unsigned to = 0;
+ unsigned len = m_vals.GetLength();
+ unsigned newlen = len - numWords;
+
+ if (from >= len)
+ {
+ // we always encode zero in short form
+ m_val = 0;
+ }
+ else
+ {
+ m_vals.m_chunks[to] = (m_vals.m_chunks[from] >> numBits);
+ from++;
+
+ while (from < len)
+ {
+ if (numBits > 0)
+ {
+ m_vals.m_chunks[to] |= m_vals.m_chunks[from] << (CHUNK_BITS - numBits);
+ }
+ to++;
+
+ m_vals.m_chunks[to] = (m_vals.m_chunks[from] >> numBits);
+ from++;
+ }
+
+ if ((newlen > 1) && (m_vals.m_chunks[newlen-1] == 0))
+ {
+ newlen--;
+ }
+
+ m_vals.SetLength(newlen);
+
+ // Convert back to small format if necessary
+ if ((newlen == 1) && (m_vals.m_chunks[0] <= MaxVal))
+ {
+ m_val = ChunkType(m_vals.m_chunks[0] << 1);
+ }
+ }
+}
+
+void BitVector::doBigAndAssign(const BitVector& arg)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ if (arg.isBig())
+ {
+ bool isZero = true; // until proven otherwise
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+
+ if (myLen > argLen)
+ {
+ // shrink our length to match argLen
+ m_vals.SetLength(argLen);
+ myLen = argLen;
+ }
+
+ for (unsigned i = 0; (i < myLen); i++)
+ {
+ ChunkType curChunk = m_vals.m_chunks[i] & arg.m_vals.m_chunks[i];
+ m_vals.m_chunks[i] = curChunk;
+ if (curChunk != 0)
+ isZero = false;
+ }
+
+ if (isZero)
+ {
+ // we always encode zero in short form
+ m_val = 0;
+ }
+ }
+ else
+ {
+ m_val = (m_vals.m_chunks[0] << 1) & arg.m_val;
+ }
+}
+
+void BitVector::doBigOrAssign(const BitVector& arg)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ if (arg.isBig())
+ {
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+
+ if (myLen < argLen)
+ {
+ // expand our length to match argLen and zero init
+ memset(m_vals.m_chunks + myLen, 0, sizeof(ChunkType) * (argLen - myLen));
+ m_vals.SetLength(argLen);
+ myLen = argLen;
+ }
+
+ for(unsigned i = 0; ((i < myLen) && (i < argLen)); i++)
+ {
+ m_vals.m_chunks[i] |= arg.m_vals.m_chunks[i];
+ }
+ }
+ else
+ {
+ m_vals.m_chunks[0] |= arg.smallBits();
+ }
+}
+
+void BitVector::doBigDiffAssign(const BitVector& arg)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+ bool isZero = true; // until proven otherwise
+
+ for (unsigned i = 0; (i < myLen); i++)
+ {
+ ChunkType nextChunk = m_vals.m_chunks[i];
+ if (i < argLen)
+ {
+ nextChunk &= ~arg.m_vals.m_chunks[i];
+ m_vals.m_chunks[i] = nextChunk;
+ }
+ else if (i == 0)
+ {
+ nextChunk &= ~arg.smallBits();
+ m_vals.m_chunks[i] = nextChunk;
+ }
+
+ if (nextChunk != 0)
+ isZero = false;
+ }
+
+ if (isZero)
+ {
+ // we always encode zero in short form
+ m_val = 0;
+ }
+}
+
+BOOL BitVector::doBigEquals(const BitVector& arg) const
+{
+ CONTRACT(BOOL)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+ unsigned maxLen = (myLen >= argLen) ? myLen : argLen;
+
+ for (unsigned i=0; (i < maxLen); i++)
+ {
+ ChunkType myVal = 0;
+ ChunkType argVal = 0;
+
+ if (i < myLen)
+ myVal = m_vals.m_chunks[i];
+
+ if (i < argLen)
+ argVal = arg.m_vals.m_chunks[i];
+
+ if (i == 0)
+ {
+ if (myLen == 0)
+ myVal = smallBits();
+ if (argLen == 0)
+ argVal = arg.smallBits();
+ }
+
+ if (myVal != argVal)
+ RETURN false;
+ }
+ RETURN true;
+}
+
+BOOL BitVector::doBigIntersect(const BitVector& arg) const
+{
+ CONTRACT(BOOL)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+ unsigned minLen = (myLen <= argLen) ? myLen : argLen;
+
+ for (unsigned i=0; (i <= minLen); i++)
+ {
+ ChunkType myVal = 0;
+ ChunkType argVal = 0;
+
+ if (i < myLen)
+ myVal = m_vals.m_chunks[i];
+
+ if (i < argLen)
+ argVal = arg.m_vals.m_chunks[i];
+
+ if (i == 0)
+ {
+ if (myLen == 0)
+ myVal = smallBits();
+ if (argLen == 0)
+ argVal = arg.smallBits();
+ }
+
+ if ((myVal & argVal) != 0)
+ RETURN true;
+ }
+ RETURN false;
+}
+
+#endif // USE_BITVECTOR
diff --git a/src/utilcode/ccomprc.cpp b/src/utilcode/ccomprc.cpp
new file mode 100644
index 0000000000..fa08b5e456
--- /dev/null
+++ b/src/utilcode/ccomprc.cpp
@@ -0,0 +1,1134 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h" // Standard header.
+#include <utilcode.h> // Utility helpers.
+#include <corerror.h>
+#include "newapis.h"
+#include "ndpversion.h"
+
+#include "../dlls/mscorrc/resource.h"
+#include "sstring.h"
+#include "stringarraylist.h"
+
+#ifdef USE_FORMATMESSAGE_WRAPPER
+// we implement the wrapper for FormatMessageW.
+// Need access to the original
+#undef WszFormatMessage
+#define WszFormatMessage ::FormatMessageW
+#endif
+
+#define MAX_VERSION_STRING 30
+
+// External prototypes.
+extern HINSTANCE GetModuleInst();
+
+#ifndef FEATURE_PAL
+
+//*****************************************************************************
+// Get the MUI ID, on downlevel platforms where MUI is not supported it
+// returns the default system ID.
+
+typedef LANGID (WINAPI *PFNGETUSERDEFAULTUILANGUAGE)(void); // kernel32!GetUserDefaultUILanguage
+
+int GetMUILanguageID(LocaleIDValue* pResult)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+#if FEATURE_USE_LCID
+ int langId=0;
+ static PFNGETUSERDEFAULTUILANGUAGE pfnGetUserDefaultUILanguage=NULL;
+
+ if( NULL == pfnGetUserDefaultUILanguage )
+ {
+ PFNGETUSERDEFAULTUILANGUAGE proc = NULL;
+
+ HMODULE hmod = GetModuleHandleA(WINDOWS_KERNEL32_DLLNAME_A);
+
+ if( hmod )
+ proc = (PFNGETUSERDEFAULTUILANGUAGE)
+ GetProcAddress(hmod, "GetUserDefaultUILanguage");
+
+ if(proc == NULL)
+ proc = (PFNGETUSERDEFAULTUILANGUAGE) -1;
+
+ PVOID value = InterlockedExchangeT(&pfnGetUserDefaultUILanguage,
+ proc);
+ }
+
+ // We should never get NULL here, the function is -1 or a valid address.
+ _ASSERTE(pfnGetUserDefaultUILanguage != NULL);
+
+
+ if( pfnGetUserDefaultUILanguage == (PFNGETUSERDEFAULTUILANGUAGE) -1)
+ langId = GetSystemDefaultLangID();
+ else
+ langId = pfnGetUserDefaultUILanguage();
+
+ *pResult= langId;
+#else // FEATURE_USE_LCID
+ _ASSERTE(sizeof(LocaleID)/sizeof(WCHAR) >=LOCALE_NAME_MAX_LENGTH);
+ return NewApis::GetSystemDefaultLocaleName(*pResult, LOCALE_NAME_MAX_LENGTH);
+#endif //FEATURE_USE_LCID
+ return 1;
+}
+
+static void BuildMUIDirectory(int langid, __out SString* pResult)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pResult));
+ }
+ CONTRACTL_END;
+
+ pResult->Printf(W("MUI\\%04x\\"), langid);
+}
+
+void GetMUILanguageName(__out SString* pResult)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pResult));
+ }
+ CONTRACTL_END;
+
+ LocaleIDValue langid;
+ GetMUILanguageID(&langid);
+
+ int lcid;
+#ifdef FEATURE_USE_LCID
+ lcid=langid;
+#else
+ lcid=NewApis::LocaleNameToLCID(langid,0);
+#endif
+
+ return BuildMUIDirectory(lcid, pResult);
+}
+
+void GetMUIParentLanguageName(SString* pResult)
+{
+ WRAPPER_NO_CONTRACT;
+ int langid = 1033;
+
+ BuildMUIDirectory(langid, pResult);
+}
+#ifndef DACCESS_COMPILE
+HRESULT GetMUILanguageNames(__inout StringArrayList* pCultureNames)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pCultureNames));
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr=S_OK;
+ EX_TRY
+ {
+ SString result;
+ GetMUILanguageName(&result);
+
+ if(!result.IsEmpty())
+ {
+ pCultureNames->Append(result);
+ }
+
+ GetMUIParentLanguageName(&result);
+
+ _ASSERTE(!result.IsEmpty());
+ pCultureNames->Append(result);
+ pCultureNames->Append(SString::Empty());
+ }
+ EX_CATCH_HRESULT(hr)
+ return hr;
+
+}
+#endif // DACCESS_COMPILE
+
+#endif // !FEATURE_PAL
+
+BOOL CCompRC::s_bIsMscoree = FALSE;
+
+//*****************************************************************************
+// Do the mapping from an langId to an hinstance node
+//*****************************************************************************
+HRESOURCEDLL CCompRC::LookupNode(LocaleID langId, BOOL &fMissing)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pHash == NULL) return NULL;
+
+// Linear search
+ int i;
+ for(i = 0; i < m_nHashSize; i ++) {
+ if (m_pHash[i].IsSet() && m_pHash[i].HasID(langId)) {
+ return m_pHash[i].GetLibraryHandle();
+ }
+ if (m_pHash[i].IsMissing() && m_pHash[i].HasID(langId))
+ {
+ fMissing = TRUE;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+//*****************************************************************************
+// Add a new node to the map and return it.
+//*****************************************************************************
+const int MAP_STARTSIZE = 7;
+const int MAP_GROWSIZE = 5;
+
+HRESULT CCompRC::AddMapNode(LocaleID langId, HRESOURCEDLL hInst, BOOL fMissing)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+
+ if (m_pHash == NULL) {
+ m_pHash = new (nothrow)CCulturedHInstance[MAP_STARTSIZE];
+ if (m_pHash==NULL)
+ return E_OUTOFMEMORY;
+ m_nHashSize = MAP_STARTSIZE;
+ }
+
+// For now, place in first open slot
+ int i;
+ for(i = 0; i < m_nHashSize; i ++) {
+ if (!m_pHash[i].IsSet() && !m_pHash[i].IsMissing()) {
+ if (fMissing)
+ {
+ m_pHash[i].SetMissing(langId);
+ }
+ else
+ {
+ m_pHash[i].Set(langId,hInst);
+ }
+
+ return S_OK;
+ }
+ }
+
+// Out of space, regrow
+ CCulturedHInstance * pNewHash = new (nothrow)CCulturedHInstance[m_nHashSize + MAP_GROWSIZE];
+ if (pNewHash)
+ {
+ memcpy(pNewHash, m_pHash, sizeof(CCulturedHInstance) * m_nHashSize);
+ delete [] m_pHash;
+ m_pHash = pNewHash;
+ if (fMissing)
+ {
+ m_pHash[m_nHashSize].SetMissing(langId);
+ }
+ else
+ {
+ m_pHash[m_nHashSize].Set(langId,hInst);
+ }
+ m_nHashSize += MAP_GROWSIZE;
+ }
+ else
+ return E_OUTOFMEMORY;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Initialize
+//*****************************************************************************
+#ifndef FEATURE_CORECLR
+LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.dll");
+#else // !FEATURE_CORECLR
+LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.debug.dll");
+LPCWSTR CCompRC::m_pFallbackResource= W("mscorrc.dll");
+#endif // !FEATURE_CORECLR
+
+HRESULT CCompRC::Init(LPCWSTR pResourceFile, BOOL bUseFallback)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ // This function is called during Watson process. We need to make sure
+ // that this function is restartable.
+ //
+ // Make sure to NEVER null out the function callbacks in the Init
+ // function. They get set for the "Default CCompRC" during EEStartup
+ // and we want to make sure we don't wipe them out.
+
+ m_bUseFallback = bUseFallback;
+
+ if (m_pResourceFile == NULL)
+ {
+ if(pResourceFile)
+ {
+ NewArrayHolder<WCHAR> pwszResourceFile(NULL);
+
+ DWORD lgth = (DWORD) wcslen(pResourceFile) + 1;
+ pwszResourceFile = new(nothrow) WCHAR[lgth];
+ if (pwszResourceFile)
+ {
+ wcscpy_s(pwszResourceFile, lgth, pResourceFile);
+ LPCWSTR pFile = pwszResourceFile.Extract();
+ if (InterlockedCompareExchangeT(&m_pResourceFile, pFile, NULL) != NULL)
+ {
+ delete [] pFile;
+ }
+ }
+ }
+ else
+ InterlockedCompareExchangeT(&m_pResourceFile, m_pDefaultResource, NULL);
+ }
+
+ if (m_pResourceFile == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (m_csMap == NULL)
+ {
+ // NOTE: there are times when the debugger's helper thread is asked to do a favor for another thread in the
+ // process. Typically, this favor involves putting up a dialog for the user. Putting up a dialog usually ends
+ // up involving the CCompRC code since (of course) the strings in the dialog are in a resource file. Thus, the
+ // debugger's helper thread will attempt to acquire this CRST. This is okay, since the helper thread only does
+ // these favors for other threads when there is no debugger attached. Thus, there are no deadlock hazards with
+ // this lock, and its safe for the helper thread to take, so this CRST is marked with CRST_DEBUGGER_THREAD.
+ CRITSEC_COOKIE csMap = ClrCreateCriticalSection(CrstCCompRC,
+ (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
+
+ if (csMap)
+ {
+ if (InterlockedCompareExchangeT(&m_csMap, csMap, NULL) != NULL)
+ {
+ ClrDeleteCriticalSection(csMap);
+ }
+ }
+ }
+
+ if (m_csMap == NULL)
+ return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+void CCompRC::SetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_fpGetThreadUICultureNames = fpGetThreadUICultureNames;
+ m_fpGetThreadUICultureId = fpGetThreadUICultureId;
+}
+
+void CCompRC::GetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if(fpGetThreadUICultureNames)
+ *fpGetThreadUICultureNames=m_fpGetThreadUICultureNames;
+
+ if(fpGetThreadUICultureId)
+ *fpGetThreadUICultureId=m_fpGetThreadUICultureId;
+}
+
+void CCompRC::Destroy()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ // Free all resource libraries
+
+ //*****************************************************************************
+ // Free the loaded library if we ever loaded it and only if we are not on
+ // Win 95 which has a known bug with DLL unloading (it randomly unloads a
+ // dll on shut down, not necessarily the one you asked for). This is done
+ // only in debug mode to make coverage runs accurate.
+ //*****************************************************************************
+
+#if defined(_DEBUG)
+ if (m_Primary.GetLibraryHandle()) {
+ ::FreeLibrary(m_Primary.GetLibraryHandle());
+ }
+
+ if (m_pHash != NULL) {
+ int i;
+ for(i = 0; i < m_nHashSize; i ++) {
+ if (m_pHash[i].GetLibraryHandle() != NULL) {
+ ::FreeLibrary(m_pHash[i].GetLibraryHandle());
+ break;
+ }
+ }
+ }
+#endif
+
+ // destroy map structure
+ if(m_pResourceFile != m_pDefaultResource)
+ delete [] m_pResourceFile;
+ m_pResourceFile = NULL;
+
+ if(m_csMap) {
+ ClrDeleteCriticalSection(m_csMap);
+ ZeroMemory(&(m_csMap), sizeof(CRITSEC_COOKIE));
+ }
+
+ if(m_pHash != NULL) {
+ delete [] m_pHash;
+ m_pHash = NULL;
+ }
+}
+
+
+//*****************************************************************************
+// Initialization is done lazily, for backwards compatibility "mscorrc.dll"
+// is consider the default location for all strings that use CCompRC.
+// An instance value for CCompRC can be created to load resources from a different
+// resource dll.
+//*****************************************************************************
+LONG CCompRC::m_dwDefaultInitialized = 0;
+CCompRC CCompRC::m_DefaultResourceDll;
+
+CCompRC* CCompRC::GetDefaultResourceDll()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ if (m_dwDefaultInitialized)
+ return &m_DefaultResourceDll;
+
+ if(FAILED(m_DefaultResourceDll.Init(NULL, TRUE)))
+ {
+ return NULL;
+ }
+ m_dwDefaultInitialized = 1;
+
+ return &m_DefaultResourceDll;
+}
+
+#ifdef FEATURE_CORECLR
+LONG CCompRC::m_dwFallbackInitialized = 0;
+CCompRC CCompRC::m_FallbackResourceDll;
+
+CCompRC* CCompRC::GetFallbackResourceDll()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ if (m_dwFallbackInitialized)
+ return &m_FallbackResourceDll;
+
+ if(FAILED(m_FallbackResourceDll.Init(m_pFallbackResource, FALSE)))
+ {
+ return NULL;
+ }
+ m_dwFallbackInitialized = 1;
+
+ return &m_FallbackResourceDll;
+}
+
+#endif // FEATURE_CORECLR
+
+
+//*****************************************************************************
+//*****************************************************************************
+
+HRESULT CCompRC::GetLibrary(LocaleID langId, HRESOURCEDLL* phInst)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ PRECONDITION(phInst != NULL);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+ HRESOURCEDLL hInst = 0;
+#ifndef DACCESS_COMPILE
+ HRESOURCEDLL hLibInst = 0; //Holds early library instance
+ BOOL fLibAlreadyOpen = FALSE; //Determine if we can close the opened library.
+#endif
+
+ // Try to match the primary entry, or else use the primary if we don't care.
+ if (m_Primary.IsSet())
+ {
+ if (langId == UICULTUREID_DONTCARE || m_Primary.HasID(langId))
+ {
+ hInst = m_Primary.GetLibraryHandle();
+ hr = S_OK;
+ }
+ }
+ else if(m_Primary.IsMissing())
+ {
+ // If primary is missing then the hash will not have anything either
+ hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ }
+#ifndef DACCESS_COMPILE
+ // If this is the first visit, we must set the primary entry
+ else
+ {
+ // Don't immediately return if LoadLibrary fails so we can indicate the file was missing
+ hr = LoadLibrary(&hLibInst);
+ // If it's a transient failure, don't cache the failure
+ if (FAILED(hr) && Exception::IsTransient(hr))
+ {
+ return hr;
+ }
+
+ CRITSEC_Holder csh (m_csMap);
+ // As we expected
+ if (!m_Primary.IsSet() && !m_Primary.IsMissing())
+ {
+ hInst = hLibInst;
+ if (SUCCEEDED(hr))
+ {
+ m_Primary.Set(langId,hLibInst);
+ }
+ else
+ {
+ m_Primary.SetMissing(langId);
+ }
+ }
+
+ // Someone got into this critical section before us and set the primary already
+ else if (m_Primary.HasID(langId))
+ {
+ hInst = m_Primary.GetLibraryHandle();
+ fLibAlreadyOpen = TRUE;
+ }
+
+ // If neither case is true, someone got into this critical section before us and
+ // set the primary to other than the language we want...
+ else
+ {
+ fLibAlreadyOpen = TRUE;
+ }
+
+ IfFailRet(hr);
+
+ if (fLibAlreadyOpen)
+ {
+ FreeLibrary(hLibInst);
+ fLibAlreadyOpen = FALSE;
+ }
+ }
+#endif
+
+ // If we enter here, we know that the primary is set to something other than the
+ // language we want - multiple languages use the hash table
+ if (hInst == NULL && !m_Primary.IsMissing())
+ {
+ // See if the resource exists in the hash table
+ {
+ CRITSEC_Holder csh(m_csMap);
+ BOOL fMissing = FALSE;
+ hInst = LookupNode(langId, fMissing);
+ if (fMissing == TRUE)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ goto Exit;
+ }
+ }
+
+#ifndef DACCESS_COMPILE
+ // If we didn't find it, we have to load the library and insert it into the hash
+ if (hInst == NULL)
+ {
+ hr = LoadLibrary(&hLibInst);
+ // If it's a transient failure, don't cache the failure
+ if (FAILED(hr) && Exception::IsTransient(hr))
+ {
+ return hr;
+ }
+ {
+ CRITSEC_Holder csh (m_csMap);
+
+ // Double check - someone may have entered this section before us
+ BOOL fMissing = FALSE;
+ hInst = LookupNode(langId, fMissing);
+ if (hInst == NULL && !fMissing)
+ {
+ if (SUCCEEDED(hr))
+ {
+ hInst = hLibInst;
+ hr = AddMapNode(langId, hInst);
+ } else
+ {
+ HRESULT hrLoadLibrary = hr;
+ hr = AddMapNode(langId, hInst, TRUE /* fMissing */);
+ if (SUCCEEDED(hr))
+ {
+ hr = hrLoadLibrary;
+ }
+ }
+ }
+ else
+ {
+ fLibAlreadyOpen = TRUE;
+ }
+ }
+
+ if (fLibAlreadyOpen || FAILED(hr))
+ {
+ FreeLibrary(hLibInst);
+ }
+ }
+
+ // We found the node, so set hr to be a success.
+ else
+ {
+ hr = S_OK;
+ }
+#endif // DACCESS_COMPILE
+ }
+Exit:
+ *phInst = hInst;
+ return hr;
+}
+
+//*****************************************************************************
+// Load the string
+// We load the localized libraries and cache the handle for future use.
+// Mutliple threads may call this, so the cache structure is thread safe.
+//*****************************************************************************
+HRESULT CCompRC::LoadString(ResourceCategory eCategory, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed)
+{
+ WRAPPER_NO_CONTRACT;
+ LocaleIDValue langIdValue;
+ LocaleID langId;
+ // Must resolve current thread's langId to a dll.
+ if(m_fpGetThreadUICultureId) {
+ int ret = (*m_fpGetThreadUICultureId)(&langIdValue);
+
+ // Callback can't return 0, since that indicates empty.
+ // To indicate empty, callback should return UICULTUREID_DONTCARE
+ _ASSERTE(ret != 0);
+
+ if (ret == 0)
+ return E_UNEXPECTED;
+ langId=langIdValue;
+
+ }
+ else {
+ langId = UICULTUREID_DONTCARE;
+ }
+
+
+ return LoadString(eCategory, langId, iResourceID, szBuffer, iMax, pcwchUsed);
+}
+
+HRESULT CCompRC::LoadString(ResourceCategory eCategory, LocaleID langId, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+#ifndef FEATURE_PAL
+ HRESULT hr;
+ HRESOURCEDLL hInst = 0; //instance of cultured resource dll
+ int length;
+
+ hr = GetLibrary(langId, &hInst);
+
+ if (SUCCEEDED(hr))
+ {
+ // Now that we have the proper dll handle, load the string
+ _ASSERTE(hInst != NULL);
+
+ length = ::WszLoadString(hInst, iResourceID, szBuffer, iMax);
+ if(length > 0)
+ {
+ if(pcwchUsed)
+ {
+ *pcwchUsed = length;
+ }
+ return (S_OK);
+ }
+ if(GetLastError()==ERROR_SUCCESS)
+ hr=HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+ else
+ hr=HRESULT_FROM_GetLastError();
+ }
+
+
+ // Failed to load string
+ if ( hr != E_OUTOFMEMORY && ShouldUseFallback())
+ {
+#ifdef FEATURE_CORECLR
+ CCompRC* pFallback=CCompRC::GetFallbackResourceDll();
+ if (pFallback)
+ {
+ //should not fall back to itself
+ _ASSERTE(pFallback != this);
+
+ // check existence in the fallback Dll
+
+ hr = pFallback->LoadString(Optional, langId, iResourceID,szBuffer, iMax, pcwchUsed);
+
+ if(SUCCEEDED(hr))
+ return hr;
+ }
+#endif
+ switch (eCategory)
+ {
+ case Optional:
+ hr = E_FAIL;
+ break;
+#ifdef FEATURE_CORECLR
+ case DesktopCLR:
+ hr = E_FAIL;
+ break;
+ case Debugging:
+ case Error:
+ // get stub message
+ {
+
+ if (pFallback)
+ {
+
+ StackSString ssErrorFormat;
+ if (eCategory == Error)
+ {
+ hr=ssErrorFormat.LoadResourceAndReturnHR(pFallback, CCompRC::Required, IDS_EE_LINK_FOR_ERROR_MESSAGES);
+ }
+ else
+ {
+ _ASSERTE(eCategory == Debugging);
+ hr=ssErrorFormat.LoadResourceAndReturnHR(pFallback, CCompRC::Required, IDS_EE_LINK_FOR_DEBUGGING_MESSAGES);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ StackSString sFormattedMessage;
+ int iErrorCode = HR_FOR_URT_MSG(iResourceID);
+
+ hr = S_OK;
+
+ DWORD_PTR args[] = {(DWORD_PTR)VER_FILEVERSION_STR_L, iResourceID, iErrorCode};
+
+ length = WszFormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY ,
+ (LPCWSTR)ssErrorFormat, 0, 0,
+ szBuffer,iMax,(va_list*)args);
+
+ if (length == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ // The buffer wasn't big enough for the message. Tell the caller this.
+ //
+ // Clear the buffer, just in case.
+ if (szBuffer && iMax)
+ *szBuffer = W('\0');
+
+ length = iMax;
+ hr=HRESULT_FROM_GetLastError();
+ }
+
+ if(length > 0)
+ {
+ if(pcwchUsed)
+ {
+ *pcwchUsed = length;
+ }
+ return hr;
+ }
+
+ // Format mesage failed
+ hr=HRESULT_FROM_GetLastError();
+
+ }
+ }
+ else // if (pFallback)
+ {
+ _ASSERTE(FAILED(hr));
+ }
+ }
+ // if we got here then we couldn't get the fallback message
+ // the fallback message is required so just falling through into "Required"
+
+#else // FEATURE_CORECLR
+ // everything that's not optional goes here for Desktop
+ case DesktopCLR:
+ case Debugging:
+ case Error:
+#endif
+ case Required:
+
+ if ( hr != E_OUTOFMEMORY)
+ {
+ // Shouldn't be any reason for this condition but the case where we
+ // used the wrong ID or didn't update the resource DLL.
+ _ASSERTE(0);
+ hr = HRESULT_FROM_GetLastError();
+ }
+ break;
+ default:
+ {
+ _ASSERTE(!"Invalid eCategory");
+ }
+ }
+ }
+
+ // Return an empty string to save the people with a bad error handling
+ if (szBuffer && iMax)
+ *szBuffer = W('\0');
+
+ return hr;
+#else // !FEATURE_PAL
+ PORTABILITY_ASSERT("UNIXTODO: Implement string loading from resources");
+ return S_OK;
+#endif // !FEATURE_PAL
+}
+
+#ifndef DACCESS_COMPILE
+HRESULT CCompRC::LoadMUILibrary(HRESOURCEDLL * pHInst)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(pHInst != NULL);
+ LocaleID langId;
+ LocaleIDValue langIdValue;
+ // Must resolve current thread's langId to a dll.
+ if(m_fpGetThreadUICultureId) {
+ int ret = (*m_fpGetThreadUICultureId)(&langIdValue);
+
+ // Callback can't return 0, since that indicates empty.
+ // To indicate empty, callback should return UICULTUREID_DONTCARE
+ _ASSERTE(ret != 0);
+ langId=langIdValue;
+ }
+ else
+ langId = UICULTUREID_DONTCARE;
+
+ HRESULT hr = GetLibrary(langId, pHInst);
+ return hr;
+}
+
+HRESULT CCompRC::LoadResourceFile(HRESOURCEDLL * pHInst, LPCWSTR lpFileName)
+{
+#ifndef FEATURE_PAL
+ DWORD dwLoadLibraryFlags;
+ if(m_pResourceFile == m_pDefaultResource)
+ dwLoadLibraryFlags = LOAD_LIBRARY_AS_DATAFILE;
+ else
+ dwLoadLibraryFlags = 0;
+
+ if ((*pHInst = WszLoadLibraryEx(lpFileName, NULL, dwLoadLibraryFlags)) == NULL) {
+ return HRESULT_FROM_GetLastError();
+ }
+#else // !FEATURE_PAL
+ PORTABILITY_ASSERT("UNIXTODO: Implement resource loading - use peimagedecoder?");
+#endif // !FEATURE_PAL
+ return S_OK;
+}
+
+//*****************************************************************************
+// Load the library for this thread's current language
+// Called once per language.
+// Search order is:
+// 1. Dll in localized path (<dir of this module>\<lang name (en-US format)>\mscorrc.dll)
+// 2. Dll in localized (parent) path (<dir of this module>\<lang name> (en format)\mscorrc.dll)
+// 3. Dll in root path (<dir of this module>\mscorrc.dll)
+// 4. Dll in current path (<current dir>\mscorrc.dll)
+//*****************************************************************************
+HRESULT CCompRC::LoadLibraryHelper(HRESOURCEDLL *pHInst,
+ __out_ecount(rcPathSize) __out_z WCHAR *rcPath, const DWORD rcPathSize)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+
+ WCHAR rcDrive[_MAX_DRIVE]; // Volume name.
+ WCHAR rcDir[_MAX_PATH]; // Directory.
+
+ size_t rcDriveLen;
+ size_t rcDirLen;
+ size_t rcPartialPathLen;
+
+
+ _ASSERTE(m_pResourceFile != NULL);
+
+ size_t rcMscorrcLen = wcslen(m_pResourceFile);
+
+ // must initialize before calling SString::Empty()
+ SString::Startup();
+
+ // Try and get both the culture fallback sequence
+
+ StringArrayList cultureNames;
+
+ if (m_fpGetThreadUICultureNames)
+ {
+ hr = (*m_fpGetThreadUICultureNames)(&cultureNames);
+ }
+ else
+ {
+ EX_TRY
+ {
+ cultureNames.Append(SString::Empty());
+ }
+ EX_CATCH_HRESULT(hr);
+ }
+
+ if (hr == E_OUTOFMEMORY)
+ return hr;
+
+ rcDir[0] = W('\0');
+ rcDrive[0] = W('\0');
+ rcPath[rcPathSize - 1] = 0;
+
+ SplitPath(rcPath, rcDrive, _MAX_DRIVE, rcDir, _MAX_PATH, 0, 0, 0, 0);
+ rcDriveLen = wcslen(rcDrive);
+ rcDirLen = wcslen(rcDir);
+
+ // Length that does not include culture name length
+ rcPartialPathLen = rcDriveLen + rcDirLen + rcMscorrcLen + 1;
+
+
+ for (DWORD i=0; i< cultureNames.GetCount();i++)
+ {
+ SString& sLang = cultureNames[i];
+ if (rcPartialPathLen + sLang.GetCount() <= rcPathSize)
+ {
+ wcscpy_s(rcPath, rcDriveLen+1, rcDrive);
+ WCHAR *rcPathPtr = rcPath + rcDriveLen;
+
+ wcscpy_s(rcPathPtr, rcDirLen+1, rcDir);
+ rcPathPtr += rcDirLen;
+
+ if(!sLang.IsEmpty())
+ {
+ wcscpy_s(rcPathPtr, sLang.GetCount()+1, sLang);
+ wcscpy_s(rcPathPtr+ sLang.GetCount(), rcMscorrcLen+1, W("\\"));
+ wcscpy_s(rcPathPtr + sLang.GetCount()+1, rcMscorrcLen+1, m_pResourceFile);
+ }
+ else
+ {
+ wcscpy_s(rcPathPtr + sLang.GetCount(), rcMscorrcLen+1, m_pResourceFile);
+ }
+
+ // Feedback for debugging to eliminate unecessary loads.
+ DEBUG_STMT(DbgWriteEx(W("Loading %s to load strings.\n"), rcPath));
+
+ // Load the resource library as a data file, so that the OS doesn't have
+ // to allocate it as code. This only works so long as the file contains
+ // only strings.
+ hr = LoadResourceFile(pHInst, rcPath);
+ if (SUCCEEDED(hr))
+ break;
+ }
+ else
+ {
+ _ASSERTE(!"Buffer not big enough");
+ hr = E_FAIL;
+
+ }
+ };
+
+ // Last ditch search effort in current directory
+ if (FAILED(hr)) {
+ hr = LoadResourceFile(pHInst, m_pResourceFile);
+ }
+
+ return hr;
+}
+
+// Two-stage approach:
+// First try module directory, then try CORSystemDirectory for default resource
+HRESULT CCompRC::LoadLibrary(HRESOURCEDLL * pHInst)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pHInst != NULL);
+
+#ifdef CROSSGEN_COMPILE
+ // The resources are embeded into the .exe itself for crossgen
+ *pHInst = GetModuleInst();
+#else
+ WCHAR rcPath[_MAX_PATH]; // Path to resource DLL.
+
+ // Try first in the same directory as this dll.
+#if defined(FEATURE_CORECLR)
+
+ VALIDATECORECLRCALLBACKS();
+
+ DWORD length = 0;
+ hr = g_CoreClrCallbacks.m_pfnGetCORSystemDirectory(rcPath, NumItems(rcPath), &length);
+ if (FAILED(hr))
+ return hr;
+
+ hr = LoadLibraryHelper(pHInst, rcPath, NumItems(rcPath));
+
+#else // FEATURE_CORECLR
+
+ if (!WszGetModuleFileName(GetModuleInst(), rcPath, NumItems(rcPath)))
+ return HRESULT_FROM_GetLastError();
+
+ hr = LoadLibraryHelper(pHInst, rcPath, NumItems(rcPath));
+ if (hr == E_OUTOFMEMORY)
+ return hr;
+
+ // In case of default rc file, also try CORSystemDirectory.
+ // Note that GetRequestedRuntimeInfo is a function in ths shim. As of 12/06, this is the only
+ // place where utilcode appears to take a dependency on the shim. This forces everyone that links
+ // with us to also dynamically link to mscoree.dll. Perhaps this should be a delay-load to prevent
+ // that static dependency and have a gracefull fallback when the shim isn't installed.
+ // We don't do this in DAC builds because mscordacwks.dll cannot take a dependency on other CLR
+ // dlls (eg. you must be able to examine a managed dump on a machine without any CLR installed).
+#ifndef DACCESS_COMPILE
+ if (FAILED(hr) && m_pResourceFile == m_pDefaultResource)
+ {
+#ifdef SELF_NO_HOST
+ WCHAR rcVersion[MAX_VERSION_STRING];
+ DWORD rcVersionSize;
+
+ DWORD corSystemPathSize;
+
+ // The reason for using GetRequestedRuntimeInfo is the ability to suppress message boxes
+ // with RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG.
+ hr = LegacyActivationShim::GetRequestedRuntimeInfo(
+ NULL,
+ W("v")VER_PRODUCTVERSION_NO_QFE_STR_L,
+ NULL,
+ 0,
+ RUNTIME_INFO_UPGRADE_VERSION|RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG|RUNTIME_INFO_CONSIDER_POST_2_0,
+ rcPath,
+ NumItems(rcPath),
+ &corSystemPathSize,
+ rcVersion,
+ NumItems(rcVersion),
+ &rcVersionSize);
+
+ if (SUCCEEDED(hr))
+ {
+ if (rcVersionSize > 0)
+ {
+ wcscat_s(rcPath, NumItems(rcPath), rcVersion) ;
+ wcscat_s(rcPath, NumItems(rcPath), W("\\")) ;
+ }
+ }
+#else
+ // If we're hosted, we have the advantage of a CoreClrCallbacks reference.
+ // More importantly, we avoid calling back to mscoree.dll.
+ DWORD cchPath;
+ hr = GetClrCallbacks().m_pfnGetCORSystemDirectory(rcPath, NumItems(rcPath), &cchPath);
+#endif
+ if (SUCCEEDED(hr))
+ {
+ hr = LoadLibraryHelper(pHInst, rcPath, NumItems(rcPath));
+ }
+ }
+#endif // !DACCESS_COMPILE
+
+#endif // FEATURE_CORECLR
+
+#endif // CROSSGEN_COMPILE
+
+ return hr;
+}
+
+#endif // DACCESS_COMPILE
+
+
+
+#ifdef USE_FORMATMESSAGE_WRAPPER
+DWORD
+PALAPI
+CCompRC::FormatMessage(
+ IN DWORD dwFlags,
+ IN LPCVOID lpSource,
+ IN DWORD dwMessageId,
+ IN DWORD dwLanguageId,
+ OUT LPWSTR lpBuffer,
+ IN DWORD nSize,
+ IN va_list *Arguments)
+{
+ STATIC_CONTRACT_NOTHROW;
+ StackSString str;
+ if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM)
+ {
+ dwFlags&=~FORMAT_MESSAGE_FROM_SYSTEM;
+ dwFlags|=FORMAT_MESSAGE_FROM_STRING;
+ str.LoadResourceAndReturnHR(NULL,CCompRC::Error,dwMessageId);
+ lpSource=str.GetUnicode();
+ }
+ return WszFormatMessage(dwFlags,
+ lpSource,
+ dwMessageId,
+ dwLanguageId,
+ lpBuffer,
+ nSize,
+ Arguments);
+}
+#endif // USE_FORMATMESSAGE_WRAPPER
+
diff --git a/src/utilcode/check.cpp b/src/utilcode/check.cpp
new file mode 100644
index 0000000000..f4f3f93344
--- /dev/null
+++ b/src/utilcode/check.cpp
@@ -0,0 +1,309 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//================================================================================
+// Assertion checking infrastructure
+//================================================================================
+
+#include "stdafx.h"
+#include <check.h>
+#include <sstring.h>
+#include <ex.h>
+#include <contract.h>
+
+#ifdef _DEBUG
+size_t CHECK::s_cLeakedBytes = 0;
+size_t CHECK::s_cNumFailures = 0;
+#endif
+
+BOOL CHECK::s_neverEnforceAsserts = 0;
+
+
+// Currently used for scan SPECIAL_HOLDER_* trickery
+DEBUG_NOINLINE BOOL CHECK::EnforceAssert_StaticCheckOnly()
+{
+ return s_neverEnforceAsserts;
+}
+
+#ifdef ENABLE_CONTRACTS_IMPL
+// Need a place to stick this, there is no contract.cpp...
+BOOL BaseContract::s_alwaysEnforceContracts = 1;
+
+
+void PAL_TryMarker::Enter()
+{
+ SCAN_SCOPE_BEGIN;
+ STATIC_CONTRACT_THROWS;
+};
+
+void PAL_TryMarker::Leave()
+{
+ SCAN_SCOPE_END;
+};
+
+
+#define SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask) \
+template<> void ContractViolationHolder<mask>::Enter() \
+{ \
+ SCAN_SCOPE_BEGIN; \
+ ANNOTATION_VIOLATION(mask); \
+ EnterInternal(mask); \
+};
+
+#define SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) \
+template<> AutoCleanupContractViolationHolder<mask>::AutoCleanupContractViolationHolder(BOOL fEnterViolation) \
+{ \
+ SCAN_SCOPE_BEGIN; \
+ ANNOTATION_VIOLATION(mask); \
+ EnterInternal(fEnterViolation ? mask : 0); \
+};
+
+#define SPECIALIZED_VIOLATION(mask) \
+ SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask); \
+ SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask)
+
+// There is a special case that requires 0... Why??? Who knows, let's fix that case.
+
+SPECIALIZED_VIOLATION(0);
+
+// Basic Specializations
+
+SPECIALIZED_VIOLATION(AllViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation);
+SPECIALIZED_VIOLATION(GCViolation);
+SPECIALIZED_VIOLATION(ModeViolation);
+SPECIALIZED_VIOLATION(FaultViolation);
+SPECIALIZED_VIOLATION(FaultNotFatal);
+SPECIALIZED_VIOLATION(SOToleranceViolation);
+SPECIALIZED_VIOLATION(HostViolation);
+SPECIALIZED_VIOLATION(TakesLockViolation);
+SPECIALIZED_VIOLATION(LoadsTypeViolation);
+
+// Other Specializations used by the RUNTIME, if you get a compile time error you need
+// to add the specific specialization that you are using here.
+
+SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|SOToleranceViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|ModeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|TakesLockViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|SOToleranceViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|TakesLockViolation|LoadsTypeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal|TakesLockViolation);
+SPECIALIZED_VIOLATION(GCViolation|SOToleranceViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultViolation|SOToleranceViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultViolation|ModeViolation|SOToleranceViolation);
+SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation);
+SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(GCViolation|ModeViolation|SOToleranceViolation|FaultNotFatal|TakesLockViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation);
+SPECIALIZED_VIOLATION(FaultViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(FaultNotFatal|TakesLockViolation);
+
+
+
+#undef SPECIALIZED_VIOLATION
+#undef SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER
+#undef SPECIALIZE_CONTRACT_VIOLATION_HOLDER
+
+#endif
+
+// Trigger triggers the actual check failure. The trigger may provide a reason
+// to include in the failure message.
+
+void CHECK::Trigger(LPCSTR reason)
+{
+ STATIC_CONTRACT_SO_NOT_MAINLINE;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ const char *messageString = NULL;
+ NewHolder<StackScratchBuffer> pScratch(NULL);
+ NewHolder<StackSString> pMessage(NULL);
+
+ EX_TRY
+ {
+ FAULT_NOT_FATAL();
+
+ pScratch = new StackScratchBuffer();
+ pMessage = new StackSString();
+
+ pMessage->AppendASCII(reason);
+ pMessage->AppendASCII(": ");
+ if (m_message != NULL)
+ pMessage->AppendASCII((m_message != (LPCSTR)1) ? m_message : "<runtime check failure>");
+
+#if _DEBUG
+ pMessage->AppendASCII("FAILED: ");
+ pMessage->AppendASCII(m_condition);
+#endif
+
+ messageString = pMessage->GetANSI(*pScratch);
+ }
+ EX_CATCH
+ {
+ messageString = "<exception occurred while building failure description>";
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+#if _DEBUG
+ DbgAssertDialog((char*)m_file, m_line, (char *)messageString);
+#else
+ OutputDebugStringA(messageString);
+ DebugBreak();
+#endif
+
+}
+
+#ifdef _DEBUG
+// Setup records context info after a failure.
+
+void CHECK::Setup(LPCSTR message, LPCSTR condition, LPCSTR file, INT line)
+{
+ STATIC_CONTRACT_SO_NOT_MAINLINE;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ //
+ // It might be nice to collect all of the message here. But for now, we will just
+ // retain the innermost one.
+ //
+
+ if (m_message == NULL)
+ {
+ m_message = message;
+ m_condition = condition;
+ m_file = file;
+ m_line = line;
+ }
+
+#ifdef _DEBUG
+ else if (IsInAssert())
+ {
+ EX_TRY
+ {
+ FAULT_NOT_FATAL();
+ // Try to build a stack of condition failures
+
+ StackSString context;
+ context.Printf("%s\n\t%s%s FAILED: %s\n\t\t%s, line: %d",
+ m_condition,
+ message && *message ? message : "",
+ message && *message ? ": " : "",
+ condition,
+ file, line);
+
+ m_condition = AllocateDynamicMessage(context);
+ }
+ EX_CATCH
+ {
+ // If anything goes wrong, we don't push extra context
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ }
+#endif
+
+#if defined(_DEBUG_IMPL)
+ if (IsInAssert() && IsDebuggerPresent())
+ {
+ DebugBreak();
+ }
+#endif
+}
+
+LPCSTR CHECK::FormatMessage(LPCSTR messageFormat, ...)
+{
+ STATIC_CONTRACT_SO_NOT_MAINLINE;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ LPCSTR result = NULL;
+
+ // We never delete this allocated string. A dtor would conflict with places
+ // we use this around SEH stuff. We could have some fancy stack-based allocator,
+ // but that's too much work for now. In fact we believe that leaking is a reasonable
+ // policy, since allocations will only happen on a failed assert, and a failed assert
+ // will generally be fatal to the process.
+
+ // The most common place for format strings will be in an assert; in
+ // which case we don't care about leaking.
+ // But to be safe, if we're not-inside an assert, then we'll just use
+ // the format string literal to avoid allocated (and leaking) any memory.
+
+ CHECK chk;
+ if (!chk.IsInAssert())
+ result = messageFormat;
+ else
+ {
+ // This path is only run in debug. TakesLockViolation suppresses
+ // problems with SString below.
+ CONTRACT_VIOLATION(FaultNotFatal|TakesLockViolation);
+
+ EX_TRY
+ {
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+
+ // Format it.
+ va_list args;
+ va_start( args, messageFormat);
+
+ SString s;
+ s.VPrintf(messageFormat, args);
+
+ va_end(args);
+
+ result = AllocateDynamicMessage(s);
+
+ }
+ EX_CATCH
+ {
+ // If anything goes wrong, just use the format string.
+ result = messageFormat;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ }
+
+ return result;
+}
+
+LPCSTR CHECK::AllocateDynamicMessage(const SString &s)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SO_NOT_MAINLINE;
+
+ // Make a copy of it.
+ StackScratchBuffer buffer;
+ const char * pMsg = s.GetANSI(buffer);
+
+ // Must copy that into our own field.
+ size_t len = strlen(pMsg) + 1;
+ char * p = new char[len];
+ strcpy(p, pMsg);
+
+ // But we'll keep counters of how much we're leaking for diagnostic purposes.
+ s_cLeakedBytes += len;
+ s_cNumFailures ++;
+
+ // This should never fire.
+ // Note use an ASSERTE (not a check) to avoid a recursive deadlock.
+ _ASSERTE(s_cLeakedBytes < 10000 || !"Warning - check misuse - leaked over 10,000B due to unexpected usage pattern");
+ return p;
+}
+
+void WINAPI ReleaseCheckTls(LPVOID pTlsData)
+{
+ CHECK::ReleaseTls(pTlsData);
+}
+
+#endif
diff --git a/src/utilcode/circularlog.cpp b/src/utilcode/circularlog.cpp
new file mode 100644
index 0000000000..49fd4fde28
--- /dev/null
+++ b/src/utilcode/circularlog.cpp
@@ -0,0 +1,312 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+// Circular file log
+//
+#include "stdafx.h"
+
+#include "utilcode.h"
+#include "circularlog.h"
+
+CircularLog::CircularLog()
+{
+ m_bInit = false;
+}
+
+CircularLog::~CircularLog()
+{
+ Shutdown();
+}
+
+bool CircularLog::Init(const WCHAR* logname, const WCHAR* logHeader, DWORD maxSize)
+{
+ Shutdown();
+
+ m_LogFilename = logname;
+ m_LogFilename.Append(W(".log"));
+
+ m_LockFilename = logname;
+ m_LockFilename.Append(W(".lock"));
+
+ m_OldLogFilename = logname;
+ m_OldLogFilename .Append(W(".old.log"));
+
+ if (logHeader)
+ m_LogHeader = logHeader;
+
+ m_MaxSize = maxSize;
+ m_uLogCount = 0;
+
+ m_bInit = true;
+
+ if (CheckLogHeader())
+ {
+ CheckForLogReset(FALSE);
+ }
+ return true;
+}
+
+void CircularLog::Shutdown()
+{
+ m_bInit = false;
+}
+
+void CircularLog::Log(const WCHAR* string)
+{
+ if (!m_bInit)
+ {
+ return;
+ }
+
+ HANDLE hLogFile = OpenFile();
+ if (hLogFile == INVALID_HANDLE_VALUE)
+ {
+ return;
+ }
+
+ // Check for file limit only once in a while
+ if ((m_uLogCount % 16) == 0)
+ {
+ // First do a quick check without acquiring lock, optimizing for the common case where file is not overflow.
+ LARGE_INTEGER fileSize;
+ if (GetFileSizeEx(hLogFile, &fileSize) && fileSize.QuadPart > m_MaxSize)
+ {
+ // Must close existing handle before calling CheckForOverflow, and re-open it afterwards.
+ CloseHandle(hLogFile);
+ CheckForLogReset(TRUE);
+ hLogFile = OpenFile();
+ if (hLogFile == INVALID_HANDLE_VALUE)
+ {
+ return;
+ }
+ }
+ }
+ m_uLogCount++;
+
+ // Replace \n with \r\n (we're writing to a binary file)
+ NewArrayHolder<WCHAR> pwszConvertedHolder = new WCHAR[wcslen(string)*2 + 1];
+ WCHAR* pD = pwszConvertedHolder;
+ WCHAR previous = W('\0');
+ for (const WCHAR* pS = string ; *pS != W('\0') ; pS++)
+ {
+ // We get mixed strings ('\n' and '\r\n'). So attempt to filter
+ // the ones that don't need to have a '\r' added.
+ if (*pS == W('\n') && previous != W('\r'))
+ {
+ *pD = W('\r');
+ pD ++;
+ }
+
+ *pD = *pS;
+ pD++;
+
+ previous = *pS;
+ }
+
+ *pD = W('\0');
+
+ // Convert to Utf8 to reduce typical log file size
+ SString logString(pwszConvertedHolder);
+ StackScratchBuffer bufUtf8;
+ COUNT_T cBytesUtf8 = 0;
+ const UTF8 * pszUtf8Log = logString.GetUTF8(bufUtf8, &cBytesUtf8);
+ // Remove null terminator from log entry buffer
+ cBytesUtf8--;
+
+ DWORD dwWritten;
+ WriteFile(hLogFile, pszUtf8Log, (DWORD)cBytesUtf8, &dwWritten, NULL);
+ CloseHandle(hLogFile);
+}
+
+
+BOOL CircularLog::CheckLogHeader()
+{
+ BOOL fNeedsPushToBackupLog = FALSE;
+
+ // Check to make sure the header on the log is utf8, if it is not, push the current file to the .bak file and try again.
+ HANDLE hLogFile = WszCreateFile(
+ m_LogFilename.GetUnicode(),
+ FILE_READ_DATA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hLogFile != INVALID_HANDLE_VALUE)
+ {
+ CHAR unicodeHeader []= {(char)0xef, (char)0xbb, (char)0xbf};
+ CHAR unicodeHeaderCheckBuf[_countof(unicodeHeader)];
+
+ DWORD dwRead = sizeof(unicodeHeaderCheckBuf);
+ fNeedsPushToBackupLog = !ReadFile(hLogFile, &unicodeHeaderCheckBuf, dwRead, &dwRead, NULL);
+
+ if (!fNeedsPushToBackupLog)
+ {
+ // Successfully read from file. Now check to ensure we read the right amount, and that we read the right data
+ if ((dwRead != sizeof(unicodeHeader)) || (0 != memcmp(unicodeHeader, unicodeHeaderCheckBuf, dwRead)))
+ {
+ fNeedsPushToBackupLog = TRUE;
+ }
+ }
+ CloseHandle(hLogFile);
+ }
+
+ return fNeedsPushToBackupLog;
+}
+
+#define MOVE_FILE_RETRY_TIME 100
+#define MOVE_FILE_RETRY_COUNT 10
+void CircularLog::CheckForLogReset(BOOL fOverflow)
+{
+ if (!m_MaxSize)
+ {
+ return;
+ }
+
+ for (int i = 0; i < MOVE_FILE_RETRY_COUNT; i++)
+ {
+ FileLockHolder lock;
+ if (FAILED(lock.AcquireNoThrow(m_LockFilename.GetUnicode())))
+ {
+ // FileLockHolder::Acquire already has a retry loop, so don't retry if it fails.
+ return;
+ }
+
+ BOOL fLogNeedsReset = FALSE;
+
+ if (fOverflow)
+ {
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+ if (WszGetFileAttributesEx(
+ m_LogFilename,
+ GetFileExInfoStandard,
+ &fileData) == FALSE)
+ {
+ return;
+ }
+
+ unsigned __int64 fileSize =
+ (((unsigned __int64) fileData.nFileSizeHigh) << 32) |
+ ((unsigned __int64) fileData.nFileSizeLow);
+
+
+ if (fileSize > (unsigned __int64) m_MaxSize)
+ {
+ fLogNeedsReset = TRUE;
+ }
+ }
+ else
+ {
+ fLogNeedsReset = CheckLogHeader();
+ }
+
+ if (fLogNeedsReset)
+ {
+ // Push current log out to .old file
+ BOOL success = WszMoveFileEx(
+ m_LogFilename.GetUnicode(),
+ m_OldLogFilename.GetUnicode(),
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
+
+ if (success || GetLastError() != ERROR_SHARING_VIOLATION)
+ {
+ return;
+ }
+ }
+ else
+ {
+ // Someone else moved the file before we can.
+ return;
+ }
+
+ // Don't want to hold the lock while sleeping.
+ lock.Release();
+ ClrSleepEx(MOVE_FILE_RETRY_TIME, FALSE);
+ }
+}
+
+#define OPEN_FILE_RETRY_TIME 100
+#define OPEN_FILE_RETRY_COUNT 10
+// Normally we open file with FILE_SHARE_WRITE, to avoid sharing violations between multiple threads or
+// processes. However, when we create a new file, the Unicode header must be written at the beginning of the
+// file. This can't be guaranteed with multiple writers, so we require exclusive access while creating a new
+// file. Our algorithm is first try to open with OPEN_EXISTING and FILE_SHARE_WRITE, and if that fails, try
+// again with OPEN_ALWAYS and no write sharing.
+HANDLE CircularLog::OpenFile()
+{
+ for (int i = 0; i < OPEN_FILE_RETRY_COUNT; i++)
+ {
+ // First try to open an existing file allowing shared write.
+ HANDLE hLogFile = WszCreateFile(
+ m_LogFilename.GetUnicode(),
+ FILE_APPEND_DATA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hLogFile != INVALID_HANDLE_VALUE)
+ {
+ return hLogFile;
+ }
+
+ if (GetLastError() == ERROR_FILE_NOT_FOUND)
+ {
+ // Try to create an new file with exclusive access.
+ HANDLE hLogFile = WszCreateFile(
+ m_LogFilename.GetUnicode(),
+ FILE_APPEND_DATA,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hLogFile != INVALID_HANDLE_VALUE)
+ {
+ LARGE_INTEGER fileSize;
+ if (! GetFileSizeEx(hLogFile, &fileSize))
+ {
+ CloseHandle(hLogFile);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ // If the file size is 0, need to write the Unicode header (utf8 bom).
+ if (fileSize.QuadPart == 0)
+ {
+ CHAR unicodeHeader []= {(char)0xef, (char)0xbb, (char)0xbf};
+ DWORD dwWritten;
+ WriteFile(hLogFile, &unicodeHeader, sizeof(unicodeHeader), &dwWritten, NULL);
+
+ // Write out header
+
+ // Convert to Utf8 to reduce typical log file size
+ StackScratchBuffer bufUtf8;
+ COUNT_T cBytesUtf8 = 0;
+ const UTF8 * pszUtf8Log = m_LogHeader.GetUTF8(bufUtf8, &cBytesUtf8);
+ // Remove null terminator from log entry buffer
+ cBytesUtf8--;
+
+ WriteFile(hLogFile, pszUtf8Log, (DWORD)cBytesUtf8, &dwWritten, NULL);
+ }
+
+ return hLogFile;
+ }
+ }
+
+ if (GetLastError() != ERROR_SHARING_VIOLATION)
+ {
+ break;
+ }
+
+ ClrSleepEx(OPEN_FILE_RETRY_TIME, FALSE);
+ }
+
+ return INVALID_HANDLE_VALUE;
+
+}
diff --git a/src/utilcode/clrconfig.cpp b/src/utilcode/clrconfig.cpp
new file mode 100644
index 0000000000..0b164694d4
--- /dev/null
+++ b/src/utilcode/clrconfig.cpp
@@ -0,0 +1,777 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// CLRConfig.cpp
+//
+
+//
+// Unified method of accessing configuration values from environment variables,
+// registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "clrconfig.h"
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+//
+// Initialize the EEConfig::GetConfiguration function pointer to NULL. If EEConfig isn't init'ed, this will
+// stay NULL and CLRConfig will ignore config files.
+//
+CLRConfig::GetConfigValueFunction CLRConfig::s_GetConfigValueCallback = NULL;
+
+//
+// Initialize the PerformanceDefaults::LookupConfigValue function pointer to NULL. If not initialized, CLRConfig
+// will ignore LookupOptions::MayHavePerformanceDefault.
+//
+CLRConfig::GetPerformanceDefaultValueFunction CLRConfig::s_GetPerformanceDefaultValueCallback = NULL;
+
+#ifdef FEATURE_WIN_DB_APPCOMPAT
+PFN_CptQuirkIsEnabled3 CLRConfig::s_IsQuirkEnabledCallback = NULL;
+PFN_CptQuirkGetData2 CLRConfig::s_GetQuirkValueCallback = NULL;
+#endif
+
+//
+// Creating structs using the macro table in CLRConfigValues.h
+//
+
+// These macros intialize ConfigDWORDInfo structs.
+#define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
+#define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
+
+// These macros intialize ConfigStringInfo structs.
+#define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
+#define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
+
+// TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to
+// CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in
+// file:../utilcode/CLRConfig.h) should be removed.
+#define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+#define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+//
+// Debug versions of the macros
+//
+#ifdef _DEBUG
+ #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
+ #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
+ #define CONFIG_STRING_INFO(symbol, name, description) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
+ #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
+ #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+ #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+#else
+ #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description)
+ #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions)
+ #define CONFIG_STRING_INFO(symbol, name, description)
+ #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions)
+ #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description)
+ #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description)
+#endif // _DEBUG
+
+ // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code.
+ #include "clrconfigvalues.h"
+
+#undef RETAIL_CONFIG_DWORD_INFO
+#undef RETAIL_CONFIG_STRING_INFO
+#undef RETAIL_CONFIG_DWORD_INFO_EX
+#undef RETAIL_CONFIG_STRING_INFO_EX
+#undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS
+#undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS
+#undef CONFIG_DWORD_INFO
+#undef CONFIG_STRING_INFO
+#undef CONFIG_DWORD_INFO_EX
+#undef CONFIG_STRING_INFO_EX
+#undef CONFIG_DWORD_INFO_DIRECT_ACCESS
+#undef CONFIG_STRING_INFO_DIRECT_ACCESS
+
+
+#ifdef FEATURE_WIN_DB_APPCOMPAT
+
+#define MAX_QUIRK_LENGTH 60
+#define WIN_DB_COMPONENT_NAME W("NETFX.")
+#define WIN_DB_COMPONENT_NAME_LENGTH 6
+
+// queries the DB if the quirk is enabled. If the quirk is enabled then it also gets the value associated with the quirk.
+// pass in quirkData as NULL if the value is not required and only enabled/disabled is needed.
+// Length of quirk cannot be greater than 60. If it is greater than 60 then this api returns E_FAIL.
+HRESULT CLRConfig::getQuirkEnabledAndValueFromWinDB(LPCWSTR wszQuirkName, BOOL* isEnabled, CPT_QUIRK_DATA* quirkData)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(isEnabled, NULL_NOT_OK));
+ }
+ CONTRACTL_END;
+
+ if(wszQuirkName == NULL)
+ return E_FAIL;
+
+ WCHAR wszCompleteQuirkName[MAX_QUIRK_LENGTH + WIN_DB_COMPONENT_NAME_LENGTH + 1];
+ WCHAR wszComponentName[] = WIN_DB_COMPONENT_NAME;
+ size_t cchCompleteQuirkName = MAX_QUIRK_LENGTH + WIN_DB_COMPONENT_NAME_LENGTH + 1;
+
+ _ASSERT(wcslen(wszComponentName) == WIN_DB_COMPONENT_NAME_LENGTH);
+
+ size_t cchOrig = wcslen(wszQuirkName);
+ if(cchOrig > MAX_QUIRK_LENGTH)
+ {
+ return E_FAIL;
+ }
+
+ // Create comlete name of the quirk. Windows expects complete quirkName i.e. componentName.quirkName
+ // eg. ETWEnabled will become NETFX.ETWEnabled
+ errno_t err = wcsncpy_s(wszCompleteQuirkName, cchCompleteQuirkName, wszComponentName, WIN_DB_COMPONENT_NAME_LENGTH);
+ if (err != 0)
+ {
+ return E_FAIL;
+ }
+
+ err = wcscat_s(wszCompleteQuirkName, cchCompleteQuirkName, wszQuirkName);
+ if (err != 0)
+ {
+ return E_FAIL;
+ }
+
+
+ UINT32 version = 0xFFFFFFFF;
+ BOOL fIsEnabled;
+ //call windows api
+ // Version passed must be 0xFFFFFFFF for NETFX. Passing any other version requires more
+ // understanding of the windows API.
+ fIsEnabled = s_IsQuirkEnabledCallback(wszCompleteQuirkName,version);
+
+ if(fIsEnabled && quirkData != NULL)
+ {
+ quirkData->Size = sizeof(CPT_QUIRK_DATA);
+ // Query for quirkData
+ if(!SUCCEEDED(s_GetQuirkValueCallback(wszCompleteQuirkName, quirkData)))
+ {
+ return E_FAIL;
+ }
+ }
+
+ *isEnabled = fIsEnabled;
+
+ return S_OK;
+}
+
+#endif
+
+
+// Return if a quirk is a enabled.
+// This will also return enabled as true when the quirk has a value set.
+BOOL CLRConfig::IsConfigEnabled(const ConfigDWORDInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ DWORD result = info.defaultValue;
+
+#ifdef FEATURE_WIN_DB_APPCOMPAT
+ // Windows Shim DB should be the first place to look as it applies microsoft enforced policy
+ // and overrides setting at any other place like config or registry
+ if(CheckLookupOption(info, IgnoreWindowsQuirkDB) == FALSE &&
+ s_IsQuirkEnabledCallback != NULL )// Check that IsQuirkEnabledCallback function has been registered.
+ {
+ BOOL enabledInDB = FALSE;
+ if(SUCCEEDED(getQuirkEnabledAndValueFromWinDB(info.name, &enabledInDB, NULL)))
+ {
+ if(enabledInDB)
+ {
+ return TRUE;
+ }
+ }
+ }
+#endif
+ //
+ // Set up REGUTIL options.
+ //
+ REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
+ BOOL prependCOMPLUS = !CheckLookupOption(info, DontPrependCOMPLUS_);
+
+ //
+ // If we aren't favoring config files, we check REGUTIL here.
+ //
+ if(CheckLookupOption(info, FavorConfigFile) == FALSE)
+ {
+ REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS);
+ if(result>0)
+ return TRUE;
+ LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level);
+ if(result != NULL && result[0] != 0)
+ {
+ return TRUE;
+ }
+ }
+
+ //
+ // Check config files through EEConfig.
+ //
+ if(CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
+ s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered.
+ {
+ LPCWSTR pvalue;
+
+ // EEConfig lookup options.
+ BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
+ BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
+
+ if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL)
+ {
+ WCHAR * end;
+ errno = 0;
+ result = wcstoul(pvalue, &end, 0);
+
+ // errno is ERANGE if the number is out of range, and end is set to pvalue if
+ // no valid conversion exists.
+ if (errno == ERANGE || end == pvalue)
+ {
+ if(pvalue[0]!=0)
+ return TRUE;
+
+ result = info.defaultValue;
+ }
+
+ if(result>0)
+ return TRUE;
+ }
+ }
+
+ //
+ // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
+ //
+ if(CheckLookupOption(info, FavorConfigFile) == TRUE)
+ {
+ REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS);
+ if(result>0)
+ return TRUE;
+ LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level);
+ if(result != NULL && result[0] != 0)
+ {
+ return TRUE;
+ }
+ }
+
+ //
+ // If we get here, the option was not listed in REGUTIL or EEConfig; check whether the option
+ // has a PerformanceDefault-specified value before falling back to the built-in default
+ //
+ DWORD performanceDefaultValue;
+ if (CheckLookupOption(info, MayHavePerformanceDefault) &&
+ s_GetPerformanceDefaultValueCallback != NULL &&
+ s_GetPerformanceDefaultValueCallback(info.name, &performanceDefaultValue))
+ {
+ if (!SUCCEEDED(REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS)))
+ {
+ if(performanceDefaultValue>0)
+ return TRUE;
+ }
+ }
+
+ if(info.defaultValue>0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+
+//
+// Look up a DWORD config value.
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+//
+// static
+DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT; // Need this to be tolerant to stack overflows since REGUTIL::GetConfigDWORD was too. (This replaces calls to REGUTIL::GetConfigDWORD)
+ }
+ CONTRACTL_END;
+
+ DWORD result = info.defaultValue;
+
+#ifdef FEATURE_WIN_DB_APPCOMPAT
+ // Windows Shim DB should be the first place to look as it applies microsoft enforced policy
+ // and overrides setting at any other place like config or registry
+ if(CheckLookupOption(info, IgnoreWindowsQuirkDB) == FALSE &&
+ s_IsQuirkEnabledCallback != NULL )// Check that IsQuirkEnabledCallback function has been registered.
+ {
+
+ BOOL isEnabledInDB = FALSE;
+ CPT_QUIRK_DATA quirkData;
+ if(SUCCEEDED(getQuirkEnabledAndValueFromWinDB(info.name, &isEnabledInDB, &quirkData)))
+ {
+ if(isEnabledInDB)
+ {
+ WCHAR *end;
+ errno = 0;
+ result = wcstoul(quirkData.CommandLine, &end, 0);
+
+ // errno is ERANGE if the number is out of range, and end is set to pvalue if
+ // no valid conversion exists.
+ if (errno != ERANGE && end != quirkData.CommandLine)
+ {
+ return result;
+ }
+ else
+ {
+ // If an invalid value is defined we treat it as the default value.
+ // i.e. we don't look further.
+ return info.defaultValue;
+ }
+ }
+ }
+ }
+#endif // FEATURE_WIN_DB_APPCOMPAT
+
+ //
+ // Set up REGUTIL options.
+ //
+ REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
+ BOOL prependCOMPLUS = !CheckLookupOption(info, DontPrependCOMPLUS_);
+
+ //
+ // If we aren't favoring config files, we check REGUTIL here.
+ //
+ if(CheckLookupOption(info, FavorConfigFile) == FALSE)
+ {
+ REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS);
+ // TODO: We are ignoring explicitly defined default values to avoid change in behavior.
+ // TODO: Ideally, the following should check the hresult for success.
+ if(result != info.defaultValue)
+ {
+ return result;
+ }
+ }
+
+ //
+ // Check config files through EEConfig.
+ //
+ if(CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
+ s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered.
+ {
+ LPCWSTR pvalue;
+
+ // EEConfig lookup options.
+ BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
+ BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
+
+ if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL)
+ {
+ WCHAR * end;
+ errno = 0;
+ result = wcstoul(pvalue, &end, 0);
+
+ // errno is ERANGE if the number is out of range, and end is set to pvalue if
+ // no valid conversion exists.
+ if (errno != ERANGE && end != pvalue)
+ {
+ return result;
+ }
+ else
+ {
+ // If an invalid value is defined we treat it as the default value.
+ // i.e. we don't look further.
+ return info.defaultValue;
+ }
+ }
+ }
+
+ //
+ // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
+ //
+ if(CheckLookupOption(info, FavorConfigFile) == TRUE)
+ {
+ REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS);
+ // TODO: We are ignoring explicitly defined default values to avoid change in behavior.
+ // TODO: Ideally, the following should check the hresult for success.
+ if(result != info.defaultValue)
+ {
+ return result;
+ }
+ }
+
+ //
+ // If we get here, the option was not listed in REGUTIL or EEConfig; check whether the option
+ // has a PerformanceDefault-specified value before falling back to the built-in default
+ //
+ DWORD performanceDefaultValue;
+ if (CheckLookupOption(info, MayHavePerformanceDefault) &&
+ s_GetPerformanceDefaultValueCallback != NULL &&
+ s_GetPerformanceDefaultValueCallback(info.name, &performanceDefaultValue))
+ {
+ // TODO: We ignore explicitly defined default values above, but we do not want to let performance defaults override these.
+ // TODO: Ideally, the above would use hresult for success and this check would be removed.
+ if (!SUCCEEDED(REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPLUS)))
+ return performanceDefaultValue;
+ }
+
+ return info.defaultValue;
+}
+
+//
+// Look up a String config value.
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+//
+// Return value:
+// * Pointer to the string value, if found. You own the string that's returned. Returns NULL if the value
+// is not found.
+//
+// static
+LPWSTR CLRConfig::GetConfigValue(const ConfigStringInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ LPWSTR result = NULL;
+
+ // TODO: We swallow OOM exception here. Is this OK?
+ FAULT_NOT_FATAL();
+
+ // If this fails, result will stay NULL.
+ GetConfigValue(info, &result);
+
+ return result;
+}
+
+//
+// Look up a string config value, passing it out through a pointer reference.
+//
+// Return value:
+// * Reports out of memory errors (HRESULT E_OUTOFMEMORY).
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+// * outVal - Set to the result string. You own the string that's returned. Set to NULL if the value is
+// not found.
+//
+// static
+HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z LPWSTR * outVal)
+{
+ CONTRACT(HRESULT) {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY);
+ POSTCONDITION(CheckPointer(outVal, NULL_OK)); // TODO: Should this check be *outVal instead of outVal?
+ } CONTRACT_END;
+
+ LPWSTR result = NULL;
+
+#ifdef FEATURE_WIN_DB_APPCOMPAT
+ // Windows Shim DB should be the first place to look as it applies microsoft enforced policy
+ // and overrides setting at any other place like config or registry
+ if(CheckLookupOption(info, IgnoreWindowsQuirkDB) == FALSE &&
+ s_IsQuirkEnabledCallback != NULL )// Check that IsQuirkEnabledCallback function has been registered.
+ {
+
+ BOOL isEnabledInDB = FALSE;
+ CPT_QUIRK_DATA quirkData;
+ if(SUCCEEDED(getQuirkEnabledAndValueFromWinDB(info.name, &isEnabledInDB, &quirkData)))
+ {
+ if(isEnabledInDB)
+ {
+ size_t len = wcslen(quirkData.CommandLine) + 1;
+ result = new (nothrow) WCHAR[len];
+ if (result == NULL)
+ {
+ RETURN E_OUTOFMEMORY;
+ }
+ wcscpy_s(result, len, quirkData.CommandLine);
+ }
+ }
+ }
+#endif // FEATURE_WIN_DB_APPCOMPAT
+
+ //
+ // Set up REGUTIL options.
+ //
+ REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
+ BOOL prependCOMPLUS = !CheckLookupOption(info, DontPrependCOMPLUS_);
+
+ //
+ // If we aren't favoring config files, we check REGUTIL here.
+ //
+ if(result == NULL && CheckLookupOption(info, FavorConfigFile) == FALSE)
+ {
+ result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level);
+ }
+
+ //
+ // Check config files through EEConfig.
+ //
+ if(result == NULL && // Check that we don't have a value from REGUTIL
+ CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
+ s_GetConfigValueCallback != NULL) // Check that GetConfigValueCallback function has been registered.
+ {
+ LPCWSTR pResult;
+
+ // EEConfig lookup options.
+ BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
+ BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
+
+ if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pResult, systemOnly, applicationFirst)) && pResult != NULL)
+ {
+ size_t len = wcslen(pResult) + 1;
+ result = new (nothrow) WCHAR[len];
+ if (result == NULL)
+ {
+ RETURN E_OUTOFMEMORY;
+ }
+ wcscpy_s(result, len, pResult);
+ }
+ }
+
+ //
+ // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
+ //
+ if(result==NULL &&
+ CheckLookupOption(info, FavorConfigFile) == TRUE)
+ {
+ result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPLUS, level);
+ }
+
+ if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue))
+ {
+ // If this fails, result remains untouched, so we'll just return the untrimmed
+ // value.
+ LPWSTR wszTrimmedResult = NULL;
+ if (SUCCEEDED(TrimWhiteSpace(result, &wszTrimmedResult)) &&
+ (wszTrimmedResult != NULL))
+ {
+ // wszTrimmedResult should be the result we return. Delete the untrimmed
+ // result.
+ delete [] result;
+ result = wszTrimmedResult;
+ }
+ }
+
+ // If we ever want a PerformanceDefault for a string value, you can replace this assert
+ // with code that follows the pattern for DWORD values above.
+ _ASSERTE(!CheckLookupOption(info, MayHavePerformanceDefault));
+
+ *outVal = result;
+ RETURN S_OK;
+}
+
+//
+// Check whether an option is specified (e.g. explicitly listed) in any location
+//
+// Arguments:
+// * name - the name field of the desired ConfigDWORDInfo/ConfigStringInfo
+//
+// static
+BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Check config files
+ {
+ LPCWSTR result = NULL;
+
+ if (s_GetConfigValueCallback != NULL &&
+ SUCCEEDED(s_GetConfigValueCallback(name, &result, FALSE, FALSE)) &&
+ result != NULL)
+ {
+ return TRUE;
+ }
+ }
+
+ // Check REGUTIL, both with and without the COMPLUS_ prefix
+ {
+ LPWSTR result = NULL;
+
+ result = REGUTIL::GetConfigString_DontUse_(name, TRUE);
+ if (result != NULL)
+ {
+ FreeConfigString(result);
+ return TRUE;
+ }
+
+ result = REGUTIL::GetConfigString_DontUse_(name, FALSE);
+ if (result != NULL)
+ {
+ FreeConfigString(result);
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given an input string, returns a newly-allocated string equal to the input but with
+// leading and trailing whitespace trimmed off. If input is already trimmed, or if
+// trimming would result in an empty string, this function sets the output string to NULL
+//
+// Caller must free *pwszTrimmed if non-NULL
+//
+// Arguments:
+// * wszOrig - String to trim
+// * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or
+// NULL)
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(wszOrig != NULL);
+ _ASSERTE(pwszTrimmed != NULL);
+
+ // In case we return early, set [out] to NULL by default
+ *pwszTrimmed = NULL;
+
+ // Get pointers into internal string that show where to do the trimming.
+ size_t cchOrig = wcslen(wszOrig);
+ if (!FitsIn<DWORD>(cchOrig))
+ return COR_E_OVERFLOW;
+ DWORD cchAfterTrim = (DWORD) cchOrig;
+ LPCWSTR wszAfterTrim = wszOrig;
+ ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim);
+
+ // Is input string already trimmed? If so, save an allocation and just return.
+ if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim))
+ {
+ // Yup, just return success
+ return S_OK;
+ }
+
+ if (cchAfterTrim == 0)
+ {
+ // After trimming, there's nothing left, so just return NULL
+ return S_OK;
+ }
+
+ // Create a new buffer to hold a copy of the trimmed string. Caller will be
+ // responsible for this buffer if we return it.
+ NewArrayHolder<WCHAR> wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]);
+ if (wszTrimmedCopy == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim);
+ if (err != 0)
+ {
+ return E_FAIL;
+ }
+
+ // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for
+ // deleting it.
+ wszTrimmedCopy.SuppressRelease();
+ *pwszTrimmed = wszTrimmedCopy;
+ return S_OK;
+}
+
+
+//
+// Deallocation function for code:CLRConfig::FreeConfigString
+//
+void CLRConfig::FreeConfigString(__in_z LPWSTR str)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ delete [] str;
+}
+
+//
+// Register EEConfig's GetConfigValueCallback function so CLRConfig can look in config files.
+//
+//static
+void CLRConfig::RegisterGetConfigValueCallback(GetConfigValueFunction func)
+{
+ LIMITED_METHOD_CONTRACT;
+ s_GetConfigValueCallback = func;
+}
+
+//
+// Register PerformanceDefaults' LookupConfigValue so CLRConfig can support 'MayHavePerformanceDefault' values
+//
+//static
+void CLRConfig::RegisterGetPerformanceDefaultValueCallback(GetPerformanceDefaultValueFunction func)
+{
+ LIMITED_METHOD_CONTRACT;
+ s_GetPerformanceDefaultValueCallback = func;
+}
+
+#ifdef FEATURE_WIN_DB_APPCOMPAT
+void CLRConfig::RegisterWinDbQuirkApis(PFN_CptQuirkIsEnabled3 func1, PFN_CptQuirkGetData2 func2)
+{
+ LIMITED_METHOD_CONTRACT;
+ s_IsQuirkEnabledCallback = func1;
+ s_GetQuirkValueCallback = func2;
+}
+#endif // FEATURE_WIN_DB_APPCOMPAT
+
+//
+// Helper method to translate LookupOptions to REGUTIL::CORConfigLevel.
+//
+//static
+REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0;
+
+ if(CheckLookupOption(options, IgnoreEnv) == FALSE)
+ level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_ENV);
+
+ if(CheckLookupOption(options, IgnoreHKCU) == FALSE)
+ level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_USER);
+
+ if(CheckLookupOption(options, IgnoreHKLM) == FALSE)
+ level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_MACHINE);
+
+ return level;
+}
diff --git a/src/utilcode/clrhelpers.cpp b/src/utilcode/clrhelpers.cpp
new file mode 100644
index 0000000000..498d3ad128
--- /dev/null
+++ b/src/utilcode/clrhelpers.cpp
@@ -0,0 +1,14 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+// This is file to compile 2 public "headers" from ..\inc directory (we have to include stdafx.h first).
+//
+/***************************************************************************/
+
+#include "stdafx.h"
+
+#include "../inc/corhlpr.cpp"
+#include "../inc/corhlprpriv.cpp"
diff --git a/src/utilcode/clrhost.cpp b/src/utilcode/clrhost.cpp
new file mode 100644
index 0000000000..6419989a75
--- /dev/null
+++ b/src/utilcode/clrhost.cpp
@@ -0,0 +1,460 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+
+#include "stdafx.h"
+
+#include "unsafe.h"
+#include "clrhost.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "hostimpl.h"
+#include "clrnt.h"
+#include "contract.h"
+#include "tls.h"
+
+#if defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE) || defined(CROSSGEN_COMPILE)
+CoreClrCallbacks g_CoreClrCallbacks;
+#endif
+
+
+// In some cirumstance (e.g, the thread suspecd another thread), allocation on heap
+// could cause dead lock. We use a counter in TLS to indicate the current thread is not allowed
+// to do heap allocation.
+//In cases where CLRTlsInfo doesn't exist and we still want to track CantAlloc info (this is important to
+//stress log), We use a global counter. This introduces the problem where one thread could disable allocation
+//for another thread, but the cases should be rare (we limit the use to stress log for now) and the period
+//should (MUST) be short
+//only stress log check this counter
+
+struct CantAllocThread
+{
+ PVOID m_fiberId;
+ LONG m_CantCount;
+};
+
+#define MaxCantAllocThreadNum 100
+static CantAllocThread g_CantAllocThreads[MaxCantAllocThreadNum] = {};
+static Volatile<LONG> g_CantAllocStressLogCount = 0;
+
+void IncCantAllocCount()
+{
+ size_t count = 0;
+ if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
+ {
+ _ASSERTE (count >= 0);
+ ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count+1));
+ return;
+ }
+ PVOID fiberId = ClrTeb::GetFiberPtrId();
+ for (int i = 0; i < MaxCantAllocThreadNum; i ++)
+ {
+ if (g_CantAllocThreads[i].m_fiberId == fiberId)
+ {
+ g_CantAllocThreads[i].m_CantCount ++;
+ return;
+ }
+ }
+ for (int i = 0; i < MaxCantAllocThreadNum; i ++)
+ {
+ if (g_CantAllocThreads[i].m_fiberId == NULL)
+ {
+ if (InterlockedCompareExchangeT(&g_CantAllocThreads[i].m_fiberId, fiberId, NULL) == NULL)
+ {
+ _ASSERTE(g_CantAllocThreads[i].m_CantCount == 0);
+ g_CantAllocThreads[i].m_CantCount = 1;
+ return;
+ }
+ }
+ }
+ count = InterlockedIncrement (&g_CantAllocStressLogCount);
+ _ASSERTE (count >= 1);
+ return;
+}
+
+void DecCantAllocCount()
+{
+ size_t count = 0;
+ if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
+ {
+ if (count > 0)
+ {
+ ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count-1));
+ return;
+ }
+ }
+ PVOID fiberId = ClrTeb::GetFiberPtrId();
+ for (int i = 0; i < MaxCantAllocThreadNum; i ++)
+ {
+ if (g_CantAllocThreads[i].m_fiberId == fiberId)
+ {
+ _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0);
+ g_CantAllocThreads[i].m_CantCount --;
+ if (g_CantAllocThreads[i].m_CantCount == 0)
+ {
+ g_CantAllocThreads[i].m_fiberId = NULL;
+ }
+ return;
+ }
+ }
+ _ASSERTE (g_CantAllocStressLogCount > 0);
+ InterlockedDecrement (&g_CantAllocStressLogCount);
+ return;
+
+}
+
+// for stress log the rule is more restrict, we have to check the global counter too
+BOOL IsInCantAllocStressLogRegion()
+{
+ size_t count = 0;
+ if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count))
+ {
+ if (count > 0)
+ {
+ return true;
+ }
+ }
+ PVOID fiberId = ClrTeb::GetFiberPtrId();
+ for (int i = 0; i < MaxCantAllocThreadNum; i ++)
+ {
+ if (g_CantAllocThreads[i].m_fiberId == fiberId)
+ {
+ _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0);
+ return true;
+ }
+ }
+
+ return g_CantAllocStressLogCount > 0;
+
+}
+
+
+#ifdef FAILPOINTS_ENABLED
+typedef int (*FHashStack) ();
+
+static FHashStack fHashStack = 0;
+static _TEB *HashStackSetupThread = NULL;
+static _TEB *RFSCustomDataSetupThread = NULL;
+
+static void SetupHashStack ()
+{
+ CANNOT_HAVE_CONTRACT;
+
+ FHashStack oldValue = InterlockedCompareExchangeT(&fHashStack,
+ reinterpret_cast<FHashStack>(1), reinterpret_cast<FHashStack>(0));
+ if ((size_t) oldValue >= 2) {
+ return;
+ }
+ else if ((size_t) oldValue == 0) {
+ // We are the first thread to initialize
+ HashStackSetupThread = NtCurrentTeb();
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HashStack) == 0) {
+ fHashStack = (FHashStack) 2;
+ return;
+ }
+
+ PAL_TRY(void *, unused, NULL) {
+ FHashStack func;
+ HMODULE hmod = LoadLibraryExA ("mscorrfs.dll", NULL, 0);
+ if (hmod) {
+ func = (FHashStack)GetProcAddress (hmod, "HashStack");
+ if (func == 0) {
+ func = (FHashStack)2;
+ }
+ }
+ else
+ func = (FHashStack)2;
+ fHashStack = func;
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fHashStack = (FHashStack) 2;
+ }
+ PAL_ENDTRY;
+ }
+ else if (NtCurrentTeb() == HashStackSetupThread) {
+ // We get here while initializing
+ return;
+ }
+ else {
+ // All other threads will wait
+ while (fHashStack == (FHashStack) 1) {
+ ClrSleepEx (100, FALSE);
+ }
+ }
+}
+
+int RFS_HashStack ()
+{
+ CANNOT_HAVE_CONTRACT;
+
+ if ((size_t)fHashStack < 2) {
+ SetupHashStack ();
+ }
+
+ if ((size_t)fHashStack <= 2) {
+ return 0;
+ }
+ else
+ return fHashStack ();
+}
+
+#endif // FAILPOINTS_ENABLED
+
+
+#if defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)
+
+//-----------------------------------------------------------------------------------
+// This is the approved way to get a module handle to mscorwks.dll (or coreclr.dll).
+// Never call GetModuleHandle(mscorwks) yourself as this will break side-by-side inproc.
+//
+// This function is safe to call before or during CRT initialization. It can not
+// legally return NULL (it only does so in the case of a broken build invariant.)
+//
+// TODO puCLR SxS utilcode work: Since this is never supposed to return NULL, it should
+// not be present in SELF_NO_HOST builds of utilcode where there isn't necessarily a
+// CLR in the process. We should also ASSERT that GetModuleHandleA isn't returning
+// NULL below - we've probably been getting away with this in SELF_NO_HOST cases like
+// mscordbi.dll.
+//-----------------------------------------------------------------------------------
+HMODULE GetCLRModule ()
+{
+ //! WARNING: At the time this function is invoked, the C Runtime has NOT been fully initialized, let alone the CLR.
+ //! So don't put in a runtime contract and don't invoke other functions in the CLR (not even _ASSERTE!)
+
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_SO_TOLERANT;
+ STATIC_CONTRACT_SUPPORTS_DAC; // DAC can call in here since we initialize the SxS callbacks in ClrDataAccess::Initialize.
+
+#ifdef DACCESS_COMPILE
+ // For DAC, "g_CoreClrCallbacks" is populated in InitUtilCode when the latter is invoked
+ // from ClrDataAccess::Initialize alongwith a reference to a structure allocated in the
+ // host-process address space.
+ //
+ // This function will be invoked in the host when DAC uses SEHException::GetHr that calls into
+ // IsComplusException, which calls into WasThrownByUs that calls GetCLRModule when EH SxS is enabled.
+ // However, this function can also be executed within the target space as well.
+ //
+ // Since DACCop gives the warning to DACize this global that, actually, would be used only
+ // in the respective address spaces and does not require marshalling, we need to ignore this
+ // warning.
+ DACCOP_IGNORE(UndacizedGlobalVariable, "g_CoreClrCallbacks has the dual mode DAC issue.");
+#endif // DACCESS_COMPILE
+ VALIDATECORECLRCALLBACKS();
+
+ // This is the normal coreclr case - we return the module handle that was captured in our DllMain.
+#ifdef DACCESS_COMPILE
+ // For DAC, "g_CoreClrCallbacks" is populated in InitUtilCode when the latter is invoked
+ // from ClrDataAccess::Initialize alongwith a reference to a structure allocated in the
+ // host-process address space.
+ //
+ // This function will be invoked in the host when DAC uses SEHException::GetHr that calls into
+ // IsComplusException, which calls into WasThrownByUs that calls GetCLRModule when EH SxS is enabled.
+ // However, this function can also be executed within the target space as well.
+ //
+ // Since DACCop gives the warning to DACize this global that, actually, would be used only
+ // in the respective address spaces and does not require marshalling, we need to ignore this
+ // warning.
+ DACCOP_IGNORE(UndacizedGlobalVariable, "g_CoreClrCallbacks has the dual mode DAC issue.");
+#endif // DACCESS_COMPILE
+ return g_CoreClrCallbacks.m_hmodCoreCLR;
+}
+
+#endif // defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)
+
+
+#if defined(SELF_NO_HOST)
+
+HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName)
+{
+ WRAPPER_NO_CONTRACT;
+ return CLRLoadLibraryEx(lpLibFileName, NULL, 0);
+}
+
+HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
+{
+ WRAPPER_NO_CONTRACT;
+ return WszLoadLibraryEx(lpLibFileName, hFile, dwFlags);
+}
+
+BOOL CLRFreeLibrary(HMODULE hModule)
+{
+ WRAPPER_NO_CONTRACT;
+ return FreeLibrary(hModule);
+}
+
+#endif // defined(SELF_NO_HOST)
+
+
+#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+//-----------------------------------------------------------------------------------------------
+// Imposes a new typeload level limit for the scope of the holder. Any attempt to load a type
+// past that limit generates a contract violation assert.
+//
+// Do not invoke this directly. Invoke it through TRIGGERS_TYPE_LOAD or OVERRIDE_TYPE_LOAD_LEVEL_LIMIT.
+//
+// Arguments:
+// fConditional - if FALSE, this holder is a nop - supports the MAYBE_* macros.
+// newLevel - a value from classloadlevel.h - specifies the new max limit.
+// fEnforceLevelChangeDirection
+// - if true, implements TRIGGERS_TYPE_LOAD (level cap only allowed to decrease.)
+// if false, implements OVERRIDE (level allowed to increase - may only be used
+// by loader and only when recursion is structurally
+// impossible.)
+// szFunction,
+// szFile,
+// lineNum - records location of holder so we can print it in assertion boxes
+//
+// Assumptions:
+// ClrDebugState must have been set up (executing any contract will do this.)
+// Thread need *not* have a Thread* structure set up.
+//
+// Notes:
+// The holder withholds the assert if a LoadsTypeViolation suppress is in effect (but
+// still sets up the new limit.)
+//
+// As with other contract annoations, however, the violation suppression is *lifted*
+// within the scope guarded by the holder itself.
+//-----------------------------------------------------------------------------------------------
+LoadsTypeHolder::LoadsTypeHolder(BOOL fConditional,
+ UINT newLevel,
+ BOOL fEnforceLevelChangeDirection,
+ const char *szFunction,
+ const char *szFile,
+ int lineNum
+ )
+{
+ // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ m_fConditional = fConditional;
+ if (m_fConditional)
+ {
+ m_pClrDebugState = CheckClrDebugState();
+ _ASSERTE(m_pClrDebugState);
+
+ m_oldClrDebugState = *m_pClrDebugState;
+
+ if (fEnforceLevelChangeDirection)
+ {
+ if (newLevel > m_pClrDebugState->GetMaxLoadTypeLevel())
+ {
+ if (!( (LoadsTypeViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
+ {
+ CONTRACT_ASSERT("Illegal attempt to load a type beyond the current level limit.",
+ (m_pClrDebugState->GetMaxLoadTypeLevel() + 1) << Contract::LOADS_TYPE_Shift,
+ Contract::LOADS_TYPE_Mask,
+ szFunction,
+ szFile,
+ lineNum
+ );
+ }
+ }
+ }
+
+ m_pClrDebugState->ViolationMaskReset(LoadsTypeViolation);
+ m_pClrDebugState->SetMaxLoadTypeLevel(newLevel);
+
+ m_contractStackRecord.m_szFunction = szFunction;
+ m_contractStackRecord.m_szFile = szFile;
+ m_contractStackRecord.m_lineNum = lineNum;
+ m_contractStackRecord.m_testmask = (Contract::ALL_Disabled & ~((UINT)(Contract::LOADS_TYPE_Mask))) | (((newLevel) + 1) << Contract::LOADS_TYPE_Shift);
+ m_contractStackRecord.m_construct = fEnforceLevelChangeDirection ? "TRIGGERS_TYPE_LOAD" : "OVERRIDE_TYPE_LOAD_LEVEL_LIMIT";
+ m_contractStackRecord.m_pNext = m_pClrDebugState->GetContractStackTrace();
+ m_pClrDebugState->SetContractStackTrace(&m_contractStackRecord);
+
+
+ }
+} // LoadsTypeHolder::LoadsTypeHolder
+
+//-----------------------------------------------------------------------------------------------
+// Restores prior typeload level limit.
+//-----------------------------------------------------------------------------------------------
+LoadsTypeHolder::~LoadsTypeHolder()
+{
+ // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ if (m_fConditional)
+ {
+ *m_pClrDebugState = m_oldClrDebugState;
+ }
+}
+
+#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+
+//--------------------------------------------------------------------------
+// Side by side inproc support
+//
+// These are new abstractions designed to support loading multiple CLR
+// versions in the same process.
+//--------------------------------------------------------------------------
+
+#if defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)
+
+//--------------------------------------------------------------------------
+// One-time initialized called by coreclr.dll in its dllmain.
+//--------------------------------------------------------------------------
+VOID InitUtilcode(CoreClrCallbacks const & cccallbacks)
+{
+ //! WARNING: At the time this function is invoked, the C Runtime has NOT been fully initialized, let alone the CLR.
+ //! So don't put in a runtime contract and don't invoke other functions in the CLR (not even _ASSERTE!)
+
+ LIMITED_METHOD_CONTRACT;
+
+ g_CoreClrCallbacks = cccallbacks;
+}
+
+CoreClrCallbacks const & GetClrCallbacks()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ VALIDATECORECLRCALLBACKS();
+ return g_CoreClrCallbacks;
+}
+
+#ifdef _DEBUG
+void OnUninitializedCoreClrCallbacks()
+{
+ // Supports DAC since it can be called from GetCLRModule which supports DAC as well.
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ // If you got here, the most likely cause of the failure is that you're loading some DLL
+ // (other than coreclr.dll) that links to utilcode.lib, or that you're using a nohost
+ // variant of utilcode.lib but hitting code that assumes there is a CLR in the process.
+ //
+ // Under FEATURE_CORECLR (and not SELF_NO_HOST), it is expected that coreclr.dll
+ // is the ONLY dll that links to utilcode libraries.
+ //
+ // If you must introduce a new dll that links to utilcode.lib, it is your responsibility
+ // to ensure that that dll invoke InitUtilcode() and forward it the right data from the *correct*
+ // loaded instance of coreclr. And you'll have to do without the CRT being initialized.
+ //
+ // Can't use an _ASSERTE here because even that's broken if we get to this point.
+ MessageBoxW(0,
+ W("g_CoreClrCallbacks not initialized."),
+ W("\n\n")
+ W("You got here because the dll that included this copy of utilcode.lib ")
+ W("did not call InitUtilcode() The most likely cause is that you're running ")
+ W("a dll (other than coreclr.dll) that links to utilcode.lib.")
+ ,
+ 0);
+ _ASSERTE(FALSE);
+ DebugBreak();
+}
+#endif // _DEBUG
+
+#endif // defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)
diff --git a/src/utilcode/clrhost_nodependencies.cpp b/src/utilcode/clrhost_nodependencies.cpp
new file mode 100644
index 0000000000..f5e4133cee
--- /dev/null
+++ b/src/utilcode/clrhost_nodependencies.cpp
@@ -0,0 +1,958 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+
+#include "stdafx.h"
+
+#include "unsafe.h"
+#include "clrhost.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "hostimpl.h"
+#include "clrnt.h"
+#include "contract.h"
+#include "tls.h"
+
+#ifdef _DEBUG_IMPL
+
+//
+// I'd very much like for this to go away. Its used to disable all THROWS contracts within whatever DLL this
+// function is called from. That's obviously very, very bad, since there's no validation of those macros. But it
+// can be difficult to remove this without actually fixing every violation at the same time.
+//
+// When this flag is finally removed, remove RealCLRThrowsExceptionWorker() too and put CONTRACT_THROWS() in place
+// of it.
+//
+//
+static BOOL dbg_fDisableThrowCheck = FALSE;
+
+void DisableThrowCheck()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ dbg_fDisableThrowCheck = TRUE;
+}
+
+#define CLRThrowsExceptionWorker() RealCLRThrowsExceptionWorker(__FUNCTION__, __FILE__, __LINE__)
+
+static void RealCLRThrowsExceptionWorker(__in_z const char *szFunction,
+ __in_z const char *szFile,
+ int lineNum)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (dbg_fDisableThrowCheck)
+ {
+ return;
+ }
+
+ CONTRACT_THROWSEX(szFunction, szFile, lineNum);
+}
+
+#endif //_DEBUG_IMPL
+
+#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+// Fls callback to deallocate ClrDebugState when our FLS block goes away.
+void FreeClrDebugState(LPVOID pTlsData)
+{
+#ifdef _DEBUG
+ ClrDebugState *pClrDebugState = (ClrDebugState*)pTlsData;
+
+ // Make sure the ClrDebugState was initialized by a compatible version of
+ // utilcode.lib. If it was initialized by an older version, we just let it leak.
+ if (pClrDebugState && (pClrDebugState->ViolationMask() & CanFreeMe) && !(pClrDebugState->ViolationMask() & BadDebugState))
+ {
+#undef HeapFree
+#undef GetProcessHeap
+
+ // Since "!(pClrDebugState->m_violationmask & BadDebugState)", we know we have
+ // a valid m_pLockData
+ _ASSERTE(pClrDebugState->GetDbgStateLockData() != NULL);
+ ::HeapFree (GetProcessHeap(), 0, pClrDebugState->GetDbgStateLockData());
+
+ ::HeapFree (GetProcessHeap(), 0, pClrDebugState);
+#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem)
+#define GetProcessHeap() Dont_Use_GetProcessHeap()
+ }
+#endif //_DEBUG
+}
+
+// This is a drastic shutoff toggle that forces all new threads to fail their CLRInitDebugState calls.
+// We only invoke this if FLS can't allocate its master block, preventing us from tracking the shutoff
+// on a per-thread basis.
+BYTE* GetGlobalContractShutoffFlag()
+{
+#ifdef SELF_NO_HOST
+
+ static BYTE gGlobalContractShutoffFlag = 0;
+ return &gGlobalContractShutoffFlag;
+#else //!SELF_NO_HOST
+ HINSTANCE hmod = GetCLRModule();
+ if (!hmod)
+ {
+ return NULL;
+ }
+ typedef BYTE*(__stdcall * PGETSHUTOFFADDRFUNC)();
+ PGETSHUTOFFADDRFUNC pGetContractShutoffFlagFunc = (PGETSHUTOFFADDRFUNC)GetProcAddress(hmod, "GetAddrOfContractShutoffFlag");
+ if (!pGetContractShutoffFlagFunc)
+ {
+ return NULL;
+ }
+ return pGetContractShutoffFlagFunc();
+#endif //!SELF_NO_HOST
+}
+
+static BOOL AreContractsShutoff()
+{
+ BYTE *pShutoff = GetGlobalContractShutoffFlag();
+ if (!pShutoff)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return 0 != *pShutoff;
+ }
+}
+
+static VOID ShutoffContracts()
+{
+ BYTE *pShutoff = GetGlobalContractShutoffFlag();
+ if (pShutoff)
+ {
+ *pShutoff = 1;
+ }
+}
+
+//=============================================================================================
+// Used to initialize the per-thread ClrDebugState. This is called once per thread (with
+// possible exceptions for OOM scenarios.)
+//
+// No matter what, this function will not return NULL. If it can't do its job because of OOM reasons,
+// it will return a pointer to &gBadClrDebugState which effectively disables contracts for
+// this thread.
+//=============================================================================================
+ClrDebugState *CLRInitDebugState()
+{
+ // workaround!
+ //
+ // The existing Fls apis didn't provide the support we need and adding support cleanly is
+ // messy because of the brittleness of IExecutionEngine.
+ //
+ // To understand this function, you need to know that the Fls routines have special semantics
+ // for the TlsIdx_ClrDebugState slot:
+ //
+ // - FlsSetValue will never throw. If it fails due to OOM on creation of the slot storage,
+ // it will silently bail. Thus, we must do a confirming FlsGetValue before we can conclude
+ // that the SetValue succeeded.
+ //
+ // - FlsAssociateCallback will not complain about multiple sets of the callback.
+ //
+ // - The mscorwks implemention of FlsAssociateCallback will ignore the passed in value
+ // and use the version of FreeClrDebugState compiled into mscorwks. This is needed to
+ // avoid dangling pointer races on shutdown.
+
+
+ // This is our global "bad" debug state that thread use when they OOM on CLRInitDebugState.
+ // We really only need to initialize it once but initializing each time is convenient
+ // and has low perf impact.
+ static ClrDebugState gBadClrDebugState;
+ gBadClrDebugState.ViolationMaskSet( AllViolation );
+ // SO_INFRASTRUCTURE_CODE() Macro to remove SO infrastructure code during build
+ SO_INFRASTRUCTURE_CODE(gBadClrDebugState.BeginSOTolerant();)
+ gBadClrDebugState.SetOkToThrow();
+
+ ClrDebugState *pNewClrDebugState = NULL;
+ ClrDebugState *pClrDebugState = NULL;
+ DbgStateLockData *pNewLockData = NULL;
+
+ // We call this first partly to force a CheckThreadState. We've hopefully chased out all the
+ // recursive contract calls inside here but if we haven't, it's best to get them out of the way
+ // early.
+ ClrFlsAssociateCallback(TlsIdx_ClrDebugState, FreeClrDebugState);
+
+
+ if (AreContractsShutoff())
+ {
+ pNewClrDebugState = NULL;
+ }
+ else
+ {
+ // Yuck. We cannot call the hosted allocator for ClrDebugState (it is impossible to maintain a guarantee
+ // that none of code paths, many of them called conditionally, don't themselves trigger a ClrDebugState creation.)
+ // We have to call the OS directly for this.
+#undef HeapAlloc
+#undef GetProcessHeap
+ pNewClrDebugState = (ClrDebugState*)::HeapAlloc(GetProcessHeap(), 0, sizeof(ClrDebugState));
+ if (pNewClrDebugState != NULL)
+ {
+ // Only allocate a DbgStateLockData if its owning ClrDebugState was successfully alloctaed
+ pNewLockData = (DbgStateLockData *)::HeapAlloc(GetProcessHeap(), 0, sizeof(DbgStateLockData));
+ }
+#define GetProcessHeap() Dont_Use_GetProcessHeap()
+#define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes)
+
+ if ((pNewClrDebugState != NULL) && (pNewLockData != NULL))
+ {
+ // Both allocations succeeded, so initialize the structures, and have
+ // pNewClrDebugState point to pNewLockData. If either of the allocations
+ // failed, we'll use gBadClrDebugState for this thread, and free whichever of
+ // pNewClrDebugState or pNewLockData actually did get allocated (if either did).
+ // (See code in this function below, outside this block.)
+
+ pNewClrDebugState->SetStartingValues();
+ pNewClrDebugState->ViolationMaskSet( CanFreeMe );
+ _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState));
+
+ pNewLockData->SetStartingValues();
+ pNewClrDebugState->SetDbgStateLockData(pNewLockData);
+ }
+ }
+
+
+ // This is getting really diseased. All the one-time host init stuff inside the ClrFlsStuff could actually
+ // have caused mscorwks contracts to be executed since the last time we actually checked to see if the ClrDebugState
+ // needed creating.
+ //
+ // So we must make one last check to see if the ClrDebugState still needs creating.
+ //
+ ClrDebugState *pTmp = (ClrDebugState*)(ClrFlsGetValue(TlsIdx_ClrDebugState));
+ if (pTmp != NULL)
+ {
+ // Recursive call set up ClrDebugState for us
+ pClrDebugState = pTmp;
+ }
+ else if ((pNewClrDebugState != NULL) && (pNewLockData != NULL))
+ {
+ // Normal case: our new ClrDebugState will be the one we just allocated.
+ // Note that we require BOTH the ClrDebugState and the DbgStateLockData
+ // structures to have been successfully allocated for contracts to be
+ // enabled for this thread.
+ _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState));
+ _ASSERTE(pNewClrDebugState->GetDbgStateLockData() == pNewLockData);
+ pClrDebugState = pNewClrDebugState;
+ }
+ else
+ {
+ // OOM case: HeapAlloc of newClrDebugState failed.
+ pClrDebugState = &gBadClrDebugState;
+ }
+
+ _ASSERTE(pClrDebugState != NULL);
+
+
+ ClrFlsSetValue(TlsIdx_ClrDebugState, (LPVOID)pClrDebugState);
+
+ // For the ClrDebugState index, ClrFlsSetValue does *not* throw on OOM.
+ // Instead, it silently throws away the value. So we must now do a confirming
+ // FlsGet to learn if our Set succeeded.
+ if (ClrFlsGetValue(TlsIdx_ClrDebugState) == NULL)
+ {
+ // Our FlsSet didn't work. That means it couldn't allocate the master FLS block for our thread.
+ // Now we're a bad state because not only can't we succeed, we can't record that we didn't succeed.
+ // And it's invalid to return a BadClrDebugState here only to return a good debug state later.
+ //
+ // So we now take the drastic step of forcing all future ClrInitDebugState calls to return the OOM state.
+ ShutoffContracts();
+ pClrDebugState = &gBadClrDebugState;
+
+ // Try once more time to set the FLS (if it doesn't work, the next call will keep cycling through here
+ // until it does succeed.)
+ ClrFlsSetValue(TlsIdx_ClrDebugState, &gBadClrDebugState);
+ }
+
+
+#if defined(_DEBUG)
+ // The ClrDebugState we allocated above made it into FLS iff
+ // the DbgStateLockData we allocated above made it into
+ // the FLS's ClrDebugState::m_pLockData
+ // These debug-only checks enforce this invariant
+
+ if (pClrDebugState != NULL)
+ {
+ // If we're here, then typically pClrDebugState is what's in FLS. However,
+ // it's possible that pClrDebugState is gBadClrDebugState, and FLS is NULL
+ // (if the last ClrFlsSetValue() failed). Either way, our checks below
+ // are valid ones to make.
+
+ if (pClrDebugState == pNewClrDebugState)
+ {
+ // ClrDebugState we allocated above made it into FLS, so DbgStateLockData
+ // must be there, too
+ _ASSERTE(pNewLockData != NULL);
+ _ASSERTE(pClrDebugState->GetDbgStateLockData() == pNewLockData);
+ }
+ else
+ {
+ // ClrDebugState we allocated above did NOT make it into FLS,
+ // so the DbgStateLockData we allocated must not be there, either
+ _ASSERTE(pClrDebugState->GetDbgStateLockData() == NULL || pClrDebugState->GetDbgStateLockData() != pNewLockData);
+ }
+ }
+
+ // One more invariant: Because of ordering & conditions around the HeapAllocs above,
+ // we'll never have a DbgStateLockData without a ClrDebugState
+ _ASSERTE((pNewLockData == NULL) || (pNewClrDebugState != NULL));
+
+#endif //_DEBUG
+
+#undef HeapFree
+#undef GetProcessHeap
+ if (pNewClrDebugState != NULL && pClrDebugState != pNewClrDebugState)
+ {
+ // We allocated a ClrDebugState which didn't make it into FLS, so free it.
+ ::HeapFree (GetProcessHeap(), 0, pNewClrDebugState);
+ if (pNewLockData != NULL)
+ {
+ // We also allocated a DbgStateLockData that didn't make it into FLS, so
+ // free it, too. (Remember, we asserted above that we can only have
+ // this unused DbgStateLockData if we had an unused ClrDebugState
+ // as well (which we just freed).)
+ ::HeapFree (GetProcessHeap(), 0, pNewLockData);
+ }
+ }
+#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem)
+#define GetProcessHeap() Dont_Use_GetProcessHeap()
+
+ // Not necessary as TLS slots are born NULL and potentially problematic for OOM cases as we can't
+ // take an exception here.
+ //ClrFlsSetValue(TlsIdx_OwnedCrstsChain, NULL);
+
+ return pClrDebugState;
+} // CLRInitDebugState
+
+#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+
+LPVOID ClrAllocInProcessHeapBootstrap (DWORD dwFlags, SIZE_T dwBytes)
+{
+ STATIC_CONTRACT_SO_INTOLERANT;
+
+#if defined(SELF_NO_HOST)
+ static HANDLE hHeap = NULL;
+
+ // This could race, but the result would be that this
+ // variable gets double initialized.
+ if (hHeap == NULL)
+ hHeap = ClrGetProcessHeap();
+
+ return ClrHeapAlloc(hHeap, dwFlags, S_SIZE_T(dwBytes));
+#else //!defined(SELF_NO_HOST)
+ FastAllocInProcessHeapFunc pfnHeapAlloc = (FastAllocInProcessHeapFunc)
+ GetClrCallbacks().m_pfnGetCLRFunction("EEHeapAllocInProcessHeap");
+ if (pfnHeapAlloc != NULL)
+ {
+ __ClrAllocInProcessHeap = pfnHeapAlloc;
+ return pfnHeapAlloc(dwFlags, dwBytes);
+ }
+ return ClrHeapAlloc(ClrGetProcessHeap(), dwFlags, S_SIZE_T(dwBytes));
+#endif // !defined(SELF_NO_HOST)
+}
+FastAllocInProcessHeapFunc __ClrAllocInProcessHeap = (FastAllocInProcessHeapFunc) ClrAllocInProcessHeapBootstrap;
+
+BOOL ClrFreeInProcessHeapBootstrap (DWORD dwFlags, LPVOID lpMem)
+{
+ STATIC_CONTRACT_SO_INTOLERANT;
+
+#if defined(SELF_NO_HOST)
+ static HANDLE hHeap = NULL;
+
+ // This could race, but the result would be that this
+ // variable gets double initialized.
+ if (hHeap == NULL)
+ hHeap = ClrGetProcessHeap();
+
+ return ClrHeapFree(hHeap, dwFlags,lpMem);
+#else //!defined(SELF_NO_HOST)
+ FastFreeInProcessHeapFunc pfnHeapFree = (FastFreeInProcessHeapFunc)
+ GetClrCallbacks().m_pfnGetCLRFunction("EEHeapFreeInProcessHeap");
+ if (pfnHeapFree)
+ {
+ __ClrFreeInProcessHeap = pfnHeapFree;
+ return (*pfnHeapFree)(dwFlags,lpMem);
+ }
+ return ClrHeapFree(ClrGetProcessHeap(),dwFlags,lpMem);
+#endif //!defined(SELF_NO_HOST)
+}
+FastFreeInProcessHeapFunc __ClrFreeInProcessHeap = (FastFreeInProcessHeapFunc) ClrFreeInProcessHeapBootstrap;
+
+const NoThrow nothrow = { 0 };
+
+void * __cdecl
+operator new(size_t n)
+{
+#ifdef _DEBUG_IMPL
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it.
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n));
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+}
+
+
+void * __cdecl
+operator new[](size_t n)
+{
+#ifdef _DEBUG_IMPL
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it.
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n));
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+};
+
+void * __cdecl operator new(size_t n, const NoThrow&)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it.
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n));
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const NoThrow&)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it.
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n));
+ TRASH_LASTERROR;
+ return result;
+}
+
+void __cdecl
+operator delete(void *p)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant.
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ if (p != NULL)
+ ClrFreeInProcessHeap(0, p);
+ TRASH_LASTERROR;
+}
+
+void __cdecl
+operator delete[](void *p)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant.
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ if (p != NULL)
+ ClrFreeInProcessHeap(0, p);
+ TRASH_LASTERROR;
+}
+
+/* ------------------------------------------------------------------------ *
+ * New operator overloading for the executable heap
+ * ------------------------------------------------------------------------ */
+
+#ifndef FEATURE_PAL
+
+const CExecutable executable = { 0 };
+
+void * __cdecl operator new(size_t n, const CExecutable&)
+{
+#if defined(_DEBUG_IMPL)
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant.
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL) {
+ ThrowOutOfMemory();
+ }
+
+ void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n));
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const CExecutable&)
+{
+#if defined(_DEBUG_IMPL)
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant.
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL) {
+ ThrowOutOfMemory();
+ }
+
+ void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n));
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new(size_t n, const CExecutable&, const NoThrow&)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant.
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL)
+ return NULL;
+
+ void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n));
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const CExecutable&, const NoThrow&)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant.
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL)
+ return NULL;
+
+ void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n));
+ TRASH_LASTERROR;
+ return result;
+}
+
+#endif // FEATURE_PAL
+
+#ifdef _DEBUG
+
+// This is a DEBUG routing to verify that a memory region complies with executable requirements
+BOOL DbgIsExecutable(LPVOID lpMem, SIZE_T length)
+{
+#if defined(CROSSGEN_COMPILE)
+ // No NX support on PAL or for crossgen compilations.
+ return TRUE;
+#else // !defined(CROSSGEN_COMPILE)
+ BYTE *regionStart = (BYTE*) ALIGN_DOWN((BYTE*)lpMem, OS_PAGE_SIZE);
+ BYTE *regionEnd = (BYTE*) ALIGN_UP((BYTE*)lpMem+length, OS_PAGE_SIZE);
+ _ASSERTE(length > 0);
+ _ASSERTE(regionStart < regionEnd);
+
+ while(regionStart < regionEnd)
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+
+ SIZE_T cbBytes = ClrVirtualQuery(regionStart, &mbi, sizeof(mbi));
+ _ASSERTE(cbBytes);
+
+ // The pages must have EXECUTE set
+ if(!(mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)))
+ return FALSE;
+
+ _ASSERTE((BYTE*)mbi.BaseAddress + mbi.RegionSize > regionStart);
+ regionStart = (BYTE*)mbi.BaseAddress + mbi.RegionSize;
+ }
+
+ return TRUE;
+#endif // defined(CROSSGEN_COMPILE)
+}
+
+#endif //_DEBUG
+
+
+
+
+// Access various ExecutionEngine support services, like a logical TLS that abstracts
+// fiber vs. thread issues. We obtain it from a DLL export via the shim.
+
+typedef IExecutionEngine * (__stdcall * IEE_FPTR) ();
+
+//
+// Access various ExecutionEngine support services, like a logical TLS that abstracts
+// fiber vs. thread issues.
+// From an IExecutionEngine is possible to get other services via QueryInterfaces such
+// as memory management
+//
+IExecutionEngine *g_pExecutionEngine = NULL;
+
+#ifdef SELF_NO_HOST
+BYTE g_ExecutionEngineInstance[sizeof(UtilExecutionEngine)];
+#endif
+
+
+IExecutionEngine *GetExecutionEngine()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_SO_TOLERANT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ if (g_pExecutionEngine == NULL)
+ {
+ IExecutionEngine* pExecutionEngine;
+#ifdef SELF_NO_HOST
+ // Create a local copy on the stack and then copy it over to the static instance.
+ // This avoids race conditions caused by multiple initializations of vtable in the constructor
+ UtilExecutionEngine local;
+ memcpy(&g_ExecutionEngineInstance, &local, sizeof(UtilExecutionEngine));
+ pExecutionEngine = (IExecutionEngine*)(UtilExecutionEngine*)&g_ExecutionEngineInstance;
+#else
+ // statically linked.
+ VALIDATECORECLRCALLBACKS();
+ pExecutionEngine = g_CoreClrCallbacks.m_pfnIEE();
+#endif // SELF_NO_HOST
+
+ //We use an explicit memory barrier here so that the reference g_pExecutionEngine is valid when
+ //it is used, This ia a requirement on platforms with weak memory model . We cannot use VolatileStore
+ //because they are the same as normal assignment for DAC builds [see code:VOLATILE]
+
+ MemoryBarrier();
+ g_pExecutionEngine = pExecutionEngine;
+ }
+
+ // It's a bug to ask for the ExecutionEngine interface in scenarios where the
+ // ExecutionEngine cannot be loaded.
+ _ASSERTE(g_pExecutionEngine);
+ return g_pExecutionEngine;
+} // GetExecutionEngine
+
+IEEMemoryManager * GetEEMemoryManager()
+{
+ STATIC_CONTRACT_SO_TOLERANT;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ static IEEMemoryManager *pEEMemoryManager = NULL;
+ if (NULL == pEEMemoryManager) {
+ IExecutionEngine *pExecutionEngine = GetExecutionEngine();
+ _ASSERTE(pExecutionEngine);
+
+ // It is dangerous to pass a global pointer to QueryInterface. The pointer may be set
+ // to NULL in the call. Imagine that thread 1 calls QI, and get a pointer. But before thread 1
+ // returns the pointer to caller, thread 2 calls QI and the pointer is set to NULL.
+ IEEMemoryManager *pEEMM;
+ pExecutionEngine->QueryInterface(IID_IEEMemoryManager, (void**)&pEEMM);
+ pEEMemoryManager = pEEMM;
+ }
+ // It's a bug to ask for the MemoryManager interface in scenarios where it cannot be loaded.
+ _ASSERTE(pEEMemoryManager);
+ return pEEMemoryManager;
+}
+
+// should return some error code or exception
+void SetExecutionEngine(IExecutionEngine *pExecutionEngine)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ _ASSERTE(pExecutionEngine && !g_pExecutionEngine);
+ if (!g_pExecutionEngine) {
+ g_pExecutionEngine = pExecutionEngine;
+ g_pExecutionEngine->AddRef();
+ }
+}
+
+void ClrFlsAssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GetExecutionEngine()->TLS_AssociateCallback(slot, callback);
+}
+
+void * __stdcall ClrFlsGetBlockGeneric()
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ return GetExecutionEngine()->TLS_GetDataBlock();
+}
+
+POPTIMIZEDTLSGETTER __ClrFlsGetBlock = (POPTIMIZEDTLSGETTER)ClrFlsGetBlockGeneric;
+
+CRITSEC_COOKIE ClrCreateCriticalSection(CrstType crstType, CrstFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->CreateLock(NULL, (LPCSTR)crstType, flags);
+}
+
+HRESULT ClrDeleteCriticalSection(CRITSEC_COOKIE cookie)
+{
+ WRAPPER_NO_CONTRACT;
+ GetExecutionEngine()->DestroyLock(cookie);
+ return S_OK;
+}
+
+void ClrEnterCriticalSection(CRITSEC_COOKIE cookie)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->AcquireLock(cookie);
+}
+
+void ClrLeaveCriticalSection(CRITSEC_COOKIE cookie)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ReleaseLock(cookie);
+}
+
+EVENT_COOKIE ClrCreateAutoEvent(BOOL bInitialState)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->CreateAutoEvent(bInitialState);
+}
+
+EVENT_COOKIE ClrCreateManualEvent(BOOL bInitialState)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->CreateManualEvent(bInitialState);
+}
+
+void ClrCloseEvent(EVENT_COOKIE event)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GetExecutionEngine()->CloseEvent(event);
+}
+
+BOOL ClrSetEvent(EVENT_COOKIE event)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrSetEvent(event);
+}
+
+BOOL ClrResetEvent(EVENT_COOKIE event)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrResetEvent(event);
+}
+
+DWORD ClrWaitEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->WaitForEvent(event, dwMilliseconds, bAlertable);
+}
+
+SEMAPHORE_COOKIE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrCreateSemaphore(dwInitial, dwMax);
+}
+
+void ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GetExecutionEngine()->ClrCloseSemaphore(semaphore);
+}
+
+BOOL ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount);
+}
+
+DWORD ClrWaitSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable);
+}
+
+MUTEX_COOKIE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
+}
+
+void ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GetExecutionEngine()->ClrCloseMutex(mutex);
+}
+
+BOOL ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrReleaseMutex(mutex);
+}
+
+DWORD ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable);
+}
+
+DWORD ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetExecutionEngine()->ClrSleepEx(dwMilliseconds, bAlertable);
+}
+
+LPVOID ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LPVOID result = GetEEMemoryManager()->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+ LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualAlloc (0x%p, 0x%06x, 0x%06x, 0x%02x) = 0x%p\n", lpAddress, dwSize, flAllocationType, flProtect, result));
+
+ return result;
+}
+
+BOOL ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualFree (0x%p, 0x%06x, 0x%04x)\n", lpAddress, dwSize, dwFreeType));
+ BOOL result = GetEEMemoryManager()->ClrVirtualFree(lpAddress, dwSize, dwFreeType);
+
+ return result;
+}
+
+SIZE_T ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualQuery (0x%p)\n", lpAddress));
+ return GetEEMemoryManager()->ClrVirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+
+BOOL ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualProtect(0x%p, 0x%06x, 0x%02x)\n", lpAddress, dwSize, flNewProtect));
+ return GetEEMemoryManager()->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+
+HANDLE ClrGetProcessHeap()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetEEMemoryManager()->ClrGetProcessHeap();
+}
+
+HANDLE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetEEMemoryManager()->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+
+BOOL ClrHeapDestroy(HANDLE hHeap)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetEEMemoryManager()->ClrHeapDestroy(hHeap);
+}
+
+LPVOID ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, S_SIZE_T dwBytes)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if(dwBytes.IsOverflow()) return NULL;
+
+ LPVOID result = GetEEMemoryManager()->ClrHeapAlloc(hHeap, dwFlags, dwBytes.Value());
+
+ return result;
+}
+
+BOOL ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ WRAPPER_NO_CONTRACT;
+
+ BOOL result = GetEEMemoryManager()->ClrHeapFree(hHeap, dwFlags, lpMem);
+
+ return result;
+}
+
+BOOL ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetEEMemoryManager()->ClrHeapValidate(hHeap, dwFlags, lpMem);
+}
+
+HANDLE ClrGetProcessExecutableHeap()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GetEEMemoryManager()->ClrGetProcessExecutableHeap();
+}
+
+void GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GetExecutionEngine()->GetLastThrownObjectExceptionFromThread(ppvException);
+}
diff --git a/src/utilcode/collections.cpp b/src/utilcode/collections.cpp
new file mode 100644
index 0000000000..10b4aeebfa
--- /dev/null
+++ b/src/utilcode/collections.cpp
@@ -0,0 +1,973 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// Collections.cpp
+//
+
+//
+// This contains Collections C++ utility classes.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+
+//
+//
+// CHashTable
+//
+//
+
+#ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// This is the second part of construction where we do all of the work that
+// can fail. We also take the array of structs here because the calling class
+// presumably needs to allocate it in its NewInit.
+//*****************************************************************************
+HRESULT CHashTable::NewInit( // Return status.
+ BYTE *pcEntries, // Array of structs we are managing.
+ ULONG iEntrySize) // Size of the entries.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(iEntrySize >= sizeof(FREEHASHENTRY));
+
+ // Allocate the bucket chain array and init it.
+ if ((m_piBuckets = new (nothrow) ULONG [m_iBuckets]) == NULL)
+ return (OutOfMemory());
+ memset(m_piBuckets, 0xff, m_iBuckets * sizeof(ULONG));
+
+ // Save the array of structs we are managing.
+ m_pcEntries = (TADDR)pcEntries;
+ m_iEntrySize = iEntrySize;
+ return (S_OK);
+}
+
+//*****************************************************************************
+// Add the struct at the specified index in m_pcEntries to the hash chains.
+//*****************************************************************************
+BYTE *CHashTable::Add( // New entry.
+ ULONG iHash, // Hash value of entry to add.
+ ULONG iIndex) // Index of struct in m_pcEntries.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HASHENTRY *psEntry; // The struct we are adding.
+
+ // Get a pointer to the entry we are adding.
+ psEntry = EntryPtr(iIndex);
+
+ // Compute the hash value for the entry.
+ iHash %= m_iBuckets;
+
+ _ASSERTE(m_piBuckets[iHash] != iIndex &&
+ (m_piBuckets[iHash] == UINT32_MAX || EntryPtr(m_piBuckets[iHash])->iPrev != iIndex));
+
+ // Setup this entry.
+ psEntry->iPrev = UINT32_MAX;
+ psEntry->iNext = m_piBuckets[iHash];
+
+ // Link it into the hash chain.
+ if (m_piBuckets[iHash] != UINT32_MAX)
+ EntryPtr(m_piBuckets[iHash])->iPrev = iIndex;
+ m_piBuckets[iHash] = iIndex;
+ return ((BYTE *) psEntry);
+}
+
+//*****************************************************************************
+// Delete the struct at the specified index in m_pcEntries from the hash chains.
+//*****************************************************************************
+void CHashTable::Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ ULONG iIndex) // Index of struct in m_pcEntries.
+{
+ WRAPPER_NO_CONTRACT;
+
+ HASHENTRY *psEntry; // Struct to delete.
+
+ // Get a pointer to the entry we are deleting.
+ psEntry = EntryPtr(iIndex);
+ Delete(iHash, psEntry);
+}
+
+//*****************************************************************************
+// Delete the struct at the specified index in m_pcEntries from the hash chains.
+//*****************************************************************************
+void CHashTable::Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ HASHENTRY *psEntry) // The struct to delete.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Compute the hash value for the entry.
+ iHash %= m_iBuckets;
+
+ _ASSERTE(psEntry->iPrev != psEntry->iNext || psEntry->iPrev == UINT32_MAX);
+
+ // Fix the predecessor.
+ if (psEntry->iPrev == UINT32_MAX)
+ m_piBuckets[iHash] = psEntry->iNext;
+ else
+ EntryPtr(psEntry->iPrev)->iNext = psEntry->iNext;
+
+ // Fix the successor.
+ if (psEntry->iNext != UINT32_MAX)
+ EntryPtr(psEntry->iNext)->iPrev = psEntry->iPrev;
+}
+
+//*****************************************************************************
+// The item at the specified index has been moved, update the previous and
+// next item.
+//*****************************************************************************
+void CHashTable::Move(
+ ULONG iHash, // Hash value for the item.
+ ULONG iNew) // New location.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HASHENTRY *psEntry; // The struct we are deleting.
+
+ psEntry = EntryPtr(iNew);
+ _ASSERTE(psEntry->iPrev != iNew && psEntry->iNext != iNew);
+
+ if (psEntry->iPrev != UINT32_MAX)
+ EntryPtr(psEntry->iPrev)->iNext = iNew;
+ else
+ m_piBuckets[iHash % m_iBuckets] = iNew;
+ if (psEntry->iNext != UINT32_MAX)
+ EntryPtr(psEntry->iNext)->iPrev = iNew;
+}
+
+#endif // !DACCESS_COMPILE
+
+//*****************************************************************************
+// Search the hash table for an entry with the specified key value.
+//*****************************************************************************
+BYTE *CHashTable::Find( // Index of struct in m_pcEntries.
+ ULONG iHash, // Hash value of the item.
+ SIZE_T key) // The key to match.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ ULONG iNext; // Used to traverse the chains.
+ HASHENTRY *psEntry; // Used to traverse the chains.
+
+ // Start at the top of the chain.
+ iNext = m_piBuckets[iHash % m_iBuckets];
+
+ // Search until we hit the end.
+
+#ifdef _DEBUG
+ unsigned count = 0;
+#endif
+
+ while (iNext != UINT32_MAX)
+ {
+ // Compare the keys.
+ psEntry = EntryPtr(iNext);
+
+#ifdef _DEBUG
+ count++;
+#endif
+ if (!Cmp(key, psEntry))
+ {
+#ifdef _DEBUG
+ if (count > m_maxSearch)
+ m_maxSearch = count;
+#endif
+
+ return ((BYTE *) psEntry);
+ }
+
+ // Advance to the next item in the chain.
+ iNext = psEntry->iNext;
+ }
+
+ // We couldn't find it.
+ return (0);
+}
+
+//*****************************************************************************
+// Search the hash table for the next entry with the specified key value.
+//*****************************************************************************
+ULONG CHashTable::FindNext( // Index of struct in m_pcEntries.
+ SIZE_T key, // The key to match.
+ ULONG iIndex) // Index of previous match.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ ULONG iNext; // Used to traverse the chains.
+ HASHENTRY *psEntry; // Used to traverse the chains.
+
+ // Start at the next entry in the chain.
+ iNext = EntryPtr(iIndex)->iNext;
+
+ // Search until we hit the end.
+ while (iNext != UINT32_MAX)
+ {
+ // Compare the keys.
+ psEntry = EntryPtr(iNext);
+ if (!Cmp(key, psEntry))
+ return (iNext);
+
+ // Advance to the next item in the chain.
+ iNext = psEntry->iNext;
+ }
+
+ // We couldn't find it.
+ return (UINT32_MAX);
+}
+
+//*****************************************************************************
+// Returns the next entry in the list.
+//*****************************************************************************
+BYTE *CHashTable::FindNextEntry( // The next entry, or0 for end of list.
+ HASHFIND *psSrch) // Search object.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ HASHENTRY *psEntry; // Used to traverse the chains.
+
+ for (;;)
+ {
+ // See if we already have one to use and if so, use it.
+ if (psSrch->iNext != UINT32_MAX)
+ {
+ psEntry = EntryPtr(psSrch->iNext);
+ psSrch->iNext = psEntry->iNext;
+ return ((BYTE *) psEntry);
+ }
+
+ // Advance to the next bucket.
+ if (psSrch->iBucket < m_iBuckets)
+ psSrch->iNext = m_piBuckets[psSrch->iBucket++];
+ else
+ break;
+ }
+
+ // There were no more entries to be found.
+ return (0);
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+CHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ ULONG numEntries)
+{
+ SUPPORTS_DAC;
+
+ // This class may be embedded so do not enum 'this'.
+ DacEnumMemoryRegion(m_pcEntries,
+ (ULONG)numEntries * m_iEntrySize);
+ DacEnumMemoryRegion(dac_cast<TADDR>(m_piBuckets),
+ (ULONG)m_iBuckets * sizeof(ULONG));
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+//
+//
+// CClosedHashBase
+//
+//
+
+//*****************************************************************************
+// Delete the given value. This will simply mark the entry as deleted (in
+// order to keep the collision chain intact). There is an optimization that
+// consecutive deleted entries leading up to a free entry are themselves freed
+// to reduce collisions later on.
+//*****************************************************************************
+void CClosedHashBase::Delete(
+ void *pData) // Key value to delete.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ BYTE *ptr;
+
+ // Find the item to delete.
+ if ((ptr = Find(pData)) == 0)
+ {
+ // You deleted something that wasn't there, why?
+ _ASSERTE(0);
+ return;
+ }
+
+ // For a perfect system, there are no collisions so it is free.
+ if (m_bPerfect)
+ {
+ SetStatus(ptr, FREE);
+
+ // One less non free entry.
+ --m_iCount;
+
+ return;
+ }
+
+ // Mark this entry deleted.
+ SetStatus(ptr, DELETED);
+
+ // If the next item is free, then we can go backwards freeing
+ // deleted entries which are no longer part of a chain. This isn't
+ // 100% great, but it will reduce collisions.
+ BYTE *pnext;
+ if ((pnext = ptr + m_iEntrySize) > EntryPtr(m_iSize - 1))
+ pnext = &m_rgData[0];
+ if (Status(pnext) != FREE)
+ return;
+
+ // We can now free consecutive entries starting with the one
+ // we just deleted, up to the first non-deleted one.
+ while (Status(ptr) == DELETED)
+ {
+ // Free this entry.
+ SetStatus(ptr, FREE);
+
+ // One less non free entry.
+ --m_iCount;
+
+ // Check the one before it, handle wrap around.
+ if ((ptr -= m_iEntrySize) < &m_rgData[0])
+ ptr = EntryPtr(m_iSize - 1);
+ }
+}
+
+
+//*****************************************************************************
+// Iterates over all active values, passing each one to pDeleteLoopFunc.
+// If pDeleteLoopFunc returns TRUE, the entry is deleted. This is safer
+// and faster than using FindNext() and Delete().
+//*****************************************************************************
+void CClosedHashBase::DeleteLoop(
+ DELETELOOPFUNC pDeleteLoopFunc, // Decides whether to delete item
+ void *pCustomizer) // Extra value passed to deletefunc.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+
+ if (m_rgData == 0)
+ {
+ return;
+ }
+
+ for (i = 0; i < m_iSize; i++)
+ {
+ BYTE *pEntry = EntryPtr(i);
+ if (Status(pEntry) == USED)
+ {
+ if (pDeleteLoopFunc(pEntry, pCustomizer))
+ {
+ if (m_bPerfect)
+ {
+ SetStatus(pEntry, FREE);
+ // One less non free entry.
+ --m_iCount;
+ }
+ else
+ {
+ SetStatus(pEntry, DELETED);
+ }
+ }
+ }
+ }
+
+ if (!m_bPerfect)
+ {
+ // Now free DELETED entries that are no longer part of a chain.
+ for (i = 0; i < m_iSize; i++)
+ {
+ if (Status(EntryPtr(i)) == FREE)
+ {
+ break;
+ }
+ }
+ if (i != m_iSize)
+ {
+ int iFirstFree = i;
+
+ do
+ {
+ if (i-- == 0)
+ {
+ i = m_iSize - 1;
+ }
+ while (Status(EntryPtr(i)) == DELETED)
+ {
+ SetStatus(EntryPtr(i), FREE);
+
+
+ // One less non free entry.
+ --m_iCount;
+
+ if (i-- == 0)
+ {
+ i = m_iSize - 1;
+ }
+ }
+
+ while (Status(EntryPtr(i)) != FREE)
+ {
+ if (i-- == 0)
+ {
+ i = m_iSize - 1;
+ }
+ }
+
+ }
+ while (i != iFirstFree);
+ }
+ }
+
+}
+
+//*****************************************************************************
+// Lookup a key value and return a pointer to the element if found.
+//*****************************************************************************
+BYTE *CClosedHashBase::Find( // The item if found, 0 if not.
+ void *pData) // The key to lookup.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ unsigned int iHash; // Hash value for this data.
+ int iBucket; // Which bucke to start at.
+ int i; // Loop control.
+
+ // Safety check.
+ if (!m_rgData || m_iCount == 0)
+ return (0);
+
+ // Hash to the bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % m_iBuckets;
+
+ // For a perfect table, the bucket is the correct one.
+ if (m_bPerfect)
+ {
+ // If the value is there, it is the correct one.
+ if (Status(EntryPtr(iBucket)) != FREE)
+ return (EntryPtr(iBucket));
+ return (0);
+ }
+
+ // Walk the bucket list looking for the item.
+ for (i=iBucket; Status(EntryPtr(i)) != FREE; )
+ {
+ // Don't look at deleted items.
+ if (Status(EntryPtr(i)) == DELETED)
+ {
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ continue;
+ }
+
+ // Check this one.
+ if (Compare(pData, EntryPtr(i)) == 0)
+ return (EntryPtr(i));
+
+ // If we never collided while adding items, then there is
+ // no point in scanning any further.
+ if (!m_iCollisions)
+ return (0);
+
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ }
+ return (0);
+}
+
+
+
+//*****************************************************************************
+// Look for an item in the table. If it isn't found, then create a new one and
+// return that.
+//*****************************************************************************
+BYTE *CClosedHashBase::FindOrAdd( // The item if found, 0 if not.
+ void *pData, // The key to lookup.
+ bool &bNew) // true if created.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ unsigned int iHash; // Hash value for this data.
+ int iBucket; // Which bucke to start at.
+ int i; // Loop control.
+
+ // If we haven't allocated any memory, or it is too small, fix it.
+ if (!m_rgData || ((m_iCount + 1) > (m_iSize * 3 / 4) && !m_bPerfect))
+ {
+ if (!ReHash())
+ return (0);
+ }
+
+ // Assume we find it.
+ bNew = false;
+
+ // Hash to the bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % m_iBuckets;
+
+ // For a perfect table, the bucket is the correct one.
+ if (m_bPerfect)
+ {
+ // If the value is there, it is the correct one.
+ if (Status(EntryPtr(iBucket)) != FREE)
+ return (EntryPtr(iBucket));
+ i = iBucket;
+ }
+ else
+ {
+ // Walk the bucket list looking for the item.
+ for (i=iBucket; Status(EntryPtr(i)) != FREE; )
+ {
+ // Don't look at deleted items.
+ if (Status(EntryPtr(i)) == DELETED)
+ {
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ continue;
+ }
+
+ // Check this one.
+ if (Compare(pData, EntryPtr(i)) == 0)
+ return (EntryPtr(i));
+
+ // One more to count.
+ ++m_iCollisions;
+
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ }
+ }
+
+ // We've found an open slot, use it.
+ _ASSERTE(Status(EntryPtr(i)) == FREE);
+ bNew = true;
+ ++m_iCount;
+ return (EntryPtr(i));
+}
+
+//*****************************************************************************
+// This helper actually does the add for you.
+//*****************************************************************************
+BYTE *CClosedHashBase::DoAdd(void *pData, BYTE *rgData, int &iBuckets, int iSize,
+ int &iCollisions, int &iCount)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ unsigned int iHash; // Hash value for this data.
+ int iBucket; // Which bucke to start at.
+ int i; // Loop control.
+
+ // Hash to the bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % iBuckets;
+
+ // For a perfect table, the bucket is free.
+ if (m_bPerfect)
+ {
+ i = iBucket;
+ _ASSERTE(Status(EntryPtr(i, rgData)) == FREE);
+ }
+ // Need to scan.
+ else
+ {
+ // Walk the bucket list looking for a slot.
+ for (i=iBucket; Status(EntryPtr(i, rgData)) != FREE; )
+ {
+ // Handle wrap around.
+ if (++i >= iSize)
+ i = 0;
+
+ // If we made it this far, we collided.
+ ++iCollisions;
+ }
+ }
+
+ // One more item in list.
+ ++iCount;
+
+ // Return the new slot for the caller.
+ return (EntryPtr(i, rgData));
+}
+
+//*****************************************************************************
+// This function is called either to init the table in the first place, or
+// to rehash the table if we ran out of room.
+//*****************************************************************************
+bool CClosedHashBase::ReHash() // true if successful.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Allocate memory if we don't have any.
+ if (!m_rgData)
+ {
+ if ((m_rgData = new (nothrow) BYTE [m_iSize * m_iEntrySize]) == 0)
+ return (false);
+ InitFree(&m_rgData[0], m_iSize);
+ return (true);
+ }
+
+ // We have entries already, allocate a new table.
+ BYTE *rgTemp, *p;
+ int iBuckets = m_iBuckets * 2 - 1;
+ int iSize = iBuckets + 7;
+ int iCollisions = 0;
+ int iCount = 0;
+
+ if ((rgTemp = new (nothrow) BYTE [iSize * m_iEntrySize]) == 0)
+ return (false);
+ InitFree(&rgTemp[0], iSize);
+ m_bPerfect = false;
+
+ // Rehash the data.
+ for (int i=0; i<m_iSize; i++)
+ {
+ // Only copy used entries.
+ if (Status(EntryPtr(i)) != USED)
+ continue;
+
+ // Add this entry to the list again.
+ VERIFY((p = DoAdd(GetKey(EntryPtr(i)), rgTemp, iBuckets,
+ iSize, iCollisions, iCount)));
+ memmove(p, EntryPtr(i), m_iEntrySize);
+ }
+
+ // Reset internals.
+ delete [] m_rgData;
+ m_rgData = rgTemp;
+ m_iBuckets = iBuckets;
+ m_iSize = iSize;
+ m_iCollisions = iCollisions;
+ m_iCount = iCount;
+ return (true);
+}
+
+
+//
+//
+// CStructArray
+//
+//
+
+
+//*****************************************************************************
+// Returns a pointer to the (iIndex)th element of the array, shifts the elements
+// in the array if the location is already full. The iIndex cannot exceed the count
+// of elements in the array.
+//*****************************************************************************
+void *CStructArray::InsertThrowing(
+ int iIndex)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(iIndex >= 0);
+
+ // We can not insert an element further than the end of the array.
+ if (iIndex > m_iCount)
+ return (NULL);
+
+ // The array should grow, if we can't fit one more element into the array.
+ Grow(1);
+
+ // The pointer to be returned.
+ BYTE *pcList = m_pList + iIndex * m_iElemSize;
+
+ // See if we need to slide anything down.
+ if (iIndex < m_iCount)
+ memmove(pcList + m_iElemSize, pcList, (m_iCount - iIndex) * m_iElemSize);
+ ++m_iCount;
+ return(pcList);
+}
+
+//*****************************************************************************
+// Non-throwing variant
+//*****************************************************************************
+void *CStructArray::Insert(int iIndex)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ void *result = NULL;
+ EX_TRY
+ {
+ result = InsertThrowing(iIndex);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+
+//*****************************************************************************
+// Allocate a new element at the end of the dynamic array and return a pointer
+// to it.
+//*****************************************************************************
+void *CStructArray::AppendThrowing()
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ // The array should grow, if we can't fit one more element into the array.
+ Grow(1);
+
+ return (m_pList + m_iCount++ * m_iElemSize);
+}
+
+
+//*****************************************************************************
+// Non-throwing variant
+//*****************************************************************************
+void *CStructArray::Append()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ void *result = NULL;
+ EX_TRY
+ {
+ result = AppendThrowing();
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+
+//*****************************************************************************
+// Allocate enough memory to have at least iCount items. This is a one shot
+// check for a block of items, instead of requiring singleton inserts. It also
+// avoids realloc headaches caused by growth, since you can do the whole block
+// in one piece of code. If the array is already large enough, this is a no-op.
+//*****************************************************************************
+void CStructArray::AllocateBlockThrowing(int iCount)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ if (m_iSize < m_iCount+iCount)
+ Grow(iCount);
+ m_iCount += iCount;
+}
+
+//*****************************************************************************
+// Non-throwing variant
+//*****************************************************************************
+int CStructArray::AllocateBlock(int iCount)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int result = FALSE;
+ EX_TRY
+ {
+ AllocateBlockThrowing(iCount);
+ result = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+
+//*****************************************************************************
+// Deletes the specified element from the array.
+//*****************************************************************************
+void CStructArray::Delete(
+ int iIndex)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(iIndex >= 0);
+
+ // See if we need to slide anything down.
+ if (iIndex < --m_iCount)
+ {
+ BYTE *pcList = m_pList + iIndex * m_iElemSize;
+ memmove(pcList, pcList + m_iElemSize, (m_iCount - iIndex) * m_iElemSize);
+ }
+}
+
+
+//*****************************************************************************
+// Grow the array if it is not possible to fit iCount number of new elements.
+//*****************************************************************************
+void CStructArray::Grow(
+ int iCount)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ BYTE *pTemp; // temporary pointer used in realloc.
+ int iGrow;
+
+ if (m_iSize < m_iCount+iCount)
+ {
+ if (m_pList == NULL)
+ {
+ iGrow = max(m_iGrowInc, iCount);
+
+ //<TODO>@todo this is a workaround because I don't want to do resize right now.</TODO>
+ //check added to make PREFAST happy
+ S_SIZE_T newSize = S_SIZE_T(iGrow) * S_SIZE_T(m_iElemSize);
+ if(newSize.IsOverflow())
+ ThrowOutOfMemory();
+ else
+ {
+ m_pList = new BYTE[newSize.Value()];
+ m_iSize = iGrow;
+ m_bFree = true;
+ }
+ }
+ else
+ {
+ // Adjust grow size as a ratio to avoid too many reallocs.
+ if (m_iSize / m_iGrowInc >= 3)
+ { // Don't overflow and go negative.
+ int newinc = m_iGrowInc * 2;
+ if (newinc > m_iGrowInc)
+ m_iGrowInc = newinc;
+ }
+
+ iGrow = max(m_iGrowInc, iCount);
+
+ // try to allocate memory for reallocation.
+ S_SIZE_T allocSize = (S_SIZE_T(m_iSize) + S_SIZE_T(iGrow)) * S_SIZE_T(m_iElemSize);
+ S_SIZE_T copyBytes = S_SIZE_T(m_iSize) * S_SIZE_T(m_iElemSize);
+ if(allocSize.IsOverflow() || copyBytes.IsOverflow())
+ ThrowOutOfMemory();
+ if (m_bFree)
+ { // We already own memory.
+ pTemp = new BYTE[allocSize.Value()];
+ memcpy (pTemp, m_pList, copyBytes.Value());
+ delete [] m_pList;
+ }
+ else
+ { // We don't own memory; get our own.
+ pTemp = new BYTE[allocSize.Value()];
+ memcpy(pTemp, m_pList, copyBytes.Value());
+ m_bFree = true;
+ }
+ m_pList = pTemp;
+ m_iSize += iGrow;
+ }
+ }
+}
+
+
+//*****************************************************************************
+// Free the memory for this item.
+//*****************************************************************************
+void CStructArray::Clear()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Free the chunk of memory.
+ if (m_bFree && m_pList != NULL)
+ delete [] m_pList;
+
+ m_pList = NULL;
+ m_iSize = 0;
+ m_iCount = 0;
+}
diff --git a/src/utilcode/comex.cpp b/src/utilcode/comex.cpp
new file mode 100644
index 0000000000..4c9d861795
--- /dev/null
+++ b/src/utilcode/comex.cpp
@@ -0,0 +1,52 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ---------------------------------------------------------------------------
+// COMEx.cpp
+//
+
+//
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "string.h"
+#include "ex.h"
+#include "holder.h"
+#include "corerror.h"
+
+// ---------------------------------------------------------------------------
+// COMException class. Implements exception API for standard COM-based error info
+// ---------------------------------------------------------------------------
+
+COMException::~COMException()
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_pErrorInfo != NULL)
+ m_pErrorInfo->Release();
+}
+
+IErrorInfo *COMException::GetErrorInfo()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ IErrorInfo *pErrorInfo = m_pErrorInfo;
+ if (pErrorInfo != NULL)
+ pErrorInfo->AddRef();
+ return pErrorInfo;
+}
+
+void COMException::GetMessage(SString &string)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (m_pErrorInfo != NULL)
+ {
+ BSTRHolder message(NULL);
+ if (SUCCEEDED(m_pErrorInfo->GetDescription(&message)))
+ string.Set(message, SysStringLen(message));
+ }
+}
+
diff --git a/src/utilcode/corimage.cpp b/src/utilcode/corimage.cpp
new file mode 100644
index 0000000000..b6fd60d785
--- /dev/null
+++ b/src/utilcode/corimage.cpp
@@ -0,0 +1,261 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+/*============================================================
+**
+** CorImage.cpp
+**
+** IMAGEHLP routines so we can avoid early binding to that DLL.
+**
+===========================================================*/
+
+#include "stdafx.h"
+#include "contract.h"
+#include <daccess.h>
+#include "corimage.h"
+#include "safemath.h"
+
+#define RTL_MEG (1024UL * 1024UL)
+#define RTLP_IMAGE_MAX_DOS_HEADER ( 256UL * RTL_MEG)
+
+// IMAGE_FIRST_SECTION doesn't need 32/64 versions since the file header is
+// the same either way.
+
+#define PTR_IMAGE_FIRST_SECTION( ntheader ) \
+ PTR_IMAGE_SECTION_HEADER \
+ (dac_cast<TADDR>(ntheader) + \
+ FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \
+ VAL16((ntheader)->FileHeader.SizeOfOptionalHeader) \
+ )
+
+#ifndef DACCESS_COMPILE
+
+IMAGE_NT_HEADERS *Cor_RtlImageNtHeader(VOID *pvBase, ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ IMAGE_NT_HEADERS *pNtHeaders = NULL;
+ if (pvBase && (pvBase != (VOID*)-1)) {
+ struct Param
+ {
+ IMAGE_DOS_HEADER *pDos;
+ ULONG FileLength;
+ IMAGE_NT_HEADERS *pNtHeaders;
+ } param;
+ param.pDos = (IMAGE_DOS_HEADER*)pvBase;
+ param.FileLength = FileLength;
+ param.pNtHeaders = pNtHeaders;
+
+ PAL_TRY(Param *, pParam, &param) {
+ if ( (pParam->pDos->e_magic == VAL16(IMAGE_DOS_SIGNATURE))
+ && ((DWORD)VAL32(pParam->pDos->e_lfanew) < RTLP_IMAGE_MAX_DOS_HEADER)
+ && ovadd_lt((DWORD)VAL32(pParam->pDos->e_lfanew), sizeof(IMAGE_FILE_HEADER) + sizeof(DWORD), pParam->FileLength)) {
+ pParam->pNtHeaders = (IMAGE_NT_HEADERS*)((BYTE*)pParam->pDos + VAL32(pParam->pDos->e_lfanew));
+ if (pParam->pNtHeaders->Signature != VAL32(IMAGE_NT_SIGNATURE))
+ pParam->pNtHeaders = NULL;
+ }
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ param.pNtHeaders = NULL;
+ }
+ PAL_ENDTRY
+
+ pNtHeaders = param.pNtHeaders;
+ }
+ return pNtHeaders;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaToSection32(PTR_IMAGE_NT_HEADERS32 NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG i;
+ PTR_IMAGE_SECTION_HEADER NtSection;
+
+ NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<NtHeaders->FileHeader.NumberOfSections; i++) {
+ if (FileLength &&
+ ((VAL32(NtSection->PointerToRawData) > FileLength)) ||
+ (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData)))
+ return NULL;
+ if (Rva >= VAL32(NtSection->VirtualAddress) &&
+ Rva < VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData))
+ return NtSection;
+
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaToSection64(PTR_IMAGE_NT_HEADERS64 NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG i;
+ PTR_IMAGE_SECTION_HEADER NtSection;
+
+ NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<VAL16(NtHeaders->FileHeader.NumberOfSections); i++) {
+ if (FileLength &&
+ ((VAL32(NtSection->PointerToRawData) > FileLength)) ||
+ (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData)))
+ return NULL;
+ if (Rva >= VAL32(NtSection->VirtualAddress) &&
+ Rva < VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData))
+ return NtSection;
+
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaToSection(PTR_IMAGE_NT_HEADERS NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ return Cor_RtlImageRvaToSection32((PTR_IMAGE_NT_HEADERS32)NtHeaders,
+ Rva, FileLength);
+ else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return Cor_RtlImageRvaToSection64((PTR_IMAGE_NT_HEADERS64)NtHeaders,
+ Rva, FileLength);
+ else {
+ _ASSERTE(!"Invalid File Type");
+ return NULL;
+ }
+}
+
+EXTERN_C PBYTE Cor_RtlImageRvaToVa32(PTR_IMAGE_NT_HEADERS32 NtHeaders,
+ PBYTE Base,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ PIMAGE_SECTION_HEADER NtSection =
+ Cor_RtlImageRvaToSection32(NtHeaders,
+ Rva,
+ FileLength);
+
+ if (NtSection != NULL)
+ return (Base +
+ (Rva - VAL32(NtSection->VirtualAddress)) +
+ VAL32(NtSection->PointerToRawData));
+ else
+ return NULL;
+}
+
+EXTERN_C PBYTE Cor_RtlImageRvaToVa64(PTR_IMAGE_NT_HEADERS64 NtHeaders,
+ PBYTE Base,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ PIMAGE_SECTION_HEADER NtSection =
+ Cor_RtlImageRvaToSection64(NtHeaders,
+ Rva,
+ FileLength);
+
+ if (NtSection != NULL)
+ return (Base +
+ (Rva - VAL32(NtSection->VirtualAddress)) +
+ VAL32(NtSection->PointerToRawData));
+ else
+ return NULL;
+}
+
+EXTERN_C PBYTE Cor_RtlImageRvaToVa(PTR_IMAGE_NT_HEADERS NtHeaders,
+ PBYTE Base,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ return Cor_RtlImageRvaToVa32((PTR_IMAGE_NT_HEADERS32)NtHeaders,
+ Base, Rva, FileLength);
+ else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return Cor_RtlImageRvaToVa64((PTR_IMAGE_NT_HEADERS64)NtHeaders,
+ Base, Rva, FileLength);
+ else {
+ _ASSERTE(!"Invalid File Type");
+ return NULL;
+ }
+}
+
+EXTERN_C PBYTE Cor_RtlImageDirToVa(PTR_IMAGE_NT_HEADERS NtHeaders,
+ PBYTE Base,
+ UINT DirIndex,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ return Cor_RtlImageRvaToVa32((PTR_IMAGE_NT_HEADERS32)NtHeaders, Base,
+ VAL32(((PTR_IMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.DataDirectory[DirIndex].VirtualAddress),
+ FileLength);
+ else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return Cor_RtlImageRvaToVa64((PTR_IMAGE_NT_HEADERS64)NtHeaders, Base,
+ VAL32(((PTR_IMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.DataDirectory[DirIndex].VirtualAddress),
+ FileLength);
+ else {
+ _ASSERTE(!"Invalid File Type");
+ return NULL;
+ }
+}
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaRangeToSection(PTR_IMAGE_NT_HEADERS NtHeaders,
+ ULONG Rva,
+ ULONG Range,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG i;
+ PTR_IMAGE_SECTION_HEADER NtSection;
+
+ if (!Range)
+ return Cor_RtlImageRvaToSection(NtHeaders, Rva, FileLength);
+
+ NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<VAL16(NtHeaders->FileHeader.NumberOfSections); i++) {
+ if (FileLength &&
+ ((VAL32(NtSection->PointerToRawData) > FileLength) ||
+ (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData))))
+ return NULL;
+ if (Rva >= VAL32(NtSection->VirtualAddress) &&
+ Rva + Range <= VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData))
+ return NtSection;
+
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+EXTERN_C DWORD Cor_RtlImageRvaToOffset(PTR_IMAGE_NT_HEADERS NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ PIMAGE_SECTION_HEADER NtSection =
+ Cor_RtlImageRvaToSection(NtHeaders,
+ Rva,
+ FileLength);
+
+ if (NtSection)
+ return ((Rva - VAL32(NtSection->VirtualAddress)) +
+ VAL32(NtSection->PointerToRawData));
+ else
+ return NULL;
+}
diff --git a/src/utilcode/crossgen/.gitmirror b/src/utilcode/crossgen/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/utilcode/crossgen/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/utilcode/crossgen/utilcode_crossgen.nativeproj b/src/utilcode/crossgen/utilcode_crossgen.nativeproj
new file mode 100644
index 0000000000..618a2eea74
--- /dev/null
+++ b/src/utilcode/crossgen/utilcode_crossgen.nativeproj
@@ -0,0 +1,21 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetCrossGen.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\utilcode.settings.targets" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>utilcode_crossgen</OutputName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <CppCompile Include="..\hostimpl.cpp" />
+ </ItemGroup>
+
+ <!--Leaf Project Items-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/utilcode/cycletimer.cpp b/src/utilcode/cycletimer.cpp
new file mode 100644
index 0000000000..3410669738
--- /dev/null
+++ b/src/utilcode/cycletimer.cpp
@@ -0,0 +1,85 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "cycletimer.h"
+#include "winbase.h"
+#include "winwrap.h"
+#include "assert.h"
+#include "utilcode.h"
+
+bool CycleTimer::GetThreadCyclesS(unsigned __int64* cycles)
+{
+ BOOL res = FALSE;
+ res = QueryThreadCycleTime(GetCurrentThread(), cycles);
+ return res != FALSE;
+}
+
+static const int SampleLoopSize = 1000000;
+
+// static
+double CycleTimer::CyclesPerSecond()
+{
+ // Windows does not provide a way of converting cycles to time -- reasonably enough,
+ // since the frequency of a machine may vary, due, e.g., to power management.
+ // Windows *does* allow you to translate QueryPerformanceCounter counts into time,
+ // however. So we'll assume that the clock speed stayed constant, and measure both the
+ // QPC counts and cycles of a short loop, to get a conversion factor.
+ LARGE_INTEGER lpFrequency;
+ if (!QueryPerformanceFrequency(&lpFrequency)) return 0.0;
+ // Otherwise...
+ LARGE_INTEGER qpcStart;
+ unsigned __int64 cycleStart;
+ if (!QueryPerformanceCounter(&qpcStart)) return 0.0;
+ if (!GetThreadCyclesS(&cycleStart)) return 0.0;
+ volatile int sum = 0;
+ for (int k = 0; k < SampleLoopSize; k++)
+ {
+ sum += k;
+ }
+ LARGE_INTEGER qpcEnd;
+ if (!QueryPerformanceCounter(&qpcEnd)) return 0.0;
+ unsigned __int64 cycleEnd;
+ if (!GetThreadCyclesS(&cycleEnd)) return 0.0;
+
+ double qpcTicks = ((double)qpcEnd.QuadPart) - ((double)qpcStart.QuadPart);
+ double secs = (qpcTicks / ((double)lpFrequency.QuadPart));
+ double cycles = ((double)cycleEnd) - ((double)cycleStart);
+ return cycles / secs;
+}
+
+// static
+unsigned __int64 CycleTimer::QueryOverhead()
+{
+ unsigned __int64 tot = 0;
+ unsigned __int64 startCycles;
+ unsigned __int64 endCycles;
+ const int N = 1000;
+ bool b = GetThreadCyclesS(&startCycles); assert(b);
+ for (int i = 0; i < N; i++)
+ {
+ b = GetThreadCyclesS(&endCycles); assert(b);
+ tot += (endCycles-startCycles);
+ startCycles = endCycles;
+ }
+ return tot/N;
+}
+
+// static
+void CycleTimer::InterlockedAddU64(unsigned __int64* loc, unsigned __int64 amount)
+{
+ volatile __int64* vloc = (volatile __int64*)loc;
+ unsigned __int64 prev = *vloc;
+ for (;;)
+ {
+ unsigned __int64 next = prev + amount;
+ __int64 snext = (__int64)next;
+ __int64 sprev = (__int64)prev;
+ __int64 res = InterlockedCompareExchange64(vloc, snext, sprev);
+ if (res == sprev) return;
+ else prev = (unsigned __int64)res;
+ }
+}
+
diff --git a/src/utilcode/dac/.gitmirror b/src/utilcode/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/utilcode/dac/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/utilcode/dac/CMakeLists.txt b/src/utilcode/dac/CMakeLists.txt
new file mode 100644
index 0000000000..d792c4ca57
--- /dev/null
+++ b/src/utilcode/dac/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+include(${CLR_DIR}/dac.cmake)
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_library(utilcode_dac STATIC ${UTILCODE_SOURCES})
+ add_dependencies(utilcode_dac CoreClrPal)
+else(CLR_CMAKE_PLATFORM_UNIX)
+ add_definitions(-DSELF_NO_HOST)
+ add_library(utilcode_dac STATIC ${UTILCODE_SOURCES} ../hostimpl.cpp)
+endif(CLR_CMAKE_PLATFORM_UNIX) \ No newline at end of file
diff --git a/src/utilcode/dac/dirs.proj b/src/utilcode/dac/dirs.proj
new file mode 100644
index 0000000000..5556c8634c
--- /dev/null
+++ b/src/utilcode/dac/dirs.proj
@@ -0,0 +1,18 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="HostLocal\dac.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/utilcode/dacutil.cpp b/src/utilcode/dacutil.cpp
new file mode 100644
index 0000000000..d21b003efb
--- /dev/null
+++ b/src/utilcode/dacutil.cpp
@@ -0,0 +1,251 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+//
+// Internal data access functionality.
+//
+
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include <winwrap.h>
+#include <utilcode.h>
+#include <dacprivate.h>
+#ifdef FEATURE_IPCMAN
+#include <ipcmanagerinterface.h>
+#endif // FEATURE_IPCMAN
+
+//----------------------------------------------------------------------------
+//
+// LiveProcDataTarget.
+//
+//----------------------------------------------------------------------------
+
+LiveProcDataTarget::LiveProcDataTarget(HANDLE process,
+ DWORD processId,
+ CLRDATA_ADDRESS baseAddressOfEngine)
+{
+ m_process = process;
+ m_processId = processId;
+ m_baseAddressOfEngine = baseAddressOfEngine;
+}
+
+STDMETHODIMP
+LiveProcDataTarget::QueryInterface(
+ THIS_
+ IN REFIID InterfaceId,
+ OUT PVOID* Interface
+ )
+{
+ if (InterfaceId == IID_IUnknown ||
+ InterfaceId == IID_ICLRDataTarget)
+ {
+ *Interface = (ICLRDataTarget*)this;
+ // No need to refcount as this class is contained.
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+LiveProcDataTarget::AddRef(
+ THIS
+ )
+{
+ // No need to refcount as this class is contained.
+ return 1;
+}
+
+STDMETHODIMP_(ULONG)
+LiveProcDataTarget::Release(
+ THIS
+ )
+{
+ SUPPORTS_DAC_HOST_ONLY;
+ // No need to refcount as this class is contained.
+ return 0;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetMachineType(
+ /* [out] */ ULONG32 *machine)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if defined(_TARGET_X86_)
+ *machine = IMAGE_FILE_MACHINE_I386;
+#elif defined(_TARGET_AMD64_)
+ *machine = IMAGE_FILE_MACHINE_AMD64;
+#elif defined(_TARGET_ARM_)
+ *machine = IMAGE_FILE_MACHINE_ARMNT;
+#else
+ PORTABILITY_ASSERT("Unknown Processor");
+#endif
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetPointerSize(
+ /* [out] */ ULONG32 *size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ *size = sizeof(void*);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetImageBase(
+ /* [string][in] */ LPCWSTR name,
+ /* [out] */ CLRDATA_ADDRESS *base)
+{
+ //
+ // The only image base that the access code cares
+ // about right now is the base of mscorwks.
+ //
+
+ if (wcscmp(name, MAIN_CLR_DLL_NAME_W))
+ {
+ return E_NOINTERFACE;
+ }
+
+ //
+ // If a base address was specified, use that
+ //
+ if (NULL != m_baseAddressOfEngine)
+ {
+ *base = m_baseAddressOfEngine;
+ return S_OK;
+ }
+
+ //
+ // Our creator must have told us WHICH clr to work with.
+ //
+ return E_FAIL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ // ReadProcessMemory will fail if any part of the
+ // region to read does not have read access. This
+ // routine attempts to read the largest valid prefix
+ // so it has to break up reads on page boundaries.
+
+ HRESULT status = S_OK;
+ ULONG32 totalDone = 0;
+ SIZE_T read;
+ ULONG32 readSize;
+
+ while (request > 0)
+ {
+ // Calculate bytes to read and don't let read cross
+ // a page boundary.
+ readSize = OS_PAGE_SIZE - (ULONG32)(address & (OS_PAGE_SIZE - 1));
+ readSize = min(request, readSize);
+
+ if (!ReadProcessMemory(m_process, (PVOID)(ULONG_PTR)address,
+ buffer, readSize, &read))
+ {
+ if (totalDone == 0)
+ {
+ // If we haven't read anything indicate failure.
+ status = E_FAIL;
+ }
+ break;
+ }
+
+ totalDone += (ULONG32)read;
+ address += read;
+ buffer += read;
+ request -= (ULONG32)read;
+ }
+
+ *done = totalDone;
+ return status;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value)
+{
+ SUPPORTS_DAC;
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID)
+{
+ SUPPORTS_DAC;
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer)
+{
+ // None supported.
+ return E_INVALIDARG;
+}
diff --git a/src/utilcode/debug.cpp b/src/utilcode/debug.cpp
new file mode 100644
index 0000000000..eec471d49d
--- /dev/null
+++ b/src/utilcode/debug.cpp
@@ -0,0 +1,949 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// Debug.cpp
+//
+// Helper code for debugging.
+//*****************************************************************************
+//
+
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "corexcep.h"
+
+#ifdef _DEBUG
+#define LOGGING
+#endif
+
+
+#include "log.h"
+
+extern "C" _CRTIMP int __cdecl _flushall(void);
+
+// Global state counter to implement SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE.
+Volatile<LONG> g_DbgSuppressAllocationAsserts = 0;
+
+
+#ifdef _DEBUG
+
+int LowResourceMessageBoxHelperAnsi(
+ LPCSTR szText, // Text message
+ LPCSTR szTitle, // Title
+ UINT uType); // Style of MessageBox
+
+//*****************************************************************************
+// This struct tracks the asserts we want to ignore in the rest of this
+// run of the application.
+//*****************************************************************************
+struct _DBGIGNOREDATA
+{
+ char rcFile[_MAX_PATH];
+ int iLine;
+ bool bIgnore;
+};
+
+typedef CDynArray<_DBGIGNOREDATA> DBGIGNORE;
+static BYTE grIgnoreMemory[sizeof(DBGIGNORE)];
+inline DBGIGNORE* GetDBGIGNORE()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ static bool fInit; // = false;
+ if (!fInit)
+ {
+ SCAN_IGNORE_THROW; // Doesn't really throw here.
+ new (grIgnoreMemory) CDynArray<_DBGIGNOREDATA>();
+ fInit = true;
+ }
+
+ return (DBGIGNORE*)grIgnoreMemory;
+}
+
+// Continue the app on an assert. Still output the assert, but
+// Don't throw up a GUI. This is useful for testing fatal error
+// paths (like FEEE) where the runtime asserts.
+BOOL ContinueOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ static ConfigDWORD fNoGui;
+ return fNoGui.val(CLRConfig::INTERNAL_ContinueOnAssert);
+}
+
+BOOL NoGuiOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ static ConfigDWORD fNoGui;
+ return fNoGui.val(CLRConfig::INTERNAL_NoGuiOnAssert);
+}
+
+void DoRaiseExceptionOnAssert(DWORD chance)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#if !defined(DACCESS_COMPILE)
+ if (chance)
+ {
+#ifndef FEATURE_PAL
+ PAL_TRY_NAKED
+ {
+ RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL);
+ }
+ PAL_EXCEPT_NAKED((chance == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ }
+ PAL_ENDTRY_NAKED
+#else // FEATURE_PAL
+ // For PAL always raise the exception.
+ RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL);
+#endif // FEATURE_PAL
+ }
+#endif // !DACCESS_COMPILE
+}
+
+enum RaiseOnAssertOptions { rTestAndRaise, rTestOnly };
+
+BOOL RaiseExceptionOnAssert(RaiseOnAssertOptions option = rTestAndRaise)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // ok for debug-only code to take locks
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ DWORD fRet = 0;
+
+#if !defined(DACCESS_COMPILE)
+ static ConfigDWORD fRaiseExceptionOnAssert;
+ //
+ // we don't want this config key to affect mscordacwks as well!
+ //
+ EX_TRY
+ {
+ fRet = fRaiseExceptionOnAssert.val(CLRConfig::INTERNAL_RaiseExceptionOnAssert);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (option == rTestAndRaise && fRet != 0)
+ {
+ DoRaiseExceptionOnAssert(fRet);
+ }
+#endif // !DACCESS_COMPILE
+
+ return fRet != 0;
+}
+
+BOOL DebugBreakOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // ok for debug-only code to take locks
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ BOOL fRet = FALSE;
+
+#ifndef DACCESS_COMPILE
+ static ConfigDWORD fDebugBreak;
+ //
+ // we don't want this config key to affect mscordacwks as well!
+ //
+ EX_TRY
+ {
+ fRet = fDebugBreak.val(CLRConfig::INTERNAL_DebugBreakOnAssert);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // DACCESS_COMPILE
+
+ return fRet;
+}
+
+VOID TerminateOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ ShutdownLogging();
+ TerminateProcess(GetCurrentProcess(), 123456789);
+}
+
+// Whether this thread is already displaying an assert dialog.
+BOOL IsDisplayingAssertDlg()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ size_t flag = 0;
+ if (ClrFlsCheckValue(TlsIdx_AssertDlgStatus, (LPVOID *)&flag))
+ {
+ return (flag != 0);
+ }
+ return FALSE;
+}
+
+void SetDisplayingAssertDlg(BOOL value)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ ClrFlsSetValue(TlsIdx_AssertDlgStatus, (LPVOID)(size_t)value);
+}
+
+VOID LogAssert(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SO_TOLERANT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ // Log asserts to the stress log. Note that we can't include the szExpr b/c that
+ // may not be a string literal (particularly for formatt-able asserts).
+ STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine);
+
+ SYSTEMTIME st;
+#ifndef FEATURE_PAL
+ GetLocalTime(&st);
+#else
+ GetSystemTime(&st);
+#endif
+
+ WCHAR exename[300];
+ WszGetModuleFileName(NULL, exename, sizeof(exename)/sizeof(WCHAR));
+
+ LOG((LF_ASSERT,
+ LL_FATALERROR,
+ "FAILED ASSERT(PID %d [0x%08x], Thread: %d [0x%x]) (%lu/%lu/%lu: %02lu:%02lu:%02lu %s): File: %s, Line %d : %s\n",
+ GetCurrentProcessId(),
+ GetCurrentProcessId(),
+ GetCurrentThreadId(),
+ GetCurrentThreadId(),
+ (ULONG)st.wMonth,
+ (ULONG)st.wDay,
+ (ULONG)st.wYear,
+ 1 + (( (ULONG)st.wHour + 11 ) % 12),
+ (ULONG)st.wMinute,
+ (ULONG)st.wSecond,
+ (st.wHour < 12) ? "am" : "pm",
+ szFile,
+ iLine,
+ szExpr));
+ LOG((LF_ASSERT, LL_FATALERROR, "RUNNING EXE: %ws\n", exename));
+}
+
+//*****************************************************************************
+
+BOOL LaunchJITDebugger()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ BOOL fSuccess = FALSE;
+#ifndef FEATURE_PAL
+ EX_TRY
+ {
+ SString debugger;
+ GetDebuggerSettingInfo(debugger, NULL);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ // We can leave this event as it is since it is inherited by a child process.
+ // We will block one scheduler, but the process is asking a user if they want to attach debugger.
+ HandleHolder eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL);
+ if (eventHandle == NULL)
+ ThrowOutOfMemory();
+
+ SString cmdLine;
+ cmdLine.Printf(debugger, GetCurrentProcessId(), eventHandle.GetValue());
+
+ STARTUPINFO StartupInfo;
+ memset(&StartupInfo, 0, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.lpDesktop = W("Winsta0\\Default");
+
+ PROCESS_INFORMATION ProcessInformation;
+ if (WszCreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
+ {
+ WaitForSingleObject(eventHandle.GetValue(), INFINITE);
+ }
+
+ fSuccess = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // !FEATURE_PAL
+ return fSuccess;
+}
+
+
+//*****************************************************************************
+// This function is called in order to ultimately return an out of memory
+// failed hresult. But this guy will check what environment you are running
+// in and give an assert for running in a debug build environment. Usually
+// out of memory on a dev machine is a bogus alloction, and this allows you
+// to catch such errors. But when run in a stress envrionment where you are
+// trying to get out of memory, assert behavior stops the tests.
+//*****************************************************************************
+HRESULT _OutOfMemory(LPCSTR szFile, int iLine)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ DbgWriteEx(W("WARNING: Out of memory condition being issued from: %hs, line %d\n"),
+ szFile, iLine);
+ return (E_OUTOFMEMORY);
+}
+
+int _DbgBreakCount = 0;
+static const char * szLowMemoryAssertMessage = "Assert failure (unable to format)";
+
+//*****************************************************************************
+// This function will handle ignore codes and tell the user what is happening.
+//*****************************************************************************
+int _DbgBreakCheck(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr,
+ BOOL fConstrained)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ RaiseExceptionOnAssert(rTestAndRaise);
+
+ if (DebugBreakOnAssert())
+ {
+ DebugBreak();
+ }
+
+ DBGIGNORE* pDBGIFNORE = GetDBGIGNORE();
+ _DBGIGNOREDATA *psData = NULL;
+ int i;
+ // Check for ignore all.
+ for (i=0, psData = pDBGIFNORE->Ptr(); i<pDBGIFNORE->Count(); i++, psData++)
+ {
+ if (psData->iLine == iLine && SString::_stricmp(psData->rcFile, szFile) == 0 &&
+ psData->bIgnore == true)
+ return (false);
+ }
+
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | TakesLockViolation);
+
+ SString debugOutput;
+ SString dialogOutput;
+ SString modulePath;
+ SString dialogTitle;
+ SString dialogIgnoreMessage;
+ BOOL formattedMessages = FALSE;
+
+ // If we are low on memory we cannot even format a message. If this happens we want to
+ // contain the exception here but display as much information as we can about the exception.
+ if (!fConstrained)
+ {
+ EX_TRY
+ {
+ ClrGetModuleFileName(0, modulePath);
+ debugOutput.Printf(W("Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n")
+ W(" File: %hs, Line: %d Image:\n"),
+ GetCurrentProcessId(), GetCurrentProcessId(),
+ GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+ debugOutput.Append(modulePath);
+ debugOutput.Append(W("\n"));
+
+ // Change format for message box. The extra spaces in the title
+ // are there to get around format truncation.
+ dialogOutput.Printf(W("%hs\n\n%hs, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n")
+ W("\n\nImage:\n"), szExpr, szFile, iLine);
+ dialogOutput.Append(modulePath);
+ dialogOutput.Append(W("\n"));
+ dialogTitle.Printf(W("Assert Failure (PID %d, Thread %d/%x) "),
+ GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId());
+
+ dialogIgnoreMessage.Printf(W("Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n\n%hs\nLine: %d\n"),
+ szFile, iLine);
+
+ formattedMessages = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ }
+
+ // Emit assert in debug output and console for easy access.
+ if (formattedMessages)
+ {
+ WszOutputDebugString(debugOutput);
+ fwprintf(stderr, W("%s"), (const WCHAR*)debugOutput);
+ }
+ else {
+ // Note: we cannot convert to unicode or concatenate in this situation.
+ OutputDebugStringA(szLowMemoryAssertMessage);
+ OutputDebugStringA("\n");
+ OutputDebugStringA(szFile);
+ OutputDebugStringA("\n");
+ OutputDebugStringA(szExpr);
+ OutputDebugStringA("\n");
+ printf(szLowMemoryAssertMessage);
+ printf("\n");
+ printf(szFile);
+ printf("\n");
+ printf("%s", szExpr);
+ printf("\n");
+ }
+
+ LogAssert(szFile, iLine, szExpr);
+ FlushLogging(); // make certain we get the last part of the log
+ _flushall();
+
+ if (ContinueOnAssert())
+ {
+ return false; // don't stop debugger. No gui.
+ }
+
+ if (NoGuiOnAssert())
+ {
+ TerminateOnAssert();
+ }
+
+ if (DebugBreakOnAssert())
+ {
+ return(true); // like a retry
+ }
+
+ if (IsDisplayingAssertDlg())
+ {
+ // We are already displaying an assert dialog box on this thread. The reason why we came here is
+ // the message loop run by the API we call to display the UI. A message was dispatched and execution
+ // ended up in the runtime where it fired another assertion. If this happens before the dialog had
+ // a chance to fully initialize the original assert may not be visible which is misleading for the
+ // user. So we just continue.
+ return false;
+ }
+ SetDisplayingAssertDlg(TRUE);
+
+ // Tell user there was an error.
+ _DbgBreakCount++;
+ int ret;
+ if (formattedMessages) {
+ ret = UtilMessageBoxCatastrophicNonLocalized(
+ W("%s"), dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION, TRUE, (const WCHAR*)dialogOutput);
+ }
+ else
+ {
+ ret = LowResourceMessageBoxHelperAnsi(
+ szExpr, szLowMemoryAssertMessage, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION);
+ }
+ --_DbgBreakCount;
+
+ SetDisplayingAssertDlg(FALSE);
+
+ switch(ret)
+ {
+ // For abort, just quit the app.
+ case IDABORT:
+ TerminateProcess(GetCurrentProcess(), 1);
+// WszFatalAppExit(0, W("Shutting down"));
+ break;
+
+ // Tell caller to break at the correct loction.
+ case IDRETRY:
+
+ if (IsDebuggerPresent())
+ {
+ SetErrorMode(0);
+ }
+ else
+ {
+ LaunchJITDebugger();
+ }
+
+ return (true);
+
+ // If we want to ignore the assert, find out if this is forever.
+ case IDIGNORE:
+ if (formattedMessages)
+ {
+ if (UtilMessageBoxCatastrophicNonLocalized(
+ dialogIgnoreMessage,
+ W("Ignore Assert Forever?"),
+ MB_ICONQUESTION | MB_YESNO,
+ TRUE) != IDYES)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (LowResourceMessageBoxHelperAnsi(
+ "Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n",
+ "Ignore Assert Forever?",
+ MB_ICONQUESTION | MB_YESNO) != IDYES)
+ {
+ break;
+ }
+ }
+ if ((psData = pDBGIFNORE->Append()) == 0)
+ return (false);
+ psData->bIgnore = true;
+ psData->iLine = iLine;
+ strcpy(psData->rcFile, szFile);
+ break;
+ }
+
+ return (false);
+}
+
+int _DbgBreakCheckNoThrow(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr,
+ BOOL fConstrained)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ RaiseExceptionOnAssert(rTestAndRaise);
+
+ if (DebugBreakOnAssert())
+ {
+ DebugBreak();
+ }
+
+ int failed = false;
+ int result = false;
+ EX_TRY
+ {
+ result = _DbgBreakCheck(szFile, iLine, szExpr, fConstrained);
+ }
+ EX_CATCH
+ {
+ failed = true;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (failed == TRUE)
+ {
+ return true;
+ }
+ return result;
+}
+
+#ifndef FEATURE_PAL
+// Get the timestamp from the PE file header. This is useful
+unsigned DbgGetEXETimeStamp()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ static ULONG cache = 0;
+ if (cache == 0) {
+ // Use GetModuleHandleA to avoid contracts - this results in a recursive loop initializing the
+ // debug allocator.
+ BYTE* imageBase = (BYTE*) GetModuleHandleA(NULL);
+ if (imageBase == 0)
+ return(0);
+ IMAGE_DOS_HEADER *pDOS = (IMAGE_DOS_HEADER*) imageBase;
+ if ((pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)) || (pDOS->e_lfanew == 0))
+ return(0);
+
+ IMAGE_NT_HEADERS *pNT = (IMAGE_NT_HEADERS*) (VAL32(pDOS->e_lfanew) + imageBase);
+ cache = VAL32(pNT->FileHeader.TimeDateStamp);
+ }
+
+ return cache;
+}
+#endif // FEATURE_PAL
+// // //
+// // // The following function
+// // // computes the binomial distribution, with which to compare
+// // // hash-table statistics. If a hash function perfectly randomizes
+// // // its input, one would expect to see F chains of length K, in a
+// // // table with N buckets and M elements, where F is
+// // //
+// // // F(K,M,N) = N * (M choose K) * (1 - 1/N)^(M-K) * (1/N)^K.
+// // //
+// // // Don't call this with a K larger than 159.
+// // //
+
+#if !defined(NO_CRT)
+
+#include <math.h>
+
+#define MAX_BUCKETS_MATH 160
+
+double Binomial (DWORD K, DWORD M, DWORD N)
+{
+ STATIC_CONTRACT_LEAF;
+
+ if (K >= MAX_BUCKETS_MATH)
+ return -1 ;
+
+ static double rgKFact [MAX_BUCKETS_MATH] ;
+ DWORD i ;
+
+ if (rgKFact[0] == 0)
+ {
+ rgKFact[0] = 1 ;
+ for (i=1; i<MAX_BUCKETS_MATH; i++)
+ rgKFact[i] = rgKFact[i-1] * i ;
+ }
+
+ double MchooseK = 1 ;
+
+ for (i = 0; i < K; i++)
+ MchooseK *= (M - i) ;
+
+ MchooseK /= rgKFact[K] ;
+
+ double OneOverNToTheK = pow (1./N, K) ;
+
+ double QToTheMMinusK = pow (1.-1./N, M-K) ;
+
+ double P = MchooseK * OneOverNToTheK * QToTheMMinusK ;
+
+ return N * P ;
+}
+
+#endif // !NO_CRT
+
+// Called from within the IfFail...() macros. Set a breakpoint here to break on
+// errors.
+VOID DebBreak()
+{
+ STATIC_CONTRACT_LEAF;
+ static int i = 0; // add some code here so that we'll be able to set a BP
+ i++;
+}
+
+
+VOID DebBreakHr(HRESULT hr)
+{
+ STATIC_CONTRACT_LEAF;
+ static int i = 0; // add some code here so that we'll be able to set a BP
+ _ASSERTE(hr != (HRESULT) 0xcccccccc);
+ i++;
+
+ // @CONSIDER: We maybe want this on retail builds.
+ #ifdef _DEBUG
+ static DWORD dwBreakHR = 99;
+
+ if (dwBreakHR == 99)
+ dwBreakHR = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnHR);
+ if (dwBreakHR == (DWORD)hr)
+ {
+ _DbgBreak();
+ }
+ #endif
+}
+
+CHAR g_szExprWithStack2[10480];
+void *dbgForceToMemory; // dummy pointer that pessimises enregistration
+
+#ifdef MDA_SUPPORTED
+#ifdef _DEBUG
+BOOL g_bMdaDisableAsserts = FALSE;
+#endif
+#endif
+
+int g_BufferLock = -1;
+
+VOID DbgAssertDialog(const char *szFile, int iLine, const char *szExpr)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ DEBUG_ONLY_FUNCTION;
+
+#ifdef MDA_SUPPORTED
+#ifdef _DEBUG
+ if (g_bMdaDisableAsserts)
+ return;
+#endif
+#endif
+
+#ifdef DACCESS_COMPILE
+ // In the DAC case, asserts can mean one of two things.
+ // Either there is a bug in the DAC infrastructure itself (a real assert), or just
+ // that the target is corrupt or being accessed at an inconsistent state (a "target
+ // consistency failure"). For target consistency failures, we need a mechanism to disable them
+ // (without affecting other asserts) so that we can test corrupt / inconsistent targets.
+
+ // @dbgtodo DAC: For now we're treating all asserts as if they are target consistency checks.
+ // In the future we should differentiate the two so that real asserts continue to fire, even when
+ // we expect the target to be inconsistent. See DevDiv Bugs 31674.
+ if( !DacTargetConsistencyAssertsEnabled() )
+ {
+ return;
+ }
+#endif // #ifndef DACCESS_COMPILE
+
+ // We increment this every time we use the SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE
+ // macro below. If it is a big number it means either a lot of threads are asserting
+ // or we have a recursion in the Assert logic (usually the latter). At least with this
+ // code in place, we don't get stack overflow (and the process torn down).
+ // the correct fix is to avoid calling asserting when allocating memory with an assert.
+ if (g_DbgSuppressAllocationAsserts > 16)
+ DebugBreak();
+
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+
+ // Raising the assert dialog can cause us to re-enter the host when allocating
+ // memory for the string. Since this is debug-only code, we can safely skip
+ // violation asserts here, particularly since they can also cause infinite
+ // recursion.
+ PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonDebugOnly);
+
+ dbgForceToMemory = &szFile; //make certain these args are available in the debugger
+ dbgForceToMemory = &iLine;
+ dbgForceToMemory = &szExpr;
+
+ RaiseExceptionOnAssert(rTestAndRaise);
+
+ if (DebugBreakOnAssert())
+ {
+ DebugBreak();
+ }
+
+ BOOL fConstrained = FALSE;
+
+ DWORD dwAssertStacktrace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertStacktrace);
+
+#if !defined(DACCESS_COMPILE) && defined(FEATURE_STACK_PROBE)
+ //global g_fpCheckNStackPagesAvailable is not present when SO infrastructure code is not present
+ // Trying to get a stack trace if there is little stack available can cause a silent process
+ // teardown, so only try to do this there is plenty of stack.
+ if ((dwAssertStacktrace) != 0 && (g_fpCheckNStackPagesAvailable != NULL))
+ {
+ if (!g_fpCheckNStackPagesAvailable(12))
+ {
+ fConstrained = TRUE;
+ }
+ }
+#endif
+
+ LONG lAlreadyOwned = InterlockedExchange((LPLONG)&g_BufferLock, 1);
+ if (fConstrained || dwAssertStacktrace == 0 || lAlreadyOwned == 1) {
+ if (1 == _DbgBreakCheckNoThrow(szFile, iLine, szExpr, fConstrained))
+ _DbgBreak();
+ } else {
+ BOOL fGotStackTrace = FALSE;
+ char *szExprToDisplay = &g_szExprWithStack2[0];
+
+#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
+ EX_TRY
+ {
+ FAULT_NOT_FATAL();
+ strcpy(szExprToDisplay, szExpr);
+ _ASSERTE(szExprToDisplay == g_szExprWithStack2);
+ strcat_s(szExprToDisplay, _countof(g_szExprWithStack2), "\n\n");
+ GetStringFromStackLevels(1, 10, szExprToDisplay + strlen(szExprToDisplay));
+ fGotStackTrace = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif
+
+ // If we failed to get the stack trace, simply display the assert without one.
+ if (!fGotStackTrace)
+ szExprToDisplay = (char*)szExpr;
+
+ if (1 == _DbgBreakCheckNoThrow(szFile, iLine, szExprToDisplay, !fGotStackTrace))
+ _DbgBreak();
+
+ g_BufferLock = 0;
+ }
+} // DbgAssertDialog
+
+#if !defined(DACCESS_COMPILE)
+//-----------------------------------------------------------------------------
+// Returns an a stacktrace for a given context.
+// Very useful inside exception filters.
+// Returns true if successful, false on failure (such as OOM).
+// This never throws.
+//-----------------------------------------------------------------------------
+bool GetStackTraceAtContext(SString & s, CONTEXT * pContext)
+{
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ // NULL means use the current context.
+ bool fSuccess = false;
+
+ FAULT_NOT_FATAL();
+
+#ifndef FEATURE_PAL
+ EX_TRY
+ {
+ const int cTotal = cfrMaxAssertStackLevels - 1;
+ // If we have a supplied context, then don't skip any frames. Else we'll
+ // be using the current context, so skip this frame.
+ const int cSkip = (pContext == NULL) ? 1 : 0;
+ char * szString = s.OpenANSIBuffer(cchMaxAssertStackLevelStringLen * cTotal);
+ GetStringFromStackLevels(cSkip, cTotal, szString, pContext);
+ s.CloseBuffer((COUNT_T) strlen(szString));
+
+ // If we made it this far w/o throwing, we succeeded.
+ fSuccess = true;
+ }
+ EX_CATCH
+ {
+ // Nothing to do here.
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // FEATURE_PAL
+
+ return fSuccess;
+} // GetStackTraceAtContext
+#endif // !defined(DACCESS_COMPILE)
+#endif // _DEBUG
+
+// This helper will throw up a message box without allocating or using stack if possible, and is
+// appropriate for either low memory or low stack situations.
+int LowResourceMessageBoxHelperAnsi(
+ LPCSTR szText, // Text message
+ LPCSTR szTitle, // Title
+ UINT uType) // Style of MessageBox
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+ }
+ CONTRACTL_END;
+
+ // In low memory or stack constrained code we cannot format or convert strings, so use the
+ // ANSI version.
+ int result = MessageBoxA(NULL, szText, szTitle, uType);
+ return result;
+}
+
+
+/****************************************************************************
+ The following two functions are defined to allow Free builds to call
+ DebugBreak or to Assert with a stack trace for unexpected fatal errors.
+ Typically these paths are enabled via a registry key in a Free Build
+*****************************************************************************/
+
+VOID __FreeBuildDebugBreak()
+{
+ WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency!
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnRetailAssert))
+ {
+ DebugBreak();
+ }
+}
+
+void *freForceToMemory; // dummy pointer that pessimises enregistration
+
+void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, const char *szExpr)
+{
+ WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency!
+
+ freForceToMemory = &szFile; //make certain these args are available in the debugger
+ freForceToMemory = &iLine;
+ freForceToMemory = &szExpr;
+
+ __FreeBuildDebugBreak();
+
+ SString buffer;
+ SString modulePath;
+
+ // Give assert in output for easy access.
+ ClrGetModuleFileName(0, modulePath);
+#ifndef FEATURE_PAL
+ buffer.Printf(W("CLR: Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n")
+ W(" File: %hs, Line: %d Image:\n"),
+ GetCurrentProcessId(), GetCurrentProcessId(),
+ GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+ buffer.Append(modulePath);
+ buffer.Append(W("\n"));
+ WszOutputDebugString(buffer);
+ // Write out the error to the console
+ _putws(buffer);
+#else // FEATURE_PAL
+ // UNIXTODO: Do this for Unix.
+#endif // FEATURE_PAL
+ // Log to the stress log. Note that we can't include the szExpr b/c that
+ // may not be a string literal (particularly for formatt-able asserts).
+ STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine);
+
+ FlushLogging(); // make certain we get the last part of the log
+
+ _flushall();
+
+ // TerminateOnAssert();
+ ShutdownLogging();
+
+ // Failing here implies an error in the runtime - hence we use
+ // COR_E_EXECUTIONENGINE
+ TerminateProcess(GetCurrentProcess(), COR_E_EXECUTIONENGINE);
+
+ UNREACHABLE();
+}
+
+//===================================================================================
+// Used by the ex.h macro: EX_CATCH_HRESULT_AND_NGEN_CLEAN(_hr)
+// which is used by ngen and mscorsvc to catch unexpected HRESULT
+// from one of the RPC calls.
+//===================================================================================
+void RetailAssertIfExpectedClean()
+{
+ static ConfigDWORD g_NGenClean;
+ if (g_NGenClean.val(CLRConfig::EXTERNAL_NGenClean) == 1)
+ {
+ _ASSERTE_ALL_BUILDS("clr/src/Utilcode/Debug.cpp", !"Error during NGen: expected no exceptions to be thrown");
+ }
+}
+
diff --git a/src/utilcode/delayloadhelpers.cpp b/src/utilcode/delayloadhelpers.cpp
new file mode 100644
index 0000000000..15dddaf75e
--- /dev/null
+++ b/src/utilcode/delayloadhelpers.cpp
@@ -0,0 +1,116 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// Contains convenience functionality for lazily loading modules
+// and getting entrypoints within them.
+//
+
+#include "stdafx.h"
+
+#include "crtwrap.h"
+#include "winwrap.h"
+#include "utilcode.h"
+#include "clrhost.h"
+#include "holder.h"
+#include "delayloadhelpers.h"
+
+namespace DelayLoad
+{
+ //=================================================================================================================
+ // Used to synchronize initialization. Is not used when initialization has already taken place.
+
+ static CRITSEC_COOKIE g_pLock = nullptr;
+
+ //=================================================================================================================
+ // Creates and initializes g_pLock when first used.
+
+ static HRESULT InitializeLock()
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ HRESULT hr = S_OK;
+
+ CRITSEC_COOKIE pLock = ClrCreateCriticalSection(CrstLeafLock, CRST_REENTRANCY);
+ IfNullRet(pLock);
+ if (InterlockedCompareExchangeT<CRITSEC_COOKIE>(&g_pLock, pLock, nullptr) != nullptr)
+ {
+ ClrDeleteCriticalSection(pLock);
+ }
+
+ return S_OK;
+ }
+
+ //=================================================================================================================
+ HRESULT Module::GetValue(HMODULE *pHMODULE)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ HRESULT hr = S_OK;
+
+ if (pHMODULE == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (!m_fInitialized)
+ {
+ IfFailRet(InitializeLock());
+
+ HModuleHolder hMod = ::LoadLibraryW(m_wzDllName);
+ hr = (hMod == nullptr) ? HRESULT_FROM_GetLastError() : S_OK;
+ _ASSERTE(FAILED(hr) == (hMod == nullptr));
+
+ { // Lock scope
+ CRITSEC_Holder lock(g_pLock);
+ if (!m_fInitialized)
+ {
+ m_hr = hr;
+ m_hMod = hMod.Extract();
+ m_fInitialized = true;
+ }
+ }
+ }
+
+ _ASSERTE(m_fInitialized);
+ *pHMODULE = m_hMod;
+ return m_hr;
+ }
+
+ //=================================================================================================================
+ HRESULT Function::GetValue(LPVOID * ppvFunc)
+ {
+ STATIC_CONTRACT_LIMITED_METHOD;
+ HRESULT hr = S_OK;
+
+ if (ppvFunc == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (!m_fInitialized)
+ {
+ HMODULE hMod = nullptr;
+ IfFailRet(m_pModule->GetValue(&hMod));
+
+ LPVOID pvFunc = ::GetProcAddress(hMod, m_szFunctionName);
+ hr = (pvFunc == nullptr) ? HRESULT_FROM_GetLastError() : S_OK;
+
+ { // Lock scope
+ CRITSEC_Holder lock(g_pLock);
+ if (!m_fInitialized)
+ {
+ m_hr = hr;
+ m_pvFunction = pvFunc;
+ m_fInitialized = true;
+ }
+ }
+ }
+
+ _ASSERTE(m_fInitialized);
+ *ppvFunc = m_pvFunction;
+ return m_hr;
+ }
+}
+
diff --git a/src/utilcode/dirs.proj b/src/utilcode/dirs.proj
new file mode 100644
index 0000000000..9749af2b11
--- /dev/null
+++ b/src/utilcode/dirs.proj
@@ -0,0 +1,27 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="dyncrt\dyncrt.nativeproj" />
+ <ProjectFile Include="dyncrtnohost\dyncrtnohost.nativeproj" />
+ <ProjectFile Include="staticcrt\staticcrt.nativeproj" />
+ <ProjectFile Include="staticnohost\staticnohost.nativeproj" />
+ <ProjectFile Include="dac\dirs.proj" />
+ </ItemGroup>
+
+ <!--The following projects will build during PHASE 1 of the Desktop build-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(FeatureCoreclr)' != 'true'">
+ <ProjectFile Include="nodependencies\utilcode-nodependencies.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/utilcode/dlwrap.cpp b/src/utilcode/dlwrap.cpp
new file mode 100644
index 0000000000..6cd8f44932
--- /dev/null
+++ b/src/utilcode/dlwrap.cpp
@@ -0,0 +1,231 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "stdafx.h" // Precompiled header key.
+#include "utilcode.h"
+#include "metadata.h"
+#include "ex.h"
+#include "pedecoder.h"
+
+#include <wininet.h>
+#include <urlmon.h>
+#include <version.h>
+
+DWORD
+GetFileVersionInfoSizeW_NoThrow(
+ LPCWSTR lptstrFilename, /* Filename of version stamped file */
+ LPDWORD lpdwHandle
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ DWORD dwRet=0;
+ EX_TRY
+ {
+ dwRet=GetFileVersionInfoSize( (LPWSTR)lptstrFilename, lpdwHandle );
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return dwRet;
+
+}
+
+BOOL
+GetFileVersionInfoW_NoThrow(
+ LPCWSTR lptstrFilename, /* Filename of version stamped file */
+ DWORD dwHandle, /* Information from GetFileVersionSize */
+ DWORD dwLen, /* Length of buffer for info */
+ LPVOID lpData
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=GetFileVersionInfo( (LPWSTR)lptstrFilename, dwHandle,dwLen,lpData );
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
+BOOL
+VerQueryValueW_NoThrow(
+ const LPVOID pBlock,
+ LPCWSTR lpSubBlock,
+ LPVOID * lplpBuffer,
+ PUINT puLen
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=VerQueryValueW( pBlock, (LPWSTR)lpSubBlock,lplpBuffer,puLen );
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+// The following functions are not used in CoreCLR. Normally LINKER can remove these functions
+// from generated files. But LINKER does it in two steps:
+// 1. If no function in a source file is used, the file is ignored by LINKER
+// 2. If one function is used, LINKER will first make sure all imported functions in the file
+// is available, and then it will remove unused functions.
+// Instead of specifying all libs for imported functions needed by the following codes, we just
+// remove them from compiling phase.
+BOOL
+CreateUrlCacheEntryW_NoThrow(
+ IN LPCWSTR lpszUrlName,
+ IN DWORD dwExpectedFileSize,
+ IN LPCWSTR lpszFileExtension,
+ __out_ecount(MAX_PATH+1) LPWSTR lpszFileName,
+ IN DWORD dwReserved
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=CreateUrlCacheEntryW(lpszUrlName,dwExpectedFileSize,lpszFileExtension,
+ lpszFileName,dwReserved);
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
+BOOL
+CommitUrlCacheEntryW_NoThrow(
+ IN LPCWSTR lpszUrlName,
+ IN LPCWSTR lpszLocalFileName,
+ IN FILETIME ExpireTime,
+ IN FILETIME LastModifiedTime,
+ IN DWORD CacheEntryType,
+ IN LPCWSTR lpHeaderInfo,
+ IN DWORD dwHeaderSize,
+ IN LPCWSTR lpszFileExtension,
+ IN LPCWSTR lpszOriginalUrl
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=CommitUrlCacheEntryW(lpszUrlName,lpszLocalFileName,ExpireTime,
+ LastModifiedTime,CacheEntryType,(LPWSTR)lpHeaderInfo,
+ dwHeaderSize,lpszFileExtension,lpszOriginalUrl);
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
+BOOL
+InternetTimeToSystemTimeA_NoThrow(
+ IN LPCSTR lpszTime, // NULL terminated string
+ OUT SYSTEMTIME *pst, // output in GMT time
+ IN DWORD dwReserved
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=InternetTimeToSystemTimeA(lpszTime,pst,dwReserved);
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
+HRESULT
+CoInternetCreateSecurityManager_NoThrow(
+ IServiceProvider *pSP,
+ IInternetSecurityManager **ppSM,
+ DWORD dwReserved
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ EX_TRY
+ {
+ hr=CoInternetCreateSecurityManager(pSP,ppSM, dwReserved);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+
+HRESULT
+URLDownloadToCacheFileW_NoThrow(
+ LPUNKNOWN lpUnkcaller,
+ LPCWSTR szURL,
+ __out_ecount(dwBufLength) LPWSTR szFileName,
+ DWORD dwBufLength,
+ DWORD dwReserved,
+ IBindStatusCallback *pBSC
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ EX_TRY
+ {
+ hr=URLDownloadToCacheFileW(lpUnkcaller,szURL,szFileName,dwBufLength,dwReserved,pBSC);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+
+HRESULT
+CoInternetGetSession_NoThrow(
+ WORD dwSessionMode,
+ IInternetSession **ppIInternetSession,
+ DWORD dwReserved
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ EX_TRY
+ {
+ hr=CoInternetGetSession(dwSessionMode,ppIInternetSession,dwReserved);
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+
+HRESULT
+CopyBindInfo_NoThrow(
+ const BINDINFO * pcbiSrc, BINDINFO * pbiDest
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ EX_TRY
+ {
+ hr=CopyBindInfo(pcbiSrc,pbiDest );
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+}
+#endif // FEATURE_CORECLR && !CROSSGEN_COMPILE
diff --git a/src/utilcode/downlevel.cpp b/src/utilcode/downlevel.cpp
new file mode 100644
index 0000000000..3b818e59f2
--- /dev/null
+++ b/src/utilcode/downlevel.cpp
@@ -0,0 +1,2736 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+////////////////////////////////////////////////////////////////////////////
+//
+// File: downlevel.cpp
+//
+
+
+//
+// Purpose: functions that need to be emulated on downlevel platforms.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+
+#if defined(ENABLE_DOWNLEVEL_FOR_NLS)
+
+#ifndef LOCALE_SNAME
+#define LOCALE_SNAME 0x0000005c
+#endif
+#include "downlevel.h"
+#include "newapis.h"
+#include "utilcode.h"
+#include "sstring.h"
+#include "ex.h"
+
+#define LCID_AZ_CYRL_AZ 0x0082c
+
+//
+// Macro to check if more than one bit is set.
+// Returns 1 if more than one bit set, 0 otherwise.
+//
+#define MORE_THAN_ONE(f, bits) (((f & bits) - 1) & (f & bits))
+
+struct LCIDEntry
+{
+ LCID lcid;
+ LPCWSTR wszName;
+};
+
+// Known (ie: <= Vista) Name/LCID lookup table, sorted by LCID
+const static LCIDEntry s_lcids[]=
+{
+ // Neutrals
+ { 0x00001, W("ar") }, // Neutral Locale
+ { 0x00002, W("bg") }, // Neutral Locale
+ { 0x00003, W("ca") }, // Neutral Locale
+ { 0x00004, W("zh-Hans") }, // Neutral Locale
+ { 0x00005, W("cs") }, // Neutral Locale
+ { 0x00006, W("da") }, // Neutral Locale
+ { 0x00007, W("de") }, // Neutral Locale
+ { 0x00008, W("el") }, // Neutral Locale
+ { 0x00009, W("en") }, // Neutral Locale
+ { 0x0000a, W("es") }, // Neutral Locale
+ { 0x0000b, W("fi") }, // Neutral Locale
+ { 0x0000c, W("fr") }, // Neutral Locale
+ { 0x0000d, W("he") }, // Neutral Locale
+ { 0x0000e, W("hu") }, // Neutral Locale
+ { 0x0000f, W("is") }, // Neutral Locale
+ { 0x00010, W("it") }, // Neutral Locale
+ { 0x00011, W("ja") }, // Neutral Locale
+ { 0x00012, W("ko") }, // Neutral Locale
+ { 0x00013, W("nl") }, // Neutral Locale
+ { 0x00014, W("no") }, // Neutral Locale
+ { 0x00015, W("pl") }, // Neutral Locale
+ { 0x00016, W("pt") }, // Neutral Locale
+ { 0x00017, W("rm") }, // Neutral Locale
+ { 0x00018, W("ro") }, // Neutral Locale
+ { 0x00019, W("ru") }, // Neutral Locale
+ { 0x0001a, W("hr") }, // Neutral Locale
+ { 0x0001b, W("sk") }, // Neutral Locale
+ { 0x0001c, W("sq") }, // Neutral Locale
+ { 0x0001d, W("sv") }, // Neutral Locale
+ { 0x0001e, W("th") }, // Neutral Locale
+ { 0x0001f, W("tr") }, // Neutral Locale
+ { 0x00020, W("ur") }, // Neutral Locale
+ { 0x00021, W("id") }, // Neutral Locale
+ { 0x00022, W("uk") }, // Neutral Locale
+ { 0x00023, W("be") }, // Neutral Locale
+ { 0x00024, W("sl") }, // Neutral Locale
+ { 0x00025, W("et") }, // Neutral Locale
+ { 0x00026, W("lv") }, // Neutral Locale
+ { 0x00027, W("lt") }, // Neutral Locale
+ { 0x00028, W("tg") }, // Neutral Locale
+ { 0x00029, W("fa") }, // Neutral Locale
+ { 0x0002a, W("vi") }, // Neutral Locale
+ { 0x0002b, W("hy") }, // Neutral Locale
+ { 0x0002c, W("az") }, // Neutral Locale
+ { 0x0002d, W("eu") }, // Neutral Locale
+ { 0x0002e, W("hsb") }, // Neutral Locale
+ { 0x0002f, W("mk") }, // Neutral Locale
+ { 0x00032, W("tn") }, // Neutral Locale
+ { 0x00034, W("xh") }, // Neutral Locale
+ { 0x00035, W("zu") }, // Neutral Locale
+ { 0x00036, W("af") }, // Neutral Locale
+ { 0x00037, W("ka") }, // Neutral Locale
+ { 0x00038, W("fo") }, // Neutral Locale
+ { 0x00039, W("hi") }, // Neutral Locale
+ { 0x0003a, W("mt") }, // Neutral Locale
+ { 0x0003b, W("se") }, // Neutral Locale
+ { 0x0003c, W("ga") }, // Neutral Locale
+ { 0x0003e, W("ms") }, // Neutral Locale
+ { 0x0003f, W("kk") }, // Neutral Locale
+ { 0x00040, W("ky") }, // Neutral Locale
+ { 0x00041, W("sw") }, // Neutral Locale
+ { 0x00042, W("tk") }, // Neutral Locale
+ { 0x00043, W("uz") }, // Neutral Locale
+ { 0x00044, W("tt") }, // Neutral Locale
+ { 0x00045, W("bn") }, // Neutral Locale
+ { 0x00046, W("pa") }, // Neutral Locale
+ { 0x00047, W("gu") }, // Neutral Locale
+ { 0x00048, W("or") }, // Neutral Locale
+ { 0x00049, W("ta") }, // Neutral Locale
+ { 0x0004a, W("te") }, // Neutral Locale
+ { 0x0004b, W("kn") }, // Neutral Locale
+ { 0x0004c, W("ml") }, // Neutral Locale
+ { 0x0004d, W("as") }, // Neutral Locale
+ { 0x0004e, W("mr") }, // Neutral Locale
+ { 0x0004f, W("sa") }, // Neutral Locale
+ { 0x00050, W("mn") }, // Neutral Locale
+ { 0x00051, W("bo") }, // Neutral Locale
+ { 0x00052, W("cy") }, // Neutral Locale
+ { 0x00053, W("km") }, // Neutral Locale
+ { 0x00054, W("lo") }, // Neutral Locale
+ { 0x00056, W("gl") }, // Neutral Locale
+ { 0x00057, W("kok") }, // Neutral Locale
+ { 0x0005a, W("syr") }, // Neutral Locale
+ { 0x0005b, W("si") }, // Neutral Locale
+ { 0x0005d, W("iu") }, // Neutral Locale
+ { 0x0005e, W("am") }, // Neutral Locale
+ { 0x0005f, W("tzm") }, // Neutral Locale
+ { 0x00061, W("ne") }, // Neutral Locale
+ { 0x00062, W("fy") }, // Neutral Locale
+ { 0x00063, W("ps") }, // Neutral Locale
+ { 0x00064, W("fil") }, // Neutral Locale
+ { 0x00065, W("dv") }, // Neutral Locale
+ { 0x00068, W("ha") }, // Neutral Locale
+ { 0x0006a, W("yo") }, // Neutral Locale
+ { 0x0006b, W("quz") }, // Neutral Locale
+ { 0x0006c, W("nso") }, // Neutral Locale
+ { 0x0006d, W("ba") }, // Neutral Locale
+ { 0x0006e, W("lb") }, // Neutral Locale
+ { 0x0006f, W("kl") }, // Neutral Locale
+ { 0x00070, W("ig") }, // Neutral Locale
+ { 0x00078, W("ii") }, // Neutral Locale
+ { 0x0007a, W("arn") }, // Neutral Locale
+ { 0x0007c, W("moh") }, // Neutral Locale
+ { 0x0007e, W("br") }, // Neutral Locale
+ { 0x00080, W("ug") }, // Neutral Locale
+ { 0x00081, W("mi") }, // Neutral Locale
+ { 0x00082, W("oc") }, // Neutral Locale
+ { 0x00083, W("co") }, // Neutral Locale
+ { 0x00084, W("gsw") }, // Neutral Locale
+ { 0x00085, W("sah") }, // Neutral Locale
+ { 0x00086, W("qut") }, // Neutral Locale
+ { 0x00087, W("rw") }, // Neutral Locale
+ { 0x00088, W("wo") }, // Neutral Locale
+ { 0x0008c, W("prs") }, // Neutral Locale
+
+ // Specific Cultures
+ { 0x00401, W("ar-SA") },
+ { 0x00402, W("bg-BG") },
+ { 0x00403, W("ca-ES") },
+ { 0x00404, W("zh-TW") },
+ { 0x00405, W("cs-CZ") },
+ { 0x00406, W("da-DK") },
+ { 0x00407, W("de-DE") },
+ { 0x00408, W("el-GR") },
+ { 0x00409, W("en-US") },
+ // es-ES_tradnl only gets used if specifically asked for because
+ // GetCultures() won't return it. (It's not a real locale, its an alt sort)
+ { 0x0040a, W("es-ES_tradnl") },
+ { 0x0040b, W("fi-FI") },
+ { 0x0040c, W("fr-FR") },
+ { 0x0040d, W("he-IL") },
+ { 0x0040e, W("hu-HU") },
+ { 0x0040f, W("is-IS") },
+ { 0x00410, W("it-IT") },
+ { 0x00411, W("ja-JP") },
+ { 0x00412, W("ko-KR") },
+ { 0x00413, W("nl-NL") },
+ { 0x00414, W("nb-NO") },
+ { 0x00415, W("pl-PL") },
+ { 0x00416, W("pt-BR") },
+ { 0x00417, W("rm-CH") },
+ { 0x00418, W("ro-RO") },
+ { 0x00419, W("ru-RU") },
+ { 0x0041a, W("hr-HR") },
+ { 0x0041b, W("sk-SK") },
+ { 0x0041c, W("sq-AL") },
+ { 0x0041d, W("sv-SE") },
+ { 0x0041e, W("th-TH") },
+ { 0x0041f, W("tr-TR") },
+ { 0x00420, W("ur-PK") },
+ { 0x00421, W("id-ID") },
+ { 0x00422, W("uk-UA") },
+ { 0x00423, W("be-BY") },
+ { 0x00424, W("sl-SI") },
+ { 0x00425, W("et-EE") },
+ { 0x00426, W("lv-LV") },
+ { 0x00427, W("lt-LT") },
+ { 0x00428, W("tg-Cyrl-TJ") },
+ { 0x00429, W("fa-IR") },
+ { 0x0042a, W("vi-VN") },
+ { 0x0042b, W("hy-AM") },
+ { 0x0042c, W("az-Latn-AZ") },
+ { 0x0042d, W("eu-ES") },
+ { 0x0042e, W("hsb-DE") },
+ { 0x0042f, W("mk-MK") },
+ { 0x00432, W("tn-ZA") },
+ { 0x00434, W("xh-ZA") },
+ { 0x00435, W("zu-ZA") },
+ { 0x00436, W("af-ZA") },
+ { 0x00437, W("ka-GE") },
+ { 0x00438, W("fo-FO") },
+ { 0x00439, W("hi-IN") },
+ { 0x0043a, W("mt-MT") },
+ { 0x0043b, W("se-NO") },
+ { 0x0043e, W("ms-MY") },
+ { 0x0043f, W("kk-KZ") },
+ { 0x00440, W("ky-KG") },
+ { 0x00441, W("sw-KE") },
+ { 0x00442, W("tk-TM") },
+ { 0x00443, W("uz-Latn-UZ") },
+ { 0x00444, W("tt-RU") },
+ { 0x00445, W("bn-IN") },
+ { 0x00446, W("pa-IN") },
+ { 0x00447, W("gu-IN") },
+ { 0x00448, W("or-IN") },
+ { 0x00449, W("ta-IN") },
+ { 0x0044a, W("te-IN") },
+ { 0x0044b, W("kn-IN") },
+ { 0x0044c, W("ml-IN") },
+ { 0x0044d, W("as-IN") },
+ { 0x0044e, W("mr-IN") },
+ { 0x0044f, W("sa-IN") },
+ { 0x00450, W("mn-MN") },
+ { 0x00451, W("bo-CN") },
+ { 0x00452, W("cy-GB") },
+ { 0x00453, W("km-KH") },
+ { 0x00454, W("lo-LA") },
+ { 0x00456, W("gl-ES") },
+ { 0x00457, W("kok-IN") },
+ { 0x0045a, W("syr-SY") },
+ { 0x0045b, W("si-LK") },
+ { 0x0045d, W("iu-Cans-CA") },
+ { 0x0045e, W("am-ET") },
+ { 0x00461, W("ne-NP") },
+ { 0x00462, W("fy-NL") },
+ { 0x00463, W("ps-AF") },
+ { 0x00464, W("fil-PH") },
+ { 0x00465, W("dv-MV") },
+ { 0x00468, W("ha-Latn-NG") },
+ { 0x0046a, W("yo-NG") },
+ { 0x0046b, W("quz-BO") },
+ { 0x0046c, W("nso-ZA") },
+ { 0x0046d, W("ba-RU") },
+ { 0x0046e, W("lb-LU") },
+ { 0x0046f, W("kl-GL") },
+ { 0x00470, W("ig-NG") },
+ { 0x00478, W("ii-CN") },
+ { 0x0047a, W("arn-CL") },
+ { 0x0047c, W("moh-CA") },
+ { 0x0047e, W("br-FR") },
+ { 0x00480, W("ug-CN") },
+ { 0x00481, W("mi-NZ") },
+ { 0x00482, W("oc-FR") },
+ { 0x00483, W("co-FR") },
+ { 0x00484, W("gsw-FR") },
+ { 0x00485, W("sah-RU") },
+ { 0x00486, W("qut-GT") },
+ { 0x00487, W("rw-RW") },
+ { 0x00488, W("wo-SN") },
+ { 0x0048c, W("prs-AF") },
+ { 0x00501, W("qps-ploc") },
+ { 0x005fe, W("qps-ploca") },
+ { 0x00801, W("ar-IQ") },
+ { 0x00804, W("zh-CN") },
+ { 0x00807, W("de-CH") },
+ { 0x00809, W("en-GB") },
+ { 0x0080a, W("es-MX") },
+ { 0x0080c, W("fr-BE") },
+ { 0x00810, W("it-CH") },
+ { 0x00813, W("nl-BE") },
+ { 0x00814, W("nn-NO") },
+ { 0x00816, W("pt-PT") },
+ { 0x0081a, W("sr-Latn-CS") },
+ { 0x0081d, W("sv-FI") },
+ { 0x0082c, W("az-Cyrl-AZ") },
+ { 0x0082e, W("dsb-DE") },
+ { 0x0083b, W("se-SE") },
+ { 0x0083c, W("ga-IE") },
+ { 0x0083e, W("ms-BN") },
+ { 0x00843, W("uz-Cyrl-UZ") },
+ { 0x00845, W("bn-BD") },
+ { 0x00850, W("mn-Mong-CN") },
+ { 0x0085d, W("iu-Latn-CA") },
+ { 0x0085f, W("tzm-Latn-DZ") },
+ { 0x0086b, W("quz-EC") },
+ { 0x009ff, W("qps-plocm") },
+ { 0x00c01, W("ar-EG") },
+ { 0x00c04, W("zh-HK") },
+ { 0x00c07, W("de-AT") },
+ { 0x00c09, W("en-AU") },
+ { 0x00c0a, W("es-ES") },
+ { 0x00c0c, W("fr-CA") },
+ { 0x00c1a, W("sr-Cyrl-CS") },
+ { 0x00c3b, W("se-FI") },
+ { 0x00c6b, W("quz-PE") },
+ { 0x01001, W("ar-LY") },
+ { 0x01004, W("zh-SG") },
+ { 0x01007, W("de-LU") },
+ { 0x01009, W("en-CA") },
+ { 0x0100a, W("es-GT") },
+ { 0x0100c, W("fr-CH") },
+ { 0x0101a, W("hr-BA") },
+ { 0x0103b, W("smj-NO") },
+ { 0x01401, W("ar-DZ") },
+ { 0x01404, W("zh-MO") },
+ { 0x01407, W("de-LI") },
+ { 0x01409, W("en-NZ") },
+ { 0x0140a, W("es-CR") },
+ { 0x0140c, W("fr-LU") },
+ { 0x0141a, W("bs-Latn-BA") },
+ { 0x0143b, W("smj-SE") },
+ { 0x01801, W("ar-MA") },
+ { 0x01809, W("en-IE") },
+ { 0x0180a, W("es-PA") },
+ { 0x0180c, W("fr-MC") },
+ { 0x0181a, W("sr-Latn-BA") },
+ { 0x0183b, W("sma-NO") },
+ { 0x01c01, W("ar-TN") },
+ { 0x01c09, W("en-ZA") },
+ { 0x01c0a, W("es-DO") },
+ { 0x01c1a, W("sr-Cyrl-BA") },
+ { 0x01c3b, W("sma-SE") },
+ { 0x02001, W("ar-OM") },
+ { 0x02009, W("en-JM") },
+ { 0x0200a, W("es-VE") },
+ { 0x0201a, W("bs-Cyrl-BA") },
+ { 0x0203b, W("sms-FI") },
+ { 0x02401, W("ar-YE") },
+ { 0x02409, W("en-029") },
+ { 0x0240a, W("es-CO") },
+ { 0x0243b, W("smn-FI") },
+ { 0x02801, W("ar-SY") },
+ { 0x02809, W("en-BZ") },
+ { 0x0280a, W("es-PE") },
+ { 0x02c01, W("ar-JO") },
+ { 0x02c09, W("en-TT") },
+ { 0x02c0a, W("es-AR") },
+ { 0x03001, W("ar-LB") },
+ { 0x03009, W("en-ZW") },
+ { 0x0300a, W("es-EC") },
+ { 0x03401, W("ar-KW") },
+ { 0x03409, W("en-PH") },
+ { 0x0340a, W("es-CL") },
+ { 0x03801, W("ar-AE") },
+ { 0x0380a, W("es-UY") },
+ { 0x03c01, W("ar-BH") },
+ { 0x03c0a, W("es-PY") },
+ { 0x04001, W("ar-QA") },
+ { 0x04009, W("en-IN") },
+ { 0x0400a, W("es-BO") },
+ { 0x04409, W("en-MY") },
+ { 0x0440a, W("es-SV") },
+ { 0x04809, W("en-SG") },
+ { 0x0480a, W("es-HN") },
+ { 0x04c0a, W("es-NI") },
+ { 0x0500a, W("es-PR") },
+ { 0x0540a, W("es-US") },
+
+ // Multiple neutrals
+ { 0x0781a, W("bs") }, // Neutral Locale
+ { 0x07c04, W("zh-Hant") }, // Neutral Locale
+ { 0x07c1a, W("sr") }, // Neutral Locale
+
+ // Alt Sorts
+ { 0x1007f, W("x-IV_mathan") },
+ { 0x10407, W("de-DE_phoneb") },
+ { 0x1040e, W("hu-HU_technl") },
+ { 0x10437, W("ka-GE_modern") },
+ { 0x20804, W("zh-CN_stroke") },
+ { 0x21004, W("zh-SG_stroke") },
+ { 0x21404, W("zh-MO_stroke") },
+ { 0x30404, W("zh-TW_pronun") },
+ { 0x40411, W("ja-JP_radstr") }
+
+ // TODO: Turkic? (Necessary ?)
+};
+
+// Known (ie: <= Vista) Name/LCID lookup table, sorted by Name
+const static LCIDEntry s_names[]=
+{
+ { 0x00036, W("af") }, // Neutral Locale
+ { 0x00436, W("af-ZA") },
+ { 0x0005e, W("am") }, // Neutral Locale
+ { 0x0045e, W("am-ET") },
+ { 0x00001, W("ar") }, // Neutral Locale
+ { 0x03801, W("ar-AE") },
+ { 0x03c01, W("ar-BH") },
+ { 0x01401, W("ar-DZ") },
+ { 0x00c01, W("ar-EG") },
+ { 0x00801, W("ar-IQ") },
+ { 0x02c01, W("ar-JO") },
+ { 0x03401, W("ar-KW") },
+ { 0x03001, W("ar-LB") },
+ { 0x01001, W("ar-LY") },
+ { 0x01801, W("ar-MA") },
+ { 0x02001, W("ar-OM") },
+ { 0x04001, W("ar-QA") },
+ { 0x00401, W("ar-SA") },
+ { 0x02801, W("ar-SY") },
+ { 0x01c01, W("ar-TN") },
+ { 0x02401, W("ar-YE") },
+ { 0x0007a, W("arn") }, // Neutral Locale
+ { 0x0047a, W("arn-CL") },
+ { 0x0004d, W("as") }, // Neutral Locale
+ { 0x0044d, W("as-IN") },
+ { 0x0002c, W("az") }, // Neutral Locale
+ { 0x0082c, W("az-Cyrl-AZ") },
+ { 0x0042c, W("az-Latn-AZ") },
+ { 0x0006d, W("ba") }, // Neutral Locale
+ { 0x0046d, W("ba-RU") },
+ { 0x00023, W("be") }, // Neutral Locale
+ { 0x00423, W("be-BY") },
+ { 0x00002, W("bg") }, // Neutral Locale
+ { 0x00402, W("bg-BG") },
+ { 0x00045, W("bn") }, // Neutral Locale
+ { 0x00845, W("bn-BD") },
+ { 0x00445, W("bn-IN") },
+ { 0x00051, W("bo") }, // Neutral Locale
+ { 0x00451, W("bo-CN") },
+ { 0x0007e, W("br") }, // Neutral Locale
+ { 0x0047e, W("br-FR") },
+ { 0x0781a, W("bs") }, // Neutral Locale
+ { 0x0201a, W("bs-Cyrl-BA") },
+ { 0x0141a, W("bs-Latn-BA") },
+ { 0x00003, W("ca") }, // Neutral Locale
+ { 0x00403, W("ca-ES") },
+ { 0x00083, W("co") }, // Neutral Locale
+ { 0x00483, W("co-FR") },
+ { 0x00005, W("cs") }, // Neutral Locale
+ { 0x00405, W("cs-CZ") },
+ { 0x00052, W("cy") }, // Neutral Locale
+ { 0x00452, W("cy-GB") },
+ { 0x00006, W("da") }, // Neutral Locale
+ { 0x00406, W("da-DK") },
+ { 0x00007, W("de") }, // Neutral Locale
+ { 0x00c07, W("de-AT") },
+ { 0x00807, W("de-CH") },
+ { 0x00407, W("de-DE") },
+ { 0x10407, W("de-DE_phoneb") }, // Alternate Sort
+ { 0x01407, W("de-LI") },
+ { 0x01007, W("de-LU") },
+ { 0x0082e, W("dsb-DE") },
+ { 0x00065, W("dv") }, // Neutral Locale
+ { 0x00465, W("dv-MV") },
+ { 0x00008, W("el") }, // Neutral Locale
+ { 0x00408, W("el-GR") },
+ { 0x00009, W("en") }, // Neutral Locale
+ { 0x02409, W("en-029") },
+ { 0x00c09, W("en-AU") },
+ { 0x02809, W("en-BZ") },
+ { 0x01009, W("en-CA") },
+ { 0x00809, W("en-GB") },
+ { 0x01809, W("en-IE") },
+ { 0x04009, W("en-IN") },
+ { 0x02009, W("en-JM") },
+ { 0x04409, W("en-MY") },
+ { 0x01409, W("en-NZ") },
+ { 0x03409, W("en-PH") },
+ { 0x04809, W("en-SG") },
+ { 0x02c09, W("en-TT") },
+ { 0x00409, W("en-US") },
+ { 0x01c09, W("en-ZA") },
+ { 0x03009, W("en-ZW") },
+ { 0x0000a, W("es") }, // Neutral Locale
+ { 0x02c0a, W("es-AR") },
+ { 0x0400a, W("es-BO") },
+ { 0x0340a, W("es-CL") },
+ { 0x0240a, W("es-CO") },
+ { 0x0140a, W("es-CR") },
+ { 0x01c0a, W("es-DO") },
+ { 0x0300a, W("es-EC") },
+ { 0x00c0a, W("es-ES") },
+ // es-ES_tradnl only gets used if specifically asked for because
+ // GetCultures() won't return it. (It's not a real locale, its an alt sort)
+ { 0x0040a, W("es-ES_tradnl") },
+ { 0x0100a, W("es-GT") },
+ { 0x0480a, W("es-HN") },
+ { 0x0080a, W("es-MX") },
+ { 0x04c0a, W("es-NI") },
+ { 0x0180a, W("es-PA") },
+ { 0x0280a, W("es-PE") },
+ { 0x0500a, W("es-PR") },
+ { 0x03c0a, W("es-PY") },
+ { 0x0440a, W("es-SV") },
+ { 0x0540a, W("es-US") },
+ { 0x0380a, W("es-UY") },
+ { 0x0200a, W("es-VE") },
+ { 0x00025, W("et") }, // Neutral Locale
+ { 0x00425, W("et-EE") },
+ { 0x0002d, W("eu") }, // Neutral Locale
+ { 0x0042d, W("eu-ES") },
+ { 0x00029, W("fa") }, // Neutral Locale
+ { 0x00429, W("fa-IR") },
+ { 0x0000b, W("fi") }, // Neutral Locale
+ { 0x0040b, W("fi-FI") },
+ { 0x00064, W("fil") }, // Neutral Locale
+ { 0x00464, W("fil-PH") },
+ { 0x00038, W("fo") }, // Neutral Locale
+ { 0x00438, W("fo-FO") },
+ { 0x0000c, W("fr") }, // Neutral Locale
+ { 0x0080c, W("fr-BE") },
+ { 0x00c0c, W("fr-CA") },
+ { 0x0100c, W("fr-CH") },
+ { 0x0040c, W("fr-FR") },
+ { 0x0140c, W("fr-LU") },
+ { 0x0180c, W("fr-MC") },
+ { 0x00062, W("fy") }, // Neutral Locale
+ { 0x00462, W("fy-NL") },
+ { 0x0003c, W("ga") }, // Neutral Locale
+ { 0x0083c, W("ga-IE") },
+ { 0x00056, W("gl") }, // Neutral Locale
+ { 0x00456, W("gl-ES") },
+ { 0x00084, W("gsw") }, // Neutral Locale
+ { 0x00484, W("gsw-FR") },
+ { 0x00047, W("gu") }, // Neutral Locale
+ { 0x00447, W("gu-IN") },
+ { 0x00068, W("ha") }, // Neutral Locale
+ { 0x00468, W("ha-Latn-NG") },
+ { 0x0000d, W("he") }, // Neutral Locale
+ { 0x0040d, W("he-IL") },
+ { 0x00039, W("hi") }, // Neutral Locale
+ { 0x00439, W("hi-IN") },
+ { 0x0001a, W("hr") }, // Neutral Locale
+ { 0x0101a, W("hr-BA") },
+ { 0x0041a, W("hr-HR") },
+ { 0x0002e, W("hsb") }, // Neutral Locale
+ { 0x0042e, W("hsb-DE") },
+ { 0x0000e, W("hu") }, // Neutral Locale
+ { 0x0040e, W("hu-HU") },
+ { 0x1040e, W("hu-HU_technl") }, // Alternate Sort
+ { 0x0002b, W("hy") }, // Neutral Locale
+ { 0x0042b, W("hy-AM") },
+ { 0x00021, W("id") }, // Neutral Locale
+ { 0x00421, W("id-ID") },
+ { 0x00070, W("ig") }, // Neutral Locale
+ { 0x00470, W("ig-NG") },
+ { 0x00078, W("ii") }, // Neutral Locale
+ { 0x00478, W("ii-CN") },
+ { 0x0000f, W("is") }, // Neutral Locale
+ { 0x0040f, W("is-IS") },
+ { 0x00010, W("it") }, // Neutral Locale
+ { 0x00810, W("it-CH") },
+ { 0x00410, W("it-IT") },
+ { 0x0005d, W("iu") }, // Neutral Locale
+ { 0x0045d, W("iu-Cans-CA") },
+ { 0x0085d, W("iu-Latn-CA") },
+ { 0x00011, W("ja") }, // Neutral Locale
+ { 0x00411, W("ja-JP") },
+ { 0x40411, W("ja-JP_radstr") }, // Alternate Sort
+ { 0x00037, W("ka") }, // Neutral Locale
+ { 0x00437, W("ka-GE") },
+ { 0x10437, W("ka-GE_modern") }, // Alternate Sort
+ { 0x0003f, W("kk") }, // Neutral Locale
+ { 0x0043f, W("kk-KZ") },
+ { 0x0006f, W("kl") }, // Neutral Locale
+ { 0x0046f, W("kl-GL") },
+ { 0x00053, W("km") }, // Neutral Locale
+ { 0x00453, W("km-KH") },
+ { 0x0004b, W("kn") }, // Neutral Locale
+ { 0x0044b, W("kn-IN") },
+ { 0x00012, W("ko") }, // Neutral Locale
+ { 0x00412, W("ko-KR") },
+ { 0x00057, W("kok") }, // Neutral Locale
+ { 0x00457, W("kok-IN") },
+ { 0x00040, W("ky") }, // Neutral Locale
+ { 0x00440, W("ky-KG") },
+ { 0x0006e, W("lb") }, // Neutral Locale
+ { 0x0046e, W("lb-LU") },
+ { 0x00054, W("lo") }, // Neutral Locale
+ { 0x00454, W("lo-LA") },
+ { 0x00027, W("lt") }, // Neutral Locale
+ { 0x00427, W("lt-LT") },
+ { 0x00026, W("lv") }, // Neutral Locale
+ { 0x00426, W("lv-LV") },
+ { 0x00081, W("mi") }, // Neutral Locale
+ { 0x00481, W("mi-NZ") },
+ { 0x0002f, W("mk") }, // Neutral Locale
+ { 0x0042f, W("mk-MK") },
+ { 0x0004c, W("ml") }, // Neutral Locale
+ { 0x0044c, W("ml-IN") },
+ { 0x00050, W("mn") }, // Neutral Locale
+ { 0x00450, W("mn-MN") },
+ { 0x00850, W("mn-Mong-CN") },
+ { 0x0007c, W("moh") }, // Neutral Locale
+ { 0x0047c, W("moh-CA") },
+ { 0x0004e, W("mr") }, // Neutral Locale
+ { 0x0044e, W("mr-IN") },
+ { 0x0003e, W("ms") }, // Neutral Locale
+ { 0x0083e, W("ms-BN") },
+ { 0x0043e, W("ms-MY") },
+ { 0x0003a, W("mt") }, // Neutral Locale
+ { 0x0043a, W("mt-MT") },
+ { 0x00414, W("nb-NO") },
+ { 0x00061, W("ne") }, // Neutral Locale
+ { 0x00461, W("ne-NP") },
+ { 0x00013, W("nl") }, // Neutral Locale
+ { 0x00813, W("nl-BE") },
+ { 0x00413, W("nl-NL") },
+ { 0x00814, W("nn-NO") },
+ { 0x00014, W("no") }, // Neutral Locale
+ { 0x0006c, W("nso") }, // Neutral Locale
+ { 0x0046c, W("nso-ZA") },
+ { 0x00082, W("oc") }, // Neutral Locale
+ { 0x00482, W("oc-FR") },
+ { 0x00048, W("or") }, // Neutral Locale
+ { 0x00448, W("or-IN") },
+ { 0x00046, W("pa") }, // Neutral Locale
+ { 0x00446, W("pa-IN") },
+ { 0x00015, W("pl") }, // Neutral Locale
+ { 0x00415, W("pl-PL") },
+ { 0x0008c, W("prs") }, // Neutral Locale
+ { 0x0048c, W("prs-AF") },
+ { 0x00063, W("ps") }, // Neutral Locale
+ { 0x00463, W("ps-AF") },
+ { 0x00016, W("pt") }, // Neutral Locale
+ { 0x00416, W("pt-BR") },
+ { 0x00816, W("pt-PT") },
+ { 0x00501, W("qps-ploc") },
+ { 0x005fe, W("qps-ploca") },
+ { 0x009ff, W("qps-plocm") },
+ { 0x00086, W("qut") }, // Neutral Locale
+ { 0x00486, W("qut-GT") },
+ { 0x0006b, W("quz") }, // Neutral Locale
+ { 0x0046b, W("quz-BO") },
+ { 0x0086b, W("quz-EC") },
+ { 0x00c6b, W("quz-PE") },
+ { 0x00017, W("rm") }, // Neutral Locale
+ { 0x00417, W("rm-CH") },
+ { 0x00018, W("ro") }, // Neutral Locale
+ { 0x00418, W("ro-RO") },
+ { 0x00019, W("ru") }, // Neutral Locale
+ { 0x00419, W("ru-RU") },
+ { 0x00087, W("rw") }, // Neutral Locale
+ { 0x00487, W("rw-RW") },
+ { 0x0004f, W("sa") }, // Neutral Locale
+ { 0x0044f, W("sa-IN") },
+ { 0x00085, W("sah") }, // Neutral Locale
+ { 0x00485, W("sah-RU") },
+ { 0x0003b, W("se") }, // Neutral Locale
+ { 0x00c3b, W("se-FI") },
+ { 0x0043b, W("se-NO") },
+ { 0x0083b, W("se-SE") },
+ { 0x0005b, W("si") }, // Neutral Locale
+ { 0x0045b, W("si-LK") },
+ { 0x0001b, W("sk") }, // Neutral Locale
+ { 0x0041b, W("sk-SK") },
+ { 0x00024, W("sl") }, // Neutral Locale
+ { 0x00424, W("sl-SI") },
+ { 0x0183b, W("sma-NO") },
+ { 0x01c3b, W("sma-SE") },
+ { 0x0103b, W("smj-NO") },
+ { 0x0143b, W("smj-SE") },
+ { 0x0243b, W("smn-FI") },
+ { 0x0203b, W("sms-FI") },
+ { 0x0001c, W("sq") }, // Neutral Locale
+ { 0x0041c, W("sq-AL") },
+ { 0x07c1a, W("sr") }, // Neutral Locale
+ { 0x01c1a, W("sr-Cyrl-BA") },
+ { 0x00c1a, W("sr-Cyrl-CS") },
+ { 0x0181a, W("sr-Latn-BA") },
+ { 0x0081a, W("sr-Latn-CS") },
+ { 0x0001d, W("sv") }, // Neutral Locale
+ { 0x0081d, W("sv-FI") },
+ { 0x0041d, W("sv-SE") },
+ { 0x00041, W("sw") }, // Neutral Locale
+ { 0x00441, W("sw-KE") },
+ { 0x0005a, W("syr") }, // Neutral Locale
+ { 0x0045a, W("syr-SY") },
+ { 0x00049, W("ta") }, // Neutral Locale
+ { 0x00449, W("ta-IN") },
+ { 0x0004a, W("te") }, // Neutral Locale
+ { 0x0044a, W("te-IN") },
+ { 0x00028, W("tg") }, // Neutral Locale
+ { 0x00428, W("tg-Cyrl-TJ") },
+ { 0x0001e, W("th") }, // Neutral Locale
+ { 0x0041e, W("th-TH") },
+ { 0x00042, W("tk") }, // Neutral Locale
+ { 0x00442, W("tk-TM") },
+ { 0x00032, W("tn") }, // Neutral Locale
+ { 0x00432, W("tn-ZA") },
+ { 0x0001f, W("tr") }, // Neutral Locale
+ { 0x0041f, W("tr-TR") },
+ { 0x00044, W("tt") }, // Neutral Locale
+ { 0x00444, W("tt-RU") },
+ { 0x0005f, W("tzm") }, // Neutral Locale
+ { 0x0085f, W("tzm-Latn-DZ") },
+ { 0x00080, W("ug") }, // Neutral Locale
+ { 0x00480, W("ug-CN") },
+ { 0x00022, W("uk") }, // Neutral Locale
+ { 0x00422, W("uk-UA") },
+ { 0x00020, W("ur") }, // Neutral Locale
+ { 0x00420, W("ur-PK") },
+ { 0x00043, W("uz") }, // Neutral Locale
+ { 0x00843, W("uz-Cyrl-UZ") },
+ { 0x00443, W("uz-Latn-UZ") },
+ { 0x0002a, W("vi") }, // Neutral Locale
+ { 0x0042a, W("vi-VN") },
+ { 0x00088, W("wo") }, // Neutral Locale
+ { 0x00488, W("wo-SN") },
+ { 0x1007f, W("x-IV_mathan") }, // Alternate Sort
+ { 0x00034, W("xh") }, // Neutral Locale
+ { 0x00434, W("xh-ZA") },
+ { 0x0006a, W("yo") }, // Neutral Locale
+ { 0x0046a, W("yo-NG") },
+ { 0x00804, W("zh-CN") },
+ { 0x20804, W("zh-CN_stroke") }, // Alternate Sort
+ { 0x00004, W("zh-Hans") }, // Neutral Locale
+ { 0x07c04, W("zh-Hant") }, // Neutral Locale
+ { 0x00c04, W("zh-HK") },
+ { 0x01404, W("zh-MO") },
+ { 0x21404, W("zh-MO_stroke") }, // Alternate Sort
+ { 0x01004, W("zh-SG") },
+ { 0x21004, W("zh-SG_stroke") }, // Alternate Sort
+ { 0x00404, W("zh-TW") },
+ { 0x30404, W("zh-TW_pronun") }, // Alternate Sort
+ { 0x00035, W("zu") }, // Neutral Locale
+ { 0x00435, W("zu-ZA") }
+};
+
+// This is the data from l_intl.nls as released for XP for the uppercase table.
+// This is used for casing with OrdinalCompareStringIgnoreCase
+// since XP is the only scenario that needs data and the only data that it needs
+// is uppercasing, we duplicate the table here.
+const static WORD s_pUppercaseIndexTableXP[] = {
+0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0170, 0x0180, 0x0100, 0x0190, 0x0100, 0x0100, 0x01a0, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x01b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01d0, 0x01e0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01f0, 0x0200, 0x0210, 0x0220,
+0x0230, 0x0240, 0x0250, 0x0260, 0x0270, 0x0280, 0x0290, 0x02a0, 0x02b0, 0x02c0,
+0x02d0, 0x02e0, 0x02f0, 0x0300, 0x0310, 0x0320, 0x01c0, 0x01c0, 0x01c0, 0x0330,
+0x0340, 0x0350, 0x0360, 0x0370, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0380,
+0x0390, 0x03a0, 0x03b0, 0x03c0, 0x03d0, 0x03e0, 0x01c0, 0x01c0, 0x01c0, 0x03f0,
+0x0400, 0x0410, 0x0420, 0x0430, 0x0440, 0x0450, 0x0460, 0x0470, 0x0480, 0x0490,
+0x04a0, 0x04b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04c0, 0x04d0,
+0x04e0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04f0, 0x0500,
+0x0510, 0x0520, 0x0530, 0x0540, 0x0550, 0x0560, 0x0570, 0x0580, 0x0590, 0x05a0,
+0x05b0, 0x05c0, 0x05d0, 0x05e0, 0x05f0, 0x0600, 0x0610, 0x0620, 0x0630, 0x0640,
+0x0650, 0x0660, 0x01c0, 0x01c0, 0x01c0, 0x0670, 0x01c0, 0x0680, 0x0690, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06a0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06b0,
+0x06c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06d0, 0x06e0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0000,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0079, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfffe, 0x0000, 0x0000, 0xfffe,
+0x0000, 0x0000, 0xfffe, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xffb1,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+0x0000, 0xfffe, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2e,
+0xff32, 0x0000, 0xff33, 0xff33, 0x0000, 0xff36, 0x0000, 0xff35, 0x0000, 0x0000,
+0x0000, 0x0000, 0xff33, 0x0000, 0x0000, 0xff31, 0x0000, 0x0000, 0x0000, 0x0000,
+0xff2f, 0xff2d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2d, 0x0000, 0x0000,
+0xff2b, 0x0000, 0x0000, 0xff2a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff26, 0x0000, 0x0000,
+0x0000, 0x0000, 0xff26, 0x0000, 0xff27, 0xff27, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0xff25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0xffda, 0xffdb, 0xffdb, 0xffdb, 0x0000, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe1, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffc0, 0xffc1, 0xffc1, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0x0000, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0,
+0xffb0, 0xffb0, 0xffb0, 0x0000, 0xffb0, 0xffb0, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008,
+0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x004a, 0x004a, 0x0056, 0x0056, 0x0056, 0x0056, 0x0064, 0x0064,
+0x0080, 0x0080, 0x0070, 0x0070, 0x007e, 0x007e, 0x0000, 0x0000, 0x0008, 0x0008,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff0, 0xfff0, 0xfff0, 0xfff0,
+0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0,
+0xfff0, 0xfff0, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6,
+0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6,
+0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+// This is the data from casing.nls as released for Win2K3/XP for the lowercase table.
+// This is used for casing when linguistic casing is needed
+// since Win2K3/XP is the only scenario that needs data, we duplicate the table here.
+// It does NOT contain Turkic I exception behavior
+const static WORD s_pLowercaseIndexTableLinguisticXP[] = {
+0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0170, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0180, 0x0190, 0x0100, 0x01a0, 0x0100, 0x0100, 0x01b0, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x01c0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01e0, 0x01f0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0200, 0x0210, 0x01d0, 0x01d0, 0x0220, 0x0230,
+0x0240, 0x0250, 0x0260, 0x0270, 0x0280, 0x0290, 0x02a0, 0x02b0, 0x02c0, 0x02d0,
+0x02e0, 0x02f0, 0x0300, 0x0310, 0x0320, 0x0330, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0340, 0x0350,
+0x0360, 0x01d0, 0x01d0, 0x0370, 0x0380, 0x01d0, 0x0390, 0x03a0, 0x03b0, 0x01d0,
+0x01d0, 0x01d0, 0x03c0, 0x03d0, 0x03e0, 0x03f0, 0x0400, 0x0410, 0x0420, 0x0430,
+0x0440, 0x0450, 0x01d0, 0x01d0, 0x01d0, 0x0460, 0x0470, 0x0480, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0490, 0x04a0,
+0x04b0, 0x01d0, 0x01d0, 0x01d0, 0x04c0, 0x04d0, 0x04e0, 0x04f0, 0x0500, 0x0510,
+0x0520, 0x0530, 0x0540, 0x0550, 0x0560, 0x0570, 0x0580, 0x0590, 0x05a0, 0x05b0,
+0x05c0, 0x05d0, 0x05e0, 0x05f0, 0x0600, 0x0610, 0x0620, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x0630, 0x0640, 0x0650, 0x0660, 0x0670, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x0680, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x0690, 0x06a0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x06b0, 0x06c0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0,
+0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0xff39, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0xff87, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+0x0000, 0x0000, 0x0000, 0x00d2, 0x0001, 0x0000, 0x0001, 0x0000, 0x00ce, 0x0001,
+0x0000, 0x00cd, 0x00cd, 0x0001, 0x0000, 0x0000, 0x004f, 0x00ca, 0x00cb, 0x0001,
+0x0000, 0x00cd, 0x00cf, 0x0000, 0x00d3, 0x00d1, 0x0001, 0x0000, 0x0000, 0x0000,
+0x00d3, 0x00d5, 0x0000, 0x00d6, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0000, 0x0001, 0x0000, 0x00da, 0x0000, 0x0000, 0x0001, 0x0000, 0x00da, 0x0001,
+0x0000, 0x00d9, 0x00d9, 0x0001, 0x0000, 0x0001, 0x0000, 0x00db, 0x0001, 0x0000,
+0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001,
+0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001,
+0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0026, 0x0000,
+0x0025, 0x0025, 0x0025, 0x0000, 0x0040, 0x0000, 0x003f, 0x003f, 0x0000, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0xfff3, 0xfffa, 0xfff7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0000, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050,
+0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0050, 0x0050, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001,
+0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
+0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000,
+0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8,
+0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0xfff8, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xfff8, 0xfff8,
+0xfff8, 0xfff8, 0xfff8, 0xfff8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0xfff8, 0xfff8, 0xffb6, 0xffb6, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffaa, 0xffaa,
+0xffaa, 0xffaa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0xfff8, 0xfff8, 0xff9c, 0xff9c, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0xfff8, 0xfff8, 0xff90, 0xff90, 0xfff9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xff80, 0xff82, 0xff82,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
+0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001a, 0x001a, 0x001a, 0x001a,
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a,
+0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a, 0x001a,
+0x001a, 0x001a, 0x0000, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
+0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000
+};
+
+// This is the data from casing.nls as released for Win2K3/XP for the uppercase table.
+// This is used for casing when linguistic casing is needed
+// since Win2K3/XP is the only scenario that needs data, we duplicate the table here.
+// It does NOT contain Turkic I exception behavior
+const static WORD s_pUppercaseIndexTableLinguisticXP[] = {
+0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0170, 0x0180, 0x0100, 0x0190, 0x0100, 0x0100, 0x01a0, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x01b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01d0, 0x01e0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01f0, 0x0200, 0x0210, 0x0220,
+0x0230, 0x0240, 0x0250, 0x0260, 0x0270, 0x0280, 0x0290, 0x02a0, 0x02b0, 0x02c0,
+0x02d0, 0x02e0, 0x02f0, 0x0300, 0x0310, 0x0320, 0x01c0, 0x01c0, 0x01c0, 0x0330,
+0x0340, 0x0350, 0x0360, 0x0370, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0380,
+0x0390, 0x03a0, 0x03b0, 0x03c0, 0x03d0, 0x03e0, 0x01c0, 0x01c0, 0x01c0, 0x03f0,
+0x0400, 0x0410, 0x0420, 0x0430, 0x0440, 0x0450, 0x0460, 0x0470, 0x0480, 0x0490,
+0x04a0, 0x04b0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04c0, 0x04d0,
+0x04e0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x04f0, 0x0500,
+0x0510, 0x0520, 0x0530, 0x0540, 0x0550, 0x0560, 0x0570, 0x0580, 0x0590, 0x05a0,
+0x05b0, 0x05c0, 0x05d0, 0x05e0, 0x05f0, 0x0600, 0x0610, 0x0620, 0x0630, 0x0640,
+0x0650, 0x0660, 0x01c0, 0x01c0, 0x01c0, 0x0670, 0x01c0, 0x0680, 0x0690, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06a0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06b0,
+0x06c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x06d0, 0x06e0, 0x01c0, 0x01c0,
+0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0000,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0x0079, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xff18, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0xfffe,
+0x0000, 0xffff, 0xfffe, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000,
+0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0xffb1,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+0xffff, 0xfffe, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2e,
+0xff32, 0x0000, 0xff33, 0xff33, 0x0000, 0xff36, 0x0000, 0xff35, 0x0000, 0x0000,
+0x0000, 0x0000, 0xff33, 0x0000, 0x0000, 0xff31, 0x0000, 0x0000, 0x0000, 0x0000,
+0xff2f, 0xff2d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff2d, 0x0000, 0x0000,
+0xff2b, 0x0000, 0x0000, 0xff2a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff26, 0x0000, 0x0000,
+0x0000, 0x0000, 0xff26, 0x0000, 0xff27, 0xff27, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0xff25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001a, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0xffda, 0xffdb, 0xffdb, 0xffdb, 0xfffb, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe1, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffc0, 0xffc1, 0xffc1, 0x0000,
+0xffc2, 0xffc7, 0x0000, 0x0000, 0x0000, 0xffd1, 0xffca, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0xffaa, 0xffb0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0x0000, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0, 0xffb0,
+0xffb0, 0xffb0, 0xffb0, 0x0000, 0xffb0, 0xffb0, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+0xffff, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0,
+0xffd0, 0xffd0, 0xffd0, 0xffd0, 0xffd0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff,
+0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008,
+0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0008, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0008, 0x0008,
+0x0008, 0x0008, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x004a, 0x004a, 0x0056, 0x0056, 0x0056, 0x0056, 0x0064, 0x0064,
+0x0080, 0x0080, 0x0070, 0x0070, 0x007e, 0x007e, 0x0000, 0x0000, 0x0008, 0x0008,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0008, 0x0008, 0x0000, 0x0000, 0x0000, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfff0, 0xfff0, 0xfff0, 0xfff0,
+0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0, 0xfff0,
+0xfff0, 0xfff0, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6,
+0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6,
+0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0xffe6, 0x0000, 0x0000,
+0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0,
+0xffe0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static CRITSEC_COOKIE g_pDownlevelNlsCrst = NULL;
+
+static void DelayCreateCriticalSection()
+{
+ // Lazily allocate a Crst to serialize update access to the info structure.
+ // Carefully synchronize to ensure we don't leak a Crst in race conditions.
+ if (g_pDownlevelNlsCrst == NULL)
+ {
+ CRITSEC_COOKIE pCrst = ClrCreateCriticalSection(CrstNls,
+ (CrstFlags)(CRST_UNSAFE_COOPGC));
+
+ if (InterlockedCompareExchangeT(&g_pDownlevelNlsCrst, pCrst, NULL) != NULL)
+ {
+ ClrDeleteCriticalSection(pCrst);
+ }
+ }
+}
+
+static void NormalizeCultureName(SString& sName)
+{
+ for (SString::Iterator i = sName.Begin(); i[0] != W('\0'); ++i)
+ {
+ sName.Replace(i,towlower(i[0]));
+ }
+}
+
+static inline BOOL IsTurkicILcid(LCID lcid)
+{
+ return (PRIMARYLANGID(lcid) == LANG_TURKISH
+ || (PRIMARYLANGID(lcid) == LANG_AZERI
+ && lcid != LCID_AZ_CYRL_AZ));
+}
+
+namespace DownLevel
+{
+
+ int GetSystemDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName)
+ {
+ LCID lcid=GetSystemDefaultLCID();
+ if (lcid == 0)
+ return 0;
+ return NewApis::LCIDToLocaleName(lcid,lpLocaleName,cchLocaleName,0);
+ };
+
+ // Xp returns lcid 0x0404 (zh-TW) for GetUserDefaultUILanguage and GetSystemDefaultUILanguage
+ // on a CHH sku (expected 0x0c04) as well as a CHT sku (as expected)
+ inline LCID WorkAroundXpTaiwanHongKongBug(LCID lcid)
+ {
+ const LCID LANGID_ZH_TW = 0x0404;
+ const LCID LANGID_ZH_HK = 0x0c04;
+
+ if(lcid == LANGID_ZH_TW && !NewApis::IsZhTwSku())
+ {
+ lcid = LANGID_ZH_HK;
+ }
+ return lcid;
+ }
+
+ DWORD GetUserPreferredUILanguages (__in DWORD dwFlags, __out PULONG pulNumLanguages, __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer, __in PULONG pcchLanguagesBuffer)
+ {
+ _ASSERTE(dwFlags==MUI_LANGUAGE_NAME);
+
+ LCID lcid=GetUserDefaultUILanguage();
+ if (lcid == 0)
+ return 0;
+
+ lcid = WorkAroundXpTaiwanHongKongBug(lcid);
+
+ WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH];
+ if (NewApis::LCIDToLocaleName(lcid, wszBuffer, NumItems(wszBuffer), 0) == 0)
+ return 0;
+
+ SIZE_T sLen=wcslen(wszBuffer)+2;
+ ULONG uLen = (ULONG)sLen;
+ if (uLen != sLen)
+ {
+ SetLastError(ERROR_ARITHMETIC_OVERFLOW);
+ return 0;
+ }
+
+ *pulNumLanguages=1;
+ if (pwszLanguagesBuffer == NULL)
+ {
+ *pcchLanguagesBuffer=uLen;
+ return 1;
+ }
+
+ if (sLen > *pcchLanguagesBuffer)
+ {
+ *pcchLanguagesBuffer=uLen;
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+
+ *pcchLanguagesBuffer=uLen;
+ wcscpy_s(pwszLanguagesBuffer, sLen, wszBuffer);
+ pwszLanguagesBuffer[sLen-1]=W('\0');
+ SetLastError(0);
+ return 1;
+ }
+
+
+ int GetUserDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName)
+ {
+ LCID lcid=GetUserDefaultLCID();
+ if (lcid == 0)
+ return 0;
+ return NewApis::LCIDToLocaleName(lcid,lpLocaleName,cchLocaleName,0);
+ }
+
+ int GetDateFormatEx(__in LPCWSTR lpLocaleName, __in DWORD dwFlags, __in_opt CONST SYSTEMTIME* lpDate, __in_opt LPCWSTR lpFormat,
+ __out_ecount(cchDate) LPWSTR lpDateStr, __in int cchDate, __in_opt LPCWSTR lpCalendar)
+ {
+ _ASSERTE(lpCalendar==NULL);
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+ return GetDateFormatW(lcid, dwFlags, lpDate, lpFormat, lpDateStr, cchDate);
+ }
+
+
+ int GetLocaleInfoEx (__in LPCWSTR lpLocaleName, __in LCTYPE LCType, __out_ecount_opt(cchData) LPWSTR lpLCData, __in int cchData)
+ {
+ _ASSERTE((lpLCData == NULL && cchData == 0) || (lpLCData != NULL && cchData > 0));
+
+ // Note that this'll return neutral LCIDs
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+
+ int iRetCode = 0;
+ // Special casing LOCALE_SPARENT to do Uplevel fallback.
+ if ( (LCType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_SPARENT )
+ {
+ // OS doesn't know some LCTypes
+ iRetCode = UplevelFallback::GetLocaleInfoEx(lpLocaleName, lcid, LCType, lpLCData, cchData);
+ }
+ else
+ {
+ iRetCode=GetLocaleInfoW(lcid,LCType,lpLCData,cchData);
+ if (iRetCode == 0 && GetLastError() == ERROR_INVALID_FLAGS)
+ {
+ // OS doesn't know some LCTypes
+ iRetCode = UplevelFallback::GetLocaleInfoEx(lpLocaleName, lcid, LCType, lpLCData, cchData);
+ }
+ }
+
+ return iRetCode;
+ };
+
+ //
+ // TurkishCompareStringIgnoreCase
+ // In downlevel platforms CompareString doesn't support Turkish I comparison. TurkishCompareStringIgnoreCase is to work around for this.
+ // In Turkish and Azeri cultures:
+ // ToUpper(i) = u0130 (Upper case I with dot above it)
+ // ToLower(I) = u0131 (Lower case i with no dot above).
+ // Toupper(i) != I
+ // ToLower(I) != i
+ // TurkishCompareStringIgnoreCase will scan the input strings and convert every i to u0130 and every I to u0131 and then call
+ // the system CompareString.
+ // if lpString1 not include i, I, u0130, and u0131 then we'll just call the system CompareString otherwise we'll scan the lpString2
+ // to detect if we need to do the conversions mentioned above.
+ //
+
+ #define TURKISH_CAPITAL_I_DOT_ABOVE ((WCHAR) 0x0130)
+ #define TURKISH_LOWERCASE_DOTLESS_I ((WCHAR) 0x0131)
+ #define LATIN_LOWERCASE_I_DOT_ABOVE ('i')
+ #define LATIN_CAPITAL_DOTLESS_I ('I')
+
+ int TurkishCompareStringIgnoreCase(LCID lcid, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2)
+ {
+ int str1Index = 0;
+ int str2Index = 0;
+ BOOL fScanStr2 = FALSE;
+
+ for (str1Index=0; str1Index<cchCount1; str1Index++)
+ {
+ if (lpString1[str1Index] == LATIN_LOWERCASE_I_DOT_ABOVE
+ || lpString1[str1Index] == LATIN_CAPITAL_DOTLESS_I)
+ {
+ break;
+ }
+ else if (lpString1[str1Index] == TURKISH_CAPITAL_I_DOT_ABOVE
+ || lpString1[str1Index] == TURKISH_LOWERCASE_DOTLESS_I)
+ {
+ fScanStr2 = TRUE;
+ }
+ }
+
+ if (str1Index >= cchCount1)
+ {
+ if (!fScanStr2)
+ {
+ return ::CompareStringW(lcid, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
+ }
+
+ for (str2Index=0; str2Index<cchCount2; str2Index++)
+ {
+ if (lpString2[str2Index] == LATIN_LOWERCASE_I_DOT_ABOVE
+ || lpString2[str2Index] == LATIN_CAPITAL_DOTLESS_I)
+ {
+ break;
+ }
+ }
+
+ if (str2Index >= cchCount2)
+ {
+ return ::CompareStringW(lcid, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
+ }
+ }
+
+ NewArrayHolder<WCHAR> pBuffer = new WCHAR[cchCount1 + cchCount2];
+
+ if (str1Index>0)
+ {
+ memcpy_s(pBuffer, cchCount1 * sizeof(WCHAR), lpString1, str1Index * sizeof(WCHAR));
+ }
+
+ for (; str1Index<cchCount1; str1Index++)
+ {
+ pBuffer[str1Index] = (lpString1[str1Index] == LATIN_LOWERCASE_I_DOT_ABOVE)
+ ? TURKISH_CAPITAL_I_DOT_ABOVE
+ : ((lpString1[str1Index] == LATIN_CAPITAL_DOTLESS_I)
+ ? TURKISH_LOWERCASE_DOTLESS_I
+ : lpString1[str1Index]);
+ }
+
+ if (str2Index>0)
+ {
+ memcpy_s(&pBuffer[cchCount1], cchCount2 * sizeof(WCHAR), lpString2, str2Index * sizeof(WCHAR));
+ }
+
+ for (; str2Index<cchCount2; str2Index++)
+ {
+ pBuffer[cchCount1 + str2Index] = (lpString2[str2Index] == LATIN_LOWERCASE_I_DOT_ABOVE)
+ ? TURKISH_CAPITAL_I_DOT_ABOVE
+ : ((lpString2[str2Index] == LATIN_CAPITAL_DOTLESS_I)
+ ? TURKISH_LOWERCASE_DOTLESS_I
+ : lpString2[str2Index]);
+ }
+
+ return ::CompareStringW(lcid, dwCmpFlags, pBuffer, cchCount1, &pBuffer[cchCount1], cchCount2);
+ }
+
+ int CompareStringEx(__in LPCWSTR lpLocaleName, __in DWORD dwCmpFlags, __in_ecount(cchCount1) LPCWSTR lpString1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR lpString2,
+ __in int cchCount2, __in_opt LPNLSVERSIONINFO lpVersionInformation, __in_opt LPVOID lpReserved, __in_opt LPARAM lParam )
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ PRECONDITION(CheckPointer(lpLocaleName));
+ PRECONDITION(CheckPointer(lpString1));
+ PRECONDITION(CheckPointer(lpString2));
+ } CONTRACTL_END;
+
+ // Invariant is like en-US (default table) on downlevel (pre-vista) OS's
+ if (lpLocaleName[0] == W('\0'))
+ {
+ // For now, use "en" instead.
+ lpLocaleName = W("en-US");
+ }
+
+ // Get an LCID to call the downlevel function
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+
+ // Need to remap flags to get rid of Vista flags and replace with downlevel flags
+ // (not used at this moment)
+ if ((dwCmpFlags & LINGUISTIC_IGNOREDIACRITIC)!=0)
+ {
+ dwCmpFlags -= LINGUISTIC_IGNOREDIACRITIC;
+ dwCmpFlags |= NORM_IGNORENONSPACE;
+ }
+ if ((dwCmpFlags & LINGUISTIC_IGNORECASE)!=0)
+ {
+ dwCmpFlags -= LINGUISTIC_IGNORECASE;
+ dwCmpFlags |= NORM_IGNORECASE;
+ }
+ dwCmpFlags &= (~NORM_LINGUISTIC_CASING);
+
+ if (((dwCmpFlags & NORM_IGNORECASE)!=0) && IsTurkicILcid(lcid))
+ {
+ return TurkishCompareStringIgnoreCase(lcid, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2);
+ }
+
+ return ::CompareStringW(lcid,dwCmpFlags,lpString1,cchCount1,lpString2,cchCount2);
+ }
+
+ __inline WCHAR MapCase(const WORD * table, WCHAR wch)
+ {
+ WORD value = table[wch >> 8];
+ value = table[value + ((wch >> 4) & 0xf)];
+ value = table[value + (wch & 0xf)];
+ return (wch + (int)((short)value));
+ }
+
+ // since this should only be run on XP, we just use the same table as XP
+ __inline WCHAR ToUpperXP(WCHAR wch)
+ {
+ return MapCase(s_pUppercaseIndexTableXP, wch);
+ }
+
+ INT32 CompareOrdinalIgnoreCaseHelper(__in_ecount(count) DWORD* strAChars, __in_ecount(count) DWORD* strBChars, int count)
+ {
+ if( count == 0)
+ return 0;
+
+ int temp = 0;
+
+ _ASSERTE( count >0);
+
+ // Only go through fast code path if two strings have the same alignment
+ if (((size_t)strAChars & 3) == ((size_t)strBChars & 3)) {
+ int unalignedBytesA = (size_t)strAChars & 3;
+
+ _ASSERTE(unalignedBytesA == 0 || unalignedBytesA == 2);
+ // First try to make the strings aligned at DWORD boundary.
+ if( unalignedBytesA != 0 ) {
+ LPWSTR ptr1 = (LPWSTR)strAChars;
+ LPWSTR ptr2 = (LPWSTR)strBChars;
+
+ if (*ptr1 != *ptr2) {
+ temp = ((int)ToUpperXP(*ptr1) - (int)ToUpperXP(*ptr2));
+ if( temp != 0) {
+ return temp;
+ }
+ }
+
+ --count;
+ strAChars = (DWORD *)(ptr1 + 1);
+ strBChars = (DWORD *)(ptr2 + 1);
+ }
+
+ // Loop comparing a DWORD at a time.
+ while (count >= 2) {
+ _ASSERTE(IS_ALIGNED((size_t)strAChars, 4) && IS_ALIGNED((size_t)strBChars, 4));
+ if ((*strAChars - *strBChars) != 0) {
+ LPCWSTR ptr1 = (WCHAR*)strAChars;
+ LPCWSTR ptr2 = (WCHAR*)strBChars;
+
+ if (*ptr1 != *ptr2) {
+ temp = ((int)ToUpperXP(*ptr1) - (int)ToUpperXP(*ptr2));
+ }
+ if (temp != 0) {
+ return (temp);
+ }
+
+ temp = (int)ToUpperXP(*(ptr1+1)) - (int)ToUpperXP(*(ptr2+1));
+ if (temp != 0) {
+ return (temp);
+ }
+ }
+ ++strBChars;
+ ++strAChars;
+ count -= 2;
+ }
+ }
+
+ // We can exit the loop when we see two different DWORDs and one of them contains surrogate
+ // or they are equal after case conversion.
+ // We can also exit the loop when there is no or only one character left.
+ if( count == 0) {
+ return 0;
+ }
+
+ // we need to handle one special case here. Say we have two strings like:
+ // A HS1 LS1 HS2 LS2 or A HS1 LS1
+ // A HS1 LS2 HS2 LS2 or A HS1 NS
+ // we need to go back a char to decide the order
+ LPCWSTR pwStrB = (LPWSTR)strBChars;
+ LPCWSTR pwStrA = (LPWSTR)strAChars;
+
+ temp = 0;
+ while ((count--) > 0)
+ {
+ WCHAR charA = *pwStrA++;
+ WCHAR charB = *pwStrB++;
+
+ if( charA != charB) {
+ charA = ToUpperXP(charA);
+ charB = ToUpperXP(charB);
+
+ temp = (int)charA - (int)charB;
+
+ if (temp != 0) {
+ return (temp);
+ }
+ }
+ }
+
+ return 0;
+ }
+
+
+ int CompareStringOrdinal(__in_ecount(cchCount1) LPCWSTR string1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR string2, __in int cchCount2, __in BOOL bIgnoreCase)
+ {
+ // This should only happen for IgnoreCase == true. The rest are hard coded
+ if (!bIgnoreCase)
+ {
+ return 0;
+ }
+
+ DWORD *strAChars, *strBChars;
+ strAChars = (DWORD*)string1;
+ strBChars = (DWORD*)string2;
+
+ // If the strings are the same length, compare exactly the right # of chars.
+ // If they are different, compare the shortest # + 1 (the '\0').
+ int count = cchCount1;
+ if( count > cchCount2)
+ count = cchCount2;
+
+ INT32 ret = CompareOrdinalIgnoreCaseHelper(strAChars, strBChars, count);
+ if( ret == 0) {
+ ret = cchCount1 - cchCount2;
+ }
+
+ if (ret > 0)
+ {
+ return CSTR_GREATER_THAN;
+ }
+ if (ret < 0)
+ {
+ return CSTR_LESS_THAN;
+ }
+
+ return CSTR_EQUAL;
+ }
+
+ inline bool HasOnlyUppercaseOrLowercaseFlag(__in DWORD flags)
+ {
+ if(((flags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))== 0)
+ || MORE_THAN_ONE(flags, LCMAP_UPPERCASE)
+ || MORE_THAN_ONE(flags, LCMAP_LOWERCASE))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ int TurkicICasing( __in DWORD flags,
+ __in_ecount(cchSrc) LPCWSTR source,
+ __in int cchSrc,
+ __out_ecount(cchDest) LPWSTR destination,
+ __in int cchDest)
+ {
+ _ASSERTE(source != NULL);
+ _ASSERTE(cchSrc > 0);
+ _ASSERTE(destination != NULL);
+ _ASSERTE(cchSrc == cchDest);
+ _ASSERTE(HasOnlyUppercaseOrLowercaseFlag(flags));
+
+ const WORD * table;
+ WCHAR dottedI, mappedDottedI;
+ WCHAR dotlessI, mappedDotlessI;
+
+ if ((flags & LCMAP_UPPERCASE) != 0)
+ {
+ table = s_pUppercaseIndexTableLinguisticXP;
+ dottedI = LATIN_LOWERCASE_I_DOT_ABOVE;
+ mappedDottedI = TURKISH_CAPITAL_I_DOT_ABOVE;
+ dotlessI = TURKISH_LOWERCASE_DOTLESS_I;
+ mappedDotlessI = LATIN_CAPITAL_DOTLESS_I;
+ }
+ else
+ {
+ table = s_pLowercaseIndexTableLinguisticXP;
+ dottedI = TURKISH_CAPITAL_I_DOT_ABOVE;
+ mappedDottedI = LATIN_LOWERCASE_I_DOT_ABOVE;
+ dotlessI = LATIN_CAPITAL_DOTLESS_I;
+ mappedDotlessI = TURKISH_LOWERCASE_DOTLESS_I;
+ }
+
+ for (int i = 0; i < cchSrc && i < cchDest; ++i)
+ {
+ if (source[i] == dottedI)
+ {
+ destination[i] = mappedDottedI;
+ }
+ else if (source[i] == dotlessI)
+ {
+ destination[i] = mappedDotlessI;
+ }
+ else {
+ destination[i] = MapCase(table, source[i]);
+ }
+ }
+
+ return cchSrc;
+ }
+
+ int DefaultLinguisticCasing( __in DWORD flags,
+ __in_ecount(cchSrc) LPCWSTR source,
+ __in int cchSrc,
+ __out_ecount(cchDest) LPWSTR destination,
+ __in int cchDest)
+ {
+ _ASSERTE(source != NULL);
+ _ASSERTE(cchSrc > 0);
+ _ASSERTE(destination != NULL);
+ _ASSERTE(cchSrc == cchDest);
+ _ASSERTE(HasOnlyUppercaseOrLowercaseFlag(flags));
+
+ const WORD * table;
+
+ if ((flags & LCMAP_UPPERCASE) != 0)
+ {
+ table = s_pUppercaseIndexTableLinguisticXP;
+ }
+ else
+ {
+ table = s_pLowercaseIndexTableLinguisticXP;
+ }
+
+ for (int i = 0; i < cchSrc && i < cchDest; ++i)
+ {
+ destination[i] = MapCase(table, source[i]);
+ }
+
+ return cchSrc;
+ }
+
+ int LinguisticCaseString(__in LCID lcid,__in DWORD flags,
+ __in_ecount(cchSrc) const WCHAR * source,
+ __in int cchSrc,
+ __out_ecount_opt(cchDest) WCHAR * destination,
+ __in int cchDest)
+ {
+ _ASSERTE(lcid != 0);
+
+ if ( (cchSrc == 0) || (cchDest < 0) || (source == NULL) ||
+ ((cchDest != 0) && (destination == NULL)) )
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return (0);
+ }
+
+ if(!HasOnlyUppercaseOrLowercaseFlag(flags))
+ {
+ SetLastError(ERROR_INVALID_FLAGS);
+ return (0);
+ }
+
+ if(cchSrc < 0)
+ {
+ cchSrc = (int)wcslen(source);
+ }
+
+ if(destination == NULL)
+ {
+ return cchSrc;
+ }
+
+ if(IsTurkicILcid(lcid))
+ {
+ return TurkicICasing(flags,
+ source, cchSrc,
+ destination, cchDest);
+ }
+ else
+ {
+ return DefaultLinguisticCasing(flags,
+ source, cchSrc,
+ destination, cchDest);
+ }
+
+ }
+
+#ifdef _MSC_VER
+// Get rid of size OACR requirement.
+#pragma warning(push)
+#pragma warning(disable:25057)
+#endif
+ int LCMapStringEx(__in LPCWSTR lpLocaleName,
+ __in DWORD dwMapFlags,
+ __in_ecount(cchSrc) LPCWSTR lpSrcStr,
+ __in int cchSrc,
+ __out_xcount_opt(cchDest) LPWSTR lpDestStr,
+ __in int cchDest,
+ __in_opt LPNLSVERSIONINFO lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ {
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+
+ // Need to remap flags to get rid of Vista flags and replace with downlevel flags
+ // (not used at this moment)
+ if ((dwMapFlags & LINGUISTIC_IGNOREDIACRITIC)!=0)
+ {
+ dwMapFlags -= LINGUISTIC_IGNOREDIACRITIC;
+ dwMapFlags |= NORM_IGNORENONSPACE;
+ }
+ if ((dwMapFlags & LINGUISTIC_IGNORECASE)!=0)
+ {
+ dwMapFlags -= LINGUISTIC_IGNORECASE;
+ dwMapFlags |= NORM_IGNORECASE;
+ }
+ dwMapFlags &= (~NORM_LINGUISTIC_CASING);
+
+// START WORK_AROUND_WIN2K3_LCMAPSTRING_BUG
+ if((dwMapFlags & LCMAP_LINGUISTIC_CASING) != 0
+ && (dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE)) != 0)
+ {
+ dwMapFlags &= (~LCMAP_LINGUISTIC_CASING);
+
+ return LinguisticCaseString(lcid, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);
+ }
+// END WORK_AROUND_WIN2K3_LCMAPSTRING_BUG
+ return ::LCMapStringW(lcid,dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);
+ }
+
+ int FindNLSStringEx(__in LPCWSTR lpLocaleName,
+ __in DWORD dwFindNLSStringFlags,
+ __in_ecount(cchSource) LPCWSTR lpStringSource,
+ __in int cchSource,
+ __in_ecount(cchValue) LPCWSTR lpStringValue,
+ __in int cchValue,
+ __out_opt LPINT pcchFound,
+ __in_opt LPNLSVERSIONINFO lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ int retValue = -1;
+
+ if (lpLocaleName == NULL || lpStringSource == NULL || lpStringValue == NULL)
+ {
+ return retValue;
+ }
+
+ if (dwFindNLSStringFlags & (FIND_ENDSWITH | FIND_FROMEND))
+ {
+ retValue = NewApis::LastIndexOfString(lpLocaleName, lpStringSource, cchSource, lpStringValue, cchValue, (int) dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION, dwFindNLSStringFlags & FIND_ENDSWITH);
+ }
+ else
+ {
+ retValue = NewApis::IndexOfString(lpLocaleName, lpStringSource, cchSource, lpStringValue, cchValue, (int) dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION, dwFindNLSStringFlags & FIND_STARTSWITH);
+ }
+ return retValue;
+ }
+
+
+ // Mac and Windows <= Windows XP
+ // Note: "Function" is unused, always handles sorting for now
+ // Note: "dwFlags" is unused, we don't have flags for now
+ // Note: "lpVersionInfo" is unused, we always presume the current version
+ BOOL IsNLSDefinedString(NLS_FUNCTION Function, DWORD dwFlags, LPNLSVERSIONINFOEX lpVersionInfo, LPCWSTR pString, int nStringLen )
+ {
+ // Ported downlevel code from comnlsinfo.cpp
+
+ CQuickBytes buffer;
+ buffer.AllocThrows(16);
+ int ich = 0;
+
+ while(ich < nStringLen)
+ {
+ WCHAR wch = pString[ich];
+
+ int dwBufSize=NewApis::LCMapStringEx(W("en-US"),LCMAP_SORTKEY|SORT_STRINGSORT,pString+ich,1,(LPWSTR)buffer.Ptr(),
+ (int)(buffer.Size()/sizeof(WCHAR)),NULL,NULL,0);
+
+ if (dwBufSize == 0)
+ {
+ buffer.AllocThrows(buffer.Size()*2);
+ continue; // try again
+ }
+
+ if (LPBYTE(buffer.Ptr())[0] == 0x1) // no weight
+ {
+ //
+ // Check for the NULL case and formatting characters case. Not
+ // defined but valid.
+ //
+ switch(wch)
+ {
+ case 0x0000: // NULL
+ case 0x0640: // TATWEEL
+ case 0x180b: // MONGOLIAN FVS 1
+ case 0x180c: // MONGOLIAN FVS 2
+ case 0x180d: // MONGOLIAN FVS 3
+ case 0x180e: // MONGOLIAN VOWEL SEPERATOR
+ case 0x200c: // ZWNJ
+ case 0x200d: // ZWJ
+ case 0x200e: // LRM
+ case 0x200f: // RLM
+ case 0x202a: // LRE
+ case 0x202b: // RLE
+ case 0x202c: // PDF
+ case 0x202d: // LRO
+ case 0x202e: // RLO
+ case 0x206a: // ISS
+ case 0x206b: // SSS
+ case 0x206c: // IAFS
+ case 0x206d: // AAFS
+ case 0x206e: // NATIONAL DS
+ case 0x206f: // NOMINAL DS
+ case 0xfeff: // ZWNBSP
+ case 0xfff9: // IAA
+ case 0xfffa: // IAS
+ case 0xfffb: // IAT
+ case 0xfffc: // ORC
+ case 0xfffd: // RC
+ ich++;
+ continue;
+
+ default:
+ return (FALSE);
+ }
+ }
+
+ //
+ // Eliminate Private Use characters. They are defined but cannot be considered
+ // valid because AD-style apps should not use them in identifiers.
+ //
+ if ((wch >= PRIVATE_USE_BEGIN) && (wch <= PRIVATE_USE_END))
+ {
+ return (FALSE);
+ }
+
+ //
+ // Eliminate invalid surogates pairs or single surrogates. Basically, all invalid
+ // high surrogates have aleady been filtered (above) since they are unsortable.
+ // All that is left is to check for standalone low surrogates and valid high
+ // surrogates without corresponding low surrogates.
+ //
+
+ if ((wch >= LOW_SURROGATE_START) && (wch <= LOW_SURROGATE_END))
+ {
+ // Leading low surrogate
+ return (FALSE);
+ }
+ else if ((wch >= HIGH_SURROGATE_START) && (wch <= HIGH_SURROGATE_END))
+ {
+ // Leading high surrogate
+ if ( ((ich + 1) < nStringLen) && // Surrogates not the last character
+ (pString[ich+1] >= LOW_SURROGATE_START) && (pString[ich+1] <= LOW_SURROGATE_END)) // Low surrogate
+ {
+ // Valid surrogates pair, High followed by a low surrogate. Skip the pair!
+ ich++;
+ }
+ else
+ {
+ // High surrogate without low surrogate, so exit.
+ return (FALSE);
+ }
+ }
+
+ ich++;
+
+ }
+ return (TRUE);
+ }
+
+
+ int GetCalendarInfoEx(__in LPCWSTR lpLocaleName,
+ __in CALID Calendar,
+ __in_opt LPCWSTR pReserved,
+ __in CALTYPE CalType,
+ __out_ecount_opt(cchData) LPWSTR lpCalData,
+ __in int cchData,
+ __out_opt LPDWORD lpValue)
+ {
+
+ _ASSERTE((lpCalData == NULL && cchData == 0) || (lpCalData != NULL && cchData > 0));
+ if ((CalType & CAL_RETURN_NUMBER))
+ {
+ // If CAL_RETURN_NUMBER, lpValue must be non-null and lpCalData must be null
+ _ASSERTE((lpValue != NULL) && (lpCalData == NULL));
+ }
+
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+
+ // zh-HK has English month/day names in older OS's, so we need to fix that (pre-Vista OS's)
+ if (lcid == 0x0c04 && Calendar == CAL_GREGORIAN &&
+ ((CalType >= CAL_SDAYNAME1 && CalType <= CAL_SABBREVMONTHNAME13) ||
+ (CalType >= CAL_SSHORTESTDAYNAME1 && CalType <= CAL_SSHORTESTDAYNAME7)))
+ {
+ // zh-TW has the English names for those month/day name values
+ lcid = 0x0404;
+ }
+
+ if (lcid == 0)
+ return 0;
+ return ::GetCalendarInfoW(lcid, Calendar, CalType, lpCalData, cchData, lpValue );
+ }
+
+
+ namespace LegacyCallbacks
+ {
+ LPARAM lDateFormatParam = NULL;
+ DATEFMT_ENUMPROCEXEX realDateCallback = NULL;
+
+ BOOL CALLBACK EnumDateFormatsProcWrapper(__in_z LPTSTR lpDateFormatString, __in CALID Calendar)
+ {
+ if (realDateCallback != NULL && lDateFormatParam != NULL)
+ return realDateCallback(lpDateFormatString, Calendar, lDateFormatParam);
+
+ // Didn't have the globals, fail
+ return false;
+ };
+
+ BOOL EnumDateFormatsExEx(DATEFMT_ENUMPROCEXEX lpDateFmtEnumProcExEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam)
+ {
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+
+ DelayCreateCriticalSection();
+
+ BOOL ret = false;
+ {
+ CRITSEC_Holder sCrstHolder(g_pDownlevelNlsCrst);
+
+ // Store our real callback and lParam
+ lDateFormatParam = lParam;
+ realDateCallback = lpDateFmtEnumProcExEx;
+ ret = EnumDateFormatsExW(EnumDateFormatsProcWrapper, lcid, dwFlags);
+ }
+ return ret;
+ };
+
+ LPARAM lTimeFormatParam = NULL;
+ TIMEFMT_ENUMPROCEX realTimeCallback = NULL;
+
+ BOOL CALLBACK EnumTimeFormatsProcWrapper(__in_z LPTSTR lpTimeFormatString)
+ {
+ if (realTimeCallback != NULL && lTimeFormatParam != NULL)
+ return realTimeCallback(lpTimeFormatString, lTimeFormatParam);
+
+ // Didn't have the globals, fail
+ return false;
+ };
+
+ BOOL EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX lpTimeFmtEnumProcEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam)
+ {
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+
+ DelayCreateCriticalSection();
+
+ BOOL ret = false;
+ {
+ CRITSEC_Holder sCrstHolder(g_pDownlevelNlsCrst);
+
+ // Store our real callback and lParam
+ lTimeFormatParam = lParam;
+ realTimeCallback = lpTimeFmtEnumProcEx;
+ ret = EnumTimeFormatsW(EnumTimeFormatsProcWrapper, lcid, dwFlags);
+ }
+ return ret;
+ };
+
+ LPARAM lCalendarInfoParam = NULL;
+ CALINFO_ENUMPROCEXEX realCalendarInfoCallback = NULL;
+
+ BOOL CALLBACK EnumCalendarInfoProcWrapper(__in_z LPTSTR lpCalendarInfoString, __in CALID Calendar)
+ {
+ if (realCalendarInfoCallback != NULL && lCalendarInfoParam != NULL)
+ return realCalendarInfoCallback(lpCalendarInfoString, Calendar, NULL, lCalendarInfoParam);
+
+ // Didn't have the globals, fail
+ return false;
+ };
+
+ BOOL EnumCalendarInfoExEx(CALINFO_ENUMPROCEXEX pCalInfoEnumProcExEx, LPCWSTR lpLocaleName, CALID Calendar, LPCWSTR lpReserved, CALTYPE CalType, LPARAM lParam)
+ {
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ if (lcid == 0)
+ return 0;
+
+ DelayCreateCriticalSection();
+
+ BOOL ret = false;
+ {
+ CRITSEC_Holder sCrstHolder(g_pDownlevelNlsCrst);
+
+ // Store our real callback and lParam
+ lCalendarInfoParam = lParam;
+ realCalendarInfoCallback = pCalInfoEnumProcExEx;
+ ret = EnumCalendarInfoExW(EnumCalendarInfoProcWrapper, lcid, Calendar, CalType);
+ }
+ return ret;
+ };
+ }
+
+ // This is where we fudge data the OS doesn't know (even on Vista)
+ namespace UplevelFallback
+ {
+ // Some properties are unknown to downlevel OS's (pre windows 7), so synthesize them
+ // Pass in LCID if calling from the downlevel APIs.
+ // Note that Vista gets here for neutrals as well as specifics
+ // The only neutral Vista properties we support are SNAME, SPARENT & INEUTRAL (which assumes its a locale)
+ // if lpLCData is NULL, caller wants required size to be returned. So we check before assigning and return
+ // buffer size.
+ int GetLocaleInfoEx(__in LPCWSTR lpLocaleName, __in LCID lcid, __in LCTYPE LCType, __out_ecount_opt(cchData) LPWSTR lpLCData, __in int cchData)
+ {
+ _ASSERTE((lpLCData == NULL && cchData == 0) || (lpLCData != NULL && cchData > 0));
+
+ LPCWSTR useString = NULL;
+ WCHAR buffer[80];
+
+ // We don't differentiate user overrides for these types
+ LCType &= ~LOCALE_NOUSEROVERRIDE;
+
+ // TODO: NLS Arrowhead -Find better ways of handling these properties:
+ // Right now we'll just fill them in with constant data
+ switch (LCType)
+ {
+ case LOCALE_SPERCENT: // Percent symbol
+ useString = W("%");
+ break;
+ case LOCALE_IPOSITIVEPERCENT | LOCALE_RETURN_NUMBER: // Positive percent format
+ if (lpLCData)
+ {
+ *((DWORD*)lpLCData) = 0;
+ }
+ return 2;
+ case LOCALE_INEGATIVEPERCENT | LOCALE_RETURN_NUMBER: // Negative percent format
+ if (lpLCData)
+ {
+ *((DWORD*)lpLCData) = 0;
+ }
+ return 2;
+ case LOCALE_SPERMILLE: // Per mille symbol
+ useString = W("\x2030");
+ break;
+ case LOCALE_SSHORTTIME: // Short time format (default)
+ // CultureData synthesizes short time from long time
+
+ case LOCALE_SENGLISHDISPLAYNAME: // English display name (ie: Fijiian (Fiji))
+ case LOCALE_SNATIVEDISPLAYNAME: // Native dispaly name (ie: Deutsch (Deutschland))
+ // native & english names are built more easily in managed code
+
+ //case LOCALE_SMONTHDAY: // month/day format
+ //we get month/day patterns from the calendar data. This would be override, but that's not assigned
+ // TODO: NLS Arrowhead - in windows 7 if we add overrides
+ break;
+ case LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER: // Is Right To Left?
+ // Use the RTL bit in the font signature
+ LOCALESIGNATURE LocSig;
+ if (NewApis::GetLocaleInfoEx( lpLocaleName, LOCALE_FONTSIGNATURE, (LPWSTR)&LocSig, sizeof(LocSig) / sizeof(WCHAR) ) ==
+ sizeof(LocSig) / sizeof(WCHAR))
+ {
+ // Got the locale signature information, get the isrtl bit 123 to see if its RTL.
+ if (lpLCData)
+ {
+ *((DWORD*)lpLCData) = ((LocSig.lsUsb[3] & 0x0800) != 0) ? 1 : 0;
+ }
+ return 2;
+ }
+ // Failed, just return 0
+ return 0;
+
+ case LOCALE_SNAME:
+ // If we don't have an LCID, find one, this is < Vista or neutrals
+ if (lcid == 0) lcid = NewApis::LocaleNameToLCID(lpLocaleName,0);
+
+ // Make sure windows recognizes this LCID, or that its zh-Hant, sr, or bs
+ if (GetLocaleInfoW(lcid, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, NULL, 0) == 0 &&
+ lcid != 0x7c04 && lcid != 0x7c1a && lcid != 0x781a)
+ {
+ // Not a real locale, fail, don't fail for neutrals zh-Hant, sr, or bs.
+ return 0;
+ }
+
+ // Convert name to LCID (so we get pretty name)
+ if (lcid != 0)
+ return NewApis::LCIDToLocaleName(lcid, lpLCData, cchData, 0);
+ else
+ return 0;
+
+ case LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER:
+ // If its XP/Win2K3 or lower, then the lcid can tell us
+
+ if (lcid != 0)
+ {
+ if (lpLCData)
+ *((DWORD*)lpLCData) = ((lcid < 0x0400 || lcid > 0x7000) && (lcid < 0x10000)) ? 1 : 0;
+ return 2;
+ }
+
+ // Vista or Win2K8 fail for neutrals
+ // Note this assumes that neutral or not it is a valid locale.
+ if (NewApis::GetLocaleInfoEx(lpLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, NULL, 0) == 0)
+ {
+ // Failed, its a neutral
+ // Note, we assumed it is a valid locale. (Caller lookind in our name/lcid tables undoubtedly)
+ if (lpLCData)
+ *((DWORD*)lpLCData) = 1;
+ }
+ else
+ {
+ // Succeeded, its not neutral
+ if (lpLCData)
+ *((DWORD*)lpLCData) = 0;
+ }
+
+ // Return "success"
+ return 2;
+
+ case LOCALE_SPARENT:
+ // Should only get here for neutrals or downlevel
+ // Downlevel only needs work if its not neutral
+ if (lcid != 0)
+ {
+ // Downlevel
+ // If its a neutral LCID then its "" Invariant
+ if ((lcid < 0x0400 || lcid > 0x7000) && (lcid < 0x10000))
+ {
+ useString = W("");
+ }
+ else
+ {
+ // Parent is same as LCID & 0x3ff, except for a few cases
+ switch (lcid)
+ {
+ case 0x0404: // zh-TW
+ case 0x0c04: // zh-HK
+ case 0x1404: // zh-MO
+ lcid = 0x7c04; // zh-Hant
+ break;
+ case 0x081a: // sr-Latn-CS
+ case 0x0c1a: // sr-Cryl-CS
+ lcid = 0x7c1a; // sr
+ break;
+ case 0x201a: // bs-Cyrl-BA
+ case 0x141a: // bs-Latn-BA
+ lcid = 0x781a; // bs
+ break;
+ default:
+ lcid &= 0x03ff;
+ break;
+ }
+
+ // Get the name from LCIDToName
+ if (NewApis::LCIDToLocaleName(lcid, buffer, NumItems(buffer), 0))
+ {
+ useString = buffer;
+ }
+ }
+ }
+ else
+ {
+ // Neutral on Vista / W2K8. Always "" Invariant
+ // or neutral LCID
+ useString = W("");
+ }
+ break;
+
+ case LOCALE_SNAN:
+ useString = W("NaN");
+ break;
+ case LOCALE_SPOSINFINITY:
+ useString = W("Infinity");
+ break;
+ case LOCALE_SNEGINFINITY:
+ useString = W("-Infinity");
+ break;
+ }
+
+ // Return number already returned, so we should have a string, else its unknown
+ if (useString == NULL) return 0;
+
+ // Copy our string to the output & return
+ int size = (int)(wcslen(useString) + 1);
+ // if cchData is 0, then caller wants us to return size
+ if (size > cchData && cchData != 0) return 0;
+ if (lpLCData)
+ {
+ memcpy(lpLCData, useString, size * sizeof(WCHAR));
+ }
+
+ return size;
+ }
+
+ LPCWSTR const arabicSuperShortDayNames[] =
+ {
+ W("\x0646"), // Day name for Monday
+ W("\x062b"),
+ W("\x0631"),
+ W("\x062e"),
+ W("\x062c"),
+ W("\x0633"),
+ W("\x062d") // Day name for Sunday
+ };
+
+ LPCWSTR const chineseSuperShortDayNames[] =
+ {
+ W("\x4e00"), // Day name for Monday
+ W("\x4e8c"),
+ W("\x4e09"),
+ W("\x56db"),
+ W("\x4e94"),
+ W("\x516d"),
+ W("\x65e5") // Day name for Sunday
+ };
+
+ LPCWSTR const hebrewSuperShortDayNames[] =
+ {
+ W("\x05d1"), // Day name for Monday
+ W("\x05d2"),
+ W("\x05d3"),
+ W("\x05d4"),
+ W("\x05d5"),
+ W("\x05e9"),
+ W("\x05d0") // Day name for Sunday
+ };
+
+ LPCWSTR const mongolianSuperShortDayNames[] =
+ {
+ W("\x0414\x0430"), // Day name for Monday
+ W("\x041c\x044f"),
+ W("\x041b\x0445"),
+ W("\x041f\x04af"),
+ W("\x0411\x0430"),
+ W("\x0411\x044f"),
+ W("\x041d\x044f") // Day name for Sunday
+ };
+
+ int GetCalendarInfoEx(__in_opt LPCWSTR lpLocaleName,
+ __in CALID Calendar,
+ __in_opt LPCWSTR pReserved,
+ __in CALTYPE CalType,
+ __out_ecount_opt(cchData) LPWSTR lpCalData,
+ __in int cchData,
+ __out_opt LPDWORD lpValue)
+ {
+
+ _ASSERTE((lpCalData == NULL && cchData == 0) || (lpCalData != NULL && cchData > 0));
+ if ((CalType & CAL_RETURN_NUMBER))
+ {
+ // If CAL_RETURN_NUMBER, lpValue must be non-null and lpCalData must be null
+ _ASSERTE((lpValue != NULL) && (lpCalData == NULL));
+ }
+
+ // We don't differentiate user overrides for these types
+ CalType &= ~CAL_NOUSEROVERRIDE;
+
+ LCID lcid=NewApis::LocaleNameToLCID(lpLocaleName,0);
+ int ret = 0;
+ LPCWSTR pUseString = W("");
+ LPCWSTR const *pDays = NULL;
+
+ //
+ // The requested info is a string.
+ //
+
+ // NOTE: Genitive names will skip this and just return empty strings
+ // not much we can do for those.
+ switch (CalType)
+ {
+ case CAL_SMONTHDAY:
+ // Special cases for older locales with names that can't be truncated
+ pUseString = W("MMMM dd");
+
+ // Special case for CJK locales
+ if ((lcid & 0x3ff) == 0x11 || // Japanese
+ (lcid & 0x3ff) == 0x04) // Chinese
+ {
+ // Japanese & Chinese
+ pUseString = W("M'\x6708'd'\x65e5'");
+ }
+ else if ((lcid & 0x3ff) == 0x012) // Korean
+ {
+ // Korean
+ pUseString = W("M'\xc6d4' d'\xc77c'");
+ }
+ break;
+
+ case CAL_SSHORTESTDAYNAME1:
+ case CAL_SSHORTESTDAYNAME2:
+ case CAL_SSHORTESTDAYNAME3:
+ case CAL_SSHORTESTDAYNAME4:
+ case CAL_SSHORTESTDAYNAME5:
+ case CAL_SSHORTESTDAYNAME6:
+ case CAL_SSHORTESTDAYNAME7:
+ // Special cases for older locales with names that can't be truncated
+
+ // Arabic
+ if (((lcid & 0x3ff) == 0x01) && (Calendar == CAL_GREGORIAN || Calendar == CAL_HIJRI || Calendar == CAL_UMALQURA))
+ pDays = arabicSuperShortDayNames;
+
+ // Chinese
+ if (((lcid & 0x3ff) == 0x04) && (Calendar == CAL_GREGORIAN || Calendar == CAL_TAIWAN))
+ pDays = chineseSuperShortDayNames;
+
+ // Hebrew
+ if (((lcid & 0x3ff) == 0x0d) && (Calendar == CAL_GREGORIAN || Calendar == CAL_HEBREW))
+ pDays = hebrewSuperShortDayNames;
+
+ // Mongolian
+ if ((lcid & 0x3ff) == 0x50) pDays = mongolianSuperShortDayNames;
+
+ if (pDays)
+ {
+ // If we have a special case string then use that
+ pUseString = pDays[CalType - CAL_SSHORTESTDAYNAME1];
+ }
+ else
+ {
+ // If lpCalData is null they just want the size
+ // NOTE: We actually always know the size so we never ask.
+ if (lpCalData == NULL)
+ {
+ ret = 5;
+ }
+ else
+ {
+ ret = NewApis::GetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CAL_SABBREVDAYNAME1 + (CalType - CAL_SSHORTESTDAYNAME1), lpCalData, cchData, lpValue);
+ if (ret > 0)
+ {
+ if (ret > 3)
+ {
+ // Just get the first two character, and NULL-terminate it.
+ PREFIX_ASSUME(3 < cchData); // we can assume this; otherwise call would have failed
+ lpCalData[3] = W('\0');
+ ret = 3;
+ }
+ }
+ }
+
+ // Done
+ return ret;
+ }
+ break;
+ default:
+ //
+ // Not a CALTYPE that this function provides. Just returns 0.
+ //
+ return 0;
+ break;
+ }
+
+ // If we have a special case string, copy to the output & return
+ ret = (int)(wcslen(pUseString) + 1);
+ if (lpCalData && cchData >= ret)
+ {
+ // If they wanted string (not just count), then return it
+ memcpy(lpCalData, pUseString, ret * sizeof(WCHAR));
+ }
+
+ return ret;
+ }
+
+ // Handle the to titlecaseflag
+ int LCMapStringEx(__in LPCWSTR lpLocaleName,
+ __in DWORD dwMapFlags,
+ __in_ecount(cchSrc) LPCWSTR lpSrcStr,
+ __in int cchSrc,
+ __out_ecount_opt(cchDest) LPWSTR lpDestStr,
+ __in int cchDest,
+ __in_opt LPNLSVERSIONINFO lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ // We only know about the title case flag...
+ if (dwMapFlags != LCMAP_TITLECASE)
+ {
+ return 0;
+ }
+
+ // Should be unused, we'll just workaround it to upper case in case there's a real problem
+ // Just call NewAPIs with the upper case flag
+ return NewApis::LCMapStringEx(lpLocaleName, LCMAP_UPPERCASE, lpSrcStr, cchSrc,
+ lpDestStr, cchDest,
+ lpVersionInformation, lpReserved, lParam);
+ }
+
+ }
+
+ static int __cdecl compareLcidToLCIDEntry ( const void *key, const void *value)
+ {
+ LCID lcid=*((LCID*)key);
+ LCIDEntry* entry=(LCIDEntry*)value;
+ return lcid-entry->lcid;
+ }
+
+ int LCIDToLocaleName(__in LCID Locale, __out_ecount_opt(cchName) LPWSTR lpName, __in int cchName, __in DWORD dwFlags)
+ {
+ _ASSERTE((lpName == NULL && cchName == 0) || (lpName != NULL && cchName > 0));
+ if (Locale==LOCALE_INVARIANT)
+ {
+ if (lpName != NULL)
+ {
+ *lpName=0;
+ }
+ return 1;
+ }
+
+ LCIDEntry* entry=(LCIDEntry*)bsearch(&Locale,s_lcids,NumItems(s_lcids),sizeof(*s_lcids),compareLcidToLCIDEntry);
+
+ if (entry == NULL)
+ {
+ //_ASSERTE(entry);
+ return 0;
+ }
+
+ int length = 0;
+ if (cchName > 0)
+ {
+ PREFIX_ASSUME(lpName != NULL); // checked above, but prefix can't figure it out
+ wcscpy_s(lpName,cchName,entry->wszName);
+ length = (int)wcslen(entry->wszName) + 1;
+ }
+ return length;
+ }
+
+ LCID LocaleNameToLCID(__in LPCWSTR lpName, __in DWORD dwFlags)
+ {
+ if (lpName == NULL || *lpName==0)
+ {
+ return LOCALE_INVARIANT;
+ }
+
+ // Try the last one first, just in case
+ static int cachedEntry = 0;
+ PREFIX_ASSUME(cachedEntry < NumItems(s_names));
+
+ int test = VolatileLoad(&cachedEntry);
+ if (_wcsicmp(lpName, s_names[test].wszName) == 0)
+ {
+ _ASSERTE(s_names[test].lcid != 0);
+ return s_names[test].lcid;
+ }
+
+ // Just do a binary lookup for the name
+ int iBottom =0;
+ int iTop = NumItems(s_names) - 1;
+
+ while (iBottom <= iTop)
+ {
+ int iMiddle = (iBottom + iTop) / 2;
+ int result = _wcsicmp(lpName, s_names[iMiddle].wszName);
+ if (result == 0)
+ {
+ _ASSERTE(s_names[iMiddle].lcid != 0);
+ cachedEntry = iMiddle;
+ return s_names[iMiddle].lcid;
+ }
+ if (result < 0)
+ {
+ // pLocaleName was < s_names[iMiddle]
+ iTop = iMiddle - 1;
+ }
+ else
+ {
+ // pLocaleName was > s_names[iMiddle]
+ iBottom = iMiddle + 1;
+ }
+ }
+
+ // Failed, return 0
+ return 0;
+ }
+
+ // Fallback for pre windows 7 machines
+ int ResolveLocaleName(__in LPCWSTR lpNameToResolve, __in_ecount_opt(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName)
+ {
+ PWSTR pSpecific = NULL;
+ int retVal = 0;
+
+ // Doesn't matter for mac, for windows map the name to LCID, then ask for LOCALE_ILANGUAGE to map the
+ // LCID to a specific (legacy GetLocaleInfo behavior), then map the LCID to a name
+ LCID lcid = LocaleNameToLCID(lpNameToResolve, 0);
+ DWORD specific;
+
+ // Some neutrals have specific values that downlevel OS's can't provide easily:
+ retVal = 2;
+ switch (lcid)
+ {
+ case 0x0004: specific = 0x0804; break; // zh-Hans::zh-CN::0804
+ case 0x000a: specific = 0x0c0a; break; // es::es-ES::0c0a
+ case 0x003c: specific = 0x083c; break; // ga::ga-IE::083c
+ case 0x005d: specific = 0x085d; break; // iu::iu-Latn-CA:085d
+ case 0x005f: specific = 0x085f; break; // tzm::tzm-Latn-DZ:085f
+ case 0x703b: specific = 0x243b; break; // smn::smn-FI::243b
+ case 0x743b: specific = 0x203b; break; // sms::sms-FI::203b
+ case 0x7804: specific = 0x0804; break; // zh::zh-CN::0804
+ case 0x7814: specific = 0x0814; break; // nn::nn-NO::0814
+ case 0x781a: specific = 0x141a; break; // bs::bs-Latn-BA:141a
+ case 0x783b: specific = 0x1c3b; break; // sma::sma-SE::1c3b
+ case 0x7c04: specific = 0x0c04; break; // zh-Hant::zh-HK::0c04
+ case 0x7c1a: specific = 0x081a; break; // sr::sr-Latn-CS:081a (this changes in win7)
+ case 0x7c2e: specific = 0x082e; break; // dsb::dsb-DE::082e
+ case 0x7c3b: specific = 0x143b; break; // smj::smj-SE::143b
+ default:
+ // Note this won't call our Downlevel API with the undesired LOCALE_ILANGUAGE
+ retVal = GetLocaleInfoW(lcid, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, (LPWSTR)&specific, sizeof(specific)/sizeof(WCHAR));
+ break;
+ }
+
+ if (retVal > 0)
+ {
+ retVal = LCIDToLocaleName(specific, lpLocaleName, cchLocaleName, 0);
+ if (retVal > 0)
+ return retVal;
+ }
+
+ // If we didn't have a specific, then use the locale name passed in
+ if (!pSpecific)
+ {
+ pSpecific = (PWSTR)lpNameToResolve;
+ }
+
+ // Copy our string to the output & return
+ int size = (int)(wcslen(pSpecific) + 1);
+ if (size > cchLocaleName) return 0;
+ memcpy(lpLocaleName, pSpecific, size * sizeof(WCHAR));
+ return size;
+ }
+
+
+ BOOL GetThreadPreferredUILanguages(__in DWORD dwFlags,
+ __out PULONG pulNumLanguages,
+ __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer,
+ __inout PULONG pcchLanguagesBuffer)
+ {
+ const WCHAR str[]=W("\0");
+ ULONG nBufSize=*pcchLanguagesBuffer;
+ *pcchLanguagesBuffer=NumItems(str);
+
+ if (nBufSize == 0 && pwszLanguagesBuffer == NULL)
+ {
+ return TRUE;
+ }
+
+ if(nBufSize<NumItems(str))
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ *pulNumLanguages=0;
+ memcpy(pwszLanguagesBuffer,str,sizeof(str));
+ return TRUE;
+
+ }
+
+}
+#endif // ENABLE_DOWNLEVEL_FOR_NLS
+
+
diff --git a/src/utilcode/dyncrt/.gitmirror b/src/utilcode/dyncrt/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/utilcode/dyncrt/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/utilcode/dyncrt/CMakeLists.txt b/src/utilcode/dyncrt/CMakeLists.txt
new file mode 100644
index 0000000000..ceb0cff1f4
--- /dev/null
+++ b/src/utilcode/dyncrt/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+if(CLR_CMAKE_PLATFORM_UNIX)
+ add_library(utilcode STATIC ${UTILCODE_SOURCES})
+ add_dependencies(utilcode CoreClrPal)
+else(CLR_CMAKE_PLATFORM_UNIX)
+ add_library(utilcode STATIC ${UTILCODE_SOURCES})
+endif(CLR_CMAKE_PLATFORM_UNIX) \ No newline at end of file
diff --git a/src/utilcode/dyncrt/dyncrt.nativeproj b/src/utilcode/dyncrt/dyncrt.nativeproj
new file mode 100644
index 0000000000..50a1fb7880
--- /dev/null
+++ b/src/utilcode/dyncrt/dyncrt.nativeproj
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <!--*****************************************************-->
+ <!--This MSBuild project file was automatically generated-->
+ <!--from the original SOURCES/DIRS file by the KBC tool.-->
+ <!--*****************************************************-->
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\utilcode.settings.targets" />
+ <PropertyGroup Label="Globals">
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>utilcode</OutputName>
+ <!--WARNING: This is not a valid MSBuild property. Ensure that you place BUILD_PRODUCES macro in your SOURCES file after you have generated the stub version using KBC-->
+ <BuildProduces>utilcode.lib</BuildProduces>
+ </PropertyGroup>
+ <!--Leaf Project Items-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+</Project>
diff --git a/src/utilcode/ex.cpp b/src/utilcode/ex.cpp
new file mode 100644
index 0000000000..af2b4edcb2
--- /dev/null
+++ b/src/utilcode/ex.cpp
@@ -0,0 +1,2121 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+// ---------------------------------------------------------------------------
+// Ex.cpp
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "string.h"
+#include "ex.h"
+#include "holder.h"
+
+// error codes
+#include "corerror.h"
+
+#include "../dlls/mscorrc/resource.h"
+
+#include "olectl.h"
+
+#include "corexcep.h"
+
+#define MAX_EXCEPTION_MSG 200
+
+// Set if fatal error (like stack overflow or out of memory) occured in this process.
+GVAL_IMPL_INIT(HRESULT, g_hrFatalError, S_OK);
+
+// Helper function to get an exception object from outside the exception. In
+// the CLR, it may be from the Thread object. Non-CLR users have no thread object,
+// and it will do nothing.
+void GetLastThrownObjectExceptionFromThread(void **ppvException);
+
+Exception *Exception::g_OOMException = NULL;
+
+// avoid global constructors
+static BYTE g_OOMExceptionInstance[sizeof(OutOfMemoryException)];
+
+Exception * Exception::GetOOMException()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!g_OOMException) {
+ // Create a local copy on the stack and then copy it over to the static instance.
+ // This avoids race conditions caused by multiple initializations of vtable in the constructor
+
+ OutOfMemoryException local(TRUE); // Construct a "preallocated" instance.
+ memcpy(&g_OOMExceptionInstance, &local, sizeof(OutOfMemoryException));
+
+ g_OOMException = (OutOfMemoryException*)&g_OOMExceptionInstance;
+ }
+
+ return g_OOMException;
+}
+
+/*virtual*/ Exception *OutOfMemoryException::Clone()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return GetOOMException();
+}
+
+//------------------------------------------------------------------------------
+void Exception::Delete(Exception* pvMemory)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ SO_TOLERANT;
+ SUPPORTS_DAC_HOST_ONLY; // Exceptions aren't currently marshalled by DAC - just used in the host
+ }
+ CONTRACTL_END;
+
+ if ((pvMemory == 0) || pvMemory->IsPreallocatedException())
+ {
+ return;
+ }
+
+ ::delete((Exception *) pvMemory);
+}
+
+void Exception::GetMessage(SString &result)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GenerateTopLevelHRExceptionMessage(GetHR(), result);
+}
+
+void HRMsgException::GetMessage(SString &result)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_msg.IsEmpty())
+ HRException::GetMessage(result);
+ else
+ result = m_msg;
+}
+
+Exception *Exception::Clone()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ NewHolder<Exception> retExcep(CloneHelper());
+ if (m_innerException)
+ {
+ retExcep->m_innerException = m_innerException->Clone();
+ }
+
+ retExcep.SuppressRelease();
+ return retExcep;
+}
+
+Exception *Exception::CloneHelper()
+{
+ StackSString s;
+ GetMessage(s);
+ return new HRMsgException(GetHR(), s);
+}
+
+Exception *Exception::DomainBoundClone()
+{
+ CONTRACTL
+ {
+ // Because we may call DomainBoundCloneHelper() of ObjrefException or CLRLastThrownObjectException
+ // this should be GC_TRIGGERS, but we can not include EE contracts in Utilcode.
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ NewHolder<Exception> retExcep(DomainBoundCloneHelper());
+ if (m_innerException)
+ {
+ retExcep->m_innerException = m_innerException->DomainBoundClone();
+ }
+
+ retExcep.SuppressRelease();
+ return retExcep;
+}
+
+BOOL Exception::IsTerminal()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+
+ // CLRException::GetHR() can eventually call BaseDomain::CreateHandle(),
+ // which can indirectly cause a lock if we get a miss in the handle table
+ // cache (TableCacheMissOnAlloc). Since CLRException::GetHR() is virtual,
+ // SCAN won't find this for you (though 40 minutes of one of the sql stress
+ // tests will :-))
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = GetHR();
+ return (COR_E_THREADABORTED == hr);
+}
+
+BOOL Exception::IsTransient()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return IsTransient(GetHR());
+}
+
+/* static */
+BOOL Exception::IsTransient(HRESULT hr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (hr == COR_E_THREADABORTED
+ || hr == COR_E_THREADINTERRUPTED
+ || hr == COR_E_THREADSTOP
+ || hr == COR_E_APPDOMAINUNLOADED
+ || hr == E_OUTOFMEMORY
+ || hr == HRESULT_FROM_WIN32(ERROR_COMMITMENT_LIMIT) // ran out of room in pagefile
+ || hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)
+ || hr == (HRESULT)STATUS_NO_MEMORY
+ || hr == COR_E_STACKOVERFLOW
+ || hr == MSEE_E_ASSEMBLYLOADINPROGRESS);
+}
+
+//------------------------------------------------------------------------------
+// Functions to manage the preallocated exceptions.
+// Virtual
+BOOL Exception::IsPreallocatedException()
+{ // Most exceptions can't be preallocated. If they can be, their class
+ // should provide a virtual override of this function.
+ return FALSE;
+}
+
+BOOL Exception::IsPreallocatedOOMException()
+{ // This is the preallocated OOM if it is preallocated and is OOM.
+ return IsPreallocatedException() && (GetInstanceType() == OutOfMemoryException::GetType());
+}
+
+//------------------------------------------------------------------------------
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+LPCSTR Exception::GetHRSymbolicName(HRESULT hr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#define CASE_HRESULT(hrname) case hrname: return #hrname;
+
+ switch (hr)
+ {
+ CASE_HRESULT(S_OK)// 0x00000000L
+ CASE_HRESULT(S_FALSE)// 0x00000001L
+
+ CASE_HRESULT(E_UNEXPECTED)// 0x8000FFFFL
+ CASE_HRESULT(E_NOTIMPL)// 0x80004001L
+ CASE_HRESULT(E_OUTOFMEMORY)// 0x8007000EL
+ CASE_HRESULT(E_INVALIDARG)// 0x80070057L
+ CASE_HRESULT(E_NOINTERFACE)// 0x80004002L
+ CASE_HRESULT(E_POINTER)// 0x80004003L
+ CASE_HRESULT(E_HANDLE)// 0x80070006L
+ CASE_HRESULT(E_ABORT)// 0x80004004L
+ CASE_HRESULT(E_FAIL)// 0x80004005L
+ CASE_HRESULT(E_ACCESSDENIED)// 0x80070005L
+
+#ifdef FEATURE_COMINTEROP
+ CASE_HRESULT(CO_E_INIT_TLS)// 0x80004006L
+ CASE_HRESULT(CO_E_INIT_SHARED_ALLOCATOR)// 0x80004007L
+ CASE_HRESULT(CO_E_INIT_MEMORY_ALLOCATOR)// 0x80004008L
+ CASE_HRESULT(CO_E_INIT_CLASS_CACHE)// 0x80004009L
+ CASE_HRESULT(CO_E_INIT_RPC_CHANNEL)// 0x8000400AL
+ CASE_HRESULT(CO_E_INIT_TLS_SET_CHANNEL_CONTROL)// 0x8000400BL
+ CASE_HRESULT(CO_E_INIT_TLS_CHANNEL_CONTROL)// 0x8000400CL
+ CASE_HRESULT(CO_E_INIT_UNACCEPTED_USER_ALLOCATOR)// 0x8000400DL
+ CASE_HRESULT(CO_E_INIT_SCM_MUTEX_EXISTS)// 0x8000400EL
+ CASE_HRESULT(CO_E_INIT_SCM_FILE_MAPPING_EXISTS)// 0x8000400FL
+ CASE_HRESULT(CO_E_INIT_SCM_MAP_VIEW_OF_FILE)// 0x80004010L
+ CASE_HRESULT(CO_E_INIT_SCM_EXEC_FAILURE)// 0x80004011L
+ CASE_HRESULT(CO_E_INIT_ONLY_SINGLE_THREADED)// 0x80004012L
+
+// ******************
+// FACILITY_ITF
+// ******************
+
+ CASE_HRESULT(OLE_E_OLEVERB)// 0x80040000L
+ CASE_HRESULT(OLE_E_ADVF)// 0x80040001L
+ CASE_HRESULT(OLE_E_ENUM_NOMORE)// 0x80040002L
+ CASE_HRESULT(OLE_E_ADVISENOTSUPPORTED)// 0x80040003L
+ CASE_HRESULT(OLE_E_NOCONNECTION)// 0x80040004L
+ CASE_HRESULT(OLE_E_NOTRUNNING)// 0x80040005L
+ CASE_HRESULT(OLE_E_NOCACHE)// 0x80040006L
+ CASE_HRESULT(OLE_E_BLANK)// 0x80040007L
+ CASE_HRESULT(OLE_E_CLASSDIFF)// 0x80040008L
+ CASE_HRESULT(OLE_E_CANT_GETMONIKER)// 0x80040009L
+ CASE_HRESULT(OLE_E_CANT_BINDTOSOURCE)// 0x8004000AL
+ CASE_HRESULT(OLE_E_STATIC)// 0x8004000BL
+ CASE_HRESULT(OLE_E_PROMPTSAVECANCELLED)// 0x8004000CL
+ CASE_HRESULT(OLE_E_INVALIDRECT)// 0x8004000DL
+ CASE_HRESULT(OLE_E_WRONGCOMPOBJ)// 0x8004000EL
+ CASE_HRESULT(OLE_E_INVALIDHWND)// 0x8004000FL
+ CASE_HRESULT(OLE_E_NOT_INPLACEACTIVE)// 0x80040010L
+ CASE_HRESULT(OLE_E_CANTCONVERT)// 0x80040011L
+ CASE_HRESULT(OLE_E_NOSTORAGE)// 0x80040012L
+ CASE_HRESULT(DV_E_FORMATETC)// 0x80040064L
+ CASE_HRESULT(DV_E_DVTARGETDEVICE)// 0x80040065L
+ CASE_HRESULT(DV_E_STGMEDIUM)// 0x80040066L
+ CASE_HRESULT(DV_E_STATDATA)// 0x80040067L
+ CASE_HRESULT(DV_E_LINDEX)// 0x80040068L
+ CASE_HRESULT(DV_E_TYMED)// 0x80040069L
+ CASE_HRESULT(DV_E_CLIPFORMAT)// 0x8004006AL
+ CASE_HRESULT(DV_E_DVASPECT)// 0x8004006BL
+ CASE_HRESULT(DV_E_DVTARGETDEVICE_SIZE)// 0x8004006CL
+ CASE_HRESULT(DV_E_NOIVIEWOBJECT)// 0x8004006DL
+ CASE_HRESULT(DRAGDROP_E_NOTREGISTERED)// 0x80040100L
+ CASE_HRESULT(DRAGDROP_E_ALREADYREGISTERED)// 0x80040101L
+ CASE_HRESULT(DRAGDROP_E_INVALIDHWND)// 0x80040102L
+ CASE_HRESULT(CLASS_E_NOAGGREGATION)// 0x80040110L
+ CASE_HRESULT(CLASS_E_CLASSNOTAVAILABLE)// 0x80040111L
+ CASE_HRESULT(VIEW_E_DRAW)// 0x80040140L
+ CASE_HRESULT(REGDB_E_READREGDB)// 0x80040150L
+ CASE_HRESULT(REGDB_E_WRITEREGDB)// 0x80040151L
+ CASE_HRESULT(REGDB_E_KEYMISSING)// 0x80040152L
+ CASE_HRESULT(REGDB_E_INVALIDVALUE)// 0x80040153L
+ CASE_HRESULT(REGDB_E_CLASSNOTREG)// 0x80040154L
+ CASE_HRESULT(CACHE_E_NOCACHE_UPDATED)// 0x80040170L
+ CASE_HRESULT(OLEOBJ_E_NOVERBS)// 0x80040180L
+ CASE_HRESULT(INPLACE_E_NOTUNDOABLE)// 0x800401A0L
+ CASE_HRESULT(INPLACE_E_NOTOOLSPACE)// 0x800401A1L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_GET)// 0x800401C0L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_PUT)// 0x800401C1L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_FMT)// 0x800401C2L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB)// 0x800401C3L
+ CASE_HRESULT(CONVERT10_E_STG_FMT)// 0x800401C4L
+ CASE_HRESULT(CONVERT10_E_STG_NO_STD_STREAM)// 0x800401C5L
+ CASE_HRESULT(CONVERT10_E_STG_DIB_TO_BITMAP)// 0x800401C6L
+ CASE_HRESULT(CLIPBRD_E_CANT_OPEN)// 0x800401D0L
+ CASE_HRESULT(CLIPBRD_E_CANT_EMPTY)// 0x800401D1L
+ CASE_HRESULT(CLIPBRD_E_CANT_SET)// 0x800401D2L
+ CASE_HRESULT(CLIPBRD_E_BAD_DATA)// 0x800401D3L
+ CASE_HRESULT(CLIPBRD_E_CANT_CLOSE)// 0x800401D4L
+ CASE_HRESULT(MK_E_CONNECTMANUALLY)// 0x800401E0L
+ CASE_HRESULT(MK_E_EXCEEDEDDEADLINE)// 0x800401E1L
+ CASE_HRESULT(MK_E_NEEDGENERIC)// 0x800401E2L
+ CASE_HRESULT(MK_E_UNAVAILABLE)// 0x800401E3L
+ CASE_HRESULT(MK_E_SYNTAX)// 0x800401E4L
+ CASE_HRESULT(MK_E_NOOBJECT)// 0x800401E5L
+ CASE_HRESULT(MK_E_INVALIDEXTENSION)// 0x800401E6L
+ CASE_HRESULT(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED)// 0x800401E7L
+ CASE_HRESULT(MK_E_NOTBINDABLE)// 0x800401E8L
+ CASE_HRESULT(MK_E_NOTBOUND)// 0x800401E9L
+ CASE_HRESULT(MK_E_CANTOPENFILE)// 0x800401EAL
+ CASE_HRESULT(MK_E_MUSTBOTHERUSER)// 0x800401EBL
+ CASE_HRESULT(MK_E_NOINVERSE)// 0x800401ECL
+ CASE_HRESULT(MK_E_NOSTORAGE)// 0x800401EDL
+ CASE_HRESULT(MK_E_NOPREFIX)// 0x800401EEL
+ CASE_HRESULT(MK_E_ENUMERATION_FAILED)// 0x800401EFL
+ CASE_HRESULT(CO_E_NOTINITIALIZED)// 0x800401F0L
+ CASE_HRESULT(CO_E_ALREADYINITIALIZED)// 0x800401F1L
+ CASE_HRESULT(CO_E_CANTDETERMINECLASS)// 0x800401F2L
+ CASE_HRESULT(CO_E_CLASSSTRING)// 0x800401F3L
+ CASE_HRESULT(CO_E_IIDSTRING)// 0x800401F4L
+ CASE_HRESULT(CO_E_APPNOTFOUND)// 0x800401F5L
+ CASE_HRESULT(CO_E_APPSINGLEUSE)// 0x800401F6L
+ CASE_HRESULT(CO_E_ERRORINAPP)// 0x800401F7L
+ CASE_HRESULT(CO_E_DLLNOTFOUND)// 0x800401F8L
+ CASE_HRESULT(CO_E_ERRORINDLL)// 0x800401F9L
+ CASE_HRESULT(CO_E_WRONGOSFORAPP)// 0x800401FAL
+ CASE_HRESULT(CO_E_OBJNOTREG)// 0x800401FBL
+ CASE_HRESULT(CO_E_OBJISREG)// 0x800401FCL
+ CASE_HRESULT(CO_E_OBJNOTCONNECTED)// 0x800401FDL
+ CASE_HRESULT(CO_E_APPDIDNTREG)// 0x800401FEL
+ CASE_HRESULT(CO_E_RELEASED)// 0x800401FFL
+
+ CASE_HRESULT(OLE_S_USEREG)// 0x00040000L
+ CASE_HRESULT(OLE_S_STATIC)// 0x00040001L
+ CASE_HRESULT(OLE_S_MAC_CLIPFORMAT)// 0x00040002L
+ CASE_HRESULT(DRAGDROP_S_DROP)// 0x00040100L
+ CASE_HRESULT(DRAGDROP_S_CANCEL)// 0x00040101L
+ CASE_HRESULT(DRAGDROP_S_USEDEFAULTCURSORS)// 0x00040102L
+ CASE_HRESULT(DATA_S_SAMEFORMATETC)// 0x00040130L
+ CASE_HRESULT(VIEW_S_ALREADY_FROZEN)// 0x00040140L
+ CASE_HRESULT(CACHE_S_FORMATETC_NOTSUPPORTED)// 0x00040170L
+ CASE_HRESULT(CACHE_S_SAMECACHE)// 0x00040171L
+ CASE_HRESULT(CACHE_S_SOMECACHES_NOTUPDATED)// 0x00040172L
+ CASE_HRESULT(OLEOBJ_S_INVALIDVERB)// 0x00040180L
+ CASE_HRESULT(OLEOBJ_S_CANNOT_DOVERB_NOW)// 0x00040181L
+ CASE_HRESULT(OLEOBJ_S_INVALIDHWND)// 0x00040182L
+ CASE_HRESULT(INPLACE_S_TRUNCATED)// 0x000401A0L
+ CASE_HRESULT(CONVERT10_S_NO_PRESENTATION)// 0x000401C0L
+ CASE_HRESULT(MK_S_REDUCED_TO_SELF)// 0x000401E2L
+ CASE_HRESULT(MK_S_ME)// 0x000401E4L
+ CASE_HRESULT(MK_S_HIM)// 0x000401E5L
+ CASE_HRESULT(MK_S_US)// 0x000401E6L
+ CASE_HRESULT(MK_S_MONIKERALREADYREGISTERED)// 0x000401E7L
+
+// ******************
+// FACILITY_WINDOWS
+// ******************
+
+ CASE_HRESULT(CO_E_CLASS_CREATE_FAILED)// 0x80080001L
+ CASE_HRESULT(CO_E_SCM_ERROR)// 0x80080002L
+ CASE_HRESULT(CO_E_SCM_RPC_FAILURE)// 0x80080003L
+ CASE_HRESULT(CO_E_BAD_PATH)// 0x80080004L
+ CASE_HRESULT(CO_E_SERVER_EXEC_FAILURE)// 0x80080005L
+ CASE_HRESULT(CO_E_OBJSRV_RPC_FAILURE)// 0x80080006L
+ CASE_HRESULT(MK_E_NO_NORMALIZED)// 0x80080007L
+ CASE_HRESULT(CO_E_SERVER_STOPPING)// 0x80080008L
+ CASE_HRESULT(MEM_E_INVALID_ROOT)// 0x80080009L
+ CASE_HRESULT(MEM_E_INVALID_LINK)// 0x80080010L
+ CASE_HRESULT(MEM_E_INVALID_SIZE)// 0x80080011L
+
+// ******************
+// FACILITY_DISPATCH
+// ******************
+
+ CASE_HRESULT(DISP_E_UNKNOWNINTERFACE)// 0x80020001L
+ CASE_HRESULT(DISP_E_MEMBERNOTFOUND)// 0x80020003L
+ CASE_HRESULT(DISP_E_PARAMNOTFOUND)// 0x80020004L
+ CASE_HRESULT(DISP_E_TYPEMISMATCH)// 0x80020005L
+ CASE_HRESULT(DISP_E_UNKNOWNNAME)// 0x80020006L
+ CASE_HRESULT(DISP_E_NONAMEDARGS)// 0x80020007L
+ CASE_HRESULT(DISP_E_BADVARTYPE)// 0x80020008L
+ CASE_HRESULT(DISP_E_EXCEPTION)// 0x80020009L
+ CASE_HRESULT(DISP_E_OVERFLOW)// 0x8002000AL
+ CASE_HRESULT(DISP_E_BADINDEX)// 0x8002000BL
+ CASE_HRESULT(DISP_E_UNKNOWNLCID)// 0x8002000CL
+ CASE_HRESULT(DISP_E_ARRAYISLOCKED)// 0x8002000DL
+ CASE_HRESULT(DISP_E_BADPARAMCOUNT)// 0x8002000EL
+ CASE_HRESULT(DISP_E_PARAMNOTOPTIONAL)// 0x8002000FL
+ CASE_HRESULT(DISP_E_BADCALLEE)// 0x80020010L
+ CASE_HRESULT(DISP_E_NOTACOLLECTION)// 0x80020011L
+ CASE_HRESULT(TYPE_E_BUFFERTOOSMALL)// 0x80028016L
+ CASE_HRESULT(TYPE_E_INVDATAREAD)// 0x80028018L
+ CASE_HRESULT(TYPE_E_UNSUPFORMAT)// 0x80028019L
+ CASE_HRESULT(TYPE_E_REGISTRYACCESS)// 0x8002801CL
+ CASE_HRESULT(TYPE_E_LIBNOTREGISTERED)// 0x8002801DL
+ CASE_HRESULT(TYPE_E_UNDEFINEDTYPE)// 0x80028027L
+ CASE_HRESULT(TYPE_E_QUALIFIEDNAMEDISALLOWED)// 0x80028028L
+ CASE_HRESULT(TYPE_E_INVALIDSTATE)// 0x80028029L
+ CASE_HRESULT(TYPE_E_WRONGTYPEKIND)// 0x8002802AL
+ CASE_HRESULT(TYPE_E_ELEMENTNOTFOUND)// 0x8002802BL
+ CASE_HRESULT(TYPE_E_AMBIGUOUSNAME)// 0x8002802CL
+ CASE_HRESULT(TYPE_E_NAMECONFLICT)// 0x8002802DL
+ CASE_HRESULT(TYPE_E_UNKNOWNLCID)// 0x8002802EL
+ CASE_HRESULT(TYPE_E_DLLFUNCTIONNOTFOUND)// 0x8002802FL
+ CASE_HRESULT(TYPE_E_BADMODULEKIND)// 0x800288BDL
+ CASE_HRESULT(TYPE_E_SIZETOOBIG)// 0x800288C5L
+ CASE_HRESULT(TYPE_E_DUPLICATEID)// 0x800288C6L
+ CASE_HRESULT(TYPE_E_INVALIDID)// 0x800288CFL
+ CASE_HRESULT(TYPE_E_TYPEMISMATCH)// 0x80028CA0L
+ CASE_HRESULT(TYPE_E_OUTOFBOUNDS)// 0x80028CA1L
+ CASE_HRESULT(TYPE_E_IOERROR)// 0x80028CA2L
+ CASE_HRESULT(TYPE_E_CANTCREATETMPFILE)// 0x80028CA3L
+ CASE_HRESULT(TYPE_E_CANTLOADLIBRARY)// 0x80029C4AL
+ CASE_HRESULT(TYPE_E_INCONSISTENTPROPFUNCS)// 0x80029C83L
+ CASE_HRESULT(TYPE_E_CIRCULARTYPE)// 0x80029C84L
+
+// ******************
+// FACILITY_STORAGE
+// ******************
+
+ CASE_HRESULT(STG_E_INVALIDFUNCTION)// 0x80030001L
+ CASE_HRESULT(STG_E_FILENOTFOUND)// 0x80030002L
+ CASE_HRESULT(STG_E_PATHNOTFOUND)// 0x80030003L
+ CASE_HRESULT(STG_E_TOOMANYOPENFILES)// 0x80030004L
+ CASE_HRESULT(STG_E_ACCESSDENIED)// 0x80030005L
+ CASE_HRESULT(STG_E_INVALIDHANDLE)// 0x80030006L
+ CASE_HRESULT(STG_E_INSUFFICIENTMEMORY)// 0x80030008L
+ CASE_HRESULT(STG_E_INVALIDPOINTER)// 0x80030009L
+ CASE_HRESULT(STG_E_NOMOREFILES)// 0x80030012L
+ CASE_HRESULT(STG_E_DISKISWRITEPROTECTED)// 0x80030013L
+ CASE_HRESULT(STG_E_SEEKERROR)// 0x80030019L
+ CASE_HRESULT(STG_E_WRITEFAULT)// 0x8003001DL
+ CASE_HRESULT(STG_E_READFAULT)// 0x8003001EL
+ CASE_HRESULT(STG_E_SHAREVIOLATION)// 0x80030020L
+ CASE_HRESULT(STG_E_LOCKVIOLATION)// 0x80030021L
+ CASE_HRESULT(STG_E_FILEALREADYEXISTS)// 0x80030050L
+ CASE_HRESULT(STG_E_INVALIDPARAMETER)// 0x80030057L
+ CASE_HRESULT(STG_E_MEDIUMFULL)// 0x80030070L
+ CASE_HRESULT(STG_E_ABNORMALAPIEXIT)// 0x800300FAL
+ CASE_HRESULT(STG_E_INVALIDHEADER)// 0x800300FBL
+ CASE_HRESULT(STG_E_INVALIDNAME)// 0x800300FCL
+ CASE_HRESULT(STG_E_UNKNOWN)// 0x800300FDL
+ CASE_HRESULT(STG_E_UNIMPLEMENTEDFUNCTION)// 0x800300FEL
+ CASE_HRESULT(STG_E_INVALIDFLAG)// 0x800300FFL
+ CASE_HRESULT(STG_E_INUSE)// 0x80030100L
+ CASE_HRESULT(STG_E_NOTCURRENT)// 0x80030101L
+ CASE_HRESULT(STG_E_REVERTED)// 0x80030102L
+ CASE_HRESULT(STG_E_CANTSAVE)// 0x80030103L
+ CASE_HRESULT(STG_E_OLDFORMAT)// 0x80030104L
+ CASE_HRESULT(STG_E_OLDDLL)// 0x80030105L
+ CASE_HRESULT(STG_E_SHAREREQUIRED)// 0x80030106L
+ CASE_HRESULT(STG_E_NOTFILEBASEDSTORAGE)// 0x80030107L
+ CASE_HRESULT(STG_S_CONVERTED)// 0x00030200L
+
+// ******************
+// FACILITY_RPC
+// ******************
+
+ CASE_HRESULT(RPC_E_CALL_REJECTED)// 0x80010001L
+ CASE_HRESULT(RPC_E_CALL_CANCELED)// 0x80010002L
+ CASE_HRESULT(RPC_E_CANTPOST_INSENDCALL)// 0x80010003L
+ CASE_HRESULT(RPC_E_CANTCALLOUT_INASYNCCALL)// 0x80010004L
+ CASE_HRESULT(RPC_E_CANTCALLOUT_INEXTERNALCALL)// 0x80010005L
+ CASE_HRESULT(RPC_E_CONNECTION_TERMINATED)// 0x80010006L
+ CASE_HRESULT(RPC_E_SERVER_DIED)// 0x80010007L
+ CASE_HRESULT(RPC_E_CLIENT_DIED)// 0x80010008L
+ CASE_HRESULT(RPC_E_INVALID_DATAPACKET)// 0x80010009L
+ CASE_HRESULT(RPC_E_CANTTRANSMIT_CALL)// 0x8001000AL
+ CASE_HRESULT(RPC_E_CLIENT_CANTMARSHAL_DATA)// 0x8001000BL
+ CASE_HRESULT(RPC_E_CLIENT_CANTUNMARSHAL_DATA)// 0x8001000CL
+ CASE_HRESULT(RPC_E_SERVER_CANTMARSHAL_DATA)// 0x8001000DL
+ CASE_HRESULT(RPC_E_SERVER_CANTUNMARSHAL_DATA)// 0x8001000EL
+ CASE_HRESULT(RPC_E_INVALID_DATA)// 0x8001000FL
+ CASE_HRESULT(RPC_E_INVALID_PARAMETER)// 0x80010010L
+ CASE_HRESULT(RPC_E_CANTCALLOUT_AGAIN)// 0x80010011L
+ CASE_HRESULT(RPC_E_SERVER_DIED_DNE)// 0x80010012L
+ CASE_HRESULT(RPC_E_SYS_CALL_FAILED)// 0x80010100L
+ CASE_HRESULT(RPC_E_OUT_OF_RESOURCES)// 0x80010101L
+ CASE_HRESULT(RPC_E_ATTEMPTED_MULTITHREAD)// 0x80010102L
+ CASE_HRESULT(RPC_E_NOT_REGISTERED)// 0x80010103L
+ CASE_HRESULT(RPC_E_FAULT)// 0x80010104L
+ CASE_HRESULT(RPC_E_SERVERFAULT)// 0x80010105L
+ CASE_HRESULT(RPC_E_CHANGED_MODE)// 0x80010106L
+ CASE_HRESULT(RPC_E_INVALIDMETHOD)// 0x80010107L
+ CASE_HRESULT(RPC_E_DISCONNECTED)// 0x80010108L
+ CASE_HRESULT(RPC_E_RETRY)// 0x80010109L
+ CASE_HRESULT(RPC_E_SERVERCALL_RETRYLATER)// 0x8001010AL
+ CASE_HRESULT(RPC_E_SERVERCALL_REJECTED)// 0x8001010BL
+ CASE_HRESULT(RPC_E_INVALID_CALLDATA)// 0x8001010CL
+ CASE_HRESULT(RPC_E_CANTCALLOUT_ININPUTSYNCCALL)// 0x8001010DL
+ CASE_HRESULT(RPC_E_WRONG_THREAD)// 0x8001010EL
+ CASE_HRESULT(RPC_E_THREAD_NOT_INIT)// 0x8001010FL
+ CASE_HRESULT(RPC_E_UNEXPECTED)// 0x8001FFFFL
+
+// ******************
+// FACILITY_CTL
+// ******************
+
+ CASE_HRESULT(CTL_E_ILLEGALFUNCTIONCALL)
+ CASE_HRESULT(CTL_E_OVERFLOW)
+ CASE_HRESULT(CTL_E_OUTOFMEMORY)
+ CASE_HRESULT(CTL_E_DIVISIONBYZERO)
+ CASE_HRESULT(CTL_E_OUTOFSTRINGSPACE)
+ CASE_HRESULT(CTL_E_OUTOFSTACKSPACE)
+ CASE_HRESULT(CTL_E_BADFILENAMEORNUMBER)
+ CASE_HRESULT(CTL_E_FILENOTFOUND)
+ CASE_HRESULT(CTL_E_BADFILEMODE)
+ CASE_HRESULT(CTL_E_FILEALREADYOPEN)
+ CASE_HRESULT(CTL_E_DEVICEIOERROR)
+ CASE_HRESULT(CTL_E_FILEALREADYEXISTS)
+ CASE_HRESULT(CTL_E_BADRECORDLENGTH)
+ CASE_HRESULT(CTL_E_DISKFULL)
+ CASE_HRESULT(CTL_E_BADRECORDNUMBER)
+ CASE_HRESULT(CTL_E_BADFILENAME)
+ CASE_HRESULT(CTL_E_TOOMANYFILES)
+ CASE_HRESULT(CTL_E_DEVICEUNAVAILABLE)
+ CASE_HRESULT(CTL_E_PERMISSIONDENIED)
+ CASE_HRESULT(CTL_E_DISKNOTREADY)
+ CASE_HRESULT(CTL_E_PATHFILEACCESSERROR)
+ CASE_HRESULT(CTL_E_PATHNOTFOUND)
+ CASE_HRESULT(CTL_E_INVALIDPATTERNSTRING)
+ CASE_HRESULT(CTL_E_INVALIDUSEOFNULL)
+ CASE_HRESULT(CTL_E_INVALIDFILEFORMAT)
+ CASE_HRESULT(CTL_E_INVALIDPROPERTYVALUE)
+ CASE_HRESULT(CTL_E_INVALIDPROPERTYARRAYINDEX)
+ CASE_HRESULT(CTL_E_SETNOTSUPPORTEDATRUNTIME)
+ CASE_HRESULT(CTL_E_SETNOTSUPPORTED)
+ CASE_HRESULT(CTL_E_NEEDPROPERTYARRAYINDEX)
+ CASE_HRESULT(CTL_E_SETNOTPERMITTED)
+ CASE_HRESULT(CTL_E_GETNOTSUPPORTEDATRUNTIME)
+ CASE_HRESULT(CTL_E_GETNOTSUPPORTED)
+ CASE_HRESULT(CTL_E_PROPERTYNOTFOUND)
+ CASE_HRESULT(CTL_E_INVALIDCLIPBOARDFORMAT)
+ CASE_HRESULT(CTL_E_INVALIDPICTURE)
+ CASE_HRESULT(CTL_E_PRINTERERROR)
+ CASE_HRESULT(CTL_E_CANTSAVEFILETOTEMP)
+ CASE_HRESULT(CTL_E_SEARCHTEXTNOTFOUND)
+ CASE_HRESULT(CTL_E_REPLACEMENTSTOOLONG)
+#endif // FEATURE_COMINTEROP
+
+#ifdef _DEBUG // @todo: do we want to burn strings for this in a free build?
+
+ CASE_HRESULT(CEE_E_ENTRYPOINT)
+ CASE_HRESULT(CEE_E_CVTRES_NOT_FOUND)
+ CASE_HRESULT(MSEE_E_LOADLIBFAILED)
+ CASE_HRESULT(MSEE_E_GETPROCFAILED)
+ CASE_HRESULT(MSEE_E_MULTCOPIESLOADED)
+ CASE_HRESULT(COR_E_APPDOMAINUNLOADED)
+ CASE_HRESULT(COR_E_CANNOTUNLOADAPPDOMAIN)
+ CASE_HRESULT(MSEE_E_ASSEMBLYLOADINPROGRESS)
+ CASE_HRESULT(MSEE_E_CANNOTCREATEAPPDOMAIN)
+ CASE_HRESULT(COR_E_FIXUPSINEXE)
+ CASE_HRESULT(COR_E_NO_LOADLIBRARY_ALLOWED)
+ CASE_HRESULT(COR_E_MODULE_HASH_CHECK_FAILED)
+ CASE_HRESULT(FUSION_E_LOADFROM_BLOCKED)
+ CASE_HRESULT(FUSION_E_CACHEFILE_FAILED)
+ CASE_HRESULT(FUSION_E_REF_DEF_MISMATCH)
+ CASE_HRESULT(FUSION_E_INVALID_PRIVATE_ASM_LOCATION)
+ CASE_HRESULT(FUSION_E_ASM_MODULE_MISSING)
+ CASE_HRESULT(FUSION_E_UNEXPECTED_MODULE_FOUND)
+ CASE_HRESULT(FUSION_E_PRIVATE_ASM_DISALLOWED)
+ CASE_HRESULT(FUSION_E_SIGNATURE_CHECK_FAILED)
+ CASE_HRESULT(FUSION_E_DATABASE_ERROR)
+ CASE_HRESULT(FUSION_E_INVALID_NAME)
+ CASE_HRESULT(FUSION_E_CODE_DOWNLOAD_DISABLED)
+ CASE_HRESULT(FUSION_E_UNINSTALL_DISALLOWED)
+ CASE_HRESULT(CLDB_E_FILE_BADREAD)
+ CASE_HRESULT(CLDB_E_FILE_BADWRITE)
+ CASE_HRESULT(CLDB_E_FILE_READONLY)
+ CASE_HRESULT(CLDB_E_NAME_ERROR)
+ CASE_HRESULT(CLDB_S_TRUNCATION)
+ CASE_HRESULT(CLDB_E_TRUNCATION)
+ CASE_HRESULT(CLDB_E_FILE_OLDVER)
+ CASE_HRESULT(CLDB_E_RELOCATED)
+ CASE_HRESULT(CLDB_S_NULL)
+ CASE_HRESULT(CLDB_E_SMDUPLICATE)
+ CASE_HRESULT(CLDB_E_NO_DATA)
+ CASE_HRESULT(CLDB_E_READONLY)
+ CASE_HRESULT(CLDB_E_INCOMPATIBLE)
+ CASE_HRESULT(CLDB_E_FILE_CORRUPT)
+ CASE_HRESULT(CLDB_E_SCHEMA_VERNOTFOUND)
+ CASE_HRESULT(CLDB_E_BADUPDATEMODE)
+ CASE_HRESULT(CLDB_E_INDEX_NONULLKEYS)
+ CASE_HRESULT(CLDB_E_INDEX_DUPLICATE)
+ CASE_HRESULT(CLDB_E_INDEX_BADTYPE)
+ CASE_HRESULT(CLDB_E_INDEX_NOTFOUND)
+ CASE_HRESULT(CLDB_S_INDEX_TABLESCANREQUIRED)
+ CASE_HRESULT(CLDB_E_RECORD_NOTFOUND)
+ CASE_HRESULT(CLDB_E_RECORD_OVERFLOW)
+ CASE_HRESULT(CLDB_E_RECORD_DUPLICATE)
+ CASE_HRESULT(CLDB_E_RECORD_PKREQUIRED)
+ CASE_HRESULT(CLDB_E_RECORD_DELETED)
+ CASE_HRESULT(CLDB_E_RECORD_OUTOFORDER)
+ CASE_HRESULT(CLDB_E_COLUMN_OVERFLOW)
+ CASE_HRESULT(CLDB_E_COLUMN_READONLY)
+ CASE_HRESULT(CLDB_E_COLUMN_SPECIALCOL)
+ CASE_HRESULT(CLDB_E_COLUMN_PKNONULLS)
+ CASE_HRESULT(CLDB_E_TABLE_CANTDROP)
+ CASE_HRESULT(CLDB_E_OBJECT_NOTFOUND)
+ CASE_HRESULT(CLDB_E_OBJECT_COLNOTFOUND)
+ CASE_HRESULT(CLDB_E_VECTOR_BADINDEX)
+ CASE_HRESULT(CLDB_E_TOO_BIG)
+ CASE_HRESULT(META_E_DUPLICATE)
+ CASE_HRESULT(META_E_GUID_REQUIRED)
+ CASE_HRESULT(META_E_TYPEDEF_MISMATCH)
+ CASE_HRESULT(META_E_MERGE_COLLISION)
+ CASE_HRESULT(META_E_METHD_NOT_FOUND)
+ CASE_HRESULT(META_E_FIELD_NOT_FOUND)
+ CASE_HRESULT(META_S_PARAM_MISMATCH)
+ CASE_HRESULT(META_E_PARAM_MISMATCH)
+ CASE_HRESULT(META_E_BADMETADATA)
+ CASE_HRESULT(META_E_INTFCEIMPL_NOT_FOUND)
+ CASE_HRESULT(META_E_CLASS_LAYOUT_INCONSISTENT)
+ CASE_HRESULT(META_E_FIELD_MARSHAL_NOT_FOUND)
+ CASE_HRESULT(META_E_METHODSEM_NOT_FOUND)
+ CASE_HRESULT(META_E_EVENT_NOT_FOUND)
+ CASE_HRESULT(META_E_PROP_NOT_FOUND)
+ CASE_HRESULT(META_E_BAD_SIGNATURE)
+ CASE_HRESULT(META_E_BAD_INPUT_PARAMETER)
+ CASE_HRESULT(META_E_METHDIMPL_INCONSISTENT)
+ CASE_HRESULT(META_E_MD_INCONSISTENCY)
+ CASE_HRESULT(META_E_CANNOTRESOLVETYPEREF)
+ CASE_HRESULT(META_S_DUPLICATE)
+ CASE_HRESULT(META_E_STRINGSPACE_FULL)
+ CASE_HRESULT(META_E_UNEXPECTED_REMAP)
+ CASE_HRESULT(META_E_HAS_UNMARKALL)
+ CASE_HRESULT(META_E_MUST_CALL_UNMARKALL)
+ CASE_HRESULT(TLBX_E_CANT_LOAD_MODULE)
+ CASE_HRESULT(TLBX_E_CANT_LOAD_CLASS)
+ CASE_HRESULT(TLBX_E_NULL_MODULE)
+ CASE_HRESULT(TLBX_E_NO_CLSID_KEY)
+ CASE_HRESULT(TLBX_E_CIRCULAR_EXPORT)
+ CASE_HRESULT(TLBX_E_CIRCULAR_EXPORT2)
+ CASE_HRESULT(TLBX_E_CIRCULAR_IMPORT)
+ CASE_HRESULT(TLBX_E_BAD_NATIVETYPE)
+ CASE_HRESULT(TLBX_E_BAD_VTABLE)
+ CASE_HRESULT(TLBX_E_CRM_NON_STATIC)
+ CASE_HRESULT(TLBX_E_CRM_INVALID_SIG)
+ CASE_HRESULT(TLBX_E_CLASS_LOAD_EXCEPTION)
+ CASE_HRESULT(TLBX_E_UNKNOWN_SIGNATURE)
+ CASE_HRESULT(TLBX_E_REFERENCED_TYPELIB)
+ CASE_HRESULT(TLBX_S_REFERENCED_TYPELIB)
+ CASE_HRESULT(TLBX_E_LAYOUT_ERROR)
+ CASE_HRESULT(TLBX_E_NOTIUNKNOWN)
+ CASE_HRESULT(TLBX_E_NONVISIBLEVALUECLASS)
+ CASE_HRESULT(TLBX_E_LPTSTR_NOT_ALLOWED)
+ CASE_HRESULT(TLBX_E_AUTO_CS_NOT_ALLOWED)
+ CASE_HRESULT(TLBX_S_NOSTDINTERFACE)
+ CASE_HRESULT(TLBX_S_DUPLICATE_DISPID)
+ CASE_HRESULT(TLBX_E_ENUM_VALUE_INVALID)
+ CASE_HRESULT(TLBX_W_ENUM_VALUE_TOOBIG)
+ CASE_HRESULT(TLBX_E_DUPLICATE_IID)
+ CASE_HRESULT(TLBX_E_NO_NESTED_ARRAYS)
+ CASE_HRESULT(TLBX_E_PARAM_ERROR_NAMED)
+ CASE_HRESULT(TLBX_E_PARAM_ERROR_UNNAMED)
+ CASE_HRESULT(TLBX_E_AGNOST_SIGNATURE)
+ CASE_HRESULT(TLBX_E_CONVERT_FAIL)
+ CASE_HRESULT(TLBX_W_DUAL_NOT_DISPATCH)
+ CASE_HRESULT(TLBX_E_BAD_SIGNATURE)
+ CASE_HRESULT(TLBX_E_ARRAY_NEEDS_NT_FIXED)
+ CASE_HRESULT(TLBX_E_CLASS_NEEDS_NT_INTF)
+ CASE_HRESULT(TLBX_E_INVALID_TYPEINFO)
+ CASE_HRESULT(TLBX_E_INVALID_TYPEINFO_UNNAMED)
+ CASE_HRESULT(TLBX_E_CTX_NESTED)
+ CASE_HRESULT(TLBX_E_ERROR_MESSAGE)
+ CASE_HRESULT(TLBX_E_CANT_SAVE)
+ CASE_HRESULT(TLBX_W_LIBNOTREGISTERED)
+ CASE_HRESULT(TLBX_E_CANTLOADLIBRARY)
+ CASE_HRESULT(TLBX_E_BAD_VT_TYPE)
+ CASE_HRESULT(TLBX_E_NO_MSCOREE_TLB)
+ CASE_HRESULT(TLBX_E_BAD_MSCOREE_TLB)
+ CASE_HRESULT(TLBX_E_TLB_EXCEPTION)
+ CASE_HRESULT(TLBX_E_MULTIPLE_LCIDS)
+ CASE_HRESULT(TLBX_I_TYPEINFO_IMPORTED)
+ CASE_HRESULT(TLBX_E_AMBIGUOUS_RETURN)
+ CASE_HRESULT(TLBX_E_PROPGET_WITHOUT_RETURN)
+ CASE_HRESULT(TLBX_E_DUPLICATE_TYPE_NAME)
+ CASE_HRESULT(TLBX_I_USEIUNKNOWN)
+ CASE_HRESULT(TLBX_I_UNCONVERTABLE_ARGS)
+ CASE_HRESULT(TLBX_I_UNCONVERTABLE_FIELD)
+ CASE_HRESULT(TLBX_I_NONSEQUENTIALSTRUCT)
+ CASE_HRESULT(TLBX_W_WARNING_MESSAGE)
+ CASE_HRESULT(TLBX_I_RESOLVEREFFAILED)
+ CASE_HRESULT(TLBX_E_ASANY)
+ CASE_HRESULT(TLBX_E_INVALIDLCIDPARAM)
+ CASE_HRESULT(TLBX_E_LCIDONDISPONLYITF)
+ CASE_HRESULT(TLBX_E_NONPUBLIC_FIELD)
+ CASE_HRESULT(TLBX_I_TYPE_EXPORTED)
+ CASE_HRESULT(TLBX_I_DUPLICATE_DISPID)
+ CASE_HRESULT(TLBX_E_BAD_NAMES)
+ CASE_HRESULT(TLBX_E_BITNESS_MISMATCH)
+ CASE_HRESULT(TLBX_I_REF_TYPE_AS_STRUCT)
+ CASE_HRESULT(TLBX_E_GENERICINST_SIGNATURE)
+ CASE_HRESULT(TLBX_E_GENERICPAR_SIGNATURE)
+ CASE_HRESULT(TLBX_I_GENERIC_TYPE)
+ CASE_HRESULT(TLBX_W_EXPORTING_AUTO_LAYOUT)
+ CASE_HRESULT(TLBX_E_TYPED_REF)
+ CASE_HRESULT(TLBX_W_DEFAULT_INTF_NOT_VISIBLE)
+ CASE_HRESULT(TLBX_W_NON_INTEGRAL_CA_TYPE)
+ CASE_HRESULT(TLBX_W_IENUM_CA_ON_IUNK)
+ CASE_HRESULT(TLBX_E_NO_SAFEHANDLE_ARRAYS)
+ CASE_HRESULT(TLBX_W_NO_PROPS_IN_EVENTS)
+ CASE_HRESULT(META_E_CA_INVALID_TARGET)
+ CASE_HRESULT(META_E_CA_INVALID_VALUE)
+ CASE_HRESULT(META_E_CA_INVALID_BLOB)
+ CASE_HRESULT(META_E_CA_REPEATED_ARG)
+ CASE_HRESULT(META_E_CA_UNKNOWN_ARGUMENT)
+ CASE_HRESULT(META_E_CA_VARIANT_NYI)
+ CASE_HRESULT(META_E_CA_ARRAY_NYI)
+ CASE_HRESULT(META_E_CA_UNEXPECTED_TYPE)
+ CASE_HRESULT(META_E_CA_INVALID_ARGTYPE)
+ CASE_HRESULT(META_E_CA_INVALID_ARG_FOR_TYPE)
+ CASE_HRESULT(META_E_CA_INVALID_UUID)
+ CASE_HRESULT(META_E_CA_INVALID_MARSHALAS_FIELDS)
+ CASE_HRESULT(META_E_CA_NT_FIELDONLY)
+ CASE_HRESULT(META_E_CA_NEGATIVE_PARAMINDEX)
+ CASE_HRESULT(META_E_CA_NEGATIVE_MULTIPLIER)
+ CASE_HRESULT(META_E_CA_NEGATIVE_CONSTSIZE)
+ CASE_HRESULT(META_E_CA_FIXEDSTR_SIZE_REQUIRED)
+ CASE_HRESULT(META_E_CA_CUSTMARSH_TYPE_REQUIRED)
+ CASE_HRESULT(META_E_CA_FILENAME_REQUIRED)
+ CASE_HRESULT(META_E_CA_BAD_FRIENDS_ARGS)
+ CASE_HRESULT(VLDTR_S_WRN)
+ CASE_HRESULT(VLDTR_S_ERR)
+ CASE_HRESULT(VLDTR_S_WRNERR)
+ CASE_HRESULT(VLDTR_E_RID_OUTOFRANGE)
+ CASE_HRESULT(VLDTR_E_CDTKN_OUTOFRANGE)
+ CASE_HRESULT(VLDTR_E_CDRID_OUTOFRANGE)
+ CASE_HRESULT(VLDTR_E_STRING_INVALID)
+ CASE_HRESULT(VLDTR_E_GUID_INVALID)
+ CASE_HRESULT(VLDTR_E_BLOB_INVALID)
+ CASE_HRESULT(VLDTR_E_MOD_MULTI)
+ CASE_HRESULT(VLDTR_E_MOD_NULLMVID)
+ CASE_HRESULT(VLDTR_E_TR_NAMENULL)
+ CASE_HRESULT(VLDTR_E_TR_DUP)
+ CASE_HRESULT(VLDTR_E_TD_NAMENULL)
+ CASE_HRESULT(VLDTR_E_TD_DUPNAME)
+ CASE_HRESULT(VLDTR_E_TD_DUPGUID)
+ CASE_HRESULT(VLDTR_E_TD_NOTIFACEOBJEXTNULL)
+ CASE_HRESULT(VLDTR_E_TD_OBJEXTENDSNONNULL)
+ CASE_HRESULT(VLDTR_E_TD_EXTENDSSEALED)
+ CASE_HRESULT(VLDTR_E_TD_DLTNORTSPCL)
+ CASE_HRESULT(VLDTR_E_TD_RTSPCLNOTDLT)
+ CASE_HRESULT(VLDTR_E_MI_DECLPRIV)
+ CASE_HRESULT(VLDTR_E_AS_BADNAME)
+ CASE_HRESULT(VLDTR_E_FILE_SYSNAME)
+ CASE_HRESULT(VLDTR_E_MI_BODYSTATIC)
+ CASE_HRESULT(VLDTR_E_TD_IFACENOTABS)
+ CASE_HRESULT(VLDTR_E_TD_IFACEPARNOTNIL)
+ CASE_HRESULT(VLDTR_E_TD_IFACEGUIDNULL)
+ CASE_HRESULT(VLDTR_E_MI_DECLFINAL)
+ CASE_HRESULT(VLDTR_E_TD_VTNOTSEAL)
+ CASE_HRESULT(VLDTR_E_PD_BADFLAGS)
+ CASE_HRESULT(VLDTR_E_IFACE_DUP)
+ CASE_HRESULT(VLDTR_E_MR_NAMENULL)
+ CASE_HRESULT(VLDTR_E_MR_VTBLNAME)
+ CASE_HRESULT(VLDTR_E_MR_DELNAME)
+ CASE_HRESULT(VLDTR_E_MR_PARNIL)
+ CASE_HRESULT(VLDTR_E_MR_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_MR_NOTVARARG)
+ CASE_HRESULT(VLDTR_E_MR_NAMEDIFF)
+ CASE_HRESULT(VLDTR_E_MR_SIGDIFF)
+ CASE_HRESULT(VLDTR_E_MR_DUP)
+ CASE_HRESULT(VLDTR_E_CL_TDAUTO)
+ CASE_HRESULT(VLDTR_E_CL_BADPCKSZ)
+ CASE_HRESULT(VLDTR_E_CL_DUP)
+ CASE_HRESULT(VLDTR_E_FL_BADOFFSET)
+ CASE_HRESULT(VLDTR_E_FL_TDNIL)
+ CASE_HRESULT(VLDTR_E_FL_NOCL)
+ CASE_HRESULT(VLDTR_E_FL_TDNOTEXPLCT)
+ CASE_HRESULT(VLDTR_E_FL_FLDSTATIC)
+ CASE_HRESULT(VLDTR_E_FL_DUP)
+ CASE_HRESULT(VLDTR_E_MODREF_NAMENULL)
+ CASE_HRESULT(VLDTR_E_MODREF_DUP)
+ CASE_HRESULT(VLDTR_E_TR_BADSCOPE)
+ CASE_HRESULT(VLDTR_E_TD_NESTEDNOENCL)
+ CASE_HRESULT(VLDTR_E_TD_EXTTRRES)
+ CASE_HRESULT(VLDTR_E_SIGNULL)
+ CASE_HRESULT(VLDTR_E_SIGNODATA)
+ CASE_HRESULT(VLDTR_E_MD_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_MD_THISSTATIC)
+ CASE_HRESULT(VLDTR_E_MD_NOTTHISNOTSTATIC)
+ CASE_HRESULT(VLDTR_E_MD_NOARGCNT)
+ CASE_HRESULT(VLDTR_E_SIG_MISSELTYPE)
+ CASE_HRESULT(VLDTR_E_SIG_MISSTKN)
+ CASE_HRESULT(VLDTR_E_SIG_TKNBAD)
+ CASE_HRESULT(VLDTR_E_SIG_MISSFPTR)
+ CASE_HRESULT(VLDTR_E_SIG_MISSFPTRARGCNT)
+ CASE_HRESULT(VLDTR_E_SIG_MISSRANK)
+ CASE_HRESULT(VLDTR_E_SIG_MISSNSIZE)
+ CASE_HRESULT(VLDTR_E_SIG_MISSSIZE)
+ CASE_HRESULT(VLDTR_E_SIG_MISSNLBND)
+ CASE_HRESULT(VLDTR_E_SIG_MISSLBND)
+ CASE_HRESULT(VLDTR_E_SIG_BADELTYPE)
+ CASE_HRESULT(VLDTR_E_FD_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_MD_NAMENULL)
+ CASE_HRESULT(VLDTR_E_MD_PARNIL)
+ CASE_HRESULT(VLDTR_E_MD_DUP)
+ CASE_HRESULT(VLDTR_E_FD_NAMENULL)
+ CASE_HRESULT(VLDTR_E_FD_PARNIL)
+ CASE_HRESULT(VLDTR_E_FD_DUP)
+ CASE_HRESULT(VLDTR_E_AS_MULTI)
+ CASE_HRESULT(VLDTR_E_AS_NAMENULL)
+ CASE_HRESULT(VLDTR_E_SIG_TOKTYPEMISMATCH)
+ CASE_HRESULT(VLDTR_E_CL_TDINTF)
+ CASE_HRESULT(VLDTR_E_ASOS_OSPLTFRMIDINVAL)
+ CASE_HRESULT(VLDTR_E_AR_NAMENULL)
+ CASE_HRESULT(VLDTR_E_TD_ENCLNOTNESTED)
+ CASE_HRESULT(VLDTR_E_AROS_OSPLTFRMIDINVAL)
+ CASE_HRESULT(VLDTR_E_FILE_NAMENULL)
+ CASE_HRESULT(VLDTR_E_CT_NAMENULL)
+ CASE_HRESULT(VLDTR_E_TD_EXTENDSCHILD)
+ CASE_HRESULT(VLDTR_E_MAR_NAMENULL)
+ CASE_HRESULT(VLDTR_E_FILE_DUP)
+ CASE_HRESULT(VLDTR_E_FILE_NAMEFULLQLFD)
+ CASE_HRESULT(VLDTR_E_CT_DUP)
+ CASE_HRESULT(VLDTR_E_MAR_DUP)
+ CASE_HRESULT(VLDTR_E_MAR_NOTPUBPRIV)
+ CASE_HRESULT(VLDTR_E_TD_ENUMNOVALUE)
+ CASE_HRESULT(VLDTR_E_TD_ENUMVALSTATIC)
+ CASE_HRESULT(VLDTR_E_TD_ENUMVALNOTSN)
+ CASE_HRESULT(VLDTR_E_TD_ENUMFLDNOTST)
+ CASE_HRESULT(VLDTR_E_TD_ENUMFLDNOTLIT)
+ CASE_HRESULT(VLDTR_E_TD_ENUMNOLITFLDS)
+ CASE_HRESULT(VLDTR_E_TD_ENUMFLDSIGMISMATCH)
+ CASE_HRESULT(VLDTR_E_TD_ENUMVALNOT1ST)
+ CASE_HRESULT(VLDTR_E_FD_NOTVALUERTSN)
+ CASE_HRESULT(VLDTR_E_FD_VALUEPARNOTENUM)
+ CASE_HRESULT(VLDTR_E_FD_INSTINIFACE)
+ CASE_HRESULT(VLDTR_E_FD_NOTPUBINIFACE)
+ CASE_HRESULT(VLDTR_E_FMD_GLOBALNOTPUBPRIVSC)
+ CASE_HRESULT(VLDTR_E_FMD_GLOBALNOTSTATIC)
+ CASE_HRESULT(VLDTR_E_FD_GLOBALNORVA)
+ CASE_HRESULT(VLDTR_E_MD_CTORZERORVA)
+ CASE_HRESULT(VLDTR_E_FD_MARKEDNOMARSHAL)
+ CASE_HRESULT(VLDTR_E_FD_MARSHALNOTMARKED)
+ CASE_HRESULT(VLDTR_E_FD_MARKEDNODEFLT)
+ CASE_HRESULT(VLDTR_E_FD_DEFLTNOTMARKED)
+ CASE_HRESULT(VLDTR_E_FMD_MARKEDNOSECUR)
+ CASE_HRESULT(VLDTR_E_FMD_SECURNOTMARKED)
+ CASE_HRESULT(VLDTR_E_FMD_PINVOKENOTSTATIC)
+ CASE_HRESULT(VLDTR_E_FMD_MARKEDNOPINVOKE)
+ CASE_HRESULT(VLDTR_E_FMD_PINVOKENOTMARKED)
+ CASE_HRESULT(VLDTR_E_FMD_BADIMPLMAP)
+ CASE_HRESULT(VLDTR_E_IMAP_BADMODREF)
+ CASE_HRESULT(VLDTR_E_IMAP_BADMEMBER)
+ CASE_HRESULT(VLDTR_E_IMAP_BADIMPORTNAME)
+ CASE_HRESULT(VLDTR_E_IMAP_BADCALLCONV)
+ CASE_HRESULT(VLDTR_E_FMD_BADACCESSFLAG)
+ CASE_HRESULT(VLDTR_E_FD_INITONLYANDLITERAL)
+ CASE_HRESULT(VLDTR_E_FD_LITERALNOTSTATIC)
+ CASE_HRESULT(VLDTR_E_FMD_RTSNNOTSN)
+ CASE_HRESULT(VLDTR_E_MD_ABSTPARNOTABST)
+ CASE_HRESULT(VLDTR_E_MD_NOTSTATABSTININTF)
+ CASE_HRESULT(VLDTR_E_MD_NOTPUBININTF)
+ CASE_HRESULT(VLDTR_E_MD_CTORININTF)
+ CASE_HRESULT(VLDTR_E_MD_GLOBALCTORCCTOR)
+ CASE_HRESULT(VLDTR_E_MD_CTORSTATIC)
+ CASE_HRESULT(VLDTR_E_MD_CTORNOTSNRTSN)
+ CASE_HRESULT(VLDTR_E_MD_CTORVIRT)
+ CASE_HRESULT(VLDTR_E_MD_CTORABST)
+ CASE_HRESULT(VLDTR_E_MD_CCTORNOTSTATIC)
+ CASE_HRESULT(VLDTR_E_MD_ZERORVA)
+ CASE_HRESULT(VLDTR_E_MD_FINNOTVIRT)
+ CASE_HRESULT(VLDTR_E_MD_STATANDFINORVIRT)
+ CASE_HRESULT(VLDTR_E_MD_ABSTANDFINAL)
+ CASE_HRESULT(VLDTR_E_MD_ABSTANDIMPL)
+ CASE_HRESULT(VLDTR_E_MD_ABSTANDPINVOKE)
+ CASE_HRESULT(VLDTR_E_MD_ABSTNOTVIRT)
+ CASE_HRESULT(VLDTR_E_MD_NOTABSTNOTIMPL)
+ CASE_HRESULT(VLDTR_E_MD_NOTABSTBADFLAGSRVA)
+ CASE_HRESULT(VLDTR_E_MD_PRIVSCOPENORVA)
+ CASE_HRESULT(VLDTR_E_MD_GLOBALABSTORVIRT)
+ CASE_HRESULT(VLDTR_E_SIG_LONGFORM)
+ CASE_HRESULT(VLDTR_E_MD_MULTIPLESEMANTICS)
+ CASE_HRESULT(VLDTR_E_MD_INVALIDSEMANTICS)
+ CASE_HRESULT(VLDTR_E_MD_SEMANTICSNOTEXIST)
+ CASE_HRESULT(VLDTR_E_MI_DECLNOTVIRT)
+ CASE_HRESULT(VLDTR_E_FMD_GLOBALITEM)
+ CASE_HRESULT(VLDTR_E_MD_MULTSEMANTICFLAGS)
+ CASE_HRESULT(VLDTR_E_MD_NOSEMANTICFLAGS)
+ CASE_HRESULT(VLDTR_E_FD_FLDINIFACE)
+ CASE_HRESULT(VLDTR_E_AS_HASHALGID)
+ CASE_HRESULT(VLDTR_E_AS_PROCID)
+ CASE_HRESULT(VLDTR_E_AR_PROCID)
+ CASE_HRESULT(VLDTR_E_CN_PARENTRANGE)
+ CASE_HRESULT(VLDTR_E_AS_BADFLAGS)
+ CASE_HRESULT(VLDTR_E_TR_HASTYPEDEF)
+ CASE_HRESULT(VLDTR_E_IFACE_BADIMPL)
+ CASE_HRESULT(VLDTR_E_IFACE_BADIFACE)
+ CASE_HRESULT(VLDTR_E_TD_SECURNOTMARKED)
+ CASE_HRESULT(VLDTR_E_TD_MARKEDNOSECUR)
+ CASE_HRESULT(VLDTR_E_MD_CCTORHASARGS)
+ CASE_HRESULT(VLDTR_E_CT_BADIMPL)
+ CASE_HRESULT(VLDTR_E_MI_ALIENBODY)
+ CASE_HRESULT(VLDTR_E_MD_CCTORCALLCONV)
+ CASE_HRESULT(VLDTR_E_MI_BADCLASS)
+ CASE_HRESULT(VLDTR_E_MI_CLASSISINTF)
+ CASE_HRESULT(VLDTR_E_MI_BADDECL)
+ CASE_HRESULT(VLDTR_E_MI_BADBODY)
+ CASE_HRESULT(VLDTR_E_MI_DUP)
+ CASE_HRESULT(VLDTR_E_FD_BADPARENT)
+ CASE_HRESULT(VLDTR_E_MD_PARAMOUTOFSEQ)
+ CASE_HRESULT(VLDTR_E_MD_PARASEQTOOBIG)
+ CASE_HRESULT(VLDTR_E_MD_PARMMARKEDNOMARSHAL)
+ CASE_HRESULT(VLDTR_E_MD_PARMMARSHALNOTMARKED)
+ CASE_HRESULT(VLDTR_E_MD_PARMMARKEDNODEFLT)
+ CASE_HRESULT(VLDTR_E_MD_PARMDEFLTNOTMARKED)
+ CASE_HRESULT(VLDTR_E_PR_BADSCOPE)
+ CASE_HRESULT(VLDTR_E_PR_NONAME)
+ CASE_HRESULT(VLDTR_E_PR_NOSIG)
+ CASE_HRESULT(VLDTR_E_PR_DUP)
+ CASE_HRESULT(VLDTR_E_PR_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_PR_MARKEDNODEFLT)
+ CASE_HRESULT(VLDTR_E_PR_DEFLTNOTMARKED)
+ CASE_HRESULT(VLDTR_E_PR_BADSEMANTICS)
+ CASE_HRESULT(VLDTR_E_PR_BADMETHOD)
+ CASE_HRESULT(VLDTR_E_PR_ALIENMETHOD)
+ CASE_HRESULT(VLDTR_E_CN_BLOBNOTNULL)
+ CASE_HRESULT(VLDTR_E_CN_BLOBNULL)
+ CASE_HRESULT(VLDTR_E_EV_BADSCOPE)
+ CASE_HRESULT(VLDTR_E_EV_NONAME)
+ CASE_HRESULT(VLDTR_E_EV_DUP)
+ CASE_HRESULT(VLDTR_E_EV_BADEVTYPE)
+ CASE_HRESULT(VLDTR_E_EV_EVTYPENOTCLASS)
+ CASE_HRESULT(VLDTR_E_EV_BADSEMANTICS)
+ CASE_HRESULT(VLDTR_E_EV_BADMETHOD)
+ CASE_HRESULT(VLDTR_E_EV_ALIENMETHOD)
+ CASE_HRESULT(VLDTR_E_EV_NOADDON)
+ CASE_HRESULT(VLDTR_E_EV_NOREMOVEON)
+ CASE_HRESULT(VLDTR_E_CT_DUPTDNAME)
+ CASE_HRESULT(VLDTR_E_MAR_BADOFFSET)
+ CASE_HRESULT(VLDTR_E_DS_BADOWNER)
+ CASE_HRESULT(VLDTR_E_DS_BADFLAGS)
+ CASE_HRESULT(VLDTR_E_DS_NOBLOB)
+ CASE_HRESULT(VLDTR_E_MAR_BADIMPL)
+ CASE_HRESULT(VLDTR_E_MR_VARARGCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_MD_CTORNOTVOID)
+ CASE_HRESULT(VLDTR_E_EV_FIRENOTVOID)
+ CASE_HRESULT(VLDTR_E_AS_BADLOCALE)
+ CASE_HRESULT(VLDTR_E_CN_PARENTTYPE)
+ CASE_HRESULT(VLDTR_E_SIG_SENTINMETHODDEF)
+ CASE_HRESULT(VLDTR_E_SIG_SENTMUSTVARARG)
+ CASE_HRESULT(VLDTR_E_SIG_MULTSENTINELS)
+ CASE_HRESULT(VLDTR_E_SIG_LASTSENTINEL)
+ CASE_HRESULT(VLDTR_E_SIG_MISSARG)
+ CASE_HRESULT(VLDTR_E_SIG_BYREFINFIELD)
+ CASE_HRESULT(VLDTR_E_MD_SYNCMETHODINVTYPE)
+ CASE_HRESULT(VLDTR_E_TD_NAMETOOLONG)
+ CASE_HRESULT(VLDTR_E_AS_PROCDUP)
+ CASE_HRESULT(VLDTR_E_ASOS_DUP)
+ CASE_HRESULT(VLDTR_E_MAR_BADFLAGS)
+ CASE_HRESULT(VLDTR_E_CT_NOTYPEDEFID)
+ CASE_HRESULT(VLDTR_E_FILE_BADFLAGS)
+ CASE_HRESULT(VLDTR_E_FILE_NULLHASH)
+ CASE_HRESULT(VLDTR_E_MOD_NONAME)
+ CASE_HRESULT(VLDTR_E_MOD_NAMEFULLQLFD)
+ CASE_HRESULT(VLDTR_E_TD_RTSPCLNOTSPCL)
+ CASE_HRESULT(VLDTR_E_TD_EXTENDSIFACE)
+ CASE_HRESULT(VLDTR_E_MD_CTORPINVOKE)
+ CASE_HRESULT(VLDTR_E_TD_SYSENUMNOTCLASS)
+ CASE_HRESULT(VLDTR_E_TD_SYSENUMNOTEXTVTYPE)
+ CASE_HRESULT(VLDTR_E_MI_SIGMISMATCH)
+ CASE_HRESULT(VLDTR_E_TD_ENUMHASMETHODS)
+ CASE_HRESULT(VLDTR_E_TD_ENUMIMPLIFACE)
+ CASE_HRESULT(VLDTR_E_TD_ENUMHASPROP)
+ CASE_HRESULT(VLDTR_E_TD_ENUMHASEVENT)
+ CASE_HRESULT(VLDTR_E_TD_BADMETHODLST)
+ CASE_HRESULT(VLDTR_E_TD_BADFIELDLST)
+ CASE_HRESULT(VLDTR_E_CN_BADTYPE)
+ CASE_HRESULT(VLDTR_E_TD_ENUMNOINSTFLD)
+ CASE_HRESULT(VLDTR_E_TD_ENUMMULINSTFLD)
+ CASE_HRESULT(VLDTR_E_INTERRUPTED)
+ CASE_HRESULT(VLDTR_E_NOTINIT)
+ CASE_HRESULT(VLDTR_E_IFACE_NOTIFACE)
+ CASE_HRESULT(VLDTR_E_FD_RVAHASNORVA)
+ CASE_HRESULT(VLDTR_E_FD_RVAHASZERORVA)
+ CASE_HRESULT(VLDTR_E_MD_RVAANDIMPLMAP)
+ CASE_HRESULT(VLDTR_E_TD_EXTRAFLAGS)
+ CASE_HRESULT(VLDTR_E_TD_EXTENDSITSELF)
+ CASE_HRESULT(VLDTR_E_TD_SYSVTNOTEXTOBJ)
+ CASE_HRESULT(VLDTR_E_TD_EXTTYPESPEC)
+ CASE_HRESULT(VLDTR_E_TD_VTNOSIZE)
+ CASE_HRESULT(VLDTR_E_TD_IFACESEALED)
+ CASE_HRESULT(VLDTR_E_NC_BADNESTED)
+ CASE_HRESULT(VLDTR_E_NC_BADENCLOSER)
+ CASE_HRESULT(VLDTR_E_NC_DUP)
+ CASE_HRESULT(VLDTR_E_NC_DUPENCLOSER)
+ CASE_HRESULT(VLDTR_E_FRVA_ZERORVA)
+ CASE_HRESULT(VLDTR_E_FRVA_BADFIELD)
+ CASE_HRESULT(VLDTR_E_FRVA_DUPRVA)
+ CASE_HRESULT(VLDTR_E_FRVA_DUPFIELD)
+ CASE_HRESULT(VLDTR_E_EP_BADTOKEN)
+ CASE_HRESULT(VLDTR_E_EP_INSTANCE)
+ CASE_HRESULT(VLDTR_E_TD_ENUMFLDBADTYPE)
+ CASE_HRESULT(VLDTR_E_MD_BADRVA)
+ CASE_HRESULT(VLDTR_E_FD_LITERALNODEFAULT)
+ CASE_HRESULT(VLDTR_E_IFACE_METHNOTIMPL)
+ CASE_HRESULT(VLDTR_E_CA_BADPARENT)
+ CASE_HRESULT(VLDTR_E_CA_BADTYPE)
+ CASE_HRESULT(VLDTR_E_CA_NOTCTOR)
+ CASE_HRESULT(VLDTR_E_CA_BADSIG)
+ CASE_HRESULT(VLDTR_E_CA_NOSIG)
+ CASE_HRESULT(VLDTR_E_CA_BADPROLOG)
+ CASE_HRESULT(VLDTR_E_MD_BADLOCALSIGTOK)
+ CASE_HRESULT(VLDTR_E_MD_BADHEADER)
+ CASE_HRESULT(VLDTR_E_EP_TOOMANYARGS)
+ CASE_HRESULT(VLDTR_E_EP_BADRET)
+ CASE_HRESULT(VLDTR_E_EP_BADARG)
+ CASE_HRESULT(VLDTR_E_SIG_BADVOID)
+ CASE_HRESULT(CORDBG_E_UNRECOVERABLE_ERROR)
+ CASE_HRESULT(CORDBG_E_PROCESS_TERMINATED)
+ CASE_HRESULT(CORDBG_E_PROCESS_NOT_SYNCHRONIZED)
+ CASE_HRESULT(CORDBG_E_CLASS_NOT_LOADED)
+ CASE_HRESULT(CORDBG_E_IL_VAR_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_BAD_REFERENCE_VALUE)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_NON_NATIVE_FRAME)
+ CASE_HRESULT(CORDBG_E_NONCONTINUABLE_EXCEPTION)
+ CASE_HRESULT(CORDBG_E_CODE_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_FUNCTION_NOT_IL)
+ CASE_HRESULT(CORDBG_S_BAD_START_SEQUENCE_POINT)
+ CASE_HRESULT(CORDBG_S_BAD_END_SEQUENCE_POINT)
+ CASE_HRESULT(CORDBG_S_INSUFFICIENT_INFO_FOR_SET_IP)
+ CASE_HRESULT(CORDBG_E_CANT_SET_IP_INTO_FINALLY)
+ CASE_HRESULT(CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY)
+ CASE_HRESULT(CORDBG_E_CANT_SET_IP_INTO_CATCH)
+ CASE_HRESULT(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME)
+ CASE_HRESULT(CORDBG_E_SET_IP_IMPOSSIBLE)
+ CASE_HRESULT(CORDBG_E_FUNC_EVAL_BAD_START_POINT)
+ CASE_HRESULT(CORDBG_E_INVALID_OBJECT)
+ CASE_HRESULT(CORDBG_E_FUNC_EVAL_NOT_COMPLETE)
+ CASE_HRESULT(CORDBG_S_FUNC_EVAL_HAS_NO_RESULT)
+ CASE_HRESULT(CORDBG_S_VALUE_POINTS_TO_VOID)
+ CASE_HRESULT(CORDBG_E_INPROC_NOT_IMPL)
+ CASE_HRESULT(CORDBG_S_FUNC_EVAL_ABORTED)
+ CASE_HRESULT(CORDBG_E_STATIC_VAR_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_OBJECT_IS_NOT_COPYABLE_VALUE_CLASS)
+ CASE_HRESULT(CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER)
+ CASE_HRESULT(CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
+ CASE_HRESULT(CORDBG_E_REMOTE_CONNECTION_CONN_RESET)
+ CASE_HRESULT(CORDBG_E_REMOTE_CONNECTION_KEEP_ALIVE)
+ CASE_HRESULT(CORDBG_E_REMOTE_CONNECTION_FATAL_ERROR)
+ CASE_HRESULT(CORDBG_E_CANT_SET_TO_JMC)
+ CASE_HRESULT(CORDBG_E_BAD_THREAD_STATE)
+ CASE_HRESULT(CORDBG_E_DEBUGGER_ALREADY_ATTACHED)
+ CASE_HRESULT(CORDBG_E_SUPERFLOUS_CONTINUE)
+ CASE_HRESULT(CORDBG_E_SET_VALUE_NOT_ALLOWED_ON_NONLEAF_FRAME)
+ CASE_HRESULT(CORDBG_E_ENC_EH_MAX_NESTING_LEVEL_CANT_INCREASE)
+ CASE_HRESULT(CORDBG_E_ENC_MODULE_NOT_ENC_ENABLED)
+ CASE_HRESULT(CORDBG_E_SET_IP_NOT_ALLOWED_ON_EXCEPTION)
+ CASE_HRESULT(CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL)
+ CASE_HRESULT(CORDBG_E_PROCESS_DETACHED)
+ CASE_HRESULT(CORDBG_E_ENC_METHOD_SIG_CHANGED)
+ CASE_HRESULT(CORDBG_E_ENC_METHOD_NO_LOCAL_SIG)
+ CASE_HRESULT(CORDBG_E_ENC_CANT_ADD_FIELD_TO_VALUE_OR_LAYOUT_CLASS)
+ CASE_HRESULT(CORDBG_E_ENC_CANT_CHANGE_FIELD)
+ CASE_HRESULT(CORDBG_E_ENC_CANT_ADD_NON_PRIVATE_MEMBER)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_STATIC)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_INSTANCE)
+ CASE_HRESULT(CORDBG_E_ENC_ZAPPED_WITHOUT_ENC)
+ CASE_HRESULT(CORDBG_E_ENC_BAD_METHOD_INFO)
+ CASE_HRESULT(CORDBG_E_ENC_JIT_CANT_UPDATE)
+ CASE_HRESULT(CORDBG_E_ENC_MISSING_CLASS)
+ CASE_HRESULT(CORDBG_E_ENC_INTERNAL_ERROR)
+ CASE_HRESULT(CORDBG_E_ENC_HANGING_FIELD)
+ CASE_HRESULT(CORDBG_E_MODULE_NOT_LOADED)
+ CASE_HRESULT(CORDBG_E_ENC_CANT_CHANGE_SUPERCLASS)
+ CASE_HRESULT(CORDBG_E_UNABLE_TO_SET_BREAKPOINT)
+ CASE_HRESULT(CORDBG_E_DEBUGGING_NOT_POSSIBLE)
+ CASE_HRESULT(CORDBG_E_KERNEL_DEBUGGER_ENABLED)
+ CASE_HRESULT(CORDBG_E_KERNEL_DEBUGGER_PRESENT)
+ CASE_HRESULT(CORDBG_E_HELPER_THREAD_DEAD)
+ CASE_HRESULT(CORDBG_E_INTERFACE_INHERITANCE_CANT_CHANGE)
+ CASE_HRESULT(CORDBG_E_INCOMPATIBLE_PROTOCOL)
+ CASE_HRESULT(CORDBG_E_TOO_MANY_PROCESSES)
+ CASE_HRESULT(CORDBG_E_INTEROP_NOT_SUPPORTED)
+ CASE_HRESULT(CORDBG_E_NO_REMAP_BREAKPIONT)
+ CASE_HRESULT(CORDBG_E_OBJECT_NEUTERED)
+ CASE_HRESULT(CORPROF_E_FUNCTION_NOT_COMPILED)
+ CASE_HRESULT(CORPROF_E_DATAINCOMPLETE)
+ CASE_HRESULT(CORPROF_E_NOT_REJITABLE_METHODS)
+ CASE_HRESULT(CORPROF_E_CANNOT_UPDATE_METHOD)
+ CASE_HRESULT(CORPROF_E_FUNCTION_NOT_IL)
+ CASE_HRESULT(CORPROF_E_NOT_MANAGED_THREAD)
+ CASE_HRESULT(CORPROF_E_CALL_ONLY_FROM_INIT)
+ CASE_HRESULT(CORPROF_E_INPROC_NOT_ENABLED)
+ CASE_HRESULT(CORPROF_E_JITMAPS_NOT_ENABLED)
+ CASE_HRESULT(CORPROF_E_INPROC_ALREADY_BEGUN)
+ CASE_HRESULT(CORPROF_E_INPROC_NOT_AVAILABLE)
+ CASE_HRESULT(CORPROF_E_NOT_YET_AVAILABLE)
+ CASE_HRESULT(SECURITY_E_XML_TO_ASN_ENCODING)
+ CASE_HRESULT(SECURITY_E_INCOMPATIBLE_SHARE)
+ CASE_HRESULT(SECURITY_E_UNVERIFIABLE)
+ CASE_HRESULT(SECURITY_E_INCOMPATIBLE_EVIDENCE)
+ CASE_HRESULT(CLDB_E_INTERNALERROR)
+ CASE_HRESULT(CORSEC_E_DECODE_SET)
+ CASE_HRESULT(CORSEC_E_ENCODE_SET)
+ CASE_HRESULT(CORSEC_E_UNSUPPORTED_FORMAT)
+ CASE_HRESULT(SN_CRYPTOAPI_CALL_FAILED)
+ CASE_HRESULT(SN_NO_SUITABLE_CSP)
+ CASE_HRESULT(CORSEC_E_INVALID_ATTR)
+ CASE_HRESULT(CORSEC_E_POLICY_EXCEPTION)
+ CASE_HRESULT(CORSEC_E_MIN_GRANT_FAIL)
+ CASE_HRESULT(CORSEC_E_NO_EXEC_PERM)
+ CASE_HRESULT(CORSEC_E_XMLSYNTAX)
+ CASE_HRESULT(CORSEC_E_INVALID_STRONGNAME)
+ CASE_HRESULT(CORSEC_E_MISSING_STRONGNAME)
+ CASE_HRESULT(CORSEC_E_CONTAINER_NOT_FOUND)
+ CASE_HRESULT(CORSEC_E_INVALID_IMAGE_FORMAT)
+ CASE_HRESULT(CORSEC_E_CRYPTO)
+ CASE_HRESULT(CORSEC_E_CRYPTO_UNEX_OPER)
+ CASE_HRESULT(CORSECATTR_E_BAD_ATTRIBUTE)
+ CASE_HRESULT(CORSECATTR_E_MISSING_CONSTRUCTOR)
+ CASE_HRESULT(CORSECATTR_E_FAILED_TO_CREATE_PERM)
+ CASE_HRESULT(CORSECATTR_E_BAD_ACTION_ASM)
+ CASE_HRESULT(CORSECATTR_E_BAD_ACTION_OTHER)
+ CASE_HRESULT(CORSECATTR_E_BAD_PARENT)
+ CASE_HRESULT(CORSECATTR_E_TRUNCATED)
+ CASE_HRESULT(CORSECATTR_E_BAD_VERSION)
+ CASE_HRESULT(CORSECATTR_E_BAD_ACTION)
+ CASE_HRESULT(CORSECATTR_E_NO_SELF_REF)
+ CASE_HRESULT(CORSECATTR_E_BAD_NONCAS)
+ CASE_HRESULT(CORSECATTR_E_ASSEMBLY_LOAD_FAILED)
+ CASE_HRESULT(CORSECATTR_E_ASSEMBLY_LOAD_FAILED_EX)
+ CASE_HRESULT(CORSECATTR_E_TYPE_LOAD_FAILED)
+ CASE_HRESULT(CORSECATTR_E_TYPE_LOAD_FAILED_EX)
+ CASE_HRESULT(CORSECATTR_E_ABSTRACT)
+ CASE_HRESULT(CORSECATTR_E_UNSUPPORTED_TYPE)
+ CASE_HRESULT(CORSECATTR_E_UNSUPPORTED_ENUM_TYPE)
+ CASE_HRESULT(CORSECATTR_E_NO_FIELD)
+ CASE_HRESULT(CORSECATTR_E_NO_PROPERTY)
+ CASE_HRESULT(CORSECATTR_E_EXCEPTION)
+ CASE_HRESULT(CORSECATTR_E_EXCEPTION_HR)
+ CASE_HRESULT(ISS_E_ISOSTORE)
+ CASE_HRESULT(ISS_E_OPEN_STORE_FILE)
+ CASE_HRESULT(ISS_E_OPEN_FILE_MAPPING)
+ CASE_HRESULT(ISS_E_MAP_VIEW_OF_FILE)
+ CASE_HRESULT(ISS_E_GET_FILE_SIZE)
+ CASE_HRESULT(ISS_E_CREATE_MUTEX)
+ CASE_HRESULT(ISS_E_LOCK_FAILED)
+ CASE_HRESULT(ISS_E_FILE_WRITE)
+ CASE_HRESULT(ISS_E_SET_FILE_POINTER)
+ CASE_HRESULT(ISS_E_CREATE_DIR)
+ CASE_HRESULT(ISS_E_STORE_NOT_OPEN)
+ CASE_HRESULT(ISS_E_CORRUPTED_STORE_FILE)
+ CASE_HRESULT(ISS_E_STORE_VERSION)
+ CASE_HRESULT(ISS_E_FILE_NOT_MAPPED)
+ CASE_HRESULT(ISS_E_BLOCK_SIZE_TOO_SMALL)
+ CASE_HRESULT(ISS_E_ALLOC_TOO_LARGE)
+ CASE_HRESULT(ISS_E_USAGE_WILL_EXCEED_QUOTA)
+ CASE_HRESULT(ISS_E_TABLE_ROW_NOT_FOUND)
+ CASE_HRESULT(ISS_E_DEPRECATE)
+ CASE_HRESULT(ISS_E_CALLER)
+ CASE_HRESULT(ISS_E_PATH_LENGTH)
+ CASE_HRESULT(ISS_E_MACHINE)
+ CASE_HRESULT(ISS_E_MACHINE_DACL)
+ CASE_HRESULT(ISS_E_ISOSTORE_END)
+ CASE_HRESULT(COR_E_APPLICATION)
+ CASE_HRESULT(COR_E_ARGUMENTOUTOFRANGE)
+ CASE_HRESULT(COR_E_ARITHMETIC)
+ CASE_HRESULT(COR_E_ARRAYTYPEMISMATCH)
+ CASE_HRESULT(COR_E_CONTEXTMARSHAL)
+ CASE_HRESULT(COR_E_TIMEOUT)
+ CASE_HRESULT(COR_E_DEVICESNOTSUPPORTED)
+ CASE_HRESULT(COR_E_DIVIDEBYZERO)
+ CASE_HRESULT(COR_E_EXCEPTION)
+ CASE_HRESULT(COR_E_EXECUTIONENGINE)
+ CASE_HRESULT(COR_E_FIELDACCESS)
+ CASE_HRESULT(COR_E_FORMAT)
+ CASE_HRESULT(COR_E_BADIMAGEFORMAT)
+ CASE_HRESULT(COR_E_ASSEMBLYEXPECTED)
+ CASE_HRESULT(COR_E_TYPEUNLOADED)
+ CASE_HRESULT(COR_E_INDEXOUTOFRANGE)
+ CASE_HRESULT(COR_E_INVALIDOPERATION)
+ CASE_HRESULT(COR_E_INVALIDPROGRAM)
+ CASE_HRESULT(COR_E_MEMBERACCESS)
+ CASE_HRESULT(COR_E_METHODACCESS)
+ CASE_HRESULT(COR_E_MISSINGFIELD)
+ CASE_HRESULT(COR_E_MISSINGMANIFESTRESOURCE)
+ CASE_HRESULT(COR_E_MISSINGMEMBER)
+ CASE_HRESULT(COR_E_MISSINGMETHOD)
+ CASE_HRESULT(COR_E_MULTICASTNOTSUPPORTED)
+ CASE_HRESULT(COR_E_NOTFINITENUMBER)
+ CASE_HRESULT(COR_E_DUPLICATEWAITOBJECT)
+ CASE_HRESULT(COR_E_PLATFORMNOTSUPPORTED)
+ CASE_HRESULT(COR_E_NOTSUPPORTED)
+ CASE_HRESULT(COR_E_OVERFLOW)
+ CASE_HRESULT(COR_E_RANK)
+ CASE_HRESULT(COR_E_REMOTING)
+ CASE_HRESULT(COR_E_SERVER)
+ CASE_HRESULT(COR_E_SERVICEDCOMPONENT)
+ CASE_HRESULT(COR_E_SECURITY)
+ CASE_HRESULT(COR_E_SERIALIZATION)
+ CASE_HRESULT(COR_E_STACKOVERFLOW)
+ CASE_HRESULT(COR_E_SYNCHRONIZATIONLOCK)
+ CASE_HRESULT(COR_E_SYSTEM)
+ CASE_HRESULT(COR_E_THREADABORTED)
+ CASE_HRESULT(COR_E_THREADINTERRUPTED)
+ CASE_HRESULT(COR_E_THREADSTATE)
+ CASE_HRESULT(COR_E_THREADSTOP)
+ CASE_HRESULT(COR_E_TYPEINITIALIZATION)
+ CASE_HRESULT(COR_E_TYPELOAD)
+ CASE_HRESULT(COR_E_ENTRYPOINTNOTFOUND)
+ CASE_HRESULT(COR_E_DLLNOTFOUND)
+ CASE_HRESULT(COR_E_VERIFICATION)
+ CASE_HRESULT(COR_E_INVALIDCOMOBJECT)
+ CASE_HRESULT(COR_E_MARSHALDIRECTIVE)
+ CASE_HRESULT(COR_E_INVALIDOLEVARIANTTYPE)
+ CASE_HRESULT(COR_E_SAFEARRAYTYPEMISMATCH)
+ CASE_HRESULT(COR_E_SAFEARRAYRANKMISMATCH)
+ CASE_HRESULT(COR_E_INVALIDFILTERCRITERIA)
+ CASE_HRESULT(COR_E_REFLECTIONTYPELOAD)
+ CASE_HRESULT(COR_E_TARGET)
+ CASE_HRESULT(COR_E_TARGETINVOCATION)
+ CASE_HRESULT(COR_E_CUSTOMATTRIBUTEFORMAT)
+ CASE_HRESULT(COR_E_ENDOFSTREAM)
+ CASE_HRESULT(COR_E_FILELOAD)
+ CASE_HRESULT(COR_E_FILENOTFOUND)
+ CASE_HRESULT(COR_E_BAD_PATHNAME)
+ CASE_HRESULT(COR_E_IO)
+ CASE_HRESULT(COR_E_DIRECTORYNOTFOUND)
+ CASE_HRESULT(COR_E_PATHTOOLONG)
+ CASE_HRESULT(COR_E_OBJECTDISPOSED)
+ CASE_HRESULT(COR_E_NEWER_RUNTIME)
+ CASE_HRESULT(CLR_E_SHIM_RUNTIMELOAD)
+ CASE_HRESULT(CLR_E_SHIM_RUNTIMEEXPORT)
+ CASE_HRESULT(CLR_E_SHIM_INSTALLROOT)
+ CASE_HRESULT(CLR_E_SHIM_INSTALLCOMP)
+ CASE_HRESULT(VER_E_HRESULT)
+ CASE_HRESULT(VER_E_OFFSET)
+ CASE_HRESULT(VER_E_OPCODE)
+ CASE_HRESULT(VER_E_OPERAND)
+ CASE_HRESULT(VER_E_TOKEN)
+ CASE_HRESULT(VER_E_EXCEPT)
+ CASE_HRESULT(VER_E_STACK_SLOT)
+ CASE_HRESULT(VER_E_LOC)
+ CASE_HRESULT(VER_E_ARG)
+ CASE_HRESULT(VER_E_FOUND)
+ CASE_HRESULT(VER_E_EXPECTED)
+ CASE_HRESULT(VER_E_UNKNOWN_OPCODE)
+ CASE_HRESULT(VER_E_SIG_CALLCONV)
+ CASE_HRESULT(VER_E_SIG_ELEMTYPE)
+ CASE_HRESULT(VER_E_RET_SIG)
+ CASE_HRESULT(VER_E_FIELD_SIG)
+ CASE_HRESULT(VER_E_INTERNAL)
+ CASE_HRESULT(VER_E_STACK_TOO_LARGE)
+ CASE_HRESULT(VER_E_ARRAY_NAME_LONG)
+ CASE_HRESULT(VER_E_FALLTHRU)
+ CASE_HRESULT(VER_E_TRY_GTEQ_END)
+ CASE_HRESULT(VER_E_TRYEND_GT_CS)
+ CASE_HRESULT(VER_E_HND_GTEQ_END)
+ CASE_HRESULT(VER_E_HNDEND_GT_CS)
+ CASE_HRESULT(VER_E_FLT_GTEQ_CS)
+ CASE_HRESULT(VER_E_TRY_START)
+ CASE_HRESULT(VER_E_HND_START)
+ CASE_HRESULT(VER_E_FLT_START)
+ CASE_HRESULT(VER_E_TRY_OVERLAP)
+ CASE_HRESULT(VER_E_TRY_EQ_HND_FIL)
+ CASE_HRESULT(VER_E_TRY_SHARE_FIN_FAL)
+ CASE_HRESULT(VER_E_HND_OVERLAP)
+ CASE_HRESULT(VER_E_HND_EQ)
+ CASE_HRESULT(VER_E_FIL_OVERLAP)
+ CASE_HRESULT(VER_E_FIL_EQ)
+ CASE_HRESULT(VER_E_FIL_CONT_TRY)
+ CASE_HRESULT(VER_E_FIL_CONT_HND)
+ CASE_HRESULT(VER_E_FIL_CONT_FIL)
+ CASE_HRESULT(VER_E_FIL_GTEQ_CS)
+ CASE_HRESULT(VER_E_FIL_START)
+ CASE_HRESULT(VER_E_FALLTHRU_EXCEP)
+ CASE_HRESULT(VER_E_FALLTHRU_INTO_HND)
+ CASE_HRESULT(VER_E_FALLTHRU_INTO_FIL)
+ CASE_HRESULT(VER_E_LEAVE)
+ CASE_HRESULT(VER_E_RETHROW)
+ CASE_HRESULT(VER_E_ENDFINALLY)
+ CASE_HRESULT(VER_E_ENDFILTER)
+ CASE_HRESULT(VER_E_ENDFILTER_MISSING)
+ CASE_HRESULT(VER_E_BR_INTO_TRY)
+ CASE_HRESULT(VER_E_BR_INTO_HND)
+ CASE_HRESULT(VER_E_BR_INTO_FIL)
+ CASE_HRESULT(VER_E_BR_OUTOF_TRY)
+ CASE_HRESULT(VER_E_BR_OUTOF_HND)
+ CASE_HRESULT(VER_E_BR_OUTOF_FIL)
+ CASE_HRESULT(VER_E_BR_OUTOF_FIN)
+ CASE_HRESULT(VER_E_RET_FROM_TRY)
+ CASE_HRESULT(VER_E_RET_FROM_HND)
+ CASE_HRESULT(VER_E_RET_FROM_FIL)
+ CASE_HRESULT(VER_E_BAD_JMP_TARGET)
+ CASE_HRESULT(VER_E_PATH_LOC)
+ CASE_HRESULT(VER_E_PATH_THIS)
+ CASE_HRESULT(VER_E_PATH_STACK)
+ CASE_HRESULT(VER_E_PATH_STACK_DEPTH)
+ CASE_HRESULT(VER_E_THIS)
+ CASE_HRESULT(VER_E_THIS_UNINIT_EXCEP)
+ CASE_HRESULT(VER_E_THIS_UNINIT_STORE)
+ CASE_HRESULT(VER_E_THIS_UNINIT_RET)
+ CASE_HRESULT(VER_E_THIS_UNINIT_V_RET)
+ CASE_HRESULT(VER_E_THIS_UNINIT_BR)
+ CASE_HRESULT(VER_E_LDFTN_CTOR)
+ CASE_HRESULT(VER_E_STACK_NOT_EQ)
+ CASE_HRESULT(VER_E_STACK_UNEXPECTED)
+ CASE_HRESULT(VER_E_STACK_EXCEPTION)
+ CASE_HRESULT(VER_E_STACK_OVERFLOW)
+ CASE_HRESULT(VER_E_STACK_UNDERFLOW)
+ CASE_HRESULT(VER_E_STACK_EMPTY)
+ CASE_HRESULT(VER_E_STACK_UNINIT)
+ CASE_HRESULT(VER_E_STACK_I_I4_I8)
+ CASE_HRESULT(VER_E_STACK_R_R4_R8)
+ CASE_HRESULT(VER_E_STACK_NO_R_I8)
+ CASE_HRESULT(VER_E_STACK_NUMERIC)
+ CASE_HRESULT(VER_E_STACK_OBJREF)
+ CASE_HRESULT(VER_E_STACK_P_OBJREF)
+ CASE_HRESULT(VER_E_STACK_BYREF)
+ CASE_HRESULT(VER_E_STACK_METHOD)
+ CASE_HRESULT(VER_E_STACK_ARRAY_SD)
+ CASE_HRESULT(VER_E_STACK_VALCLASS)
+ CASE_HRESULT(VER_E_STACK_P_VALCLASS)
+ CASE_HRESULT(VER_E_STACK_NO_VALCLASS)
+ CASE_HRESULT(VER_E_LOC_DEAD)
+ CASE_HRESULT(VER_E_LOC_NUM)
+ CASE_HRESULT(VER_E_ARG_NUM)
+ CASE_HRESULT(VER_E_TOKEN_RESOLVE)
+ CASE_HRESULT(VER_E_TOKEN_TYPE)
+ CASE_HRESULT(VER_E_TOKEN_TYPE_MEMBER)
+ CASE_HRESULT(VER_E_TOKEN_TYPE_FIELD)
+ CASE_HRESULT(VER_E_TOKEN_TYPE_SIG)
+ CASE_HRESULT(VER_E_UNVERIFIABLE)
+ CASE_HRESULT(VER_E_LDSTR_OPERAND)
+ CASE_HRESULT(VER_E_RET_PTR_TO_STACK)
+ CASE_HRESULT(VER_E_RET_VOID)
+ CASE_HRESULT(VER_E_RET_MISSING)
+ CASE_HRESULT(VER_E_RET_EMPTY)
+ CASE_HRESULT(VER_E_RET_UNINIT)
+ CASE_HRESULT(VER_E_ARRAY_ACCESS)
+ CASE_HRESULT(VER_E_ARRAY_V_STORE)
+ CASE_HRESULT(VER_E_ARRAY_SD)
+ CASE_HRESULT(VER_E_ARRAY_SD_PTR)
+ CASE_HRESULT(VER_E_ARRAY_FIELD)
+ CASE_HRESULT(VER_E_ARGLIST)
+ CASE_HRESULT(VER_E_VALCLASS)
+ CASE_HRESULT(VER_E_METHOD_ACCESS)
+ CASE_HRESULT(VER_E_FIELD_ACCESS)
+ CASE_HRESULT(VER_E_DEAD)
+ CASE_HRESULT(VER_E_FIELD_STATIC)
+ CASE_HRESULT(VER_E_FIELD_NO_STATIC)
+ CASE_HRESULT(VER_E_ADDR)
+ CASE_HRESULT(VER_E_ADDR_BYREF)
+ CASE_HRESULT(VER_E_ADDR_LITERAL)
+ CASE_HRESULT(VER_E_INITONLY)
+ CASE_HRESULT(VER_E_THROW)
+ CASE_HRESULT(VER_E_CALLVIRT_VALCLASS)
+ CASE_HRESULT(VER_E_CALL_SIG)
+ CASE_HRESULT(VER_E_CALL_STATIC)
+ CASE_HRESULT(VER_E_CTOR)
+ CASE_HRESULT(VER_E_CTOR_VIRT)
+ CASE_HRESULT(VER_E_CTOR_OR_SUPER)
+ CASE_HRESULT(VER_E_CTOR_MUL_INIT)
+ CASE_HRESULT(VER_E_SIG)
+ CASE_HRESULT(VER_E_SIG_ARRAY)
+ CASE_HRESULT(VER_E_SIG_ARRAY_PTR)
+ CASE_HRESULT(VER_E_SIG_ARRAY_BYREF)
+ CASE_HRESULT(VER_E_SIG_ELEM_PTR)
+ CASE_HRESULT(VER_E_SIG_VARARG)
+ CASE_HRESULT(VER_E_SIG_VOID)
+ CASE_HRESULT(VER_E_SIG_BYREF_BYREF)
+ CASE_HRESULT(VER_E_CODE_SIZE_ZERO)
+ CASE_HRESULT(VER_E_BAD_VARARG)
+ CASE_HRESULT(VER_E_TAIL_CALL)
+ CASE_HRESULT(VER_E_TAIL_BYREF)
+ CASE_HRESULT(VER_E_TAIL_RET)
+ CASE_HRESULT(VER_E_TAIL_RET_VOID)
+ CASE_HRESULT(VER_E_TAIL_RET_TYPE)
+ CASE_HRESULT(VER_E_TAIL_STACK_EMPTY)
+ CASE_HRESULT(VER_E_METHOD_END)
+ CASE_HRESULT(VER_E_BAD_BRANCH)
+ CASE_HRESULT(VER_E_FIN_OVERLAP)
+ CASE_HRESULT(VER_E_LEXICAL_NESTING)
+ CASE_HRESULT(VER_E_VOLATILE)
+ CASE_HRESULT(VER_E_UNALIGNED)
+ CASE_HRESULT(VER_E_INNERMOST_FIRST)
+ CASE_HRESULT(VER_E_CALLI_VIRTUAL)
+ CASE_HRESULT(VER_E_CALL_ABSTRACT)
+ CASE_HRESULT(VER_E_STACK_UNEXP_ARRAY)
+ CASE_HRESULT(VER_E_NOT_IN_GC_HEAP)
+ CASE_HRESULT(VER_E_TRY_N_EMPTY_STACK)
+ CASE_HRESULT(VER_E_DLGT_CTOR)
+ CASE_HRESULT(VER_E_DLGT_BB)
+ CASE_HRESULT(VER_E_DLGT_PATTERN)
+ CASE_HRESULT(VER_E_DLGT_LDFTN)
+ CASE_HRESULT(VER_E_FTN_ABSTRACT)
+ CASE_HRESULT(VER_E_SIG_C_VC)
+ CASE_HRESULT(VER_E_SIG_VC_C)
+ CASE_HRESULT(VER_E_BOX_PTR_TO_STACK)
+ CASE_HRESULT(VER_E_SIG_BYREF_TB_AH)
+ CASE_HRESULT(VER_E_SIG_ARRAY_TB_AH)
+ CASE_HRESULT(VER_E_ENDFILTER_STACK)
+ CASE_HRESULT(VER_E_DLGT_SIG_I)
+ CASE_HRESULT(VER_E_DLGT_SIG_O)
+ CASE_HRESULT(VER_E_RA_PTR_TO_STACK)
+ CASE_HRESULT(VER_E_CATCH_VALUE_TYPE)
+ CASE_HRESULT(VER_E_CATCH_BYREF)
+ CASE_HRESULT(VER_E_FIL_PRECEED_HND)
+ CASE_HRESULT(VER_E_LDVIRTFTN_STATIC)
+ CASE_HRESULT(VER_E_CALLVIRT_STATIC)
+ CASE_HRESULT(VER_E_INITLOCALS)
+ CASE_HRESULT(VER_E_BR_TO_EXCEPTION)
+ CASE_HRESULT(VER_E_CALL_CTOR)
+ CASE_HRESULT(VER_E_BAD_PE)
+ CASE_HRESULT(VER_E_BAD_MD)
+ CASE_HRESULT(VER_E_BAD_APPDOMAIN)
+ CASE_HRESULT(VER_E_TYPELOAD)
+ CASE_HRESULT(VER_E_PE_LOAD)
+ CASE_HRESULT(VER_E_WRITE_RVA_STATIC)
+ CASE_HRESULT(CORDBG_E_THREAD_NOT_SCHEDULED)
+#endif
+
+ default:
+ return NULL;
+ }
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
+// ---------------------------------------------------------------------------
+// HRException class. Implements exception API for exceptions from HRESULTS
+// ---------------------------------------------------------------------------
+
+HRESULT HRException::GetHR()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_hr;
+}
+
+// ---------------------------------------------------------------------------
+// COMException class. - moved to COMEx.cpp
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// SEHException class. Implements exception API for SEH exception info
+// ---------------------------------------------------------------------------
+
+HRESULT SEHException::GetHR()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ if (IsComPlusException(&m_exception)) // EE exception
+ return (HRESULT) m_exception.ExceptionInformation[0];
+ else
+ return m_exception.ExceptionCode;
+}
+
+IErrorInfo *SEHException::GetErrorInfo()
+{
+ LIMITED_METHOD_CONTRACT;
+ return NULL;
+}
+
+void SEHException::GetMessage(SString &string)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (IsComPlusException(&m_exception)) // EE exception
+ {
+ GenerateTopLevelHRExceptionMessage(GetHR(), string);
+ }
+ else
+ {
+ if (m_exception.ExceptionCode != 0)
+ {
+ string.Printf("Exception code 0x%.8x", m_exception.ExceptionCode);
+ }
+ else
+ {
+ // If we don't have a valid exception code, then give a generic message that's a little nicer than
+ // "code 0x00000000".
+ string.Printf("Unknown exception");
+ }
+ }
+}
+
+//==============================================================================
+// DelegatingException class. Implements exception API for "foreign" exceptions.
+//==============================================================================
+
+DelegatingException::DelegatingException()
+ : m_delegatedException((Exception*)DELEGATE_NOT_YET_SET)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+} // DelegatingException::DelegatingException()
+
+//------------------------------------------------------------------------------
+DelegatingException::~DelegatingException()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If there is a valid delegate pointer (inited and non-NULL), delete it.
+ if (IsDelegateValid())
+ Delete(m_delegatedException);
+
+ // Avoid confusion.
+ m_delegatedException = NULL;
+} // DelegatingException::~DelegatingException()
+
+//------------------------------------------------------------------------------
+// Retrieve the delegating exception, or get one from the Thread, or get NULL.
+Exception* DelegatingException::GetDelegate()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If we haven't gotten the exception pointer before..
+ if (!IsDelegateSet())
+ {
+ // .. get it now. NULL in case there isn't one and we take default action.
+ m_delegatedException = NULL;
+ GetLastThrownObjectExceptionFromThread(reinterpret_cast<void**>(&m_delegatedException));
+ }
+
+ return m_delegatedException;
+} // Exception* DelegatingException::GetDelegate()
+
+//------------------------------------------------------------------------------
+// Virtual overrides
+HRESULT DelegatingException::GetHR()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ // Retrieve any delegating exception.
+ Exception *pDelegate = GetDelegate();
+
+ // If there is a delegate exception, defer to it. Otherwise,
+ // default to E_FAIL.
+ return pDelegate ? pDelegate->GetHR() : E_FAIL;
+
+} // HRESULT DelegatingException::GetHR()
+
+//------------------------------------------------------------------------------
+IErrorInfo *DelegatingException::GetErrorInfo()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Retrieve any delegating exception.
+ Exception *pDelegate = GetDelegate();
+
+ // If there is a delegate exception, defer to it. Otherwise,
+ // default to NULL.
+ return pDelegate ? pDelegate->GetErrorInfo() : NULL;
+
+} // IErrorInfo *DelegatingException::GetErrorInfo()
+
+//------------------------------------------------------------------------------
+void DelegatingException::GetMessage(SString &result)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Retrieve any delegating exception.
+ Exception *pDelegate = GetDelegate();
+
+ // If there is a delegate exception, defer to it. Otherwise,
+ // default to a generic message.
+ if (pDelegate)
+ {
+ pDelegate->GetMessage(result);
+ }
+ else
+ {
+ // If we don't have a valid exception code, then give a generic message
+ // that's a little nicer than "code 0x00000000".
+ result.Printf("Unknown exception");
+ }
+} // void DelegatingException::GetMessage()
+
+//------------------------------------------------------------------------------
+Exception *DelegatingException::Clone()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Clone the base exception, this will also take care of cloning the inner
+ // exception if there is one.
+ NewHolder<DelegatingException> retExcep((DelegatingException*)Exception::Clone());
+
+ // If there is a valid delegating exception...
+ if (IsDelegateValid())
+ { // ... clone it.
+ retExcep->m_delegatedException = m_delegatedException->Clone();
+ }
+ else
+ { // ... but if there is not, just copy -- either NULL or DELEGATE_NOT_YET_SET
+ retExcep->m_delegatedException = m_delegatedException;
+ }
+
+ retExcep.SuppressRelease();
+ return retExcep;
+} // virtual Exception *DelegatingException::Clone()
+
+//==============================================================================
+//==============================================================================
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr)
+{
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG1(LF_EH, LL_INFO100, "ThrowHR: HR = %x\n", hr);
+
+ if (hr == E_OUTOFMEMORY)
+ ThrowOutOfMemory();
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+ if (hr == S_OK)
+ hr = E_FAIL;
+
+ EX_THROW(HRException, (hr));
+}
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr, SString const &msg)
+{
+ STATIC_CONTRACT_SO_TOLERANT;
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG1(LF_EH, LL_INFO100, "ThrowHR: HR = %x\n", hr);
+
+ if (hr == E_OUTOFMEMORY)
+ ThrowOutOfMemory();
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+ if (hr == S_OK)
+ hr = E_FAIL;
+
+ EX_THROW(HRMsgException, (hr, msg));
+}
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr, UINT uText)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ if (hr == E_OUTOFMEMORY)
+ ThrowOutOfMemory();
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+ if (hr == S_OK)
+ hr = E_FAIL;
+
+ SString sExceptionText;
+
+ // We won't check the return value here. If it fails, we'll just
+ // throw the HR
+ sExceptionText.LoadResource(CCompRC::Error, uText);
+
+ EX_THROW(HRMsgException, (hr, sExceptionText));
+}
+
+void DECLSPEC_NORETURN ThrowWin32(DWORD err)
+{
+ STATIC_CONTRACT_SO_TOLERANT;
+ WRAPPER_NO_CONTRACT;
+ if (err == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ {
+ ThrowHR(HRESULT_FROM_WIN32(err));
+ }
+}
+
+void DECLSPEC_NORETURN ThrowLastError()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ ThrowWin32(GetLastError());
+}
+
+void DECLSPEC_NORETURN ThrowOutOfMemory()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+
+ g_hrFatalError = COR_E_OUTOFMEMORY;
+
+ // Regular CLR builds - throw our pre-created OOM exception object
+ PAL_CPP_THROW(Exception *, Exception::GetOOMException());
+
+#else
+
+ // DAC builds - raise a DacError
+ DacError(E_OUTOFMEMORY);
+
+ // DacError always throws but isn't marked DECLSPEC_NORETURN so we have to
+ // tell the compiler that this code is unreachable. We could mark DacError
+ // (and DacNotImpl) as DECLSPEC_NORETURN, but then we've have to update a
+ // lot of code where we do something afterwards. Also, due to inlining,
+ // we'd sometimes have to change functions which call functions that only
+ // call DacNotImpl. I have these changes in a bbpack and some of them look
+ // nice, but I'm not sure if it's worth the risk of merge conflicts.
+ UNREACHABLE();
+
+#endif
+}
+
+#include "corexcep.h"
+
+#ifdef FEATURE_STACK_PROBE
+void DECLSPEC_NORETURN ThrowStackOverflow()
+{
+ CONTRACTL
+ {
+ // This should be throws... But it isn't because a SO doesn't technically
+ // fall into the same THROW/NOTHROW conventions as the rest of the contract
+ // infrastructure.
+ NOTHROW;
+
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ //g_hrFatalError=COR_E_STACKOVERFLOW;
+ PTR_INT32 p_ghrFatalError = dac_cast<PTR_INT32>(GVAL_ADDR(g_hrFatalError));
+ _ASSERTE(p_ghrFatalError != NULL);
+ *p_ghrFatalError = COR_E_STACKOVERFLOW;
+
+
+ RaiseException(EXCEPTION_SOFTSO, 0, 0, NULL);
+ UNREACHABLE();
+}
+#endif
+
+void DECLSPEC_NORETURN ThrowMessage(LPCSTR string, ...)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ StackSString message;
+
+ va_list args;
+ va_start(args, string);
+ message.VPrintf(string, args);
+ va_end(args);
+
+ EX_THROW(HRMsgException, (E_FAIL, message));
+}
+
+
+//--------------------------------------------------------------------------------
+// Helper for EX_THROW_WITH_INNER()
+//
+// Clones an exception into the current domain. Also handles special cases for
+// OOM and other stuff. Making this a function so we don't inline all this logic
+// every place we call EX_THROW_WITH_INNER.
+//
+// If the "inner" is a transient exception such as OOM or ThreadAbort, this function
+// will just throw it rather than allow it to be wrapped in another exception.
+//--------------------------------------------------------------------------------
+Exception *ExThrowWithInnerHelper(Exception *inner)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ // Yes, NULL is a legal case. Makes it easier to author uniform helpers for
+ // both wrapped and normal exceptions.
+ if (inner == NULL)
+ {
+ return NULL;
+ }
+
+ if (inner == Exception::GetOOMException())
+ {
+ // We don't want to do allocations if we're already throwing an OOM!
+ PAL_CPP_THROW(Exception*, inner);
+ }
+
+ inner = inner->DomainBoundClone();
+
+ // It isn't useful to wrap OOMs and StackOverflows in other exceptions. Just throw them now.
+ //
+ if (inner->IsTransient())
+ {
+ PAL_CPP_THROW(Exception*, inner);
+ }
+ return inner;
+}
+
+#ifdef _DEBUG
+
+#pragma warning(disable: 4748)
+#pragma optimize("", off)
+#pragma warning(disable: 4748)
+
+
+void ExThrowTrap(const char *fcn, const char *file, int line, const char *szType, HRESULT hr, const char *args)
+{
+ SUPPORTS_DAC;
+ return;
+}
+#endif
+
+
+
+
+//-------------------------------------------------------------------------------------------
+// This routine will generate the most descriptive possible error message for an hresult.
+// It will generate at minimum the hex value. It will also try to generate the symbolic name
+// (E_POINTER) and the friendly description (from the message tables.)
+//
+// bNoGeekStuff suppresses hex HR codes. Use this sparingly as most error strings generated by the
+// CLR are aimed at developers, not end-users.
+//-------------------------------------------------------------------------------------------
+void GetHRMsg(HRESULT hr, SString &result, BOOL bNoGeekStuff/* = FALSE*/)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ result = W(""); // Make sure this routine isn't an inadvertent data-leak exploit!
+
+
+
+ SString strDescr;
+ BOOL fHaveDescr = FALSE;
+
+ if (FAILED(hr) && HRESULT_FACILITY(hr) == FACILITY_URT && HRESULT_CODE(hr) < MAX_URT_HRESULT_CODE)
+ {
+ fHaveDescr = strDescr.LoadResource(CCompRC::Error, MSG_FOR_URT_HR(hr));
+ }
+ else
+ {
+ DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
+ dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
+
+#if FEATURE_USE_LCID
+ fHaveDescr = strDescr.FormatMessage(dwFlags, 0, hr, LANG_USER_DEFAULT);
+#else
+ fHaveDescr = strDescr.FormatMessage(dwFlags, 0, hr, 0);
+#endif
+ }
+
+ LPCSTR name = Exception::GetHRSymbolicName(hr);
+
+ // If we can't get a resource string, print the hresult regardless.
+ if (!fHaveDescr)
+ {
+ bNoGeekStuff = FALSE;
+ }
+
+ if (fHaveDescr)
+ {
+ result.Append(strDescr);
+ }
+
+
+ if (!bNoGeekStuff)
+ {
+ if (fHaveDescr)
+ {
+ result.Append(W(" ("));
+ }
+
+ SString strExcepFromHR;
+ strExcepFromHR.LoadResource(CCompRC::Error, IDS_EE_EXCEPTION_FROM_HRESULT);
+ result.Append(strExcepFromHR);
+ result.AppendPrintf(W("0x%.8X"), hr);
+ if (name != NULL)
+ {
+ result.AppendPrintf(W(" (%S)"), name);
+ }
+
+
+ if (fHaveDescr)
+ {
+ result.Append(W(")"));
+ }
+
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------
+// Similar to GetHRMsg but phrased for top-level exception message.
+//-------------------------------------------------------------------------------------------
+void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ result = W(""); // Make sure this routine isn't an inadvertent data-leak exploit!
+
+ GetHRMsg(hresult, result);
+}
+
+#if !defined(DACCESS_COMPILE)
+
+void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo)
+{
+ WRAPPER_NO_CONTRACT;
+
+ PEXCEPTION_RECORD pRecord = (PEXCEPTION_RECORD)ClrFlsGetValue(TlsIdx_PEXCEPTION_RECORD);
+ PCONTEXT pContext = (PCONTEXT)ClrFlsGetValue(TlsIdx_PCONTEXT);
+
+ pExceptionInfo->ContextRecord = pContext;
+ pExceptionInfo->ExceptionRecord = pRecord;
+
+#ifdef _DEBUG
+ if (pRecord != NULL)
+ {
+ _ASSERTE ((PVOID)(pRecord) > (PVOID)(&pRecord));
+ }
+#endif
+}
+#endif // !defined(DACCESS_COMPILE)
+
+DWORD GetCurrentExceptionCode()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ return (DWORD)(size_t)ClrFlsGetValue(TlsIdx_EXCEPTION_CODE);
+}
+
+bool IsCurrentExceptionSO()
+{
+ WRAPPER_NO_CONTRACT;
+ DWORD exceptionCode = GetCurrentExceptionCode();
+ return IsSOExceptionCode(exceptionCode);
+}
+
+bool IsSOExceptionCode(DWORD exceptionCode)
+{
+ if (exceptionCode == STATUS_STACK_OVERFLOW
+#ifdef FEATURE_STACK_PROBE
+ || exceptionCode == EXCEPTION_SOFTSO
+#endif
+ )
+ {
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+//===========================================================================================
+// These abstractions hide the difference between legacy desktop CLR's (that don't support
+// side-by-side-inproc and rely on a fixed SEH code to identify managed exceptions) and
+// new CLR's that support side-by-side inproc.
+//
+// The new CLR's use a different set of SEH codes to avoid conflicting with the legacy CLR's.
+// In addition, to distinguish between EH's raised by different inproc instances of the CLR,
+// the module handle of the owning CLR is stored in ExceptionRecord.ExceptionInformation[4].
+//
+// (Note: all existing SEH's use either only slot [0] or no slots at all. We are leaving
+// slots [1] thru [3] open for future expansion.)
+//===========================================================================================
+
+// Is this exception code one of the special CLR-specific SEH codes that participate in the
+// instance-tagging scheme?
+BOOL IsInstanceTaggedSEHCode(DWORD dwExceptionCode)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return dwExceptionCode == EXCEPTION_COMPLUS;
+}
+
+// This set of overloads generates the NumberParameters and ExceptionInformation[] array to
+// pass to RaiseException().
+//
+// Parameters:
+// exceptionArgs: a fixed-size array of size INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE.
+// This will get filled in by this function. (The module handle goes
+// in the last slot if this is a side-by-side-inproc enabled build.)
+//
+// exceptionArg1... up to four arguments that go in slots [0]..[3]. These depends
+// the specific requirements of your exception code.
+//
+// Returns:
+// The NumberParameters to pass to RaiseException().
+//
+// Basically, this is either INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE or the count of your
+// fixed arguments depending on whether this tagged-SEH-enabled build.
+//
+// This function is not permitted to fail.
+
+// (the existing system can support more overloads up to 4 fixed arguments but we don't need them at this time.)
+
+static DWORD MarkAsThrownByUsWorker(UINT numArgs, /*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0 = 0)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ _ASSERTE(numArgs < INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE);
+ FillMemory(exceptionArgs, sizeof(ULONG_PTR) * INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE, 0);
+
+ exceptionArgs[0] = arg0;
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && (defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE))
+ exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] = (ULONG_PTR) (GetCLRModule());
+#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)
+
+ return INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE;
+}
+
+DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE])
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return MarkAsThrownByUsWorker(0, exceptionArgs);
+}
+
+DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return MarkAsThrownByUsWorker(1, exceptionArgs, arg0);
+}
+
+// Given an exception record, checks if it's exception code matches a specific exception code
+// *and* whether it was tagged by the calling instance of the CLR.
+//
+// If this is a non-tagged-SEH-enabled build, it is blindly assumed to be tagged by the
+// calling instance of the CLR.
+BOOL WasThrownByUs(const EXCEPTION_RECORD *pcER, DWORD dwExceptionCode)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ _ASSERTE(IsInstanceTaggedSEHCode(dwExceptionCode));
+ _ASSERTE(pcER != NULL);
+ if (dwExceptionCode != pcER->ExceptionCode)
+ {
+ return FALSE;
+ }
+
+ if (pcER->NumberParameters != INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE)
+ {
+ return FALSE;
+ }
+#if!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && (defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE))
+ if ( ((ULONG_PTR)(GetCLRModule())) != pcER->ExceptionInformation[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] )
+ {
+ return FALSE;
+ }
+ return TRUE;
+#else // !(!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && (defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)))
+ return FALSE;
+#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && defined(FEATURE_CORECLR) || !defined(SELF_NO_HOST) || defined(DACCESS_COMPILE)
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// The following group wraps the basic abstracts specifically for EXCEPTION_COMPLUS.
+//-----------------------------------------------------------------------------------
+BOOL IsComPlusException(const EXCEPTION_RECORD *pcER)
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ return WasThrownByUs(pcER, EXCEPTION_COMPLUS);
+}
+
+VOID RaiseComPlusException()
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE];
+ DWORD numParams = MarkAsThrownByUs(exceptionArgs);
+ RaiseException(EXCEPTION_COMPLUS, 0, numParams, exceptionArgs);
+}
+
+//===========================================================================================
+//===========================================================================================
diff --git a/src/utilcode/format1.cpp b/src/utilcode/format1.cpp
new file mode 100644
index 0000000000..83946a7761
--- /dev/null
+++ b/src/utilcode/format1.cpp
@@ -0,0 +1,121 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/***************************************************************************/
+/* routines for parsing file format stuff ... */
+/* this is split off from format.cpp because this uses meta-data APIs that
+ are not present in many builds. Thus if someone needs things in the format.cpp
+ file but does not have the meta-data APIs, I want it to link */
+
+#include "stdafx.h"
+#include "cor.h"
+#include "corpriv.h"
+
+//---------------------------------------------------------------------------------------
+//
+static LONG FilterAllExceptions(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ if ((pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) ||
+ (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ARRAY_BOUNDS_EXCEEDED) ||
+ (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR))
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+//---------------------------------------------------------------------------------------
+//
+COR_ILMETHOD_DECODER::COR_ILMETHOD_DECODER(
+ COR_ILMETHOD * header,
+ void * pInternalImport,
+ DecoderStatus * wbStatus)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ // Can't put contract because of SEH
+ // CONTRACTL
+ // {
+ // NOTHROW;
+ // GC_NOTRIGGER;
+ // FORBID_FAULT;
+ // }
+ // CONTRACTL_END
+
+ bool fErrorInInit = false;
+ struct Param
+ {
+ COR_ILMETHOD_DECODER * pThis;
+ COR_ILMETHOD * header;
+ } param;
+ param.pThis = this;
+ param.header = header;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ // Decode the COR header into a more convenient form
+ DecoderInit(pParam->pThis, pParam->header);
+ }
+ PAL_EXCEPT_FILTER(FilterAllExceptions)
+ {
+ fErrorInInit = true;
+ Code = 0;
+ SetLocalVarSigTok(0);
+ if (wbStatus != NULL)
+ {
+ *wbStatus = FORMAT_ERROR;
+ }
+ }
+ PAL_ENDTRY
+
+ if (fErrorInInit)
+ {
+ return;
+ }
+
+ // If there is a local variable sig, fetch it into 'LocalVarSig'
+ if ((GetLocalVarSigTok() != 0) && (pInternalImport != NULL))
+ {
+ IMDInternalImport * pMDI = reinterpret_cast<IMDInternalImport *>(pInternalImport);
+
+ if (wbStatus != NULL)
+ {
+ if ((!pMDI->IsValidToken(GetLocalVarSigTok())) ||
+ (TypeFromToken(GetLocalVarSigTok()) != mdtSignature) ||
+ (RidFromToken(GetLocalVarSigTok()) == 0))
+ {
+ *wbStatus = FORMAT_ERROR; // failure bad local variable signature token
+ return;
+ }
+ }
+
+ if (FAILED(pMDI->GetSigFromToken(GetLocalVarSigTok(), &cbLocalVarSig, &LocalVarSig)))
+ {
+ // Failure bad local variable signature token
+ if (wbStatus != NULL)
+ {
+ *wbStatus = FORMAT_ERROR;
+ }
+ LocalVarSig = NULL;
+ cbLocalVarSig = 0;
+ return;
+ }
+
+ if (wbStatus != NULL)
+ {
+ if (FAILED(validateTokenSig(GetLocalVarSigTok(), LocalVarSig, cbLocalVarSig, 0, pMDI)) ||
+ (*LocalVarSig != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG))
+ {
+ *wbStatus = VERIFICATION_ERROR; // failure validating local variable signature
+ return;
+ }
+ }
+ }
+
+ if (wbStatus != NULL)
+ {
+ *wbStatus = SUCCESS;
+ }
+} // COR_ILMETHOD_DECODER::COR_ILMETHOD_DECODER
diff --git a/src/utilcode/fstream.cpp b/src/utilcode/fstream.cpp
new file mode 100644
index 0000000000..c0c76c3ae7
--- /dev/null
+++ b/src/utilcode/fstream.cpp
@@ -0,0 +1,296 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "stdafx.h" // Precompiled header key.
+#include "fstream.h"
+
+CFileStream::CFileStream()
+: _cRef(1)
+, _hFile(INVALID_HANDLE_VALUE)
+{
+}
+
+CFileStream::~CFileStream()
+{
+ Close();
+}
+
+HRESULT CFileStream::OpenForRead(LPCWSTR wzFilePath)
+{
+ HRESULT hr = S_OK;
+ DWORD dwShareMode = FILE_SHARE_READ;
+
+ dwShareMode |= FILE_SHARE_DELETE;
+
+ _ASSERTE(_hFile == INVALID_HANDLE_VALUE && wzFilePath);
+ if (_hFile != INVALID_HANDLE_VALUE || !wzFilePath) {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ _hFile = WszCreateFile(wzFilePath, GENERIC_READ,
+ dwShareMode, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto Exit;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::OpenForWrite(LPCWSTR wzFilePath)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(_hFile == INVALID_HANDLE_VALUE && wzFilePath);
+ if (_hFile != INVALID_HANDLE_VALUE || !wzFilePath) {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ _hFile = WszCreateFile(wzFilePath, GENERIC_WRITE,
+ FILE_SHARE_READ, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (_hFile == INVALID_HANDLE_VALUE)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto Exit;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::QueryInterface(REFIID riid, void **ppv)
+{
+ HRESULT hr = S_OK;
+
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream)) {
+ *ppv = static_cast<IStream *>(this);
+ }
+ else {
+ hr = E_NOINTERFACE;
+ }
+
+ if (*ppv) {
+ AddRef();
+ }
+
+ return hr;
+}
+
+STDMETHODIMP_(ULONG) CFileStream::AddRef()
+{
+ return InterlockedIncrement(&_cRef);
+}
+
+STDMETHODIMP_(ULONG) CFileStream::Release()
+{
+ ULONG ulRef = InterlockedDecrement(&_cRef);
+
+ if (!ulRef) {
+ delete this;
+ }
+
+ return ulRef;
+}
+
+HRESULT CFileStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
+{
+ HRESULT hr = S_OK;
+ ULONG cbRead = 0;
+
+ if (pcbRead != NULL) {
+ *pcbRead = 0;
+ }
+
+ _ASSERTE(_hFile != INVALID_HANDLE_VALUE);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ if (!::ReadFile(_hFile, pv, cb, &cbRead, NULL)) {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ goto Exit;
+ }
+
+ if (cbRead == 0) {
+ hr = S_FALSE;
+ }
+ else {
+ hr = NOERROR;
+ }
+
+ if (pcbRead != NULL) {
+ *pcbRead = cbRead;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::Write(void const *pv, ULONG cb, ULONG *pcbWritten)
+{
+ HRESULT hr = S_OK;
+ ULONG cbWritten = 0;
+
+ if (pcbWritten != NULL) {
+ *pcbWritten = 0;
+ }
+
+ _ASSERTE(_hFile != INVALID_HANDLE_VALUE);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ if (!::WriteFile(_hFile, pv, cb, &cbWritten, NULL)) {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ goto Exit;
+ }
+
+ if (cbWritten == 0) {
+ hr = S_FALSE;
+ }
+ else {
+ hr = S_OK;
+ }
+
+ if (pcbWritten != NULL) {
+ *pcbWritten = cbWritten;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
+{
+#if 1 // SetFilePointerEx not supported on Win9x
+ return E_NOTIMPL;
+#else
+ HRESULT hr = S_OK;
+ DWORD dwFileOrigin;
+ BOOL bRet;
+
+ _ASSERTE(_hFile != INVALID_HANDLE_VALUE);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ switch (dwOrigin) {
+ case STREAM_SEEK_SET:
+ dwFileOrigin = FILE_BEGIN;
+ break;
+
+ case STREAM_SEEK_CUR:
+ dwFileOrigin = FILE_CURRENT;
+ break;
+
+ case STREAM_SEEK_END:
+ dwFileOrigin = FILE_END;
+ break;
+
+ default:
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ bRet = SetFilePointerEx(_hFile, dlibMove, (LARGE_INTEGER *)plibNewPosition,
+ dwFileOrigin);
+ if (!bRet) {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ goto Exit;
+ }
+
+
+Exit:
+ return hr;
+#endif
+}
+
+HRESULT CFileStream::SetSize(ULARGE_INTEGER libNewSize)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::Commit(DWORD grfCommitFlags)
+{
+ HRESULT hr = S_OK;
+
+ if (grfCommitFlags != 0) {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ if (!Close()) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::Revert()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::Clone(IStream **ppIStream)
+{
+ return E_NOTIMPL;
+}
+
+
+BOOL CFileStream::Close()
+{
+ BOOL fSuccess = FALSE;
+
+ if (_hFile != INVALID_HANDLE_VALUE) {
+ if (!::CloseHandle(_hFile)) {
+ _hFile = INVALID_HANDLE_VALUE;
+ goto Exit;
+ }
+
+ _hFile = INVALID_HANDLE_VALUE;
+ }
+
+ fSuccess = TRUE;
+
+Exit:
+ return fSuccess;
+}
+
diff --git a/src/utilcode/fstring.cpp b/src/utilcode/fstring.cpp
new file mode 100644
index 0000000000..c26be635f4
--- /dev/null
+++ b/src/utilcode/fstring.cpp
@@ -0,0 +1,317 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ---------------------------------------------------------------------------
+// FString.cpp
+//
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "ex.h"
+#include "holder.h"
+
+#include "fstring.h"
+
+
+namespace FString
+{
+
+#ifdef _MSC_VER
+#pragma optimize("t", on)
+#endif // _MSC_VER
+
+#define MAX_LENGTH 0x1fffff00
+
+
+HRESULT Unicode_Utf8_Length(__in_z LPCWSTR pString, __out bool * pAllAscii, __out DWORD * pLength)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ * pAllAscii = true;
+
+ LPCWSTR p = pString;
+
+ while (true)
+ {
+ WCHAR ch = * p;
+
+ // Single check for termination and non ASCII
+ if (((unsigned) (ch - 1)) >= 0x7F)
+ {
+ if (ch != 0)
+ {
+ * pAllAscii = false;
+ }
+
+ break;
+ }
+
+ p ++;
+ }
+
+ if (* pAllAscii)
+ {
+ if ((p - pString) > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+
+ * pLength = (DWORD) (p - pString);
+ }
+ else // use WideCharToMultiByte to calculate result length
+ {
+ * pLength = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, NULL, 0, NULL, NULL);
+
+ if (*pLength == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ if (*pLength > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+ }
+
+ return S_OK;
+}
+
+
+// UNICODE to UTF8
+HRESULT Unicode_Utf8(__in_z LPCWSTR pString, bool allAscii, __out_z LPSTR pBuffer, DWORD length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ pBuffer[length] = 0;
+
+ if (allAscii)
+ {
+ LPCWSTR p = pString;
+
+ LPSTR q = pBuffer;
+
+ LPCWSTR endP = p + length - 8;
+
+ // Unfold to optimize for long string: 8 chars per iteration
+ while (p < endP)
+ {
+ q[0] = (char) p[0];
+ q[1] = (char) p[1];
+ q[2] = (char) p[2];
+ q[3] = (char) p[3];
+
+ q[4] = (char) p[4];
+ q[5] = (char) p[5];
+ q[6] = (char) p[6];
+ q[7] = (char) p[7];
+
+ q += 8;
+ p += 8;
+ }
+
+ endP += 8;
+
+ while (p < endP)
+ {
+ * q ++ = (char) * p ++;
+ }
+ }
+ else
+ {
+ length = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, pBuffer, (int) length, NULL, NULL);
+
+ if (length == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+
+HRESULT Utf8_Unicode_Length(__in_z LPCSTR pString, __out bool * pAllAscii, __out DWORD * pLength)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ * pAllAscii = true;
+
+ LPCSTR p = pString;
+
+ while (true)
+ {
+ char ch = * p;
+
+ // Single check for termination and non ASCII
+ if (((unsigned) (ch - 1)) >= 0x7F)
+ {
+ if (ch != 0)
+ {
+ * pAllAscii = false;
+ }
+
+ break;
+ }
+
+ p ++;
+ }
+
+ if (* pAllAscii)
+ {
+ if ((p - pString) > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+
+ * pLength = (DWORD)(p - pString);
+ }
+ else
+ {
+ * pLength = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, NULL, 0);
+
+ if (* pLength == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ if (* pLength > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+ }
+
+ return S_OK;
+}
+
+
+// UTF8 to ANSI
+
+HRESULT Utf8_Unicode(__in_z LPCSTR pString, bool allAscii, __out_z LPWSTR pBuffer, DWORD length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ pBuffer[length] = 0;
+
+ if (allAscii)
+ {
+ LPCSTR p = pString;
+
+ LPWSTR q = pBuffer;
+
+ LPCSTR endP = p + length - 8;
+
+ // Unfold to optimize for long string: 4 chars per iteration
+ while (p < endP)
+ {
+ q[0] = (WCHAR) p[0];
+ q[1] = (WCHAR) p[1];
+ q[2] = (WCHAR) p[2];
+ q[3] = (WCHAR) p[3];
+
+ q[4] = (WCHAR) p[4];
+ q[5] = (WCHAR) p[5];
+ q[6] = (WCHAR) p[6];
+ q[7] = (WCHAR) p[7];
+
+ q += 8;
+ p += 8;
+ }
+
+ endP += 8;
+
+ while (p < endP)
+ {
+ * q ++ = (WCHAR) * p ++;
+ }
+ }
+ else
+ {
+ length = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, pBuffer, (int) length);
+
+ if (length == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+
+HRESULT ConvertUnicode_Utf8(__in_z LPCWSTR pString, __out_z LPSTR * pBuffer)
+{
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = Unicode_Utf8_Length(pString, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ * pBuffer = new (nothrow) char[length + 1];
+
+ if (* pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = Unicode_Utf8(pString, allAscii, * pBuffer, length);
+ }
+ }
+
+ return hr;
+}
+
+
+HRESULT ConvertUtf8_Unicode(__in_z LPCSTR pString, __out_z LPWSTR * pBuffer)
+{
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = Utf8_Unicode_Length(pString, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ * pBuffer = new (nothrow) WCHAR[length + 1];
+
+ if (* pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = Utf8_Unicode(pString, allAscii, * pBuffer, length);
+ }
+ }
+
+ return hr;
+}
+
+
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif // _MSC_VER
+
+} // namespace FString
diff --git a/src/utilcode/genericstackprobe.cpp b/src/utilcode/genericstackprobe.cpp
new file mode 100644
index 0000000000..23e7726a8f
--- /dev/null
+++ b/src/utilcode/genericstackprobe.cpp
@@ -0,0 +1,511 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+
+//
+//*****************************************************************************
+// genericstackprobe.cpp
+//
+// This contains code for generic SO stack probes outside the VM, where we don't have a thread object
+//
+//*****************************************************************************
+
+#include "stdafx.h" // Precompiled header key.
+#include "utilcode.h"
+#include "genericstackprobe.h"
+#include "log.h"
+
+#if defined(FEATURE_STACK_PROBE) && !defined(DACCESS_COMPILE)
+
+#ifdef ENABLE_CONTRACTS_IMPL
+BOOL g_EnableDefaultRWValidation = FALSE;
+#endif
+
+bool g_StackProbingEnabled;
+void (*g_fpCheckForSOInSOIntolerantCode)();
+void (*g_fpSetSOIntolerantTransitionMarker)();
+BOOL (*g_fpDoProbe)(unsigned int n);
+void (*g_fpHandleSoftStackOverflow)(BOOL fSkipDebugger);
+
+// This function is used for NO_THROW probes that have no error return path. In this
+// case, we'll just force a stack overflow exception. Do not call it directly - use
+// one of the FORCE_SO macros.
+void DontCallDirectlyForceStackOverflow()
+{
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:26001) // "Suppress PREFast warning about underflows"
+#endif
+
+ UINT_PTR *sp = NULL;
+ // we don't have access to GetCurrentSP from here, so just get an approximation
+ sp = (UINT_PTR *)&sp;
+ while (TRUE)
+ {
+ sp -= (OS_PAGE_SIZE / sizeof(UINT_PTR));
+ *sp = NULL;
+ }
+
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+}
+
+void (*g_fpHandleStackOverflowAfterCatch)() = 0;
+
+// HandleStackOverflowAfterCatch
+//
+void HandleStackOverflowAfterCatch()
+{
+ if (!g_fpHandleStackOverflowAfterCatch)
+ {
+ // If g_fpUnwindGuardChainTo has not been set, then we haven't called InitStackProbes
+ // and we aren't probing, so bail.
+ return;
+ }
+
+ // Reset the SO-tolerance state and restore the current guard
+ g_fpHandleStackOverflowAfterCatch();
+}
+
+NOINLINE void SOIntolerantTransitionHandler::CtorImpl()
+{
+ m_exceptionOccured = true;
+ m_pPreviousHandler = ClrFlsGetValue(TlsIdx_SOIntolerantTransitionHandler);
+ g_fpSetSOIntolerantTransitionMarker();
+}
+
+NOINLINE void SOIntolerantTransitionHandler::DtorImpl()
+{
+ // if we take a stack overflow exception in SO intolerant code, then we must
+ // rip the process. We check this by determining if the SP is beyond the calculated
+ // limit. Checking for the guard page being present is too much overhead during
+ // exception handling (if you can believe that) and impacts perf.
+
+ if (m_exceptionOccured)
+ {
+ g_fpCheckForSOInSOIntolerantCode();
+ }
+
+ ClrFlsSetValue(TlsIdx_SOIntolerantTransitionHandler, m_pPreviousHandler);
+}
+
+#ifdef STACK_GUARDS_DEBUG
+
+// If this is TRUE, we'll make the stack page that we put our stack marker in PAGE_NOACCESS so that you get an AV
+// as soon as you go past the stack guard.
+BOOL g_ProtectStackPagesInDebugger = FALSE;
+
+// This is the smallest size backout probe for which we will try to do a virtual protect for debugging.
+// If this number is too small, the 1 page ganularity of VirtualProtect becomes a problem. This number
+// should be less than or equal to the default backout probe size.
+#define MINIMUM_PAGES_FOR_DEBUGGER_PROTECTION 4.0
+
+void (*g_fpRestoreCurrentStackGuard)(BOOL fDisabled) = 0;
+BOOL g_EnableBackoutStackValidation = FALSE;
+BOOL (*g_fpShouldValidateSOToleranceOnThisThread)() = 0;
+BOOL (*g_fp_BaseStackGuard_RequiresNStackPages)(BaseStackGuardGeneric *pGuard, unsigned int n, BOOL fThrowOnSO) = NULL;
+void (*g_fp_BaseStackGuard_CheckStack)(BaseStackGuardGeneric *pGuard) = NULL;
+BOOL (*g_fpCheckNStackPagesAvailable)(unsigned int n) = NULL;
+
+// Always initialize g_EntryPointProbeAmount to a valid value as there could be a race where a
+// function probes with g_EntryPointProbeAmount's value before it is initialized in InitStackProbes.
+DWORD g_EntryPointProbeAmount = DEFAULT_ENTRY_PROBE_SIZE;
+
+// RestoreSOToleranceState
+//
+// Restores the EE SO-tolerance state after a catch.
+
+void RestoreSOToleranceState()
+{
+ if (!g_fpRestoreCurrentStackGuard)
+ {
+ // If g_fpUnwindGuardChainTo has not been set, then we haven't called InitStackProbes
+ // and we aren't probing, so bail.
+ return;
+ }
+
+ // Reset the SO-tolerance state and restore the current guard
+ g_fpRestoreCurrentStackGuard(FALSE);
+}
+
+//
+// EnsureSOTolerant ASSERTS if we are not in an SO-tolerant mode
+//
+void EnsureSOTolerant()
+{
+#ifdef ENABLE_CONTRACTS_IMPL
+ ClrDebugState *pClrDebugState = GetClrDebugState();
+ _ASSERTE(! pClrDebugState || pClrDebugState->IsSOTolerant());
+#endif
+}
+
+DEBUG_NOINLINE DebugSOIntolerantTransitionHandler::DebugSOIntolerantTransitionHandler()
+ : SOIntolerantTransitionHandler()
+{
+ SCAN_SCOPE_BEGIN;
+ // This CANNOT be a STATIC_CONTRACT_SO_INTOLERANT b/c that isn't
+ // really just a static contract, it is actually calls EnsureSOIntolerantOK
+ // as well. Instead we just use the annotation.
+ ANNOTATION_FN_SO_INTOLERANT;
+#ifdef ENABLE_CONTRACTS_IMPL
+ m_clrDebugState = GetClrDebugState();
+ if (m_clrDebugState)
+ {
+ m_prevSOTolerantState = m_clrDebugState->BeginSOIntolerant();
+ }
+#endif
+}
+
+DEBUG_NOINLINE DebugSOIntolerantTransitionHandler::~DebugSOIntolerantTransitionHandler()
+{
+ SCAN_SCOPE_END;
+
+ if (m_clrDebugState)
+ {
+ m_clrDebugState->SetSOTolerance(m_prevSOTolerantState);
+ }
+}
+
+// This is effectively an implicit probe, because we are guaranteeing that we have
+// enought stack to run and will not take an SO. So we enter SO-intolerant code when
+// we install one of these.
+DEBUG_NOINLINE BaseStackMarker::BaseStackMarker(float numPages, BOOL fAllowDisabling)
+ : m_prevWasSOTolerant(FALSE)
+ , m_pDebugState(
+#ifdef ENABLE_CONTRACTS_IMPL
+ CheckClrDebugState()
+#else
+ NULL
+#endif
+ )
+ , m_fMarkerSet(FALSE)
+ , m_fTemporarilyDisabled(FALSE), m_fAddedToStack(FALSE), m_pPrevious(NULL)
+ , m_numPages(0.0), m_pMarker(NULL)
+ , m_fProtectedStackPage(FALSE), m_fAllowDisabling(fAllowDisabling)
+{
+ SCAN_SCOPE_BEGIN;
+ // This CANNOT be a STATIC_CONTRACT_SO_INTOLERANT b/c that isn't
+ // really just a static contract, it is actually calls EnsureSOIntolerantOK
+ // as well. Instead we just use the annotation.
+ ANNOTATION_FN_SO_INTOLERANT;
+
+ {
+ DEBUG_ONLY_REGION();
+ // If backout stack validation isn't enabled then we are done.
+ if (!g_EnableBackoutStackValidation)
+ {
+ return;
+ }
+
+ // If we can't talk to other markers then the markers could get in each others way
+ if (!m_pDebugState)
+ {
+ return;
+ }
+
+ // Allow only the lowest marker to be active at any one time. Yes, this means that
+ // the stack will only ever have one element in it. However having multiple markers
+ // is problematic for debugging and conflicts with the VirtualProtect option. It
+ // adds little value, in that small backout checks stop happening in exception
+ // codepaths, but these get plenty of coverage in success cases and the lowest
+ // placed marked is the one that could actually indicate a stack overflow.
+ if (!m_pDebugState->GetStackMarkerStack().IsEmpty())
+ {
+ return;
+ }
+
+ // Switch the SO tolerance mode
+ m_prevWasSOTolerant = m_pDebugState->SetSOTolerance(FALSE);
+
+ // If we have less then numPages left before the end of the stack then there is
+ // no point in adding a marker since we will take an SO anyway if we use too much
+ // stack. Putting the marker is actually very bad since it artificially forces an
+ // SO in cases where it wouldn't normally occur if we use less than num pages of stack.
+ if (g_fpCheckNStackPagesAvailable &&
+ !g_fpCheckNStackPagesAvailable(numPages < 1 ? 1 : (unsigned int)numPages))
+ {
+ return;
+ }
+
+ if (m_fAllowDisabling)
+ {
+ // Push ourselves on to the stack of stack markers on the CLR debug state.
+ m_pDebugState->GetStackMarkerStack().PushStackMarker(this);
+ m_fAddedToStack = TRUE;
+ }
+
+ // Set the actual stack guard marker if we have enough stack to do so.
+ SetMarker(numPages);
+
+ if (m_fMarkerSet && m_fAllowDisabling)
+ {
+ ProtectMarkerPageInDebugger();
+ }
+ }
+}
+
+// we have this so that the check of the global can be inlined
+// and we don't make the call to CheckMarker unless we need to.
+DEBUG_NOINLINE void BaseStackMarker::CheckForBackoutViolation()
+{
+ SCAN_SCOPE_END;
+
+ // If backout stack validation isn't enabled then we are done.
+ if (!g_EnableBackoutStackValidation)
+ {
+ return;
+ }
+
+ {
+ DEBUG_ONLY_REGION()
+
+ // The marker should always be re-enabled at this point.
+ CONSISTENCY_CHECK_MSG(!m_fTemporarilyDisabled, "The stack guard was disabled but not properly re-enabled. This is a bug somewhere in the code called after this marker has been set up.");
+
+ if (!m_pDebugState || m_fTemporarilyDisabled)
+ {
+ return;
+ }
+
+ // Reset the SO tolerance of the thread.
+ m_pDebugState->SetSOTolerance(m_prevWasSOTolerant);
+
+ if (m_fAddedToStack)
+ {
+ // Pop ourselves off of the stack of stack markers on the CLR debug state.
+ CONSISTENCY_CHECK(m_pDebugState != NULL);
+ BaseStackMarker *pPopResult = m_pDebugState->GetStackMarkerStack().PopStackMarker();
+
+ CONSISTENCY_CHECK_MSG(pPopResult == this, "The marker we pop off the stack should always be the current marker.");
+ CONSISTENCY_CHECK_MSG(m_pPrevious == NULL, "PopStackMarker should reset the current marker's m_pPrevious field to NULL.");
+ }
+
+ // Not cancellable markers should only be checked when no cancellable markers are present.
+ if (!m_fAllowDisabling && !(m_pDebugState->GetStackMarkerStack().IsEmpty()))
+ {
+ return;
+ }
+
+ if (m_fProtectedStackPage)
+ {
+ UndoPageProtectionInDebugger();
+ }
+
+ if (m_fMarkerSet)
+ {
+ // Check to see if we overwrote the stack guard marker.
+ CheckMarker();
+ }
+ }
+}
+
+void BaseStackMarker::SetMarker(float numPages)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ m_numPages = numPages;
+
+ // Use the address of the argument to get the current stack pointer. Note that this
+ // won't be the exact SP; however it will be close enough.
+ LPVOID pStack = &numPages;
+
+ UINT_PTR *pMarker = (UINT_PTR*)pStack - (int)(OS_PAGE_SIZE / sizeof(UINT_PTR) * m_numPages);
+
+ // We might not have committed our stack yet, so allocate the number of pages
+ // we need so that they will be commited and we won't AV when we try to set the mark.
+ _alloca( (int)(OS_PAGE_SIZE * m_numPages) );
+ m_pMarker = pMarker;
+ *m_pMarker = STACK_COOKIE_VALUE;
+
+ m_fMarkerSet = TRUE;
+
+}
+
+void BaseStackMarker::RareDisableMarker()
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ if (m_fProtectedStackPage)
+ {
+ UndoPageProtectionInDebugger();
+ }
+
+ m_fTemporarilyDisabled = TRUE;
+
+ if (m_fMarkerSet)
+ {
+ *m_pMarker = DISABLED_STACK_COOKIE_VALUE;
+ }
+}
+
+void BaseStackMarker::RareReEnableMarker()
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ m_fTemporarilyDisabled = FALSE;
+
+ if (m_fMarkerSet) {
+ *m_pMarker = STACK_COOKIE_VALUE;
+ }
+
+ if (m_fProtectedStackPage)
+ {
+ ProtectMarkerPageInDebugger();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Protect the page where we put the marker if a debugger is attached. That way, you get an AV right away
+// when you go past the stack guard when running under a debugger.
+//-----------------------------------------------------------------------------
+void BaseStackMarker::ProtectMarkerPageInDebugger()
+{
+ WRAPPER_NO_CONTRACT;
+ DEBUG_ONLY_FUNCTION;
+
+ if (!g_ProtectStackPagesInDebugger)
+ {
+ return;
+ }
+
+ if (m_numPages < MINIMUM_PAGES_FOR_DEBUGGER_PROTECTION)
+ {
+ return;
+ }
+
+ DWORD flOldProtect;
+
+ LOG((LF_EH, LL_INFO100000, "BSM::PMP: m_pMarker 0x%p, value 0x%p\n", m_pMarker, *m_pMarker));
+
+ // We cannot call into host for VirtualProtect. EEVirtualProtect will try to restore previous
+ // guard, but the location has been marked with PAGE_NOACCESS.
+#undef VirtualProtect
+ BOOL fSuccess = ::VirtualProtect(m_pMarker, 1, PAGE_NOACCESS, &flOldProtect);
+ _ASSERTE(fSuccess);
+
+#define VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) \
+ Dont_Use_VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect)
+
+ m_fProtectedStackPage = fSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Remove page protection installed for this probe
+//-----------------------------------------------------------------------------
+void BaseStackMarker::UndoPageProtectionInDebugger()
+{
+ WRAPPER_NO_CONTRACT;
+ DEBUG_ONLY_FUNCTION;
+
+ _ASSERTE(m_fProtectedStackPage);
+ _ASSERTE(!m_fTemporarilyDisabled);
+
+ DWORD flOldProtect;
+ // EEVirtualProtect installs a BoundaryStackGuard. To avoid recursion, we call
+ // into OS for VirtualProtect instead.
+#undef VirtualProtect
+ BOOL fSuccess = ::VirtualProtect(m_pMarker, 1, PAGE_READWRITE, &flOldProtect);
+ _ASSERTE(fSuccess);
+
+ LOG((LF_EH, LL_INFO100000, "BSM::UMP m_pMarker 0x%p\n", m_pMarker));
+
+#define VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) \
+ Dont_Use_VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect)
+}
+
+void BaseStackMarker::CheckMarker()
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ if ( IsMarkerOverrun(m_pMarker) )
+ {
+ SOBackoutViolation(__FUNCTION__, __FILE__, __LINE__);
+ }
+}
+
+AutoCleanupDisableBackoutStackValidation::AutoCleanupDisableBackoutStackValidation()
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+#ifdef ENABLE_CONTRACTS_IMPL
+ m_fAlreadyDisabled = GetClrDebugState()->GetStackMarkerStack().IsDisabled();
+ if (!m_fAlreadyDisabled)
+ {
+ GetClrDebugState()->GetStackMarkerStack().RareDisableStackMarkers();
+ }
+#endif
+}
+
+AutoCleanupDisableBackoutStackValidation::~AutoCleanupDisableBackoutStackValidation()
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+#ifdef ENABLE_CONTRACTS_IMPL
+ if (!m_fAlreadyDisabled)
+ {
+ GetClrDebugState()->GetStackMarkerStack().RareReEnableStackMarkers();
+ }
+#endif
+}
+
+inline void StackMarkerStack::PushStackMarker(BaseStackMarker *pStackMarker)
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ pStackMarker->m_pPrevious = m_pTopStackMarker;
+ m_pTopStackMarker = pStackMarker;
+}
+
+BaseStackMarker *StackMarkerStack::PopStackMarker()
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ BaseStackMarker *pOldTop = m_pTopStackMarker;
+ m_pTopStackMarker = pOldTop->m_pPrevious;
+ pOldTop->m_pPrevious = NULL;
+ return pOldTop;
+}
+
+void StackMarkerStack::RareDisableStackMarkers()
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ // Walk up the stack of markers and disable them all.
+ BaseStackMarker *pCurrentStackMarker = m_pTopStackMarker;
+ while (pCurrentStackMarker)
+ {
+ pCurrentStackMarker->RareDisableMarker();
+ pCurrentStackMarker = pCurrentStackMarker->m_pPrevious;
+ }
+ m_fDisabled = TRUE;
+}
+
+void StackMarkerStack::RareReEnableStackMarkers()
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ // Walk up the stack of markers and re-enable them all.
+ BaseStackMarker *pCurrentStackMarker = m_pTopStackMarker;
+ while (pCurrentStackMarker)
+ {
+ pCurrentStackMarker->RareReEnableMarker();
+ pCurrentStackMarker = pCurrentStackMarker->m_pPrevious;
+ }
+ m_fDisabled = FALSE;
+}
+
+#endif // STACK_GUARDS_DEBUG
+
+#endif // FEATURE_STACK_PROBE && !DACCESS_COMPILE
diff --git a/src/utilcode/guidfromname.cpp b/src/utilcode/guidfromname.cpp
new file mode 100644
index 0000000000..dad72b5f84
--- /dev/null
+++ b/src/utilcode/guidfromname.cpp
@@ -0,0 +1,210 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+// GuidFromName
+
+// Algorithm from Internet Draft document "UUIDs and GUIDs"
+// By Paul J. Leach and Rich Sals, February 4, 1998.
+
+// This function has been adapted from the routines in the document
+// uuid_create_from_name and format_uuid_v3
+
+// Changes from documented routines:
+// 1. Changed all instances of uuid_t to GUID.
+// uuid_t field time_low is GUID field Data1.
+// uuid_t field time_mid is GUID field Data2.
+// uuid_t field time_hi_and_version is GUID field Data3.
+// uuid_t field clock_seq_hi_and_reserved is GUID field Data4[0].
+// uuid_t field clock_seq_low is GUID field Data4[1].
+// uuid_t field node[6] is GUID field Data4[2] through Data4[8].
+//
+// 2. Use a c++ implementation of the md5 cryptographic hash function.
+//
+// 3. Implemented the htonl, htons, ntohl, ntohs socket routines as inlines.
+//
+// 4. Renamed variables and types to suit my biases.
+
+/*
+** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
+** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
+** Digital Equipment Corporation, Maynard, Mass.
+** To anyone who acknowledges that this file is provided "AS IS"
+** without any express or implied warranty: permission to use, copy,
+** modify, and distribute this file for any purpose is hereby
+** granted without fee, provided that the above copyright notices and
+** this notice appears in all source code copies, and that none of
+** the names of Open Software Foundation, Inc., Hewlett-Packard
+** Company, or Digital Equipment Corporation be used in advertising
+** or publicity pertaining to distribution of the software without
+** specific, written prior permission. Neither Open Software
+** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
+** Corporation makes any representations about the suitability of
+** this software for any purpose.
+*/
+
+#include "stdafx.h"
+
+#include "md5.h" // cryptographic hash function
+#include "guidfromname.h" // verify our function signature
+#include "contract.h"
+
+#if BIGENDIAN
+#define BigEndian() true
+#else
+#define BigEndian() false
+#endif
+
+//=============================================================================
+// htons, htonl, ntohs, ntohl equivalents copied and adapted from socket library.
+//=============================================================================
+
+// HostToNetworkLong converts a 32-bit long to network byte order
+
+inline ULONG HostToNetworkLong(ULONG hostlong)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return hostlong;
+ else
+ return ( (hostlong >> 24) & 0x000000FFL) |
+ ( (hostlong >> 8) & 0x0000FF00L) |
+ ( (hostlong << 8) & 0x00FF0000L) |
+ ( (hostlong << 24) & 0xFF000000L);
+}
+
+// HostToNetworkLong converts a 16-bit short to network byte order
+
+inline USHORT HostToNetworkShort(USHORT hostshort)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return hostshort;
+ else
+ return ((hostshort >> 8) & 0x00FF) | ((hostshort << 8) & 0xFF00);
+}
+
+// NetworkToHostLong converts a 32-bit long to local host byte order
+
+inline ULONG NetworkToHostLong(ULONG netlong)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return netlong;
+ else
+ return ( (netlong >> 24) & 0x000000FFL) |
+ ( (netlong >> 8) & 0x0000FF00L) |
+ ( (netlong << 8) & 0x00FF0000L) |
+ ( (netlong << 24) & 0xFF000000L);
+}
+
+// NetworkToHostShort converts a 16-bit short to local host byte order
+
+inline USHORT NetworkToHostShort(USHORT netshort)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return netshort;
+ else
+ return ((netshort >> 8) & 0x00FF) | ((netshort << 8) & 0xFF00);
+}
+
+//=============================================================================
+// GuidFromName(GUID * pGuidResult, REFGUID refGuidNsid,
+// const void * pvName, DWORD dwcbName);
+//=============================================================================
+
+void GuidFromName
+(
+ GUID * pGuidResult, // resulting GUID
+ REFGUID refGuidNsid, // Name Space GUID, so identical names from
+ // different name spaces generate different GUIDs
+ const void * pvName, // the name from which to generate a GUID
+ DWORD dwcbName // name length in bytes
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ MD5 md5; // Cryptographic hash class instance
+ MD5HASHDATA md5HashData; // 128-bit hash result
+ GUID guidNsid; // context NameSpace GUID in network byte order
+
+ GUID guidTemp;
+
+ // put name space ID in network byte order so it hashes the same
+ // no matter what endian machine we're on
+ guidNsid = refGuidNsid;
+
+ // The sample code in the IETF draft document discards the result of
+ // htonl and htons. I've implemented what I think is meant and I've
+ // sent a note to the author asking for confirmation that this is
+ // his intent.
+ if (!BigEndian()) // evaluated at compile time in retail builds
+ {
+ guidNsid.Data1 = HostToNetworkLong (guidNsid.Data1);
+ guidNsid.Data2 = HostToNetworkShort(guidNsid.Data2);
+ guidNsid.Data3 = HostToNetworkShort(guidNsid.Data3);
+ }
+
+ md5.Init();
+ md5.HashMore(&guidNsid, sizeof(GUID));
+ md5.HashMore(pvName, dwcbName);
+ md5.GetHashValue(&md5HashData);
+
+ // the hash is in network byte order at this point
+ memcpy(&guidTemp, &md5HashData, sizeof(GUID));
+
+ // Remainder adapted from function "format_uuid_v3" in IETF draft document
+ // Construct a version 3 uuid with the pseudo-random number plus a few constants.
+ // convert GUID from network order to local byte order
+ if (!BigEndian()) // evaluated at compile time in retail builds
+ {
+ guidTemp.Data1 = NetworkToHostLong (guidTemp.Data1);
+ guidTemp.Data2 = NetworkToHostShort(guidTemp.Data2);
+ guidTemp.Data3 = NetworkToHostShort(guidTemp.Data3);
+ }
+
+ // set version number
+ guidTemp.Data3 &= 0x0FFF; // clear version number nibble
+ guidTemp.Data3 |= (3 << 12);// set version 3 = name-based
+
+ // set variant field
+ guidTemp.Data4[0] &= 0x3F; // clear variant bits
+ guidTemp.Data4[0] |= 0x80; // set variant = 100b
+
+ // If two GuidFromName calls were made from different threads with the same parameters,
+ // we may get incorrect result even though the expected result is the same, because
+ // GuidFromName is operating on the same pGuidResult buffer.
+ // Fix this problem by using a temp GUID buffer and then copy to the pGuidResult buffer.
+ memcpy(pGuidResult, &guidTemp, sizeof(GUID));
+}
+
+
+// This guid is used for calling GuidFromName function as COM+ runtime uniqualifier
+//
+// {69F9CBC9-DA05-11d1-9408-0000F8083460}
+static const GUID COMPLUS_RUNTIME_GUID = {0x69f9cbc9, 0xda05, 0x11d1,
+ {0x94, 0x8, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60}};
+
+void CorGuidFromNameW
+(
+ GUID * pGuidResult, // resulting GUID
+ LPCWSTR wzName, // the unicode name from which to generate a GUID
+ SIZE_T cchName // name length in count of unicode character
+)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GuidFromName(
+ pGuidResult,
+ COMPLUS_RUNTIME_GUID,
+ wzName,
+ (DWORD)((cchName == (SIZE_T) -1 ? (lstrlenW(wzName)+1) : cchName) * sizeof(WCHAR)));
+}
diff --git a/src/utilcode/hostimpl.cpp b/src/utilcode/hostimpl.cpp
new file mode 100644
index 0000000000..75ee7b6e1c
--- /dev/null
+++ b/src/utilcode/hostimpl.cpp
@@ -0,0 +1,426 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+//
+
+//
+// ==--==
+
+#include "stdafx.h"
+
+#include "mscoree.h"
+#include "clrinternal.h"
+#include "hostimpl.h"
+#include "predeftlsslot.h"
+#include "unsafe.h"
+
+// to avoid to include clrhost.h in this file
+#ifdef FAILPOINTS_ENABLED
+extern int RFS_HashStack();
+#endif
+
+static DWORD TlsIndex = TLS_OUT_OF_INDEXES;
+static PTLS_CALLBACK_FUNCTION Callbacks[MAX_PREDEFINED_TLS_SLOT];
+
+#ifdef SELF_NO_HOST
+HANDLE (*g_fnGetExecutableHeapHandle)();
+#endif
+
+extern LPVOID (*__ClrFlsGetBlock)();
+
+//
+// FLS getter to avoid unnecessary indirection via execution engine.
+//
+VOID * ClrFlsGetBlockDirect()
+{
+ return UnsafeTlsGetValue(TlsIndex);
+}
+
+//
+// utility functions for tls functionality
+//
+static void **CheckThreadState(DWORD slot, BOOL force = TRUE)
+{
+ // Treat as a runtime assertion, since the invariant spans many DLLs.
+ _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT);
+
+ // Ensure we have a TLS Index
+ if (TlsIndex == TLS_OUT_OF_INDEXES)
+ {
+ DWORD tmp = UnsafeTlsAlloc();
+
+ if (InterlockedCompareExchange((LONG*)&TlsIndex, tmp, TLS_OUT_OF_INDEXES) != (LONG) TLS_OUT_OF_INDEXES)
+ {
+ // We lost the race with another thread.
+ UnsafeTlsFree(tmp);
+ }
+
+ // Switch to faster TLS getter now that the TLS slot is initialized
+ __ClrFlsGetBlock = ClrFlsGetBlockDirect;
+ }
+
+ _ASSERTE(TlsIndex != TLS_OUT_OF_INDEXES);
+
+ void **pTlsData = (void **)TlsGetValue(TlsIndex);
+
+ if (pTlsData == 0 && force) {
+
+ // !!! Contract uses our TLS support. Contract may be used before our host support is set up.
+ // !!! To better support contract, we call into OS for memory allocation.
+ pTlsData = (void**) ::HeapAlloc(GetProcessHeap(),0,MAX_PREDEFINED_TLS_SLOT*sizeof(void*));
+
+
+ if (pTlsData == NULL)
+ {
+ // workaround! We don't want exceptions being thrown during ClrInitDebugState. Just return NULL out of TlsSetValue.
+ // ClrInitDebugState will do a confirming FlsGet to see if the value stuck.
+
+ // If this is for the stack probe, and we failed to allocate memory for it, we won't
+ // put in a guard page.
+ if (slot == TlsIdx_ClrDebugState || slot == TlsIdx_StackProbe)
+ {
+ return NULL;
+ }
+ RaiseException(STATUS_NO_MEMORY, 0, 0, NULL);
+ }
+ for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++)
+ pTlsData[i] = 0;
+ UnsafeTlsSetValue(TlsIndex, pTlsData);
+ }
+
+ return pTlsData;
+} // CheckThreadState
+
+// This function should only be called during process detatch for
+// mscoree.dll.
+VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex()
+{
+ if (TlsIndex != TLS_OUT_OF_INDEXES)
+ if (UnsafeTlsFree(TlsIndex))
+ TlsIndex = TLS_OUT_OF_INDEXES;
+} // TLS_FreeMasterSlotIndex
+
+
+
+HRESULT STDMETHODCALLTYPE UtilExecutionEngine::QueryInterface(REFIID id, void **pInterface)
+{
+ if (!pInterface)
+ return E_POINTER;
+
+ *pInterface = NULL;
+
+ if (id == IID_IExecutionEngine)
+ *pInterface = (IExecutionEngine *)this;
+ else if (id == IID_IEEMemoryManager)
+ *pInterface = (IEEMemoryManager *)this;
+ else if (id == IID_IUnknown)
+ *pInterface = (IUnknown *)(IExecutionEngine *)this;
+ else
+ return E_NOINTERFACE;
+
+ AddRef();
+ return S_OK;
+} // UtilExecutionEngine::QueryInterface
+
+//
+// lifetime of this object is that of the app it lives in so no point in AddRef/Release
+//
+ULONG STDMETHODCALLTYPE UtilExecutionEngine::AddRef()
+{
+ return 1;
+}
+
+ULONG STDMETHODCALLTYPE UtilExecutionEngine::Release()
+{
+ return 1;
+}
+
+VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback)
+{
+ CheckThreadState(slot);
+
+ // They can toggle between a callback and no callback. But anything else looks like
+ // confusion on their part.
+ //
+ // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But
+ // all the callbacks are equally good.)
+ _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0);
+ Callbacks[slot] = callback;
+}
+
+LPVOID* STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetDataBlock()
+{
+ if (TlsIndex == TLS_OUT_OF_INDEXES)
+ return NULL;
+
+ return (LPVOID *)UnsafeTlsGetValue(TlsIndex);
+}
+
+LPVOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetValue(DWORD slot)
+{
+ void **pTlsData = CheckThreadState(slot, FALSE);
+ if (pTlsData)
+ return pTlsData[slot];
+ else
+ return NULL;
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue)
+{
+ void **pTlsData = CheckThreadState(slot, FALSE);
+ if (pTlsData)
+ {
+ *pValue = pTlsData[slot];
+ return TRUE;
+ }
+ return FALSE;
+}
+
+VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData)
+{
+ void **pTlsData = CheckThreadState(slot);
+ if (pTlsData) // Yes, CheckThreadState(slot, TRUE) can return NULL now.
+ {
+ pTlsData[slot] = pData;
+ }
+}
+
+VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_ThreadDetaching()
+{
+ void **pTlsData = CheckThreadState(0, FALSE);
+ if (pTlsData)
+ {
+ for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++)
+ {
+ // If we have some data and a callback, issue it.
+ if (Callbacks[i] != 0 && pTlsData[i] != 0)
+ (*Callbacks[i])(pTlsData[i]);
+ }
+ ::HeapFree (GetProcessHeap(),0,pTlsData);
+
+ }
+}
+
+CRITSEC_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags)
+{
+ CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
+ UnsafeInitializeCriticalSection(cs);
+ return (CRITSEC_COOKIE)cs;
+}
+
+void STDMETHODCALLTYPE UtilExecutionEngine::DestroyLock(CRITSEC_COOKIE lock)
+{
+ _ASSERTE(lock);
+ UnsafeDeleteCriticalSection((CRITICAL_SECTION*)lock);
+ free(lock);
+}
+
+void STDMETHODCALLTYPE UtilExecutionEngine::AcquireLock(CRITSEC_COOKIE lock)
+{
+ _ASSERTE(lock);
+ UnsafeEnterCriticalSection((CRITICAL_SECTION*)lock);
+}
+
+void STDMETHODCALLTYPE UtilExecutionEngine::ReleaseLock(CRITSEC_COOKIE lock)
+{
+ _ASSERTE(lock);
+ UnsafeLeaveCriticalSection((CRITICAL_SECTION*)lock);
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateAutoEvent(BOOL bInitialState)
+{
+ HANDLE handle = UnsafeCreateEvent(NULL, FALSE, bInitialState, NULL);
+ _ASSERTE(handle);
+ return (EVENT_COOKIE)handle;
+}
+
+EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateManualEvent(BOOL bInitialState)
+{
+ HANDLE handle = UnsafeCreateEvent(NULL, TRUE, bInitialState, NULL);
+ _ASSERTE(handle);
+ return (EVENT_COOKIE)handle;
+}
+
+void STDMETHODCALLTYPE UtilExecutionEngine::CloseEvent(EVENT_COOKIE event)
+{
+ _ASSERTE(event);
+ CloseHandle((HANDLE)event);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrSetEvent(EVENT_COOKIE event)
+{
+ _ASSERTE(event);
+ return UnsafeSetEvent((HANDLE)event);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrResetEvent(EVENT_COOKIE event)
+{
+ _ASSERTE(event);
+ return UnsafeResetEvent((HANDLE)event);
+}
+
+DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ _ASSERTE(event);
+ return WaitForSingleObjectEx((HANDLE)event, dwMilliseconds, bAlertable);
+}
+
+DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds)
+{
+ _ASSERTE(handle);
+ return WaitForSingleObject(handle, dwMilliseconds);
+}
+
+SEMAPHORE_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax)
+{
+ HANDLE handle = UnsafeCreateSemaphore(NULL, (LONG)dwInitial, (LONG)dwMax, NULL);
+ _ASSERTE(handle);
+ return (SEMAPHORE_COOKIE)handle;
+}
+
+void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore)
+{
+ _ASSERTE(semaphore);
+ CloseHandle((HANDLE)semaphore);
+}
+
+DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable)
+{
+ _ASSERTE(semaphore);
+ return WaitForSingleObjectEx((HANDLE)semaphore, dwMilliseconds, bAlertable);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount)
+{
+ _ASSERTE(semaphore);
+ return UnsafeReleaseSemaphore((HANDLE)semaphore, lReleaseCount, lpPreviousCount);
+}
+
+MUTEX_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName)
+{
+ return (MUTEX_COOKIE)WszCreateMutex(lpMutexAttributes,bInitialOwner,lpName);
+}
+
+void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseMutex(MUTEX_COOKIE mutex)
+{
+ _ASSERTE(mutex);
+ CloseHandle((HANDLE)mutex);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseMutex(MUTEX_COOKIE mutex)
+{
+ _ASSERTE(mutex);
+ return ReleaseMutex((HANDLE)mutex);
+}
+
+DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable)
+{
+ _ASSERTE(mutex);
+ return WaitForSingleObjectEx ((HANDLE)mutex, dwMilliseconds, bAlertable);
+}
+
+DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return SleepEx (dwMilliseconds, bAlertable);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrAllocationDisallowed()
+{
+ return FALSE;
+}
+
+LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+#ifdef FAILPOINTS_ENABLED
+ if (RFS_HashStack ())
+ return NULL;
+#endif
+ return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return VirtualFree(lpAddress, dwSize, dwFreeType);
+}
+
+SIZE_T STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return VirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+
+HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessHeap()
+{
+ return GetProcessHeap();
+}
+
+HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessExecutableHeap()
+{
+#ifndef CROSSGEN_COMPILE
+ _ASSERTE(g_fnGetExecutableHeapHandle);
+ return (g_fnGetExecutableHeapHandle != NULL) ? g_fnGetExecutableHeapHandle() : NULL;
+#else
+ return GetProcessHeap();
+#endif
+}
+
+HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)
+{
+ return HeapCreate(flOptions, dwInitialSize, dwMaximumSize);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapDestroy(HANDLE hHeap)
+{
+ return HeapDestroy(hHeap);
+}
+
+LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
+{
+#ifdef FAILPOINTS_ENABLED
+ if (RFS_HashStack ())
+ return NULL;
+#endif
+ return HeapAlloc(hHeap, dwFlags, dwBytes);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
+{
+ return HeapFree(hHeap, dwFlags, lpMem);
+}
+
+BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)
+{
+ return HeapValidate(hHeap, dwFlags, lpMem);
+}
+
+
+//------------------------------------------------------------------------------
+// Helper function to get an exception from outside the exception. In
+// the CLR, it may be from the Thread object. Non-CLR users have no thread object,
+// and it will do nothing.
+
+void UtilExecutionEngine::GetLastThrownObjectExceptionFromThread(void **ppvException)
+{
+ // Declare class so we can declare Exception**
+ class Exception;
+
+ // Cast to our real type.
+ Exception **ppException = reinterpret_cast<Exception**>(ppvException);
+
+ *ppException = NULL;
+} // UtilExecutionEngine::GetLastThrownObjectExceptionFromThread
+
diff --git a/src/utilcode/hostimpl.h b/src/utilcode/hostimpl.h
new file mode 100644
index 0000000000..f82ffda2a6
--- /dev/null
+++ b/src/utilcode/hostimpl.h
@@ -0,0 +1,112 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+// ==++==
+//
+
+//
+//
+
+//
+// ==--==
+
+#ifndef __HOSTIMPL_H__
+#define __HOSTIMPL_H__
+
+#ifdef SELF_NO_HOST
+extern HANDLE g_ExecutableHeapHandle;
+#endif
+
+// We have an internal class that is used to make sure the hosting api
+// is forwarded to the os. This is a must for the shim because mscorwks
+// which normally contains the implementation of the hosting api has not
+// been loaded yet. In fact the shim is the one component responsible
+// for that loading
+class UtilExecutionEngine : public IExecutionEngine, public IEEMemoryManager
+{
+private:
+
+ //***************************************************************************
+ // IUnknown methods
+ //***************************************************************************
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, void **pInterface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ //***************************************************************************
+ // IExecutionEngine methods for TLS
+ //***************************************************************************
+
+ // Associate a callback for cleanup with a TLS slot
+ VOID STDMETHODCALLTYPE TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback);
+ // Get the master TLS slot index
+ LPVOID* STDMETHODCALLTYPE TLS_GetDataBlock();
+
+ // Get the value at a slot
+ LPVOID STDMETHODCALLTYPE TLS_GetValue(DWORD slot);
+
+ // Get the value at a slot, return FALSE if TLS info block doesn't exist
+ BOOL STDMETHODCALLTYPE TLS_CheckValue(DWORD slot, LPVOID * pValue);
+ // Set the value at a slot
+ VOID STDMETHODCALLTYPE TLS_SetValue(DWORD slot, LPVOID pData);
+ // Free TLS memory block and make callback
+ VOID STDMETHODCALLTYPE TLS_ThreadDetaching();
+
+ //***************************************************************************
+ // IExecutionEngine methods for locking
+ //***************************************************************************
+
+ CRITSEC_COOKIE STDMETHODCALLTYPE CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags);
+ void STDMETHODCALLTYPE DestroyLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE AcquireLock(CRITSEC_COOKIE lock);
+ void STDMETHODCALLTYPE ReleaseLock(CRITSEC_COOKIE lock);
+
+ EVENT_COOKIE STDMETHODCALLTYPE CreateAutoEvent(BOOL bInitialState);
+ EVENT_COOKIE STDMETHODCALLTYPE CreateManualEvent(BOOL bInitialState);
+ void STDMETHODCALLTYPE CloseEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrSetEvent(EVENT_COOKIE event);
+ BOOL STDMETHODCALLTYPE ClrResetEvent(EVENT_COOKIE event);
+ DWORD STDMETHODCALLTYPE WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable);
+ DWORD STDMETHODCALLTYPE WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds);
+
+ SEMAPHORE_COOKIE STDMETHODCALLTYPE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax);
+ void STDMETHODCALLTYPE ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore);
+ DWORD STDMETHODCALLTYPE ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable);
+ BOOL STDMETHODCALLTYPE ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount);
+
+ MUTEX_COOKIE STDMETHODCALLTYPE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ BOOL bInitialOwner,
+ LPCTSTR lpName);
+ void STDMETHODCALLTYPE ClrCloseMutex(MUTEX_COOKIE mutex);
+ BOOL STDMETHODCALLTYPE ClrReleaseMutex(MUTEX_COOKIE mutex);
+ DWORD STDMETHODCALLTYPE ClrWaitForMutex(MUTEX_COOKIE mutex,
+ DWORD dwMilliseconds,
+ BOOL bAlertable);
+
+ DWORD STDMETHODCALLTYPE ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable);
+
+ BOOL STDMETHODCALLTYPE ClrAllocationDisallowed();
+
+ void STDMETHODCALLTYPE GetLastThrownObjectExceptionFromThread(void **ppvException);
+
+ //***************************************************************************
+ // IEEMemoryManager methods for locking
+ //***************************************************************************
+ LPVOID STDMETHODCALLTYPE ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
+ BOOL STDMETHODCALLTYPE ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
+ SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+ BOOL STDMETHODCALLTYPE ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessHeap();
+ HANDLE STDMETHODCALLTYPE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
+ BOOL STDMETHODCALLTYPE ClrHeapDestroy(HANDLE hHeap);
+ LPVOID STDMETHODCALLTYPE ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
+ BOOL STDMETHODCALLTYPE ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+ BOOL STDMETHODCALLTYPE ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+ HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap();
+
+}; // class UtilExecutionEngine
+
+#endif //__HOSTIMPL_H__
diff --git a/src/utilcode/iallocator.cpp b/src/utilcode/iallocator.cpp
new file mode 100644
index 0000000000..85966dcc7f
--- /dev/null
+++ b/src/utilcode/iallocator.cpp
@@ -0,0 +1,15 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h" // Precompiled header key.
+#include "iallocator.h"
+
+// static
+DefaultAllocator DefaultAllocator::s_singleton;
+
+// static
+ProcessHeapAllocator ProcessHeapAllocator::s_singleton;
+
+int AllowZeroAllocator::s_zeroLenAllocTarg;
diff --git a/src/utilcode/ilformatter.cpp b/src/utilcode/ilformatter.cpp
new file mode 100644
index 0000000000..41842c6e4d
--- /dev/null
+++ b/src/utilcode/ilformatter.cpp
@@ -0,0 +1,822 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/***************************************************************************/
+/* ILFormatter.h */
+/***************************************************************************/
+
+#include "stdafx.h"
+#include <cor.h>
+#include <debugmacros.h> // for ASSERTE
+#include "ilformatter.h"
+#include "outstring.h"
+#include "opinfo.h"
+
+/***************************************************************************/
+void ILFormatter::init(IMetaDataImport* aMeta, const BYTE* aStart,
+ const BYTE* aLimit, unsigned maxStack, const COR_ILMETHOD_SECT_EH* eh) {
+ this->~ILFormatter(); // clean out old stuff
+
+ meta = aMeta;
+ start = aStart;
+ limit = aLimit;
+ if (maxStack == 0) maxStack++;
+ stackStart = stackCur = new StackEntry[maxStack];
+ stackEnd = stackStart + maxStack;
+ targetStart = targetCur = targetEnd = 0;
+ if (eh != 0) {
+ COR_ILMETHOD_SECT_EH_CLAUSE_FAT buff;
+ const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clause;
+ for(unsigned i = 0; i < eh->EHCount(); i++) {
+ clause = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)eh->EHClause(i, &buff);
+ // is it a regular catch clause ?
+ if ((clause->GetFlags() & (COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT)) == 0)
+ setTarget(clause->GetHandlerOffset(), 1);
+ if(clause->GetFlags() & COR_ILEXCEPTION_CLAUSE_FILTER)
+ setTarget(clause->GetFilterOffset(), 1);
+ }
+ }
+}
+
+/***************************************************************************/
+inline size_t ILFormatter::stackDepth() {
+ return(stackCur - stackStart);
+}
+
+/***************************************************************************/
+inline void ILFormatter::pushAndClear(OutString* val, int prec) {
+ if (stackCur >= stackEnd) {
+ _ASSERTE(!"Stack Overflow (can be ignored)");
+ return; // Ignore overflow in free build
+ }
+ stackCur->val.swap(*val);
+ val->clear();
+ stackCur->prec = prec;
+ stackCur++;
+}
+
+/***************************************************************************/
+inline OutString* ILFormatter::top() {
+ if (stackDepth() == 0) {
+ _ASSERTE(!"Stack underflow (can be ignored)");
+ stackStart->val.clear();
+ stackStart->val << "<UNDERFLOW ERROR>";
+ return (&stackStart->val);
+ }
+ return(&stackCur[-1].val);
+}
+
+/***************************************************************************/
+inline OutString* ILFormatter::pop(int prec) {
+ if (stackDepth() == 0) {
+ _ASSERTE(!"Stack underflow (can be ignored)");
+ stackStart->val.clear();
+ stackStart->val << "<UNDERFLOW ERROR>";
+ return (&stackStart->val);
+ }
+ --stackCur;
+ if (stackCur->prec < prec) {
+ stackCur->val.prepend('(');
+ stackCur->val << ')';
+ }
+ return(&stackCur->val);
+}
+
+/***************************************************************************/
+inline void ILFormatter::popN(size_t num) {
+ if (stackCur-stackStart < (SSIZE_T)num) {
+ _ASSERTE(!"Stack underflow (can be ignored)");
+ stackCur = stackStart;
+ return;
+ }
+ stackCur -= num;
+}
+
+/***************************************************************************/
+void ILFormatter::setStackAsTarget(size_t ilOffset) {
+
+ Target*ptr = targetStart;
+ for(;;) {
+ if (ptr >= targetCur)
+ return;
+ if (ptr->ilOffset == ilOffset)
+ break;
+ ptr++;
+ }
+
+ for(size_t i = 0; i < ptr->stackDepth; i++) {
+ stackStart[i].val.clear();
+ stackStart[i].val << "@STK" << (unsigned)i;
+ }
+ stackCur = stackStart + ptr->stackDepth;
+}
+
+/***************************************************************************/
+void ILFormatter::setTarget(size_t ilOffset, size_t depth) {
+ if (depth == 0)
+ return;
+
+ if (targetCur >= targetEnd) {
+ Target* targetOld = targetStart;
+ size_t oldLen = targetCur-targetStart;
+ targetStart = new Target[oldLen+10];
+ targetEnd = &targetStart[oldLen+10];
+ targetCur = &targetStart[oldLen];
+ memcpy(targetStart, targetOld, sizeof(Target)*oldLen);
+ delete [] targetOld;
+ }
+ targetCur->ilOffset = ilOffset;
+ targetCur->stackDepth = depth;
+ targetCur++;
+}
+
+/***************************************************************************/
+void ILFormatter::spillStack(OutString* out) {
+
+ for(unsigned i = 0; i < stackDepth(); i++) {
+ // don't bother spilling something already spilled.
+ if (memcmp(stackStart[i].val.val(), "@STK", 4) != 0)
+ *out << "@STK" << i << " = " << stackStart[i].val.val() << "\n";
+ stackStart[i].val.clear();
+ stackStart[i].val << "@STK" << i ;
+ }
+}
+
+/***************************************************************************/
+const BYTE* ILFormatter::formatInstr(const BYTE* instrPtr, OutString* out) {
+
+ _ASSERTE(start < instrPtr && instrPtr < limit);
+ OpArgsVal arg;
+ OpInfo op;
+ instrPtr = op.fetch(instrPtr, &arg);
+ *out << op.getName();
+ if (op.getArgsInfo() != InlineNone)
+ *out << ' ';
+ formatInstrArgs(op, arg, out, instrPtr - start);
+ return(instrPtr);
+}
+
+/***************************************************************************/
+void ILFormatter::formatArgs(unsigned numArgs, OutString* out) {
+
+ *out << '(';
+ if (numArgs > stackDepth()) {
+ _ASSERTE(!"Underflow error");
+ *out << "<UNDERFLOW ERROR>";
+ }
+ else {
+ popN(numArgs);
+ for(unsigned i = 0; i < numArgs; i++) {
+ if (i != 0) *out << ", ";
+ *out << stackCur[i].val.val();
+ }
+ }
+ *out << ')';
+}
+
+/***************************************************************************/
+void ILFormatter::formatInstrArgs(OpInfo op, OpArgsVal arg, OutString* out, size_t curILOffset) {
+
+ MDUTF8CSTR typeName=0;
+ HRESULT hr = S_OK;
+ switch(op.getArgsInfo() & PrimaryMask) {
+ case InlineNone:
+ break;
+ case InlineVar:
+ *out << arg.i;
+ break;
+ case InlineI:
+ case InlineRVA:
+ out->hex(arg.i, 0, OutString::put0x);
+ break;
+ case InlineR:
+ *out << arg.r;
+ break;
+ case InlineBrTarget: {
+ _ASSERTE(curILOffset != INVALID_IL_OFFSET);
+ size_t target = curILOffset + arg.i;
+ setTarget(target, stackDepth());
+ *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
+ } break;
+ case InlineI8:
+ out->hex(arg.i, 0, OutString::put0x);
+ break;
+ case InlineString: {
+ ULONG numChars;
+ wchar_t str[84];
+
+ hr = meta->GetUserString(arg.i, str, 80, &numChars);
+ _ASSERTE(SUCCEEDED(hr));
+ if (numChars < 80)
+ str[numChars] = 0;
+ wcscpy_s(&str[79], 4, W("..."));
+ *out << '"';
+ wchar_t* ptr = str;
+ while(*ptr != 0) {
+ if (*ptr == '\n')
+ *out << "\\n";
+ else if (*ptr == '"')
+ *out << "\\\"";
+ else if (*ptr < 0x20 || * ptr >= 0x80) {
+ *out << '\\';
+ out->hex(*ptr, 4, OutString::zeroFill);
+ }
+ else
+ *out << char(*ptr);
+ ptr++;
+ }
+ *out << '"';
+ } break;
+ case InlineMethod:
+ case InlineField:
+ case InlineTok: {
+ // Get the typeName if possible
+ mdToken mdType = mdTypeDefNil;
+ if (TypeFromToken(arg.i) == mdtMethodDef)
+ hr = meta->GetMethodProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0);
+ else if (TypeFromToken(arg.i) == mdtMemberRef)
+ hr = meta->GetMemberRefProps(mdMemberRef(arg.i), &mdType, 0, 0, 0, 0, 0);
+ else if (TypeFromToken(arg.i) == mdtFieldDef)
+ hr = meta->GetFieldProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (SUCCEEDED(hr) && mdType != mdTypeDefNil) {
+ hr = meta->GetNameFromToken(mdType, &typeName);
+ }
+ }
+ // FALL THROUGH
+ case InlineType: {
+ // FIX handle case if (TypeFromToken(arg.i) == mdtTypeSpec)
+ MDUTF8CSTR name;
+ hr = meta->GetNameFromToken(arg.i, &name);
+ if (SUCCEEDED(hr)) {
+ if (typeName) {
+ const char* lastDot = strrchr(typeName, '.');
+ if (lastDot) typeName = lastDot + 1;
+ *out << typeName << "::";
+ }
+ *out << name;
+ }
+ else {
+ *out << "TOK<";
+ out->hex(arg.i, 0, OutString::put0x);
+ *out << '>';
+ }
+ } break;
+ case InlineSig:
+ *out << "SIG<";
+ out->hex(arg.i, 0, OutString::put0x);
+ *out << '>';
+ break;
+ case InlineSwitch: {
+ _ASSERTE(curILOffset != INVALID_IL_OFFSET);
+ unsigned count = arg.switch_.count;
+ unsigned i;
+ for (i = 0; i < count; i++) {
+ size_t target = curILOffset + GET_UNALIGNED_VAL32(&arg.switch_.targets[i]);
+ setTarget(target, stackDepth()-1);
+ *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
+ if (i < count)
+ *out << ' ';
+ }
+ } break;
+ case InlinePhi: {
+ unsigned count = arg.phi.count;
+ unsigned i;
+ for (i = 0; i < count; i++) {
+ *out << GET_UNALIGNED_VAL32(&arg.phi.vars[i]);
+ if (i < count)
+ *out << ' ';
+ }
+ } break;
+ default:
+ _ASSERTE(!"BadType");
+ }
+}
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+/***************************************************************************/
+const BYTE* ILFormatter::formatStatement(const BYTE* instrPtr, OutString* out) {
+
+ OutString result;
+ OpInfo op;
+ OutString *lhs, *rhs, *idx;
+ const char* name;
+ int prec = 0;
+
+ // set stack as it would be if it was begin jumped to
+ setStackAsTarget(instrPtr - start);
+
+ while(instrPtr < limit) {
+ OpArgsVal inlineArg;
+ instrPtr = op.fetch(instrPtr, &inlineArg);
+
+ switch(op.getOpcode()) {
+ case CEE_UNALIGNED:
+ case CEE_TAILCALL:
+ case CEE_VOLATILE:
+ // for now just skip these
+ break;
+
+ case CEE_LDARGA_S:
+ case CEE_LDARGA:
+ result << "&";
+ goto DO_LDARG;
+
+ case CEE_LDARG_0:
+ case CEE_LDARG_1:
+ case CEE_LDARG_2:
+ case CEE_LDARG_3:
+ inlineArg.i = op.getOpcode() - CEE_LDARG_0;
+ goto DO_LDARG;
+
+ case CEE_LDARG:
+ case CEE_LDARG_S:
+ DO_LDARG:
+ name = "arg";
+ DO_LDARG_LDLOC:
+ result << name << inlineArg.i;
+ prec = 0x1000;
+ goto DO_PUSH;
+ DO_PUSH:
+ pushAndClear(&result, prec); // also clears result!
+ break;
+
+ case CEE_LDLOCA_S:
+ case CEE_LDLOCA:
+ result << "&";
+ goto DO_LDLOC;
+
+ case CEE_LDLOC_0:
+ case CEE_LDLOC_1:
+ case CEE_LDLOC_2:
+ case CEE_LDLOC_3:
+ inlineArg.i = op.getOpcode() - CEE_LDLOC_0;
+ goto DO_LDLOC;
+
+ case CEE_LDLOC:
+ case CEE_LDLOC_S:
+ DO_LDLOC:
+ name = "loc";
+ goto DO_LDARG_LDLOC;
+
+ case CEE_STARG:
+ case CEE_STARG_S:
+ name = "arg";
+ DO_STARG_STLOC:
+ lhs = pop(0x10);
+ result << name << inlineArg.i << " = " << lhs->val();
+ DO_STMT:
+ spillStack(out);
+ *out << result.val() << '\n';
+ // if flow of control does not fall through,
+ // assume the stack is empty
+ if (op.getFlow() == FLOW_BRANCH || op.getFlow() == FLOW_RETURN ||
+ op.getFlow() == FLOW_THROW) {
+ popN(stackDepth());
+ }
+ return(instrPtr);
+
+ case CEE_STLOC_0:
+ case CEE_STLOC_1:
+ case CEE_STLOC_2:
+ case CEE_STLOC_3:
+ inlineArg.i = op.getOpcode() - CEE_STLOC_0;
+ goto DO_STLOC;
+
+ case CEE_STLOC:
+ case CEE_STLOC_S:
+ DO_STLOC:
+ name = "loc";
+ goto DO_STARG_STLOC;
+
+ case CEE_LDC_I4_M1:
+ case CEE_LDC_I4_0:
+ case CEE_LDC_I4_1:
+ case CEE_LDC_I4_2:
+ case CEE_LDC_I4_3:
+ case CEE_LDC_I4_4:
+ case CEE_LDC_I4_5:
+ case CEE_LDC_I4_6:
+ case CEE_LDC_I4_7:
+ case CEE_LDC_I4_8:
+ inlineArg.i = op.getOpcode() - CEE_LDC_I4_0;
+ // FALL THROUGH
+ case CEE_LDC_I4:
+ case CEE_LDC_I4_S:
+ result << inlineArg.i;
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDC_I8:
+ result.hex(inlineArg.i8);
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDC_R4:
+ case CEE_LDC_R8:
+ result << inlineArg.r;
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDNULL:
+ result << "null";
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDSTR:
+ formatInstrArgs(op, inlineArg, &result);
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_BEQ:
+ case CEE_BEQ_S:
+ name = "=="; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BGE:
+ case CEE_BGE_S:
+ name = ">="; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BGE_UN:
+ case CEE_BGE_UN_S:
+ name = ">=un"; prec = 0x40; goto DO_BR_BINOP;
+
+ case CEE_BGT:
+ case CEE_BGT_S:
+ name = ">"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BGT_UN:
+ case CEE_BGT_UN_S:
+ name = ">un"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLE:
+ case CEE_BLE_S:
+ name = "<="; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLE_UN:
+ case CEE_BLE_UN_S:
+ name = "<=un"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLT:
+ case CEE_BLT_S:
+ name = "<"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLT_UN:
+ case CEE_BLT_UN_S:
+ name = "<un"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BNE_UN:
+ case CEE_BNE_UN_S:
+ name = "!=un"; prec = 0x40; goto DO_BR_BINOP;
+ DO_BR_BINOP:
+ rhs = pop(prec);
+ lhs = pop(prec-1);
+ result << "if (" << lhs->val() << ' ' << name << ' ' << rhs->val() << ") ";
+ goto DO_BR;
+
+ case CEE_LEAVE_S:
+ case CEE_LEAVE:
+ while (stackDepth() > 0) {
+ lhs = pop();
+ *lhs << '\n' << result; // put the result in front of anything else
+ result.swap(*lhs);
+ }
+ /* fall through */
+ case CEE_BR_S:
+ case CEE_BR:
+ DO_BR: {
+ size_t target = (instrPtr - start) + inlineArg.i;
+ setTarget(target, stackDepth());
+ result << "goto IL_"; result.hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
+ } goto DO_STMT;
+
+ case CEE_BRFALSE_S:
+ case CEE_BRFALSE:
+ name = "!";
+ goto DO_BR_UNOP;
+ case CEE_BRTRUE_S:
+ case CEE_BRTRUE:
+ name = "";
+ DO_BR_UNOP:
+ lhs = pop();
+ result << "if (" << name << lhs->val() << ") ";
+ goto DO_BR;
+
+ case CEE_OR:
+ name = "|"; prec = 0x20; goto DO_BINOP;
+ case CEE_XOR:
+ name = "^"; prec = 0x20; goto DO_BINOP;
+ case CEE_AND:
+ name = "&"; prec = 0x30; goto DO_BINOP;
+ case CEE_SHL:
+ name = "<<"; prec = 0x50; goto DO_BINOP;
+ case CEE_SHR:
+ name = ">>"; prec = 0x50; goto DO_BINOP;
+ case CEE_SHR_UN:
+ name = ">>un"; prec = 0x50; goto DO_BINOP;
+ case CEE_CEQ:
+ name = "=="; prec = 0x40; goto DO_BINOP;
+ case CEE_CGT:
+ name = ">"; prec = 0x40; goto DO_BINOP;
+ case CEE_CGT_UN:
+ name = ">un"; prec = 0x40; goto DO_BINOP;
+ case CEE_CLT:
+ name = "<"; prec = 0x40; goto DO_BINOP;
+ case CEE_CLT_UN:
+ name = "<un"; prec = 0x40; goto DO_BINOP;
+ case CEE_ADD:
+ name = "+"; prec = 0x60; goto DO_BINOP;
+ case CEE_ADD_OVF:
+ name = "+ovf"; prec = 0x60; goto DO_BINOP;
+ case CEE_ADD_OVF_UN:
+ name = "+ovf.un";prec = 0x60; goto DO_BINOP;
+ case CEE_SUB:
+ name = "-"; prec = 0x60; goto DO_BINOP;
+ case CEE_SUB_OVF:
+ name = "-ovf"; prec = 0x60; goto DO_BINOP;
+ case CEE_SUB_OVF_UN:
+ name = "-ovf.un";prec = 0x60; goto DO_BINOP;
+ case CEE_MUL:
+ name = "*"; prec = 0x70; goto DO_BINOP;
+ case CEE_MUL_OVF:
+ name = "*ovf"; prec = 0x70; goto DO_BINOP;
+ case CEE_MUL_OVF_UN:
+ name = "*ovf.un";prec = 0x70; goto DO_BINOP;
+ case CEE_DIV:
+ name = "/"; prec = 0x70; goto DO_BINOP;
+ case CEE_DIV_UN:
+ name = "/un"; prec = 0x70; goto DO_BINOP;
+ case CEE_REM:
+ name = "%"; prec = 0x70; goto DO_BINOP;
+ case CEE_REM_UN:
+ name = "%un"; prec = 0x70; goto DO_BINOP;
+ DO_BINOP:
+ rhs = pop(prec);
+ lhs = pop(prec-1);
+ result << lhs->val() << ' ' << name << ' ' << rhs->val();
+ goto DO_PUSH;
+
+ case CEE_NOT:
+ name = "~"; prec = 0x80; goto DO_UNOP;
+ case CEE_NEG:
+ name = "-"; prec = 0x80; goto DO_UNOP;
+ DO_UNOP:
+ lhs = pop(prec-1);
+ result << name << lhs->val();
+ goto DO_PUSH;
+
+ case CEE_RET:
+ _ASSERTE(stackDepth() <= 1);
+ result << "return";
+ if (stackDepth() > 0) {
+ lhs = pop();
+ result << ' ' << lhs->val();
+ }
+ goto DO_STMT;
+
+ case CEE_POP:
+ lhs = pop();
+ result.swap(*lhs);
+ goto DO_STMT;
+
+ case CEE_DUP:
+ spillStack(out);
+ lhs = top();
+ result << lhs->val();
+ prec = 0x1000; // spillstack makes them temps, so they have high prec
+ goto DO_PUSH;
+
+ case CEE_LDFLDA:
+ name = "&";
+ goto DO_LDFLD_LDFLDA;
+ case CEE_LDFLD:
+ name = "";
+ DO_LDFLD_LDFLDA:
+ prec = 0x110;
+ lhs = pop(prec-1);
+ result << name << lhs->val() << '.';
+ formatInstrArgs(op, inlineArg, &result);
+ goto DO_PUSH;
+
+ case CEE_LDSFLDA:
+ name = "&";
+ goto DO_LDSFLD_LDSFLDA;
+ case CEE_LDSFLD:
+ name = "";
+ DO_LDSFLD_LDSFLDA:
+ prec = 0x1000;
+ result << name;
+ formatInstrArgs(op, inlineArg, &result);
+ goto DO_PUSH;
+
+ case CEE_STFLD:
+ rhs = pop(0x10);
+ lhs = pop(0x110-1);
+ result << lhs->val() << '.';
+ formatInstrArgs(op, inlineArg, &result);
+ result << " = " << rhs->val();
+ goto DO_STMT;
+
+ case CEE_STSFLD:
+ rhs = pop(0x20);
+ formatInstrArgs(op, inlineArg, &result);
+ result << " = " << rhs->val();
+ goto DO_STMT;
+
+ case CEE_CALLI:
+ lhs = pop();
+ result << "CALLI<" << lhs->val() << '>';
+ goto DO_CALL;
+
+ case CEE_NEWOBJ:
+ result << "new ";
+ // FALL THROUGH
+ case CEE_CALL:
+ case CEE_CALLVIRT: {
+ formatInstrArgs(op, inlineArg, &result);
+
+ DO_CALL:
+ // Get the signature stuff
+ PCCOR_SIGNATURE sig;
+ ULONG cSig;
+ HRESULT hr;
+ if (TypeFromToken(inlineArg.i) == mdtMethodDef)
+ hr = meta->GetMethodProps(mdMethodDef(inlineArg.i), 0, 0, 0, 0, 0, &sig, &cSig, 0, 0);
+ else if (TypeFromToken(inlineArg.i) == mdtMemberRef)
+ hr = meta->GetMemberRefProps(mdMemberRef(inlineArg.i), 0, 0, 0, 0, &sig, &cSig);
+ else
+ hr = meta->GetSigFromToken(mdSignature(inlineArg.i), &sig, &cSig);
+ _ASSERTE(SUCCEEDED(hr));
+ unsigned callConv = CorSigUncompressData(sig);
+ unsigned hasThis = callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS;
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ CorSigUncompressData(sig);
+ }
+ unsigned numArgs = CorSigUncompressData(sig);
+ while(*sig == ELEMENT_TYPE_CMOD_REQD || *sig == ELEMENT_TYPE_CMOD_OPT) {
+ sig++;
+ CorSigUncompressToken(sig);
+ }
+
+ formatArgs(numArgs, &result);
+ if (hasThis && op.getOpcode() != CEE_NEWOBJ) {
+ lhs = pop(0x90);
+ result.swap(*lhs);
+ result << '.' << lhs->val();
+ }
+ prec = 0x1000;
+ if (op.getOpcode() == CEE_NEWOBJ || *sig != ELEMENT_TYPE_VOID)
+ goto DO_PUSH;
+ } goto DO_STMT;
+
+ case CEE_LDELEM_I1:
+ case CEE_LDELEM_I2:
+ case CEE_LDELEM_I4:
+ case CEE_LDELEM_I8:
+ case CEE_LDELEM_REF:
+ case CEE_LDELEM_R4:
+ case CEE_LDELEM_R8:
+ case CEE_LDELEM_U1:
+ case CEE_LDELEM_U2:
+ case CEE_LDELEM_I:
+ rhs = pop(0x100);
+ lhs = pop();
+ result << lhs->val() << '[' << rhs->val() << ']';
+ prec = 0x100;
+ goto DO_PUSH;
+
+ case CEE_STELEM_I1:
+ case CEE_STELEM_I2:
+ case CEE_STELEM_I4:
+ case CEE_STELEM_I8:
+ case CEE_STELEM_REF:
+ case CEE_STELEM_R4:
+ case CEE_STELEM_R8:
+ case CEE_STELEM_I:
+ rhs = pop(0x100);
+ idx = pop();
+ lhs = pop(0x20);
+ result << lhs->val() << '[' << idx->val() << "] = " << rhs->val();
+ goto DO_STMT;
+
+ case CEE_LDIND_I1: name = "I1"; goto DO_LDIND;
+ case CEE_LDIND_I2: name = "I2"; goto DO_LDIND;
+ case CEE_LDIND_I4: name = "I4"; goto DO_LDIND;
+ case CEE_LDIND_I8: name = "I8"; goto DO_LDIND;
+ case CEE_LDIND_I: name = "I"; goto DO_LDIND;
+ case CEE_LDIND_R4: name = "R4"; goto DO_LDIND;
+ case CEE_LDIND_R8: name = "R8"; goto DO_LDIND;
+ case CEE_LDIND_U1: name = "U1"; goto DO_LDIND;
+ case CEE_LDIND_U2: name = "U2"; goto DO_LDIND;
+ case CEE_LDIND_REF: name = "REF";goto DO_LDIND;
+ DO_LDIND:
+ prec = 0x90;
+ lhs = pop(prec);
+ result << name << "(*" << lhs->val() << ')';
+ goto DO_PUSH;
+
+ case CEE_STIND_I1: name = "I1"; goto DO_STIND;
+ case CEE_STIND_I2: name = "I2"; goto DO_STIND;
+ case CEE_STIND_I4: name = "I4"; goto DO_STIND;
+ case CEE_STIND_I8: name = "I8"; goto DO_STIND;
+ case CEE_STIND_REF: name = "REF";goto DO_STIND;
+ case CEE_STIND_R4: name = "R4"; goto DO_STIND;
+ case CEE_STIND_R8: name = "R8"; goto DO_STIND;
+ DO_STIND:
+ rhs = pop();
+ lhs = pop(0x90);
+ result << '*' << lhs->val() << " = " << name << '(' << rhs->val() << ')';
+ goto DO_STMT;
+
+ case CEE_LDVIRTFTN:
+ case CEE_ARGLIST:
+ case CEE_BREAK:
+ case CEE_ENDFILTER:
+ case CEE_CPBLK:
+ case CEE_INITBLK:
+ case CEE_LDOBJ:
+ case CEE_CPOBJ:
+ case CEE_STOBJ:
+ case CEE_INITOBJ:
+ case CEE_LOCALLOC:
+ case CEE_NOP:
+ case CEE_SWITCH:
+ case CEE_CASTCLASS:
+ case CEE_ISINST:
+ case CEE_LDLEN:
+ case CEE_JMP:
+ case CEE_NEWARR:
+ case CEE_THROW:
+ case CEE_RETHROW:
+ case CEE_LDELEM_U4:
+ case CEE_LDIND_U4:
+ case CEE_LDELEMA:
+ case CEE_ENDFINALLY:
+ case CEE_STIND_I:
+ case CEE_CKFINITE:
+ case CEE_MKREFANY:
+ case CEE_REFANYTYPE:
+ case CEE_REFANYVAL:
+ case CEE_CONV_I1:
+ case CEE_CONV_I2:
+ case CEE_CONV_I4:
+ case CEE_CONV_I8:
+ case CEE_CONV_R4:
+ case CEE_CONV_R8:
+ case CEE_CONV_R_UN:
+ case CEE_CONV_OVF_I_UN:
+ case CEE_CONV_OVF_I1_UN:
+ case CEE_CONV_OVF_I2_UN:
+ case CEE_CONV_OVF_I4_UN:
+ case CEE_CONV_OVF_I8_UN:
+ case CEE_CONV_OVF_U_UN:
+ case CEE_CONV_OVF_U1_UN:
+ case CEE_CONV_OVF_U2_UN:
+ case CEE_CONV_OVF_U4_UN:
+ case CEE_CONV_OVF_U8_UN:
+ case CEE_CONV_OVF_I1:
+ case CEE_CONV_OVF_I2:
+ case CEE_CONV_OVF_I4:
+ case CEE_CONV_OVF_I8:
+ case CEE_CONV_OVF_U1:
+ case CEE_CONV_OVF_U2:
+ case CEE_CONV_OVF_U4:
+ case CEE_CONV_OVF_U8:
+ case CEE_CONV_U4:
+ case CEE_CONV_U8:
+ case CEE_CONV_U2:
+ case CEE_CONV_U1:
+ case CEE_CONV_I:
+ case CEE_CONV_OVF_I:
+ case CEE_CONV_OVF_U:
+ case CEE_CONV_U:
+ case CEE_BOX:
+ case CEE_LDELEM:
+ case CEE_STELEM:
+ case CEE_UNBOX_ANY:
+ case CEE_UNBOX:
+ case CEE_LDFTN:
+ case CEE_LDTOKEN:
+ case CEE_SIZEOF:
+ default:
+ result << op.getName();
+ if (op.getArgsInfo() != InlineNone) {
+ result << '<';
+ formatInstrArgs(op, inlineArg, &result, instrPtr-start);
+ result << '>';
+ }
+
+ _ASSERTE(op.getNumPop() >= 0);
+ if (op.getNumPop() > 0)
+ formatArgs(op.getNumPop(), &result);
+
+ prec = 0x1000;
+ _ASSERTE(op.getNumPush() == 0 || op.getNumPush() == 1);
+ if (op.getNumPush() > 0)
+ goto DO_PUSH;
+ goto DO_STMT;
+ }
+ }
+ return(instrPtr);
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
diff --git a/src/utilcode/jitperf.cpp b/src/utilcode/jitperf.cpp
new file mode 100644
index 0000000000..1630afa343
--- /dev/null
+++ b/src/utilcode/jitperf.cpp
@@ -0,0 +1,132 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h"
+#include "jitperf.h"
+#include "perflog.h"
+#include "clrhost.h"
+#include "contract.h"
+
+//=============================================================================
+// ALL THE JIT PERF STATS GATHERING CODE IS COMPILED ONLY IF THE ENABLE_JIT_PERF WAS DEFINED.
+#if defined(ENABLE_JIT_PERF)
+
+__int64 g_JitCycles = 0;
+size_t g_NonJitCycles = 0;
+CRITSEC_COOKIE g_csJit;
+__int64 g_tlsJitCycles = 0;
+int g_fJitPerfOn;
+
+size_t g_dwTlsx86CodeSize = 0;
+size_t g_TotalILCodeSize = 0;
+size_t g_Totalx86CodeSize = 0;
+size_t g_TotalMethodsJitted = 0;
+
+void OutputStats ()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ LARGE_INTEGER cycleFreq;
+ if (QueryPerformanceFrequency (&cycleFreq))
+ {
+ double dJitC = (double) g_JitCycles;
+ double dNonJitC = (double) g_NonJitCycles;
+ double dFreq = (double)cycleFreq.QuadPart;
+ double compileSpeed = (double)g_TotalILCodeSize/(dJitC/dFreq);
+
+ PERFLOG((W("Jit Cycles"), (dJitC - dNonJitC), CYCLES));
+ PERFLOG((W("Jit Time"), (dJitC - dNonJitC)/dFreq, SECONDS));
+ PERFLOG((W("Non Jit Cycles"), dNonJitC, CYCLES));
+ PERFLOG((W("Non Jit Time"), dNonJitC/dFreq, SECONDS));
+ PERFLOG((W("Total Jit Cycles"), dJitC, CYCLES));
+ PERFLOG((W("Total Jit Time"), dJitC/dFreq, SECONDS));
+ PERFLOG((W("Methods Jitted"), (UINT_PTR)g_TotalMethodsJitted, COUNT));
+ PERFLOG((W("IL Code Compiled"), (UINT_PTR)g_TotalILCodeSize, BYTES));
+ PERFLOG((W("X86 Code Emitted"), (UINT_PTR)g_Totalx86CodeSize, BYTES));
+ // Included the perf counter description in this case because its not obvious what we are reporting.
+ PERFLOG((W("ExecTime"), compileSpeed/1000, KBYTES_PER_SEC, W("IL Code compiled/sec")));
+ }
+}
+
+void InitJitPerf(void)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ wchar_t lpszValue[2];
+ DWORD cchValue = 2;
+
+ g_fJitPerfOn = WszGetEnvironmentVariable (W("JIT_PERF_OUTPUT"), lpszValue, cchValue);
+ if (g_fJitPerfOn)
+ {
+ g_csJit = ClrCreateCriticalSection(CrstJitPerf,CRST_UNSAFE_ANYMODE);
+ }
+}
+
+void DoneJitPerfStats()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ if (g_fJitPerfOn)
+ {
+ ClrDeleteCriticalSection(g_csJit);
+
+ // Output stats to stdout and if necessary to the perf automation file.
+ OutputStats();
+ }
+
+
+}
+
+void StartNonJITPerfWorker(LARGE_INTEGER * pCycleStart)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ pCycleStart->QuadPart = 0;
+
+ size_t pTlsNonJitCycles = (size_t) ClrFlsGetValue (TlsIdx_JitPerf);
+ if ((pTlsNonJitCycles & 1) == 0 ) { /* odd value indicates we are in the EE */
+ ClrFlsSetValue(TlsIdx_JitPerf, (LPVOID)(pTlsNonJitCycles + 1));
+ QueryPerformanceCounter(pCycleStart);
+ }
+}
+
+void StopNonJITPerfWorker(LARGE_INTEGER * pCycleStart)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ LARGE_INTEGER CycleStop;
+
+ if (pCycleStart->QuadPart != 0 && QueryPerformanceCounter(&CycleStop) ) {
+ size_t pTlsNonJitCycles = (size_t)ClrFlsGetValue (TlsIdx_JitPerf);
+ pTlsNonJitCycles += static_cast<size_t>(CycleStop.QuadPart - pCycleStart->QuadPart);
+ pTlsNonJitCycles &= ~1; /* even indicate we are not in EE */
+ ClrFlsSetValue(TlsIdx_JitPerf, (LPVOID)(pTlsNonJitCycles));
+ }
+}
+
+
+#endif //ENABLE_JIT_PERF
+
+
diff --git a/src/utilcode/lazycow.cpp b/src/utilcode/lazycow.cpp
new file mode 100644
index 0000000000..9a5fcfbf1c
--- /dev/null
+++ b/src/utilcode/lazycow.cpp
@@ -0,0 +1,315 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// --------------------------------------------------------------------------------
+// LazyCOW.cpp
+//
+
+#include "pedecoder.h"
+#include "volatile.h"
+#include "lazycow.h"
+
+#ifdef FEATURE_LAZY_COW_PAGES
+
+//
+// We can't use the hosted ClrVirtualProtect, because EnsureWritablePages is called in places where we can't
+// call into the host.
+//
+#ifdef VirtualProtect
+#undef VirtualProtect
+#endif
+
+#ifdef VirtualAlloc
+#undef VirtualAlloc
+#endif
+
+#ifdef VirtualFree
+#undef VirtualFree
+#endif
+
+LONG* g_pCOWPageMap; // one bit for each page in the virtual address space
+LONG g_COWPageMapMap; // one bit for each page in g_pCOWPageMap.
+
+LONG* EnsureCOWPageMapAllocated()
+{
+ if (g_pCOWPageMap == NULL)
+ {
+ // We store one bit for every page in the virtual address space. We may need to revisit this for 64-bit. :)
+ MEMORYSTATUSEX stats;
+ stats.dwLength = sizeof(stats);
+ if (GlobalMemoryStatusEx(&stats))
+ {
+ _ASSERTE(stats.ullTotalVirtual < 0x100000000ULL);
+
+ SIZE_T mapSize = (SIZE_T)((stats.ullTotalVirtual / PAGE_SIZE) / 8);
+ _ASSERTE(mapSize / PAGE_SIZE <= 32); // g_COWPageMapMap can only track 32 pages
+
+ // Note that VirtualAlloc will zero-fill the pages for us.
+ LONG* pMap = (LONG*)VirtualAlloc(
+ NULL,
+ mapSize,
+ MEM_RESERVE,
+ PAGE_READWRITE);
+
+ if (pMap != NULL)
+ {
+ if (NULL != InterlockedCompareExchangeT(&g_pCOWPageMap, pMap, NULL))
+ VirtualFree(pMap, 0, MEM_RELEASE);
+ }
+ }
+ }
+ return g_pCOWPageMap;
+}
+
+bool EnsureCOWPageMapElementAllocated(LONG* elem)
+{
+ _ASSERTE(elem > g_pCOWPageMap);
+ _ASSERTE(g_pCOWPageMap != NULL);
+
+ size_t offset = (size_t)elem - (size_t)g_pCOWPageMap;
+ size_t page = offset / PAGE_SIZE;
+
+ _ASSERTE(page < 32);
+ int bit = (int)(1 << page);
+
+ if (!(g_COWPageMapMap & bit))
+ {
+ if (!VirtualAlloc(elem, 1, MEM_COMMIT, PAGE_READWRITE))
+ return false;
+
+ InterlockedOr(&g_COWPageMapMap, bit);
+ }
+
+ return true;
+}
+
+bool IsCOWPageMapElementAllocated(LONG* elem)
+{
+ _ASSERTE(elem >= g_pCOWPageMap);
+ _ASSERTE(g_pCOWPageMap != NULL);
+
+ size_t offset = (size_t)elem - (size_t)g_pCOWPageMap;
+ size_t page = offset / PAGE_SIZE;
+
+ _ASSERTE(page < 32);
+ int bit = (int)(1 << page);
+
+ return (g_COWPageMapMap & bit) != 0;
+}
+
+bool SetCOWPageBits(BYTE* pStart, size_t len, bool value)
+{
+ _ASSERTE(len > 0);
+
+ // we don't need a barrier here, since:
+ // a) all supported hardware maintains ordering of dependent reads
+ // b) it's ok if additional reads happen, because this never changes
+ // once initialized.
+ LONG* pCOWPageMap = g_pCOWPageMap;
+
+ //
+ // Write the bits in 32-bit chunks, to avoid doing one interlocked instruction for each bit.
+ //
+ size_t page = (size_t)pStart / PAGE_SIZE;
+ size_t lastPage = (size_t)(pStart+len-1) / PAGE_SIZE;
+ size_t elem = page / 32;
+ LONG bits = 0;
+ do
+ {
+ bits |= 1 << (page % 32);
+
+ ++page;
+
+ //
+ // if we've moved to a new element of the map, or we've covered every page,
+ // we need to write out the already-accumulated element.
+ //
+ size_t newElem = page / 32;
+ if (page > lastPage || newElem != elem)
+ {
+ LONG* pElem = &pCOWPageMap[elem];
+ if (!EnsureCOWPageMapElementAllocated(pElem))
+ return false;
+
+ if (value)
+ InterlockedOr(&pCOWPageMap[elem], bits);
+ else
+ InterlockedAnd(&pCOWPageMap[elem], ~bits);
+
+ elem = newElem;
+ bits = 0;
+ }
+ }
+ while (page <= lastPage);
+
+ return true;
+}
+
+bool SetCOWPageBitsForImage(PEDecoder * pImage, bool value)
+{
+ if (!pImage->HasNativeHeader())
+ return true;
+
+ bool success = true;
+
+ IMAGE_SECTION_HEADER* pSection;
+ BYTE * pStart;
+ size_t len;
+
+ pSection = pImage->FindSection(".data");
+ if (pSection != NULL)
+ {
+ pStart = (BYTE*) dac_cast<TADDR>(pImage->GetBase()) + pSection->VirtualAddress;
+ len = pSection->Misc.VirtualSize;
+
+ if (!SetCOWPageBits(pStart, len, value))
+ success = false;
+ }
+
+ pSection = pImage->FindSection(".xdata");
+ if (pSection != NULL)
+ {
+ pStart = (BYTE*) dac_cast<TADDR>(pImage->GetBase()) + pSection->VirtualAddress;
+ len = pSection->Misc.VirtualSize;
+
+ if (!SetCOWPageBits(pStart, len, value))
+ success = false;
+ }
+
+ return success;
+}
+
+bool AreAnyCOWPageBitsSet(BYTE* pStart, size_t len)
+{
+ LONG* pCOWPageMap = g_pCOWPageMap;
+ if (pCOWPageMap == NULL)
+ return false;
+
+ _ASSERTE(len > 0);
+ size_t page = (size_t)pStart / PAGE_SIZE;
+ size_t lastPage = (size_t)(pStart+len-1) / PAGE_SIZE;
+ do
+ {
+ LONG* pElem = &pCOWPageMap[page / 32];
+ if (IsCOWPageMapElementAllocated(pElem) &&
+ (*pElem & (1 << (page %32))))
+ {
+ return true;
+ }
+ ++page;
+ }
+ while (page <= lastPage);
+
+ return false;
+}
+
+
+void AllocateLazyCOWPages(PEDecoder * pImage)
+{
+ //
+ // Note: it's ok to call AllocateLazyCOWPages multiple times for the same loaded image.
+ // This will result in any already-writable pages being incorrectly marked as read-only
+ // in our records, but that just means we'll call VirtualProtect one more time.
+ //
+ // However, FreeLazyCOWPages must be called just once per loaded image, when it is actually.
+ // unloaded. Otherwise we will lose track of the COW pages in that image while it might
+ // still be accessible.
+ //
+
+ if (!EnsureCOWPageMapAllocated())
+ ThrowOutOfMemory();
+
+ if (!SetCOWPageBitsForImage(pImage, true))
+ ThrowOutOfMemory();
+}
+
+void FreeLazyCOWPages(PEDecoder * pImage)
+{
+ //
+ // Must be called just once per image; see note in AllocateLazyCOWPages.
+ //
+ SetCOWPageBitsForImage(pImage, false);
+}
+
+bool IsInReadOnlyLazyCOWPage(void* p)
+{
+ return AreAnyCOWPageBitsSet((BYTE*)p, 1);
+}
+
+
+bool MakeWritable(BYTE* pStart, size_t len, DWORD protect)
+{
+ DWORD oldProtect;
+ if (!VirtualProtect(pStart, len, protect, &oldProtect))
+ return false;
+ INDEBUG(bool result = ) SetCOWPageBits(pStart, len, false);
+ _ASSERTE(result); // we already set these, so we must be able to clear them.
+ return true;
+}
+
+bool EnsureWritablePagesNoThrow(void* p, size_t len)
+{
+ BYTE* pStart = (BYTE*)p;
+
+ if (len == 0)
+ return true;
+
+ if (!AreAnyCOWPageBitsSet(pStart, len))
+ return true;
+
+ return MakeWritable(pStart, len, PAGE_READWRITE);
+}
+
+void EnsureWritablePages(void* p, size_t len)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ BYTE* pStart = (BYTE*)p;
+
+ if (len == 0)
+ return;
+
+ if (!AreAnyCOWPageBitsSet(pStart, len))
+ return;
+
+ if (!MakeWritable(pStart, len, PAGE_READWRITE))
+ ThrowOutOfMemory();
+}
+
+bool EnsureWritableExecutablePagesNoThrow(void* p, size_t len)
+{
+ BYTE* pStart = (BYTE*) p;
+
+ if (len == 0)
+ return true;
+
+ if (!AreAnyCOWPageBitsSet(pStart, len))
+ return true;
+
+ return MakeWritable(pStart, len, PAGE_EXECUTE_READWRITE);
+}
+
+void EnsureWritableExecutablePages(void* p, size_t len)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ BYTE* pStart = (BYTE*) p;
+
+ if (len == 0)
+ return;
+
+ if (!AreAnyCOWPageBitsSet(pStart, len))
+ return;
+
+ if (!MakeWritable(pStart, len, PAGE_EXECUTE_READWRITE))
+ ThrowOutOfMemory();
+}
+
+#endif // FEATURE_LAZY_COW_PAGES
+
+
diff --git a/src/utilcode/loaderheap.cpp b/src/utilcode/loaderheap.cpp
new file mode 100644
index 0000000000..caa01b7064
--- /dev/null
+++ b/src/utilcode/loaderheap.cpp
@@ -0,0 +1,2262 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h" // Precompiled header key.
+#include "loaderheap.h"
+#include "perfcounters.h"
+#include "ex.h"
+#include "pedecoder.h"
+#define DONOT_DEFINE_ETW_CALLBACK
+#include "eventtracebase.h"
+
+#ifndef DACCESS_COMPILE
+
+INDEBUG(DWORD UnlockedLoaderHeap::s_dwNumInstancesOfLoaderHeaps = 0;)
+
+#ifdef RANDOMIZE_ALLOC
+#include <time.h>
+static class Random
+{
+public:
+ Random() { seed = (unsigned int)time(NULL); }
+ unsigned int Next()
+ {
+ return ((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff;
+ }
+private:
+ unsigned int seed;
+} s_random;
+#endif
+
+namespace
+{
+#if !defined(SELF_NO_HOST) // ETW available only in the runtime
+ inline void EtwAllocRequest(UnlockedLoaderHeap * const pHeap, void* ptr, size_t dwSize)
+ {
+ FireEtwAllocRequest(pHeap, ptr, static_cast<unsigned int>(dwSize), 0, 0, GetClrInstanceId());
+ }
+#else
+#define EtwAllocRequest(pHeap, ptr, dwSize) ((void)0)
+#endif // SELF_NO_HOST
+}
+
+//
+// RangeLists are constructed so they can be searched from multiple
+// threads without locking. They do require locking in order to
+// be safely modified, though.
+//
+
+RangeList::RangeList()
+{
+ WRAPPER_NO_CONTRACT;
+
+ InitBlock(&m_starterBlock);
+
+ m_firstEmptyBlock = &m_starterBlock;
+ m_firstEmptyRange = 0;
+}
+
+RangeList::~RangeList()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ RangeListBlock *b = m_starterBlock.next;
+
+ while (b != NULL)
+ {
+ RangeListBlock *bNext = b->next;
+ delete b;
+ b = bNext;
+ }
+}
+
+void RangeList::InitBlock(RangeListBlock *b)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Range *r = b->ranges;
+ Range *rEnd = r + RANGE_COUNT;
+ while (r < rEnd)
+ r++->id = NULL;
+
+ b->next = NULL;
+}
+
+BOOL RangeList::AddRangeWorker(const BYTE *start, const BYTE *end, void *id)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END
+
+ _ASSERTE(id != NULL);
+
+ RangeListBlock *b = m_firstEmptyBlock;
+ Range *r = b->ranges + m_firstEmptyRange;
+ Range *rEnd = b->ranges + RANGE_COUNT;
+
+ while (TRUE)
+ {
+ while (r < rEnd)
+ {
+ if (r->id == NULL)
+ {
+ r->start = (TADDR)start;
+ r->end = (TADDR)end;
+ r->id = (TADDR)id;
+
+ r++;
+
+ m_firstEmptyBlock = b;
+ m_firstEmptyRange = r - b->ranges;
+
+ return TRUE;
+ }
+ r++;
+ }
+
+ //
+ // If there are no more blocks, allocate a
+ // new one.
+ //
+
+ if (b->next == NULL)
+ {
+ RangeListBlock *newBlock = new (nothrow) RangeListBlock;
+
+ if (newBlock == NULL)
+ {
+ m_firstEmptyBlock = b;
+ m_firstEmptyRange = r - b->ranges;
+ return FALSE;
+ }
+
+ InitBlock(newBlock);
+
+ newBlock->next = NULL;
+ b->next = newBlock;
+ }
+
+ //
+ // Next block
+ //
+
+ b = b->next;
+ r = b->ranges;
+ rEnd = r + RANGE_COUNT;
+ }
+}
+
+void RangeList::RemoveRangesWorker(void *id, const BYTE* start, const BYTE* end)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ RangeListBlock *b = &m_starterBlock;
+ Range *r = b->ranges;
+ Range *rEnd = r + RANGE_COUNT;
+
+ //
+ // Find the first free element, & mark it.
+ //
+
+ while (TRUE)
+ {
+ //
+ // Clear entries in this block.
+ //
+
+ while (r < rEnd)
+ {
+ if (r->id != NULL)
+ {
+ if (start != NULL)
+ {
+ _ASSERTE(end != NULL);
+
+ if (r->start >= (TADDR)start && r->start < (TADDR)end)
+ {
+ CONSISTENCY_CHECK_MSGF(r->end >= (TADDR)start &&
+ r->end <= (TADDR)end,
+ ("r: %p start: %p end: %p", r, start, end));
+ r->id = NULL;
+ }
+ }
+ else if (r->id == (TADDR)id)
+ {
+ r->id = NULL;
+ }
+ }
+
+ r++;
+ }
+
+ //
+ // If there are no more blocks, we're done.
+ //
+
+ if (b->next == NULL)
+ {
+ m_firstEmptyRange = 0;
+ m_firstEmptyBlock = &m_starterBlock;
+
+ return;
+ }
+
+ //
+ // Next block.
+ //
+
+ b = b->next;
+ r = b->ranges;
+ rEnd = r + RANGE_COUNT;
+ }
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL RangeList::IsInRangeWorker(TADDR address, TADDR *pID /* = NULL */)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END
+
+ SUPPORTS_DAC;
+
+ RangeListBlock* b = &m_starterBlock;
+ Range* r = b->ranges;
+ Range* rEnd = r + RANGE_COUNT;
+
+ //
+ // Look for a matching element
+ //
+
+ while (TRUE)
+ {
+ while (r < rEnd)
+ {
+ if (r->id != NULL &&
+ address >= r->start
+ && address < r->end)
+ {
+ if (pID != NULL)
+ {
+ *pID = r->id;
+ }
+ return TRUE;
+ }
+ r++;
+ }
+
+ //
+ // If there are no more blocks, we're done.
+ //
+
+ if (b->next == NULL)
+ return FALSE;
+
+ //
+ // Next block.
+ //
+
+ b = b->next;
+ r = b->ranges;
+ rEnd = r + RANGE_COUNT;
+ }
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+RangeList::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ WRAPPER_NO_CONTRACT;
+
+ // This class is almost always contained in something
+ // else so there's no enumeration of 'this'.
+
+ RangeListBlock* block = &m_starterBlock;
+ block->EnumMemoryRegions(flags);
+
+ while (block->next.IsValid())
+ {
+ block->next.EnumMem();
+ block = block->next;
+
+ block->EnumMemoryRegions(flags);
+ }
+}
+
+void
+RangeList::RangeListBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ Range* range;
+ TADDR BADFOOD;
+ TSIZE_T size;
+ int i;
+
+ // The code below iterates each range stored in the RangeListBlock and
+ // dumps the memory region represented by each range.
+ // It is too much memory for a mini-dump, so we just bail out for mini-dumps.
+ if (flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ return;
+ }
+
+ WIN64_ONLY( BADFOOD = 0xbaadf00dbaadf00d; );
+ NOT_WIN64( BADFOOD = 0xbaadf00d; );
+
+ for (i=0; i<RANGE_COUNT; i++)
+ {
+ range = &(this->ranges[i]);
+ if (range->id == NULL || range->start == NULL || range->end == NULL ||
+ // just looking at the lower 4bytes is good enough on WIN64
+ range->start == BADFOOD || range->end == BADFOOD)
+ {
+ break;
+ }
+
+ size = range->end - range->start;
+ _ASSERTE( size < ULONG_MAX ); // ranges should be less than 4gig!
+
+ // We can't be sure this entire range is mapped. For example, the code:StubLinkStubManager
+ // keeps track of all ranges in the code:BaseDomain::m_pStubHeap LoaderHeap, and
+ // code:LoaderHeap::UnlockedReservePages adds a range for the entire reserved region, instead
+ // of updating the RangeList when pages are committed. But in that case, the committed region of
+ // memory will be enumerated by the LoaderHeap anyway, so it's OK if this fails
+ DacEnumMemoryRegion(range->start, size, false);
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+
+//=====================================================================================
+// In DEBUG builds only, we tag live blocks with the requested size and the type of
+// allocation (AllocMem, AllocAlignedMem, AllocateOntoReservedMem). This is strictly
+// to validate that those who call Backout* are passing in the right values.
+//
+// For simplicity, we'll use one LoaderHeapValidationTag structure for all types even
+// though not all fields are applicable to all types.
+//=====================================================================================
+#ifdef _DEBUG
+enum AllocationType
+{
+ kAllocMem = 1,
+ kFreedMem = 4,
+};
+
+struct LoaderHeapValidationTag
+{
+ size_t m_dwRequestedSize; // What the caller requested (not what was actually allocated)
+ AllocationType m_allocationType; // Which api allocated this block.
+ const char * m_szFile; // Who allocated me
+ int m_lineNum; // Who allocated me
+
+};
+#endif //_DEBUG
+
+
+
+
+
+//=====================================================================================
+// These classes do detailed loaderheap sniffing to help in debugging heap crashes
+//=====================================================================================
+#ifdef _DEBUG
+
+// This structure logs the results of an Alloc or Free call. They are stored in reverse time order
+// with UnlockedLoaderHeap::m_pEventList pointing to the most recent event.
+struct LoaderHeapEvent
+{
+ LoaderHeapEvent *m_pNext;
+ AllocationType m_allocationType; //Which api was called
+ const char *m_szFile; //Caller Id
+ int m_lineNum; //Caller Id
+ const char *m_szAllocFile; //(BackoutEvents): Who allocated the block?
+ int m_allocLineNum; //(BackoutEvents): Who allocated the block?
+ void *m_pMem; //Starting address of block
+ size_t m_dwRequestedSize; //Requested size of block
+ size_t m_dwSize; //Actual size of block (including validation tags, padding, everything)
+
+
+ void Describe(SString *pSString)
+ {
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ DISABLED(NOTHROW);
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ pSString->AppendASCII("\n");
+
+ {
+ StackSString buf;
+ if (m_allocationType == kFreedMem)
+ {
+ buf.Printf(" Freed at: %s (line %d)\n", m_szFile, m_lineNum);
+ buf.Printf(" (block originally allocated at %s (line %d)\n", m_szAllocFile, m_allocLineNum);
+ }
+ else
+ {
+ buf.Printf(" Allocated at: %s (line %d)\n", m_szFile, m_lineNum);
+ }
+ pSString->Append(buf);
+ }
+
+ if (!QuietValidate())
+ {
+ pSString->AppendASCII(" *** THIS BLOCK HAS BEEN CORRUPTED ***\n");
+ }
+
+
+
+ {
+ StackSString buf;
+ buf.Printf(" Type: ");
+ switch (m_allocationType)
+ {
+ case kAllocMem:
+ buf.AppendASCII("AllocMem()\n");
+ break;
+ case kFreedMem:
+ buf.AppendASCII("Free\n");
+ break;
+ default:
+ break;
+ }
+ pSString->Append(buf);
+ }
+
+
+ {
+ StackSString buf;
+ buf.Printf(" Start of block: 0x%p\n", m_pMem);
+ pSString->Append(buf);
+ }
+
+ {
+ StackSString buf;
+ buf.Printf(" End of block: 0x%p\n", ((BYTE*)m_pMem) + m_dwSize - 1);
+ pSString->Append(buf);
+ }
+
+ {
+ StackSString buf;
+ buf.Printf(" Requested size: %lu (0x%lx)\n", (ULONG)m_dwRequestedSize, (ULONG)m_dwRequestedSize);
+ pSString->Append(buf);
+ }
+
+ {
+ StackSString buf;
+ buf.Printf(" Actual size: %lu (0x%lx)\n", (ULONG)m_dwSize, (ULONG)m_dwSize);
+ pSString->Append(buf);
+ }
+
+ pSString->AppendASCII("\n");
+ }
+
+
+
+ BOOL QuietValidate();
+
+};
+
+
+class LoaderHeapSniffer
+{
+ public:
+ static DWORD InitDebugFlags()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ DWORD dwDebugFlags = 0;
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LoaderHeapCallTracing))
+ {
+ dwDebugFlags |= UnlockedLoaderHeap::kCallTracing;
+ }
+ return dwDebugFlags;
+ }
+
+
+ static VOID RecordEvent(UnlockedLoaderHeap *pHeap,
+ AllocationType allocationType,
+ __in const char *szFile,
+ int lineNum,
+ __in const char *szAllocFile,
+ int allocLineNum,
+ void *pMem,
+ size_t dwRequestedSize,
+ size_t dwSize
+ );
+
+ static VOID ClearEvents(UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LoaderHeapEvent *pEvent = pHeap->m_pEventList;
+ while (pEvent)
+ {
+ LoaderHeapEvent *pNext = pEvent->m_pNext;
+ delete pEvent;
+ pEvent = pNext;
+ }
+ pHeap->m_pEventList = NULL;
+ }
+
+
+ static VOID CompactEvents(UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LoaderHeapEvent **ppEvent = &(pHeap->m_pEventList);
+ while (*ppEvent)
+ {
+ LoaderHeapEvent *pEvent = *ppEvent;
+ if (pEvent->m_allocationType != kFreedMem)
+ {
+ ppEvent = &(pEvent->m_pNext);
+ }
+ else
+ {
+ LoaderHeapEvent **ppWalk = &(pEvent->m_pNext);
+ BOOL fMatchFound = FALSE;
+ while (*ppWalk && !fMatchFound)
+ {
+ LoaderHeapEvent *pWalk = *ppWalk;
+ if (pWalk->m_allocationType != kFreedMem &&
+ pWalk->m_pMem == pEvent->m_pMem &&
+ pWalk->m_dwRequestedSize == pEvent->m_dwRequestedSize)
+ {
+ // Delete matched pairs
+
+ // Order is important here - updating *ppWalk may change pEvent->m_pNext, and we want
+ // to get the updated value when we unlink pEvent.
+ *ppWalk = pWalk->m_pNext;
+ *ppEvent = pEvent->m_pNext;
+
+ delete pEvent;
+ delete pWalk;
+ fMatchFound = TRUE;
+ }
+ else
+ {
+ ppWalk = &(pWalk->m_pNext);
+ }
+ }
+
+ if (!fMatchFound)
+ {
+ ppEvent = &(pEvent->m_pNext);
+ }
+ }
+ }
+ }
+ static VOID PrintEvents(UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ printf("\n------------- LoaderHeapEvents (in reverse time order!) --------------------");
+
+ LoaderHeapEvent *pEvent = pHeap->m_pEventList;
+ while (pEvent)
+ {
+ printf("\n");
+ switch (pEvent->m_allocationType)
+ {
+ case kAllocMem: printf("AllocMem "); break;
+ case kFreedMem: printf("BackoutMem "); break;
+
+ }
+ printf(" ptr = 0x%-8p", pEvent->m_pMem);
+ printf(" rqsize = 0x%-8x", pEvent->m_dwRequestedSize);
+ printf(" actsize = 0x%-8x", pEvent->m_dwSize);
+ printf(" (at %s@%d)", pEvent->m_szFile, pEvent->m_lineNum);
+ if (pEvent->m_allocationType == kFreedMem)
+ {
+ printf(" (original allocation at %s@%d)", pEvent->m_szAllocFile, pEvent->m_allocLineNum);
+ }
+
+ pEvent = pEvent->m_pNext;
+
+ }
+ printf("\n------------- End of LoaderHeapEvents --------------------------------------");
+ printf("\n");
+
+ }
+
+
+ static VOID PitchSniffer(SString *pSString)
+ {
+ WRAPPER_NO_CONTRACT;
+ pSString->AppendASCII("\n"
+ "\nBecause call-tracing wasn't turned on, we couldn't provide details about who last owned the affected memory block. To get more precise diagnostics,"
+ "\nset the following registry DWORD value:"
+ "\n"
+ "\n HKLM\\Software\\Microsoft\\.NETFramework\\LoaderHeapCallTracing = 1"
+ "\n"
+ "\nand rerun the scenario that crashed."
+ "\n"
+ "\n");
+ }
+
+ static LoaderHeapEvent *FindEvent(UnlockedLoaderHeap *pHeap, void *pAddr)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ LoaderHeapEvent *pEvent = pHeap->m_pEventList;
+ while (pEvent)
+ {
+ if (pAddr >= pEvent->m_pMem && pAddr <= ( ((BYTE*)pEvent->m_pMem) + pEvent->m_dwSize - 1))
+ {
+ return pEvent;
+ }
+ pEvent = pEvent->m_pNext;
+ }
+ return NULL;
+
+ }
+
+
+ static void ValidateFreeList(UnlockedLoaderHeap *pHeap);
+
+ static void WeGotAFaultNowWhat(UnlockedLoaderHeap *pHeap)
+ {
+ WRAPPER_NO_CONTRACT;
+ ValidateFreeList(pHeap);
+
+ //If none of the above popped up an assert, pop up a generic one.
+ _ASSERTE(!("Unexpected AV inside LoaderHeap. The usual reason is that someone overwrote the end of a block or wrote into a freed block.\n"));
+
+ }
+
+};
+
+
+#endif
+
+
+#ifdef _DEBUG
+#define LOADER_HEAP_BEGIN_TRAP_FAULT BOOL __faulted = FALSE; EX_TRY {
+#define LOADER_HEAP_END_TRAP_FAULT } EX_CATCH {__faulted = TRUE; } EX_END_CATCH(SwallowAllExceptions) if (__faulted) LoaderHeapSniffer::WeGotAFaultNowWhat(pHeap);
+#else
+#define LOADER_HEAP_BEGIN_TRAP_FAULT
+#define LOADER_HEAP_END_TRAP_FAULT
+#endif
+
+
+size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap);
+
+//=====================================================================================
+// This freelist implementation is a first cut and probably needs to be tuned.
+// It should be tuned with the following assumptions:
+//
+// - Freeing LoaderHeap memory is done primarily for OOM backout. LoaderHeaps
+// weren't designed to be general purpose heaps and shouldn't be used that way.
+//
+// - And hence, when memory is freed, expect it to be freed in large clumps and in a
+// LIFO order. Since the LoaderHeap normally hands out memory with sequentially
+// increasing addresses, blocks will typically be freed with sequentially decreasing
+// addresses.
+//
+// The first cut of the freelist is a single-linked list of free blocks using first-fit.
+// Assuming the above alloc-free pattern holds, the list will end up mostly sorted
+// in increasing address order. When a block is freed, we'll attempt to coalesce it
+// with the first block in the list. We could also choose to be more aggressive about
+// sorting and coalescing but this should probably catch most cases in practice.
+//=====================================================================================
+
+// When a block is freed, we place this structure on the first bytes of the freed block (Allocations
+// are bumped in size if necessary to make sure there's room.)
+struct LoaderHeapFreeBlock
+{
+ public:
+ LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list
+ size_t m_dwSize; // Total size of this block (including this header)
+//! Try not to grow the size of this structure. It places a minimum size on LoaderHeap allocations.
+
+ static void InsertFreeBlock(LoaderHeapFreeBlock **ppHead, void *pMem, size_t dwTotalSize, UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ LOADER_HEAP_BEGIN_TRAP_FAULT
+
+ // It's illegal to insert a free block that's smaller than the minimum sized allocation -
+ // it may stay stranded on the freelist forever.
+#ifdef _DEBUG
+ if (!(dwTotalSize >= AllocMem_TotalSize(1, pHeap)))
+ {
+ LoaderHeapSniffer::ValidateFreeList(pHeap);
+ _ASSERTE(dwTotalSize >= AllocMem_TotalSize(1, pHeap));
+ }
+
+ if (!(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT)))
+ {
+ LoaderHeapSniffer::ValidateFreeList(pHeap);
+ _ASSERTE(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT));
+ }
+#endif
+
+ INDEBUG(memset(pMem, 0xcc, dwTotalSize);)
+ LoaderHeapFreeBlock *pNewBlock = (LoaderHeapFreeBlock*)pMem;
+ pNewBlock->m_pNext = *ppHead;
+ pNewBlock->m_dwSize = dwTotalSize;
+ *ppHead = pNewBlock;
+
+ MergeBlock(pNewBlock, pHeap);
+
+ LOADER_HEAP_END_TRAP_FAULT
+ }
+
+
+ static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, BOOL fRemoveFromFreeList, UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ INCONTRACT(_ASSERTE_IMPL(!ARE_FAULTS_FORBIDDEN()));
+
+ void *pResult = NULL;
+ LOADER_HEAP_BEGIN_TRAP_FAULT
+
+ LoaderHeapFreeBlock **ppWalk = ppHead;
+ while (*ppWalk)
+ {
+ LoaderHeapFreeBlock *pCur = *ppWalk;
+ size_t dwCurSize = pCur->m_dwSize;
+ if (dwCurSize == dwSize)
+ {
+ pResult = pCur;
+ // Exact match. Hooray!
+ if (fRemoveFromFreeList)
+ {
+ *ppWalk = pCur->m_pNext;
+ }
+ break;
+ }
+ else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap))
+ {
+ // Partial match. Ok...
+ pResult = pCur;
+ if (fRemoveFromFreeList)
+ {
+ *ppWalk = pCur->m_pNext;
+ InsertFreeBlock(ppWalk, ((BYTE*)pCur) + dwSize, dwCurSize - dwSize, pHeap );
+ }
+ break;
+ }
+
+ // Either block is too small or splitting the block would leave a remainder that's smaller than
+ // the minimum block size. Onto next one.
+
+ ppWalk = &( pCur->m_pNext );
+ }
+
+ if (pResult && fRemoveFromFreeList)
+ {
+ // Callers of loaderheap assume allocated memory is zero-inited so we must preserve this invariant!
+ memset(pResult, 0, dwSize);
+ }
+ LOADER_HEAP_END_TRAP_FAULT
+ return pResult;
+
+
+
+ }
+
+
+ private:
+ // Try to merge pFreeBlock with its immediate successor. Return TRUE if a merge happened. FALSE if no merge happened.
+ static BOOL MergeBlock(LoaderHeapFreeBlock *pFreeBlock, UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+
+ BOOL result = FALSE;
+
+ LOADER_HEAP_BEGIN_TRAP_FAULT
+
+ LoaderHeapFreeBlock *pNextBlock = pFreeBlock->m_pNext;
+ size_t dwSize = pFreeBlock->m_dwSize;
+
+ if (pNextBlock == NULL || ((BYTE*)pNextBlock) != (((BYTE*)pFreeBlock) + dwSize))
+ {
+ result = FALSE;
+ }
+ else
+ {
+ size_t dwCombinedSize = dwSize + pNextBlock->m_dwSize;
+ LoaderHeapFreeBlock *pNextNextBlock = pNextBlock->m_pNext;
+ INDEBUG(memset(pFreeBlock, 0xcc, dwCombinedSize);)
+ pFreeBlock->m_pNext = pNextNextBlock;
+ pFreeBlock->m_dwSize = dwCombinedSize;
+
+ result = TRUE;
+ }
+
+ LOADER_HEAP_END_TRAP_FAULT
+ return result;
+
+ }
+
+};
+
+
+
+
+//=====================================================================================
+// These helpers encapsulate the actual layout of a block allocated by AllocMem
+// and UnlockedAllocMem():
+//
+// ==> Starting address is always pointer-aligned.
+//
+// - x bytes of user bytes (where "x" is the actual dwSize passed into AllocMem)
+//
+// - y bytes of "EE" (DEBUG-ONLY) (where "y" == LOADER_HEAP_DEBUG_BOUNDARY (normally 0))
+// - z bytes of pad (DEBUG-ONLY) (where "z" is just enough to pointer-align the following byte)
+// - a bytes of tag (DEBUG-ONLY) (where "a" is sizeof(LoaderHeapValidationTag)
+//
+// - b bytes of pad (if total size after all this < sizeof(LoaderHeapFreeBlock), pad enough to make it the size of LoaderHeapFreeBlock)
+// - c bytes of pad (where "c" is just enough to pointer-align the following byte)
+//
+// ==> Following address is always pointer-aligned
+//=====================================================================================
+
+// Convert the requested size into the total # of bytes we'll actually allocate (including padding)
+inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ size_t dwSize = dwRequestedSize;
+#ifdef _DEBUG
+ dwSize += LOADER_HEAP_DEBUG_BOUNDARY;
+ dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
+
+ if (!pHeap->m_fExplicitControl)
+ {
+ dwSize += sizeof(LoaderHeapValidationTag);
+ }
+#endif
+ if (!pHeap->m_fExplicitControl)
+ {
+ if (dwSize < sizeof(LoaderHeapFreeBlock))
+ {
+ dwSize = sizeof(LoaderHeapFreeBlock);
+ }
+ }
+ dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
+
+ return dwSize;
+}
+
+
+#ifdef _DEBUG
+LoaderHeapValidationTag *AllocMem_GetTag(LPVOID pBlock, size_t dwRequestedSize)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ size_t dwSize = dwRequestedSize;
+ dwSize += LOADER_HEAP_DEBUG_BOUNDARY;
+ dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
+ return (LoaderHeapValidationTag *)( ((BYTE*)pBlock) + dwSize );
+}
+#endif
+
+
+
+
+
+//=====================================================================================
+// UnlockedLoaderHeap methods
+//=====================================================================================
+
+#ifndef DACCESS_COMPILE
+
+UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize,
+ DWORD dwCommitBlockSize,
+ const BYTE* dwReservedRegionAddress,
+ SIZE_T dwReservedRegionSize,
+ size_t *pPrivatePerfCounter_LoaderBytes,
+ RangeList *pRangeList,
+ BOOL fMakeExecutable)
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ m_pCurBlock = NULL;
+ m_pFirstBlock = NULL;
+
+ m_dwReserveBlockSize = dwReserveBlockSize;
+ m_dwCommitBlockSize = dwCommitBlockSize;
+
+ m_pPtrToEndOfCommittedRegion = NULL;
+ m_pEndReservedRegion = NULL;
+ m_pAllocPtr = NULL;
+
+ m_pRangeList = pRangeList;
+
+ // Round to VIRTUAL_ALLOC_RESERVE_GRANULARITY
+ m_dwTotalAlloc = 0;
+
+#ifdef _DEBUG
+ m_dwDebugWastedBytes = 0;
+ s_dwNumInstancesOfLoaderHeaps++;
+ m_pEventList = NULL;
+ m_dwDebugFlags = LoaderHeapSniffer::InitDebugFlags();
+ m_fPermitStubsWithUnwindInfo = FALSE;
+ m_fStubUnwindInfoUnregistered= FALSE;
+#endif
+
+ m_pPrivatePerfCounter_LoaderBytes = pPrivatePerfCounter_LoaderBytes;
+
+ if (fMakeExecutable)
+ m_flProtect = PAGE_EXECUTE_READWRITE;
+ else
+ m_flProtect = PAGE_READWRITE;
+
+ m_pFirstFreeBlock = NULL;
+
+ if (dwReservedRegionAddress != NULL && dwReservedRegionSize > 0)
+ {
+ m_reservedBlock.Init((void *)dwReservedRegionAddress, dwReservedRegionSize, FALSE);
+ }
+}
+
+// ~LoaderHeap is not synchronised (obviously)
+UnlockedLoaderHeap::~UnlockedLoaderHeap()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ _ASSERTE(!m_fPermitStubsWithUnwindInfo || m_fStubUnwindInfoUnregistered);
+
+ if (m_pRangeList != NULL)
+ m_pRangeList->RemoveRanges((void *) this);
+
+ LoaderHeapBlock *pSearch, *pNext;
+
+ for (pSearch = m_pFirstBlock; pSearch; pSearch = pNext)
+ {
+ void * pVirtualAddress;
+ BOOL fReleaseMemory;
+
+ pVirtualAddress = pSearch->pVirtualAddress;
+ fReleaseMemory = pSearch->m_fReleaseMemory;
+ pNext = pSearch->pNext;
+
+ if (fReleaseMemory)
+ {
+ BOOL fSuccess;
+ fSuccess = ClrVirtualFree(pVirtualAddress, 0, MEM_RELEASE);
+ _ASSERTE(fSuccess);
+ }
+ }
+
+ if (m_reservedBlock.m_fReleaseMemory)
+ {
+ BOOL fSuccess;
+ fSuccess = ClrVirtualFree(m_reservedBlock.pVirtualAddress, 0, MEM_RELEASE);
+ _ASSERTE(fSuccess);
+ }
+
+ if (m_pPrivatePerfCounter_LoaderBytes)
+ *m_pPrivatePerfCounter_LoaderBytes = *m_pPrivatePerfCounter_LoaderBytes - (DWORD) m_dwTotalAlloc;
+
+ INDEBUG(s_dwNumInstancesOfLoaderHeaps --;)
+}
+
+void UnlockedLoaderHeap::UnlockedSetReservedRegion(BYTE* dwReservedRegionAddress, SIZE_T dwReservedRegionSize, BOOL fReleaseMemory)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(m_reservedBlock.pVirtualAddress == NULL);
+ m_reservedBlock.Init((void *)dwReservedRegionAddress, dwReservedRegionSize, fReleaseMemory);
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+#if 0
+// Disables access to all pages in the heap - useful when trying to determine if someone is
+// accessing something in the low frequency heap
+void UnlockedLoaderHeap::DebugGuardHeap()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapBlock *pSearch, *pNext;
+
+ for (pSearch = m_pFirstBlock; pSearch; pSearch = pNext)
+ {
+ void * pResult;
+ void * pVirtualAddress;
+
+ pVirtualAddress = pSearch->pVirtualAddress;
+ pNext = pSearch->pNext;
+
+ pResult = ClrVirtualAlloc(pVirtualAddress, pSearch->dwVirtualSize, MEM_COMMIT, PAGE_NOACCESS);
+ _ASSERTE(pResult != NULL);
+ }
+}
+#endif
+
+size_t UnlockedLoaderHeap::GetBytesAvailCommittedRegion()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pAllocPtr < m_pPtrToEndOfCommittedRegion)
+ return (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr);
+ else
+ return 0;
+}
+
+size_t UnlockedLoaderHeap::GetBytesAvailReservedRegion()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pAllocPtr < m_pEndReservedRegion)
+ return (size_t)(m_pEndReservedRegion- m_pAllocPtr);
+ else
+ return 0;
+}
+
+#define SETUP_NEW_BLOCK(pData, dwSizeToCommit, dwSizeToReserve) \
+ m_pPtrToEndOfCommittedRegion = (BYTE *) (pData) + (dwSizeToCommit); \
+ m_pAllocPtr = (BYTE *) (pData) + sizeof(LoaderHeapBlock); \
+ m_pEndReservedRegion = (BYTE *) (pData) + (dwSizeToReserve);
+
+
+#ifndef DACCESS_COMPILE
+
+BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ size_t dwSizeToReserve;
+
+ // Add sizeof(LoaderHeapBlock)
+ dwSizeToCommit += sizeof(LoaderHeapBlock);
+
+ // Round to page size again
+ dwSizeToCommit = ALIGN_UP(dwSizeToCommit, PAGE_SIZE);
+
+ void *pData = NULL;
+ BOOL fReleaseMemory = TRUE;
+
+ // We were provided with a reserved memory block at instance creation time, so use it if it's big enough.
+ if (m_reservedBlock.pVirtualAddress != NULL &&
+ m_reservedBlock.dwVirtualSize >= dwSizeToCommit)
+ {
+ // Get the info out of the block.
+ pData = m_reservedBlock.pVirtualAddress;
+ dwSizeToReserve = m_reservedBlock.dwVirtualSize;
+ fReleaseMemory = m_reservedBlock.m_fReleaseMemory;
+
+ // Zero the block so this memory doesn't get used again.
+ m_reservedBlock.Init(NULL, 0, FALSE);
+ }
+ // The caller is asking us to allocate the memory
+ else
+ {
+ if (m_fExplicitControl)
+ {
+ return FALSE;
+ }
+
+ // Figure out how much to reserve
+ dwSizeToReserve = max(dwSizeToCommit, m_dwReserveBlockSize);
+
+ // Round to VIRTUAL_ALLOC_RESERVE_GRANULARITY
+ dwSizeToReserve = ALIGN_UP(dwSizeToReserve, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+
+ _ASSERTE(dwSizeToCommit <= dwSizeToReserve);
+
+ //
+ // Reserve pages
+ //
+
+ pData = ClrVirtualAllocExecutable(dwSizeToReserve, MEM_RESERVE, PAGE_NOACCESS);
+ if (pData == NULL)
+ {
+ return FALSE;
+ }
+ }
+
+ // When the user passes in the reserved memory, the commit size is 0 and is adjusted to be the sizeof(LoaderHeap).
+ // If for some reason this is not true then we just catch this via an assertion and the dev who changed code
+ // would have to add logic here to handle the case when committed mem is more than the reserved mem. One option
+ // could be to leak the users memory and reserve+commit a new block, Another option would be to fail the alloc mem
+ // and notify the user to provide more reserved mem.
+ _ASSERTE((dwSizeToCommit <= dwSizeToReserve) && "Loaderheap tried to commit more memory than reserved by user");
+
+ if (pData == NULL)
+ {
+ //_ASSERTE(!"Unable to ClrVirtualAlloc reserve in a loaderheap");
+ return FALSE;
+ }
+
+ // Commit first set of pages, since it will contain the LoaderHeapBlock
+ void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, m_flProtect);
+ if (pTemp == NULL)
+ {
+ //_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap");
+
+ // Unable to commit - release pages
+ if (fReleaseMemory)
+ ClrVirtualFree(pData, 0, MEM_RELEASE);
+
+ return FALSE;
+ }
+
+ if (m_pPrivatePerfCounter_LoaderBytes)
+ *m_pPrivatePerfCounter_LoaderBytes = *m_pPrivatePerfCounter_LoaderBytes + (DWORD) dwSizeToCommit;
+
+ // Record reserved range in range list, if one is specified
+ // Do this AFTER the commit - otherwise we'll have bogus ranges included.
+ if (m_pRangeList != NULL)
+ {
+ if (!m_pRangeList->AddRange((const BYTE *) pData,
+ ((const BYTE *) pData) + dwSizeToReserve,
+ (void *) this))
+ {
+
+ if (fReleaseMemory)
+ ClrVirtualFree(pData, 0, MEM_RELEASE);
+
+ return FALSE;
+ }
+ }
+
+ m_dwTotalAlloc += dwSizeToCommit;
+
+ LoaderHeapBlock *pNewBlock;
+
+ pNewBlock = (LoaderHeapBlock *) pData;
+
+ pNewBlock->dwVirtualSize = dwSizeToReserve;
+ pNewBlock->pVirtualAddress = pData;
+ pNewBlock->pNext = NULL;
+ pNewBlock->m_fReleaseMemory = fReleaseMemory;
+
+ LoaderHeapBlock *pCurBlock = m_pCurBlock;
+
+ // Add to linked list
+ while (pCurBlock != NULL &&
+ pCurBlock->pNext != NULL)
+ pCurBlock = pCurBlock->pNext;
+
+ if (pCurBlock != NULL)
+ m_pCurBlock->pNext = pNewBlock;
+ else
+ m_pFirstBlock = pNewBlock;
+
+ // If we want to use the memory immediately...
+ m_pCurBlock = pNewBlock;
+
+ SETUP_NEW_BLOCK(pData, dwSizeToCommit, dwSizeToReserve);
+
+ return TRUE;
+}
+
+// Get some more committed pages - either commit some more in the current reserved region, or, if it
+// has run out, reserve another set of pages.
+// Returns: FALSE if we can't get any more memory
+// TRUE: We can/did get some more memory - check to see if it's sufficient for
+// the caller's needs (see UnlockedAllocMem for example of use)
+BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ // If we have memory we can use, what are you doing here!
+ _ASSERTE(dwMinSize > (SIZE_T)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr));
+
+ // Does this fit in the reserved region?
+ if (dwMinSize <= (size_t)(m_pEndReservedRegion - m_pAllocPtr))
+ {
+ SIZE_T dwSizeToCommit = (m_pAllocPtr + dwMinSize) - m_pPtrToEndOfCommittedRegion;
+
+ if (dwSizeToCommit < m_dwCommitBlockSize)
+ dwSizeToCommit = min((SIZE_T)(m_pEndReservedRegion - m_pPtrToEndOfCommittedRegion), (SIZE_T)m_dwCommitBlockSize);
+
+ // Round to page size
+ dwSizeToCommit = ALIGN_UP(dwSizeToCommit, PAGE_SIZE);
+
+ // Yes, so commit the desired number of reserved pages
+ void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, m_flProtect);
+ if (pData == NULL)
+ return FALSE;
+
+ if (m_pPrivatePerfCounter_LoaderBytes)
+ *m_pPrivatePerfCounter_LoaderBytes = *m_pPrivatePerfCounter_LoaderBytes + (DWORD) dwSizeToCommit;
+
+ m_dwTotalAlloc += dwSizeToCommit;
+
+ m_pPtrToEndOfCommittedRegion += dwSizeToCommit;
+ return TRUE;
+ }
+
+ // Need to allocate a new set of reserved pages
+ INDEBUG(m_dwDebugWastedBytes += (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr);)
+
+ // Note, there are unused reserved pages at end of current region -can't do much about that
+ // Provide dwMinSize here since UnlockedReservePages will round up the commit size again
+ // after adding in the size of the LoaderHeapBlock header.
+ return UnlockedReservePages(dwMinSize);
+}
+
+void *UnlockedLoaderHeap::UnlockedAllocMem(size_t dwSize
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACT(void*)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(ThrowOutOfMemory(););
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ void *pResult = UnlockedAllocMem_NoThrow(
+ dwSize COMMA_INDEBUG(szFile) COMMA_INDEBUG(lineNum));
+
+ if (pResult == NULL)
+ ThrowOutOfMemory();
+
+ RETURN pResult;
+}
+
+#ifdef _DEBUG
+static DWORD ShouldInjectFault()
+{
+ static DWORD fInjectFault = 99;
+
+ if (fInjectFault == 99)
+ fInjectFault = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) != 0);
+ return fInjectFault;
+}
+
+#define SHOULD_INJECT_FAULT(return_statement) \
+ do { \
+ if (ShouldInjectFault() & 0x1) \
+ { \
+ char *a = new (nothrow) char; \
+ if (a == NULL) \
+ { \
+ return_statement; \
+ } \
+ delete a; \
+ } \
+ } while (FALSE)
+
+#else
+
+#define SHOULD_INJECT_FAULT(return_statement) do { (void)((void *)0); } while (FALSE)
+
+#endif
+
+void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACT(void*)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(CONTRACT_RETURN NULL;);
+ PRECONDITION(dwSize != 0);
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ SHOULD_INJECT_FAULT(RETURN NULL);
+
+ INDEBUG(size_t dwRequestedSize = dwSize;)
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+#ifdef RANDOMIZE_ALLOC
+ if (!m_fExplicitControl)
+ dwSize += s_random.Next() % 256;
+#endif
+
+ dwSize = AllocMem_TotalSize(dwSize, this);
+
+again:
+
+ {
+ // Any memory available on the free list?
+ void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, TRUE /*fRemoveFromFreeList*/, this);
+ if (!pData)
+ {
+ // Enough bytes available in committed region?
+ if (dwSize <= GetBytesAvailCommittedRegion())
+ {
+ pData = m_pAllocPtr;
+ m_pAllocPtr += dwSize;
+ }
+ }
+
+ if (pData)
+ {
+#ifdef _DEBUG
+
+ BYTE *pAllocatedBytes = (BYTE *)pData;
+#if LOADER_HEAP_DEBUG_BOUNDARY > 0
+ // Don't fill the memory we allocated - it is assumed to be zeroed - fill the memory after it
+ memset(pAllocatedBytes + dwRequestedSize, 0xEE, LOADER_HEAP_DEBUG_BOUNDARY);
+#endif
+ if (dwRequestedSize > 0)
+ {
+ _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
+ "LoaderHeap must return zero-initialized memory");
+ }
+
+ if (!m_fExplicitControl)
+ {
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(pData, dwRequestedSize);
+ pTag->m_allocationType = kAllocMem;
+ pTag->m_dwRequestedSize = dwRequestedSize;
+ pTag->m_szFile = szFile;
+ pTag->m_lineNum = lineNum;
+ }
+
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ LoaderHeapSniffer::RecordEvent(this,
+ kAllocMem,
+ szFile,
+ lineNum,
+ szFile,
+ lineNum,
+ pData,
+ dwRequestedSize,
+ dwSize
+ );
+ }
+
+#endif
+
+ EtwAllocRequest(this, pData, dwSize);
+ RETURN pData;
+ }
+ }
+
+ // Need to commit some more pages in reserved region.
+ // If we run out of pages in the reserved region, ClrVirtualAlloc some more pages
+ if (GetMoreCommittedPages(dwSize))
+ goto again;
+
+ // We could not satisfy this allocation request
+ RETURN NULL;
+}
+
+void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
+ size_t dwRequestedSize
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum)
+ COMMA_INDEBUG(__in const char *szAllocFile)
+ COMMA_INDEBUG(int allocLineNum))
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ // Because the primary use of this function is backout, we'll be nice and
+ // define Backout(NULL) be a legal NOP.
+ if (pMem == NULL)
+ {
+ return;
+ }
+
+#ifdef _DEBUG
+ {
+ DEBUG_ONLY_REGION();
+
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(pMem, dwRequestedSize);
+
+ if (pTag->m_dwRequestedSize != dwRequestedSize || pTag->m_allocationType != kAllocMem)
+ {
+ CONTRACT_VIOLATION(ThrowsViolation|FaultViolation); // We're reporting a heap corruption - who cares about violations
+
+ StackSString message;
+ message.Printf("HEAP VIOLATION: Invalid BackoutMem() call made at:\n"
+ "\n"
+ " File: %s\n"
+ " Line: %d\n"
+ "\n"
+ "Attempting to free block originally allocated at:\n"
+ "\n"
+ " File: %s\n"
+ " Line: %d\n"
+ "\n"
+ "The arguments to BackoutMem() were:\n"
+ "\n"
+ " Pointer: 0x%p\n"
+ " Size: %lu (0x%lx)\n"
+ "\n"
+ ,szFile
+ ,lineNum
+ ,szAllocFile
+ ,allocLineNum
+ ,pMem
+ ,(ULONG)dwRequestedSize
+ ,(ULONG)dwRequestedSize
+ );
+
+
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ message.AppendASCII("*** CALLTRACING ENABLED ***\n");
+ LoaderHeapEvent *pEvent = LoaderHeapSniffer::FindEvent(this, pMem);
+ if (!pEvent)
+ {
+ message.AppendASCII("This pointer doesn't appear to have come from this LoaderHeap.\n");
+ }
+ else
+ {
+ message.AppendASCII(pMem == pEvent->m_pMem ? "We have the following data about this pointer:" : "This pointer points to the middle of the following block:");
+ pEvent->Describe(&message);
+ }
+ }
+
+ if (pTag->m_dwRequestedSize != dwRequestedSize)
+ {
+ StackSString buf;
+ buf.Printf(
+ "Possible causes:\n"
+ "\n"
+ " - This pointer wasn't allocated from this loaderheap.\n"
+ " - This pointer was allocated by AllocAlignedMem and you didn't adjust for the \"extra.\"\n"
+ " - This pointer has already been freed.\n"
+ " - You passed in the wrong size. You must pass the exact same size you passed to AllocMem().\n"
+ " - Someone wrote past the end of this block making it appear as if one of the above were true.\n"
+ );
+ message.Append(buf);
+
+ }
+ else
+ {
+ message.AppendASCII("This memory block is completely unrecognizable.\n");
+ }
+
+
+ if (!(m_dwDebugFlags & kCallTracing))
+ {
+ LoaderHeapSniffer::PitchSniffer(&message);
+ }
+
+ StackScratchBuffer scratch;
+ DbgAssertDialog(szFile, lineNum, (char*) message.GetANSI(scratch));
+
+ }
+ }
+#endif
+
+ size_t dwSize = AllocMem_TotalSize(dwRequestedSize, this);
+
+#ifdef _DEBUG
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ DEBUG_ONLY_REGION();
+
+ LoaderHeapValidationTag *pTag = m_fExplicitControl ? NULL : AllocMem_GetTag(pMem, dwRequestedSize);
+
+
+ LoaderHeapSniffer::RecordEvent(this,
+ kFreedMem,
+ szFile,
+ lineNum,
+ (pTag && (allocLineNum < 0)) ? pTag->m_szFile : szAllocFile,
+ (pTag && (allocLineNum < 0)) ? pTag->m_lineNum : allocLineNum,
+ pMem,
+ dwRequestedSize,
+ dwSize
+ );
+ }
+#endif
+
+ if (m_pAllocPtr == ( ((BYTE*)pMem) + dwSize ))
+ {
+ // Cool. This was the last block allocated. We can just undo the allocation instead
+ // of going to the freelist.
+ memset(pMem, 0, dwSize); // Must zero init this memory as AllocMem expect it
+ m_pAllocPtr = (BYTE*)pMem;
+ }
+ else
+ {
+ LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, pMem, dwSize, this);
+ }
+
+}
+
+
+// Allocates memory aligned on power-of-2 boundary.
+//
+// The return value is a pointer that's guaranteed to be aligned.
+//
+// FREEING THIS BLOCK: Underneath, the actual block allocated may
+// be larger and start at an address prior to the one you got back.
+// It is this adjusted size and pointer that you pass to BackoutMem.
+// The required adjustment is passed back thru the pdwExtra pointer.
+//
+// Here is how to properly backout the memory:
+//
+// size_t dwExtra;
+// void *pMem = UnlockedAllocAlignedMem(dwRequestedSize, alignment, &dwExtra);
+// _ASSERTE( 0 == (pMem & (alignment - 1)) );
+// UnlockedBackoutMem( ((BYTE*)pMem) - dExtra, dwRequestedSize + dwExtra );
+//
+// If you use the AllocMemHolder or AllocMemTracker, all this is taken care of
+// behind the scenes.
+//
+//
+void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSize,
+ size_t alignment,
+ size_t *pdwExtra
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACT(void*)
+ {
+ NOTHROW;
+
+ // Macro syntax can't handle this INJECT_FAULT expression - we'll use a precondition instead
+ //INJECT_FAULT( do{ if (*pdwExtra) {*pdwExtra = 0} RETURN NULL; } while(0) );
+
+ PRECONDITION( alignment != 0 );
+ PRECONDITION(0 == (alignment & (alignment - 1))); // require power of 2
+ POSTCONDITION( (RETVAL) ?
+ (0 == ( ((UINT_PTR)(RETVAL)) & (alignment - 1))) : // If non-null, pointer must be aligned
+ (pdwExtra == NULL || 0 == *pdwExtra) // or else *pdwExtra must be set to 0
+ );
+ }
+ CONTRACT_END
+
+ STATIC_CONTRACT_FAULT;
+
+ // Set default value
+ if (pdwExtra)
+ {
+ *pdwExtra = 0;
+ }
+
+ SHOULD_INJECT_FAULT(RETURN NULL);
+
+ void *pResult;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ // Check for overflow if we align the allocation
+ if (dwRequestedSize + alignment < dwRequestedSize)
+ {
+ RETURN NULL;
+ }
+
+ // We don't know how much "extra" we need to satisfy the alignment until we know
+ // which address will be handed out which in turn we don't know because we don't
+ // know whether the allocation will fit within the current reserved range.
+ //
+ // Thus, we'll request as much heap growth as is needed for the worst case (extra == alignment)
+ size_t dwRoomSize = AllocMem_TotalSize(dwRequestedSize + alignment, this);
+ if (dwRoomSize > GetBytesAvailCommittedRegion())
+ {
+ if (!GetMoreCommittedPages(dwRoomSize))
+ {
+ RETURN NULL;
+ }
+ }
+
+ pResult = m_pAllocPtr;
+
+ size_t extra = alignment - ((size_t)pResult & ((size_t)alignment - 1));
+
+// On DEBUG, we force a non-zero extra so people don't forget to adjust for it on backout
+#ifndef _DEBUG
+ if (extra == alignment)
+ {
+ extra = 0;
+ }
+#endif
+
+ S_SIZE_T cbAllocSize = S_SIZE_T( dwRequestedSize ) + S_SIZE_T( extra );
+ if( cbAllocSize.IsOverflow() )
+ {
+ RETURN NULL;
+ }
+
+ size_t dwSize = AllocMem_TotalSize( cbAllocSize.Value(), this);
+ m_pAllocPtr += dwSize;
+
+
+ ((BYTE*&)pResult) += extra;
+#ifdef _DEBUG
+ BYTE *pAllocatedBytes = (BYTE *)pResult;
+#if LOADER_HEAP_DEBUG_BOUNDARY > 0
+ // Don't fill the entire memory - we assume it is all zeroed -just the memory after our alloc
+ memset(pAllocatedBytes + dwRequestedSize, 0xee, LOADER_HEAP_DEBUG_BOUNDARY);
+#endif
+
+ if (dwRequestedSize != 0)
+ {
+ _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
+ "LoaderHeap must return zero-initialized memory");
+ }
+
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ LoaderHeapSniffer::RecordEvent(this,
+ kAllocMem,
+ szFile,
+ lineNum,
+ szFile,
+ lineNum,
+ ((BYTE*)pResult) - extra,
+ dwRequestedSize + extra,
+ dwSize
+ );
+ }
+
+ EtwAllocRequest(this, pResult, dwSize);
+
+ if (!m_fExplicitControl)
+ {
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(((BYTE*)pResult) - extra, dwRequestedSize + extra);
+ pTag->m_allocationType = kAllocMem;
+ pTag->m_dwRequestedSize = dwRequestedSize + extra;
+ pTag->m_szFile = szFile;
+ pTag->m_lineNum = lineNum;
+ }
+#endif //_DEBUG
+
+ if (pdwExtra)
+ {
+ *pdwExtra = extra;
+ }
+
+ RETURN pResult;
+
+}
+
+
+
+void *UnlockedLoaderHeap::UnlockedAllocAlignedMem(size_t dwRequestedSize,
+ size_t dwAlignment,
+ size_t *pdwExtra
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACTL
+ {
+ THROWS;
+ INJECT_FAULT(ThrowOutOfMemory());
+ }
+ CONTRACTL_END
+
+ void *pResult = UnlockedAllocAlignedMem_NoThrow(dwRequestedSize,
+ dwAlignment,
+ pdwExtra
+ COMMA_INDEBUG(szFile)
+ COMMA_INDEBUG(lineNum));
+
+ if (!pResult)
+ {
+ ThrowOutOfMemory();
+ }
+
+ return pResult;
+
+
+}
+
+
+
+void *UnlockedLoaderHeap::UnlockedAllocMemForCode_NoThrow(size_t dwHeaderSize, size_t dwCodeSize, DWORD dwCodeAlignment)
+{
+ CONTRACT(void*)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(CONTRACT_RETURN NULL;);
+ PRECONDITION(0 == (dwCodeAlignment & (dwCodeAlignment - 1))); // require power of 2
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ _ASSERTE(m_fExplicitControl);
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ // We don't know how much "extra" we need to satisfy the alignment until we know
+ // which address will be handed out which in turn we don't know because we don't
+ // know whether the allocation will fit within the current reserved range.
+ //
+ // Thus, we'll request as much heap growth as is needed for the worst case (we request an extra dwCodeAlignment – 1 bytes)
+
+ S_SIZE_T cbAllocSize = S_SIZE_T(dwHeaderSize) + S_SIZE_T(dwCodeSize) + S_SIZE_T(dwCodeAlignment - 1);
+ if( cbAllocSize.IsOverflow() )
+ {
+ RETURN NULL;
+ }
+
+ if (cbAllocSize.Value() > GetBytesAvailCommittedRegion())
+ {
+ if (GetMoreCommittedPages(cbAllocSize.Value()) == FALSE)
+ {
+ RETURN NULL;
+ }
+ }
+
+ BYTE *pResult = (BYTE *)ALIGN_UP(m_pAllocPtr + dwHeaderSize, dwCodeAlignment);
+ EtwAllocRequest(this, pResult, (pResult + dwCodeSize) - m_pAllocPtr);
+ m_pAllocPtr = pResult + dwCodeSize;
+
+ RETURN pResult;
+}
+
+
+#endif // #ifndef DACCESS_COMPILE
+
+#ifdef DACCESS_COMPILE
+
+void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ DAC_ENUM_DTHIS();
+
+ PTR_LoaderHeapBlock block = m_pFirstBlock;
+ while (block.IsValid())
+ {
+ // All we know is the virtual size of this block. We don't have any way to tell how
+ // much of this space was actually comitted, so don't expect that this will always
+ // succeed.
+ // @dbgtodo : Ideally we'd reduce the risk of corruption causing problems here.
+ // We could extend LoaderHeapBlock to track a commit size,
+ // but it seems wasteful (eg. makes each AppDomain objects 32 bytes larger on x64).
+ TADDR addr = dac_cast<TADDR>(block->pVirtualAddress);
+ TSIZE_T size = block->dwVirtualSize;
+ DacEnumMemoryRegion(addr, size, false);
+
+ block = block->pNext;
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+
+void UnlockedLoaderHeap::EnumPageRegions (EnumPageRegionsCallback *pCallback)
+{
+ WRAPPER_NO_CONTRACT;
+
+ PTR_LoaderHeapBlock block = m_pFirstBlock;
+ while (block)
+ {
+ (*pCallback)(block->pVirtualAddress, block->dwVirtualSize);
+
+ block = block->pNext;
+ }
+}
+
+
+#ifdef _DEBUG
+
+void UnlockedLoaderHeap::DumpFreeList()
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_pFirstFreeBlock == NULL)
+ {
+ printf("FREEDUMP: FreeList is empty\n");
+ }
+ else
+ {
+ LoaderHeapFreeBlock *pBlock = m_pFirstFreeBlock;
+ while (pBlock != NULL)
+ {
+ size_t dwsize = pBlock->m_dwSize;
+ BOOL ccbad = FALSE;
+ BOOL sizeunaligned = FALSE;
+ BOOL sizesmall = FALSE;
+
+ if ( 0 != (dwsize & ALLOC_ALIGN_CONSTANT) )
+ {
+ sizeunaligned = TRUE;
+ }
+ if ( dwsize < sizeof(LoaderHeapBlock))
+ {
+ sizesmall = TRUE;
+ }
+
+ for (size_t i = sizeof(LoaderHeapFreeBlock); i < dwsize; i++)
+ {
+ if ( ((BYTE*)pBlock)[i] != 0xcc )
+ {
+ ccbad = TRUE;
+ break;
+ }
+ }
+
+ printf("Addr = %pxh, Size = %lxh", pBlock, ((ULONG)dwsize));
+ if (ccbad) printf(" *** ERROR: NOT CC'd ***");
+ if (sizeunaligned) printf(" *** ERROR: size not a multiple of ALLOC_ALIGN_CONSTANT ***");
+ if (sizesmall) printf(" *** ERROR: size smaller than sizeof(LoaderHeapFreeBlock) ***");
+ printf("\n");
+
+ pBlock = pBlock->m_pNext;
+ }
+ }
+}
+
+
+void UnlockedLoaderHeap::UnlockedClearEvents()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapSniffer::ClearEvents(this);
+}
+
+void UnlockedLoaderHeap::UnlockedCompactEvents()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapSniffer::CompactEvents(this);
+}
+
+void UnlockedLoaderHeap::UnlockedPrintEvents()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapSniffer::PrintEvents(this);
+}
+
+
+#endif //_DEBUG
+
+//************************************************************************************
+// LOADERHEAP SNIFFER METHODS
+//************************************************************************************
+#ifdef _DEBUG
+
+/*static*/ VOID LoaderHeapSniffer::RecordEvent(UnlockedLoaderHeap *pHeap,
+ AllocationType allocationType,
+ __in const char *szFile,
+ int lineNum,
+ __in const char *szAllocFile,
+ int allocLineNum,
+ void *pMem,
+ size_t dwRequestedSize,
+ size_t dwSize
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT; //If we OOM in here, we just throw the event away.
+ }
+ CONTRACTL_END
+
+ LoaderHeapEvent *pNewEvent;
+ {
+ {
+ FAULT_NOT_FATAL();
+ pNewEvent = new (nothrow) LoaderHeapEvent;
+ }
+ if (!pNewEvent)
+ {
+ if (!(pHeap->m_dwDebugFlags & pHeap->kEncounteredOOM))
+ {
+ pHeap->m_dwDebugFlags |= pHeap->kEncounteredOOM;
+ _ASSERTE(!"LOADERHEAPSNIFFER: Failed allocation of LoaderHeapEvent. Call tracing information will be incomplete.");
+ }
+ }
+ else
+ {
+ pNewEvent->m_allocationType = allocationType;
+ pNewEvent->m_szFile = szFile;
+ pNewEvent->m_lineNum = lineNum;
+ pNewEvent->m_szAllocFile = szAllocFile;
+ pNewEvent->m_allocLineNum = allocLineNum;
+ pNewEvent->m_pMem = pMem;
+ pNewEvent->m_dwRequestedSize = dwRequestedSize;
+ pNewEvent->m_dwSize = dwSize;
+
+ pNewEvent->m_pNext = pHeap->m_pEventList;
+ pHeap->m_pEventList = pNewEvent;
+ }
+ }
+}
+
+
+
+/*static*/
+void LoaderHeapSniffer::ValidateFreeList(UnlockedLoaderHeap *pHeap)
+{
+ CANNOT_HAVE_CONTRACT;
+
+ // No contract. This routine is only called if we've AV'd inside the
+ // loaderheap. The system is already toast. We're trying to be a hero
+ // and produce the best diagnostic info we can. Last thing we need here
+ // is a secondary assert inside the contract stuff.
+ //
+ // This contract violation is permanent.
+ CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation); // This violation won't be removed
+
+ LoaderHeapFreeBlock *pFree = pHeap->m_pFirstFreeBlock;
+ LoaderHeapFreeBlock *pPrev = NULL;
+
+
+ void *pBadAddr = NULL;
+ LoaderHeapFreeBlock *pProbeThis = NULL;
+ const char *pExpected = NULL;
+
+ while (pFree != NULL)
+ {
+ if ( 0 != ( ((ULONG_PTR)pFree) & ALLOC_ALIGN_CONSTANT ))
+ {
+ // Not aligned - can't be a valid freeblock. Most likely we followed a bad pointer from the previous block.
+ pProbeThis = pPrev;
+ pBadAddr = pPrev ? &(pPrev->m_pNext) : &(pHeap->m_pFirstFreeBlock);
+ pExpected = "a pointer to a valid LoaderHeapFreeBlock";
+ break;
+ }
+
+ size_t dwSize = pFree->m_dwSize;
+ if (dwSize < AllocMem_TotalSize(1, pHeap) ||
+ 0 != (dwSize & ALLOC_ALIGN_CONSTANT))
+ {
+ // Size is not a valid value (out of range or unaligned.)
+ pProbeThis = pFree;
+ pBadAddr = &(pFree->m_dwSize);
+ pExpected = "a valid block size (multiple of pointer size)";
+ break;
+ }
+
+ size_t i;
+ for (i = sizeof(LoaderHeapFreeBlock); i < dwSize; i++)
+ {
+ if ( ((BYTE*)pFree)[i] != 0xcc )
+ {
+ pProbeThis = pFree;
+ pBadAddr = i + ((BYTE*)pFree);
+ pExpected = "0xcc (our fill value for free blocks)";
+ break;
+ }
+ }
+ if (i != dwSize)
+ {
+ break;
+ }
+
+
+
+ pPrev = pFree;
+ pFree = pFree->m_pNext;
+ }
+
+ if (pFree == NULL)
+ {
+ return; // No problems found
+ }
+
+ {
+ StackSString message;
+
+ message.Printf("A loaderheap freelist has been corrupted. The bytes at or near address 0x%p appears to have been overwritten. We expected to see %s here.\n"
+ "\n"
+ " LoaderHeap: 0x%p\n"
+ " Suspect address at: 0x%p\n"
+ " Start of suspect freeblock: 0x%p\n"
+ "\n"
+ , pBadAddr
+ , pExpected
+ , pHeap
+ , pBadAddr
+ , pProbeThis
+ );
+
+ if (!(pHeap->m_dwDebugFlags & pHeap->kCallTracing))
+ {
+ message.AppendASCII("\nThe usual reason is that someone wrote past the end of a block or wrote into a block after freeing it."
+ "\nOf course, the culprit is long gone so it's probably too late to debug this now. Try turning on call-tracing"
+ "\nand reproing. We can attempt to find out who last owned the surrounding pieces of memory."
+ "\n"
+ "\nTo turn on call-tracing, set the following registry DWORD value:"
+ "\n"
+ "\n HKLM\\Software\\Microsoft\\.NETFramework\\LoaderHeapCallTracing = 1"
+ "\n"
+ );
+
+ }
+ else
+ {
+ LoaderHeapEvent *pBadAddrEvent = FindEvent(pHeap, pBadAddr);
+
+ message.AppendASCII("*** CALL TRACING ENABLED ***\n\n");
+
+ if (pBadAddrEvent)
+ {
+ message.AppendASCII("\nThe last known owner of the corrupted address was:\n");
+ pBadAddrEvent->Describe(&message);
+ }
+ else
+ {
+ message.AppendASCII("\nNo known owner of last corrupted address.\n");
+ }
+
+ LoaderHeapEvent *pPrevEvent = FindEvent(pHeap, ((BYTE*)pProbeThis) - 1);
+
+ int count = 3;
+ while (count-- &&
+ pPrevEvent != NULL &&
+ ( ((UINT_PTR)pProbeThis) - ((UINT_PTR)(pPrevEvent->m_pMem)) + pPrevEvent->m_dwSize ) < 1024)
+ {
+ message.AppendASCII("\nThis block is located close to the corruption point. ");
+ if (pPrevEvent->QuietValidate())
+ {
+ message.AppendASCII("If it was overrun, it might have caused this.");
+ }
+ else
+ {
+ message.AppendASCII("*** CORRUPTION DETECTED IN THIS BLOCK ***");
+ }
+ pPrevEvent->Describe(&message);
+ pPrevEvent = FindEvent(pHeap, ((BYTE*)(pPrevEvent->m_pMem)) - 1);
+ }
+
+
+ }
+
+ StackScratchBuffer scratch;
+ DbgAssertDialog(__FILE__, __LINE__, (char*) message.GetANSI(scratch));
+
+ }
+
+
+
+}
+
+
+BOOL LoaderHeapEvent::QuietValidate()
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_allocationType == kAllocMem)
+ {
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(m_pMem, m_dwRequestedSize);
+ return (pTag->m_allocationType == m_allocationType && pTag->m_dwRequestedSize == m_dwRequestedSize);
+ }
+ else
+ {
+ // We can't easily validate freed blocks.
+ return TRUE;
+ }
+}
+
+
+#endif //_DEBUG
+
+#ifndef DACCESS_COMPILE
+
+AllocMemTracker::AllocMemTracker()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
+ m_FirstBlock.m_pNext = NULL;
+ m_FirstBlock.m_nextFree = 0;
+ m_pFirstBlock = &m_FirstBlock;
+
+ m_fReleased = FALSE;
+}
+
+AllocMemTracker::~AllocMemTracker()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ if (!m_fReleased)
+ {
+ AllocMemTrackerBlock *pBlock = m_pFirstBlock;
+ while (pBlock)
+ {
+ // Do the loop in reverse - loaderheaps work best if
+ // we allocate and backout in LIFO order.
+ for (int i = pBlock->m_nextFree - 1; i >= 0; i--)
+ {
+ AllocMemTrackerNode *pNode = &(pBlock->m_Node[i]);
+ pNode->m_pHeap->RealBackoutMem(pNode->m_pMem
+ ,pNode->m_dwRequestedSize
+#ifdef _DEBUG
+ ,__FILE__
+ ,__LINE__
+ ,pNode->m_szAllocFile
+ ,pNode->m_allocLineNum
+#endif
+ );
+
+ }
+
+ pBlock = pBlock->m_pNext;
+ }
+ }
+
+
+ AllocMemTrackerBlock *pBlock = m_pFirstBlock;
+ while (pBlock != &m_FirstBlock)
+ {
+ AllocMemTrackerBlock *pNext = pBlock->m_pNext;
+ delete pBlock;
+ pBlock = pNext;
+ }
+
+ INDEBUG(memset(this, 0xcc, sizeof(*this));)
+}
+
+void *AllocMemTracker::Track(TaggedMemAllocPtr tmap)
+{
+ CONTRACTL
+ {
+ THROWS;
+ INJECT_FAULT(ThrowOutOfMemory(););
+ }
+ CONTRACTL_END
+
+ _ASSERTE(this);
+
+ void *pv = Track_NoThrow(tmap);
+ if (!pv)
+ {
+ ThrowOutOfMemory();
+ }
+ return pv;
+}
+
+void *AllocMemTracker::Track_NoThrow(TaggedMemAllocPtr tmap)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ _ASSERTE(this);
+
+ // Calling Track() after calling SuppressRelease() is almost certainly a bug. You're supposed to call SuppressRelease() only after you're
+ // sure no subsequent failure will force you to backout the memory.
+ _ASSERTE( (!m_fReleased) && "You've already called SuppressRelease on this AllocMemTracker which implies you've passed your point of no failure. Why are you still doing allocations?");
+
+
+ if (tmap.m_pMem != NULL)
+ {
+ AllocMemHolder<void*> holder(tmap); // If anything goes wrong in here, this holder will backout the allocation for the caller.
+ if (m_fReleased)
+ {
+ holder.SuppressRelease();
+ }
+ AllocMemTrackerBlock *pBlock = m_pFirstBlock;
+ if (pBlock->m_nextFree == kAllocMemTrackerBlockSize)
+ {
+ AllocMemTrackerBlock *pNewBlock = new (nothrow) AllocMemTrackerBlock;
+ if (!pNewBlock)
+ {
+ return NULL;
+ }
+
+ pNewBlock->m_pNext = m_pFirstBlock;
+ pNewBlock->m_nextFree = 0;
+
+ m_pFirstBlock = pNewBlock;
+
+ pBlock = pNewBlock;
+ }
+
+ // From here on, we can't fail
+ pBlock->m_Node[pBlock->m_nextFree].m_pHeap = tmap.m_pHeap;
+ pBlock->m_Node[pBlock->m_nextFree].m_pMem = tmap.m_pMem;
+ pBlock->m_Node[pBlock->m_nextFree].m_dwRequestedSize = tmap.m_dwRequestedSize;
+#ifdef _DEBUG
+ pBlock->m_Node[pBlock->m_nextFree].m_szAllocFile = tmap.m_szFile;
+ pBlock->m_Node[pBlock->m_nextFree].m_allocLineNum = tmap.m_lineNum;
+#endif
+
+ pBlock->m_nextFree++;
+
+ holder.SuppressRelease();
+
+
+ }
+ return (void *)tmap;
+
+
+
+}
+
+
+void AllocMemTracker::SuppressRelease()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(this);
+
+ m_fReleased = TRUE;
+}
+
+#endif //#ifndef DACCESS_COMPILE
diff --git a/src/utilcode/loadrc-impl.cpp b/src/utilcode/loadrc-impl.cpp
new file mode 100644
index 0000000000..4c8ee32ee4
--- /dev/null
+++ b/src/utilcode/loadrc-impl.cpp
@@ -0,0 +1,322 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+// LoadRC-impl.cpp : Utility to load a localized file (primarily a DLL)
+
+#include "palclr.h"
+
+#if defined(USE_SSTRING)
+
+// This is the normal path: use SafeString, wrappers, etc...
+
+#include "sstring.h"
+#include "safewrap.h"
+
+typedef SString MyString;
+typedef SString::CIterator MyStringIterator;
+#define EndsWithChar(OBJ, CHAR) (OBJ.EndsWith(CHAR))
+#define AppendChar(OBJ, CHAR) (OBJ.Append(CHAR))
+#define AppendStr(OBJ, STR) (OBJ.Append(STR))
+#define TrimLastChar(OBJ) (OBJ.Truncate(OBJ.End() - 1))
+#define GetChars(OBJ) (OBJ.GetUnicode())
+#define IsEmptyStr(OBJ) (OBJ.IsEmpty())
+#define CharLength(OBJ) (OBJ.GetCount())
+#define StrBeginIter(OBJ) (OBJ.Begin())
+#define StrEndIter(OBJ) (OBJ.End())
+#define FindNext(OBJ, ITER, CHAR) (OBJ.Find(ITER, CHAR))
+#define MakeString(DST, OBJ, BEG, END) (DST.Set(OBJ, BEG, END))
+#define StrEquals(STR1, STR2) (STR1.Compare(STR2)==0)
+#define FindLast(OBJ, ITER, CHAR) (OBJ.FindBack(ITER, CHAR))
+void SkipChars(MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (str.Skip(i, c1) || str.Skip(i, c2)); }
+
+#elif defined(USE_WSTRING)
+
+// This stuff is used by GacUtil, because it _really_ doesn't want to link with utilcode :-(
+
+#include <string>
+#include <algorithm>
+typedef std::wstring MyString;
+typedef std::wstring::const_iterator MyStringIterator;
+#define EndsWithChar(OBJ, CHAR) (*(OBJ.rbegin()) == CHAR)
+#define AppendChar(OBJ, CHAR) (OBJ.push_back(CHAR))
+#define AppendStr(OBJ, STR) (OBJ += STR)
+#define TrimLastChar(OBJ) (OBJ.resize(OBJ.size() - 1))
+#define GetChars(OBJ) (OBJ.c_str())
+#define IsEmptyStr(OBJ) (OBJ.empty())
+#define CharLength(OBJ) (OBJ.size())
+#define StrBeginIter(OBJ) (OBJ.begin())
+#define StrEndIter(OBJ) (OBJ.end())
+#define FindNext(OBJ, ITER, CHAR) (ITER = std::find<std::wstring::const_iterator>(ITER, OBJ.end(), CHAR))
+#define MakeString(DST, OBJ, BEG, END) (DST = MyString(BEG, END))
+#define StrEquals(STR1, STR2) (STR1 == STR2)
+#define ClrGetEnvironmentVariable(var, res) GetEnvVar(L##var, res)
+bool FindLast(const MyString &str, MyStringIterator &iter, wchar_t c)
+{
+ size_t pos = str.find_last_of(c);
+ iter = (pos == std::wstring::npos) ? str.end() : (str.begin() + pos);
+ return pos != std::wstring::npos;
+}
+void SkipChars(const MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (*i == c1 || *i == c2) i++; }
+bool GetEnvVar(_In_z_ wchar_t *var, MyString &res)
+{
+ wchar_t *buffer;
+ size_t size;
+ _wdupenv_s(&buffer, &size, var);
+ if (!size || !buffer)
+ return false;
+ res = buffer;
+ free(buffer); // Don't forget to free the buffer!
+ return true;
+}
+void ClrGetModuleFileName(HMODULE hModule, MyString& value)
+{
+ wchar_t driverpath_tmp[_MAX_PATH];
+ GetModuleFileNameW(hModule, driverpath_tmp, _MAX_PATH);
+ value = driverpath_tmp;
+}
+
+#else
+
+#error You must define either USE_SSTRING or USE_WSTRING to use this file
+
+#endif
+
+// This is a helper for loading localized string resource DLL files
+HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf);
+
+// This is a slight variation that can be used for anything else (ildasm.chm, for example)
+typedef void* (__cdecl *LocalizedFileHandler)(LPCWSTR);
+void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePath);
+
+// Helper functions to combine paths
+static MyString MakePath(const MyString &root, const MyString &file)
+{
+ MyString res = root;
+ if (!EndsWithChar(res, W('\\')))
+ AppendChar(res, W('\\'));
+ AppendStr(res, file);
+ return res;
+}
+static MyString MakePath(const MyString &root, const MyString &dir, const MyString &file)
+{
+ return MakePath(MakePath(root, dir), file);
+}
+
+// Helper to deal with occasional training back-slashes
+static bool FileExists(const MyString &file)
+{
+ if (!EndsWithChar(file, W('\\')))
+ return GetFileAttributesW(GetChars(file)) != INVALID_FILE_ATTRIBUTES;
+ else
+ {
+ MyString tmp(file);
+ TrimLastChar(tmp);
+ return GetFileAttributesW(GetChars(tmp)) != INVALID_FILE_ATTRIBUTES;
+ }
+}
+
+// Little helper function to get the codepage integer ID from the LocaleInfo
+static UINT GetCodePage(LANGID LanguageID, DWORD locale)
+{
+ wchar_t CodePageInt[12];
+ GetLocaleInfo(MAKELCID(LanguageID, SORT_DEFAULT), LOCALE_IDEFAULTCODEPAGE, CodePageInt, _countof(CodePageInt));
+ return _wtoi(CodePageInt);
+}
+
+// LCID helper macro
+#define ENGLISH_LCID MAKELCID(MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ), SORT_DEFAULT)
+
+// FindLocaleDirectory: Search the provided path for one of the expected code page subdirectories
+// Returns empty string on failure, or the full path to the c:\my\directory\1033\myrcfile.dll
+static MyString FindLocaleDirectory(const MyString &path, const MyString &dllName)
+{
+ // We'll be checking for 3 different locales: The user's default locale
+ // The user's primary language locale, and english (in that order)
+ const LCID lcidUser = MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT);
+ LCID rglcid[3] = {lcidUser, MAKELCID(MAKELANGID(PRIMARYLANGID(lcidUser), SUBLANG_DEFAULT), SORTIDFROMLCID(lcidUser)), ENGLISH_LCID};
+
+ for (int i = 0; i < _countof(rglcid); i++)
+ {
+ LCID lcid = rglcid[i];
+ // Turn the LCID into a string
+ wchar_t wzNumBuf[12];
+ _itow_s(lcid, wzNumBuf, _countof(wzNumBuf), 10);
+ MyString localePath = MakePath(path, wzNumBuf, dllName);
+
+ // Check to see if the file exists
+ if (FileExists(localePath))
+ {
+ // make sure the console can support a codepage for this language.
+ UINT ConsoleCP = GetConsoleOutputCP();
+
+ // Dev10 #843375: For a GUI application, GetConsoleOutputCP returns 0
+ // If that's the case, we don't care about capabilities of the console,
+ // since we're not outputting to the console, anyway...
+ if ( ConsoleCP != 0 && lcid != ENGLISH_LCID )
+ {
+ LANGID LanguageID = MAKELANGID( lcid, SUBLANGID(lcid) );
+ // we know the console cannot support arabic or hebrew (right to left scripts?)
+ if( PRIMARYLANGID(LanguageID) == LANG_ARABIC || PRIMARYLANGID(LanguageID) == LANG_HEBREW )
+ continue;
+
+ UINT LangOEMCodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTCODEPAGE);
+ UINT LangANSICodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTANSICODEPAGE);
+
+ // We can only support it if the console's code page is UTF8, OEM, or ANSI
+ if( ConsoleCP != CP_UTF8 && ConsoleCP != LangOEMCodepage && ConsoleCP != LangANSICodepage )
+ continue;
+ }
+
+ return localePath;
+ }
+ }
+ return W("");
+}
+
+// Attempt to load the resource file from the locale, first.
+// If that fails, then just try any subdirectory of of the path provided
+static void *LoadLocalFile(const MyString &path, const MyString &dllName, LocalizedFileHandler lfh)
+{
+ if (IsEmptyStr(path) || IsEmptyStr(dllName))
+ return NULL;
+
+ MyString pathTemp = path;
+
+ // Languages are checked in the following order.
+ // 1) The UI language: this is returned by GetUserDefaultUILanguage.
+ // 2) As step 1, but with SUBLANG_DEFAULT
+ // 3) English
+ // 4) Any language that can be found!
+
+ MyString localePath = FindLocaleDirectory(pathTemp, dllName);
+
+ if (IsEmptyStr(localePath))
+ {
+ // None of the default choices exists, so now look for the first version of the dll in the given path.
+ // We don't bother to see if the console supports the dll's language.
+ MyString wildCard = MakePath(pathTemp, W("*.*"));
+ WIN32_FIND_DATAW wfdw;
+ HANDLE hDirs = FindFirstFileW(GetChars(wildCard), &wfdw);
+ if (hDirs == INVALID_HANDLE_VALUE)
+ return NULL;
+ do
+ {
+ // We are only interested in directories, since at this level, that should
+ // be the only thing in this directory, i.e, LCID sub dirs
+ if (wfdw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ MyString file(wfdw.cFileName);
+
+ if (StrEquals(file, W(".")))
+ continue;
+
+ if (StrEquals(file, W("..")))
+ continue;
+
+ // Does this dir have the resource dll?
+ MyString fullPath = MakePath(pathTemp, file, dllName);
+
+ if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES)
+ {
+ localePath = fullPath; // Got it - bail out of here
+ break;
+ }
+ }
+ } while (FindNextFileW(hDirs, &wfdw));
+
+ FindClose(hDirs);
+
+
+ if (IsEmptyStr(localePath))
+ {
+ //
+ // With CoreCLR we have the resource dll directly in the bin directory so check there now.
+ //
+
+ // Does this dir have the resource dll?
+ MyString fullPath = MakePath(path, dllName);
+
+ if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES)
+ {
+ localePath = fullPath; // Got it - bail out of here
+ }
+ }
+ }
+
+ // Attempt to load the library
+ // Beware! A dll loaded with LOAD_LIBRARY_AS_DATAFILE won't
+ // let you use LoadIcon and things like that (only general calls like FindResource and LoadResource).
+ return IsEmptyStr(localePath) ? NULL : lfh(GetChars(localePath));
+}
+
+// Try to load the resource DLL from [each directory in %PATH%]/<lcid>/
+static void *LoadSearchPath(const MyString &resourceDllName, LocalizedFileHandler lfh)
+{
+ void *hmod = NULL;
+
+ // Get the PATH variable into a C++ string
+ MyString envPath;
+
+ if (ClrGetEnvironmentVariable("PATH", envPath))
+ return hmod;
+
+ MyStringIterator endOfChunk, startOfChunk = StrBeginIter(envPath);
+ MyString tryPath;
+ for (SkipChars(envPath, startOfChunk, W(' '), W(';'));
+ hmod == NULL && startOfChunk != StrEndIter(envPath);
+ SkipChars(envPath, startOfChunk, W(' '), W(';')))
+ {
+ // copy this chunk of the path into our trypath
+ endOfChunk = startOfChunk;
+ FindNext(envPath, endOfChunk, W(';'));
+ MakeString(tryPath, envPath, startOfChunk, endOfChunk);
+
+ // Don't try invalid locations
+ if (IsEmptyStr(tryPath) || CharLength(tryPath) >= _MAX_PATH)
+ continue;
+
+ // Try to load the dll
+ hmod = LoadLocalFile(tryPath, resourceDllName, lfh);
+ startOfChunk = endOfChunk;
+ }
+ return hmod;
+}
+
+void * __cdecl LibraryLoader(_In_z_ LPCWSTR lpFileName)
+{
+ return (void *)(LoadLibraryExW(lpFileName, NULL, LOAD_LIBRARY_AS_DATAFILE));
+}
+
+void *FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePathW)
+{
+ // find path of the modulePath
+ MyString driverPath;
+ MyString modulePath;
+ ClrGetModuleFileName(GetModuleHandleW(modulePathW), modulePath);
+
+ // Rip off the application name.
+ MyStringIterator trailingSlashLocation = StrEndIter(modulePath);
+ if (FindLast(modulePath, trailingSlashLocation, W('\\')))
+ MakeString(driverPath, modulePath, StrBeginIter(modulePath), trailingSlashLocation);
+ else
+ // If it's not a full path, look in the current directory
+ driverPath = W(".");
+
+ // return the first of the local directory's copy or the resource DLL on %PATH%
+ void *hmod = LoadLocalFile(driverPath, wzResourceDllName, lfh);
+ if (hmod == NULL)
+ hmod = LoadSearchPath(wzResourceDllName, lfh);
+ return hmod;
+}
+
+// load the satellite dll which contains string resources
+HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf)
+{
+ HMODULE hmod = (HMODULE)FindLocalizedFile(wzResourceDllName, &LibraryLoader, modulePath);
+ if (hmod == NULL && trySelf)
+ hmod = GetModuleHandleW(NULL);
+ return hmod;
+}
diff --git a/src/utilcode/loadrc.cpp b/src/utilcode/loadrc.cpp
new file mode 100644
index 0000000000..6bc8d656d3
--- /dev/null
+++ b/src/utilcode/loadrc.cpp
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+// Loads an localized string resource file
+// this is used by sn.exe, ildasm.exe, peverify.exe, gacutil.exe, and fuslogvw.exe
+// To use it by itself (not requiring utilcode*.lib), you'll need to use this CPP file
+// along with safewrap, sstring
+//
+
+
+#include "stdafx.h"
+#include "sstring.h"
+#define USE_SSTRING
+#include "loadrc-impl.cpp"
+
diff --git a/src/utilcode/log.cpp b/src/utilcode/log.cpp
new file mode 100644
index 0000000000..0429597e29
--- /dev/null
+++ b/src/utilcode/log.cpp
@@ -0,0 +1,447 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+// Simple Logging Facility
+//
+#include "stdafx.h"
+
+//
+// Define LOGGING by default in a checked build. If you want to log in a free
+// build, define logging independent of _DEBUG here and each place you want
+// to use it.
+//
+#ifdef _DEBUG
+#define LOGGING
+#endif
+
+#include "log.h"
+#include "utilcode.h"
+
+#ifdef LOGGING
+
+#define DEFAULT_LOGFILE_NAME "COMPLUS.LOG"
+
+#define LOG_ENABLE_FILE_LOGGING 0x0001
+#define LOG_ENABLE_FLUSH_FILE 0x0002
+#define LOG_ENABLE_CONSOLE_LOGGING 0x0004
+#define LOG_ENABLE_APPEND_FILE 0x0010
+#define LOG_ENABLE_DEBUGGER_LOGGING 0x0020
+#define LOG_ENABLE 0x0040
+
+
+static DWORD LogFlags = 0;
+static char szLogFileName[MAX_PATH+1] = DEFAULT_LOGFILE_NAME;
+static HANDLE LogFileHandle = INVALID_HANDLE_VALUE;
+static MUTEX_COOKIE LogFileMutex = 0;
+static DWORD LogFacilityMask = LF_ALL;
+static DWORD LogFacilityMask2 = 0;
+static DWORD LogVMLevel = LL_INFO100;
+ // <TODO>@todo FIX should probably only display warnings and above by default</TODO>
+
+
+VOID InitLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ // <TODO>FIX bit of a workaround for now, check for the log file in the
+ // registry and if there, turn on file logging VPM</TODO>
+
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogEnable, LOG_ENABLE);
+ LogFacilityMask = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS;
+ LogVMLevel = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LogVMLevel);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFileAppend, LOG_ENABLE_APPEND_FILE);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFlushFile, LOG_ENABLE_FLUSH_FILE);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToDebugger, LOG_ENABLE_DEBUGGER_LOGGING);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToFile, LOG_ENABLE_FILE_LOGGING);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToConsole, LOG_ENABLE_CONSOLE_LOGGING);
+
+ LogFacilityMask2 = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS;
+
+ LPWSTR fileName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFile);
+ if (fileName != 0)
+ {
+ int ret;
+ ret = WszWideCharToMultiByte(CP_ACP, 0, fileName, -1, szLogFileName, sizeof(szLogFileName)-1, NULL, NULL);
+ _ASSERTE(ret != 0);
+ delete fileName;
+ }
+
+ if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogWithPid, FALSE))
+ {
+ char szPid[20];
+ sprintf_s(szPid, COUNTOF(szPid), ".%d", GetCurrentProcessId());
+ strcat_s(szLogFileName, _countof(szLogFileName), szPid);
+ }
+
+ if ((LogFlags & LOG_ENABLE) &&
+ (LogFlags & LOG_ENABLE_FILE_LOGGING) &&
+ (LogFileHandle == INVALID_HANDLE_VALUE))
+ {
+ DWORD fdwCreate = (LogFlags & LOG_ENABLE_APPEND_FILE) ? OPEN_ALWAYS : CREATE_ALWAYS;
+ LogFileHandle = CreateFileA(
+ szLogFileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ fdwCreate,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | ((LogFlags & LOG_ENABLE_FLUSH_FILE) ? FILE_FLAG_WRITE_THROUGH : 0),
+ NULL);
+
+ if(0 == LogFileMutex)
+ {
+ LogFileMutex = ClrCreateMutex(
+ NULL,
+ FALSE,
+ NULL);
+ _ASSERTE(LogFileMutex != 0);
+ }
+
+ // Some other logging may be going on, try again with another file name
+ if (LogFileHandle == INVALID_HANDLE_VALUE)
+ {
+ char* ptr = szLogFileName + strlen(szLogFileName) + 1;
+ ptr[-1] = '.';
+ ptr[0] = '0';
+ ptr[1] = 0;
+
+ for(int i = 0; i < 10; i++)
+ {
+ LogFileHandle = CreateFileA(
+ szLogFileName,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ fdwCreate,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | ((LogFlags & LOG_ENABLE_FLUSH_FILE) ? FILE_FLAG_WRITE_THROUGH : 0),
+ NULL);
+ if (LogFileHandle != INVALID_HANDLE_VALUE)
+ break;
+ *ptr = *ptr + 1;
+ }
+ if (LogFileHandle == INVALID_HANDLE_VALUE) {
+ DWORD written;
+ char buff[MAX_PATH+60];
+ strcpy(buff, "Could not open log file, logging to ");
+ strcat_s(buff, _countof(buff), szLogFileName);
+ // ARULM--Changed WriteConsoleA to WriteFile to be CE compat
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buff, (DWORD)strlen(buff), &written, 0);
+ }
+ }
+ if (LogFileHandle == INVALID_HANDLE_VALUE)
+ UtilMessageBoxNonLocalized(NULL, W("Could not open log file"), W("CLR logging"), MB_OK | MB_ICONINFORMATION, FALSE, TRUE);
+ if (LogFileHandle != INVALID_HANDLE_VALUE)
+ {
+ if (LogFlags & LOG_ENABLE_APPEND_FILE)
+ SetFilePointer(LogFileHandle, 0, NULL, FILE_END);
+ LogSpew( LF_ALWAYS, FATALERROR, "************************ New Output *****************\n" );
+ }
+ }
+}
+
+VOID EnterLogLock()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // We don't care about violating CANNOT_TAKE_LOCK in debug-only builds, and it's
+ // rather hard to care about this, as we LOG all over the place.
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ if(LogFileMutex != 0)
+ {
+ DWORD status;
+ status = ClrWaitForMutex(LogFileMutex, INFINITE, FALSE);
+ _ASSERTE(WAIT_OBJECT_0 == status);
+ }
+}
+
+VOID LeaveLogLock()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if(LogFileMutex != 0)
+ {
+ BOOL success;
+ success = ClrReleaseMutex(LogFileMutex);
+ _ASSERTE(success);
+ }
+}
+
+static bool bLoggingInitialized = false;
+VOID InitializeLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ if (bLoggingInitialized)
+ return;
+ bLoggingInitialized = true;
+
+ InitLogging(); // You can call this in the debugger to fetch new settings
+}
+
+VOID FlushLogging() {
+ STATIC_CONTRACT_NOTHROW;
+
+ if (LogFileHandle != INVALID_HANDLE_VALUE)
+ {
+ // We must take the lock, as an OS deadlock can occur between
+ // FlushFileBuffers and WriteFile.
+ EnterLogLock();
+ FlushFileBuffers( LogFileHandle );
+ LeaveLogLock();
+ }
+}
+
+VOID ShutdownLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ if (LogFileHandle != INVALID_HANDLE_VALUE) {
+ LogSpew( LF_ALWAYS, FATALERROR, "Logging shutting down\n");
+ CloseHandle( LogFileHandle );
+ }
+ LogFileHandle = INVALID_HANDLE_VALUE;
+ bLoggingInitialized = false;
+}
+
+
+bool LoggingEnabled()
+{
+ STATIC_CONTRACT_LEAF;
+
+ return ((LogFlags & LOG_ENABLE) != 0);
+}
+
+
+bool LoggingOn(DWORD facility, DWORD level) {
+ STATIC_CONTRACT_LEAF;
+
+ _ASSERTE(LogFacilityMask & LF_ALWAYS); // LF_ALWAYS should always be enabled
+
+ return((LogFlags & LOG_ENABLE) &&
+ level <= LogVMLevel &&
+ (facility & LogFacilityMask));
+}
+
+bool Logging2On(DWORD facility2, DWORD level) {
+ STATIC_CONTRACT_LEAF;
+
+ _ASSERTE(LogFacilityMask2 & LF_ALWAYS); // LF_ALWAYS should always be enabled
+
+ return((LogFlags & LOG_ENABLE) &&
+ level <= LogVMLevel &&
+ (facility2 & LogFacilityMask2));
+}
+
+//
+// Don't use me directly, use the macros in log.h
+//
+VOID LogSpewValist(DWORD facility, DWORD level, const char *fmt, va_list args)
+{
+ SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!LoggingOn(facility, level))
+ return;
+
+ DEBUG_ONLY_FUNCTION;
+
+ LogSpewAlwaysValist(fmt, args);
+}
+
+
+VOID LogSpew2Valist(DWORD facility2, DWORD level, const char *fmt, va_list args)
+{
+ SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!Logging2On(facility2, level))
+ return;
+
+ DEBUG_ONLY_FUNCTION;
+
+ LogSpewAlwaysValist(fmt, args);
+}
+
+
+VOID LogSpewAlwaysValist(const char *fmt, va_list args)
+{
+ SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ DEBUG_ONLY_FUNCTION;
+
+ // We can't do heap allocations at all. The current thread may have
+ // suspended another thread, and the suspended thread may be inside of the
+ // heap lock.
+ //
+ // (Some historical comments:)
+ //
+ // We must operate with a very small stack (in case we're logging durring
+ // a stack overflow)
+ //
+ // We're going to bypass our debug memory allocator and just allocate memory from
+ // the process heap. Why? Because our debug memory allocator will log out of memory
+ // conditions. If we're low on memory, and we try to log an out of memory condition, and we try
+ // and allocate memory again using the debug allocator, we could (and probably will) hit
+ // another low memory condition, try to log it, and we spin indefinately until we hit a stack overflow.
+
+ const int BUFFERSIZE = 1000;
+ static char rgchBuffer[BUFFERSIZE];
+
+ EnterLogLock();
+
+ char * pBuffer = &rgchBuffer[0];
+ DWORD buflen = 0;
+ DWORD written;
+
+ static bool needsPrefix = true;
+
+ if (needsPrefix)
+ buflen = sprintf_s(pBuffer, COUNTOF(rgchBuffer), "TID %04x: ", GetCurrentThreadId());
+
+ needsPrefix = (fmt[strlen(fmt)-1] == '\n');
+
+ int cCountWritten = _vsnprintf(&pBuffer[buflen], BUFFERSIZE-buflen, fmt, args );
+ pBuffer[BUFFERSIZE-1] = 0;
+ if (cCountWritten < 0) {
+ buflen = BUFFERSIZE - 1;
+ } else {
+ buflen += cCountWritten;
+ }
+
+ // Its a little late for this, but at least you wont continue
+ // trashing your program...
+ _ASSERTE((buflen < (DWORD) BUFFERSIZE) && "Log text is too long!") ;
+
+#if !PLATFORM_UNIX
+ //convert NL's to CR NL to fixup notepad
+ const int BUFFERSIZE2 = BUFFERSIZE + 500;
+ char rgchBuffer2[BUFFERSIZE2];
+ char * pBuffer2 = &rgchBuffer2[0];
+
+ char *d = pBuffer2;
+ for (char *p = pBuffer; *p != '\0'; p++)
+ {
+ if (*p == '\n') {
+ _ASSERTE(d < pBuffer2 + BUFFERSIZE2);
+ *(d++) = '\r';
+ }
+
+ _ASSERTE(d < pBuffer2 + BUFFERSIZE2);
+ *(d++) = *p;
+ }
+ *d = 0;
+
+ buflen = (DWORD)(d - pBuffer2);
+ pBuffer = pBuffer2;
+#endif // PLATFORM_UNIX
+
+ if (LogFlags & LOG_ENABLE_FILE_LOGGING && LogFileHandle != INVALID_HANDLE_VALUE)
+ {
+ WriteFile(LogFileHandle, pBuffer, buflen, &written, NULL);
+ if (LogFlags & LOG_ENABLE_FLUSH_FILE) {
+ FlushFileBuffers( LogFileHandle );
+ }
+ }
+
+ if (LogFlags & LOG_ENABLE_CONSOLE_LOGGING)
+ {
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pBuffer, buflen, &written, 0);
+ //<TODO>@TODO ...Unnecessary to flush console?</TODO>
+ if (LogFlags & LOG_ENABLE_FLUSH_FILE)
+ FlushFileBuffers( GetStdHandle(STD_OUTPUT_HANDLE) );
+ }
+
+ if (LogFlags & LOG_ENABLE_DEBUGGER_LOGGING)
+ {
+ OutputDebugStringA(pBuffer);
+ }
+
+ LeaveLogLock();
+}
+
+VOID LogSpew(DWORD facility, DWORD level, const char *fmt, ... )
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ ENTER_SO_NOT_MAINLINE_CODE;
+
+#ifdef SELF_NO_HOST
+ if (TRUE)
+#else //!SELF_NO_HOST
+ if (CanThisThreadCallIntoHost())
+#endif //!SELF_NO_HOST
+ {
+ va_list args;
+ va_start( args, fmt );
+ LogSpewValist (facility, level, fmt, args);
+ }
+ else
+ {
+ // Cannot acquire the required lock, as this would call back
+ // into the host. Eat the log message.
+ }
+
+ LEAVE_SO_NOT_MAINLINE_CODE;
+}
+
+VOID LogSpew2(DWORD facility2, DWORD level, const char *fmt, ... )
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ ENTER_SO_NOT_MAINLINE_CODE;
+
+#ifdef SELF_NO_HOST
+ if (TRUE)
+#else //!SELF_NO_HOST
+ if (CanThisThreadCallIntoHost())
+#endif //!SELF_NO_HOST
+ {
+ va_list args;
+ va_start( args, fmt );
+ LogSpew2Valist(facility2, level, fmt, args);
+ }
+ else
+ {
+ // Cannot acquire the required lock, as this would call back
+ // into the host. Eat the log message.
+ }
+
+ LEAVE_SO_NOT_MAINLINE_CODE;
+}
+
+VOID LogSpewAlways (const char *fmt, ... )
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ ENTER_SO_NOT_MAINLINE_CODE;
+
+#ifdef SELF_NO_HOST
+ if (TRUE)
+#else //!SELF_NO_HOST
+ if (CanThisThreadCallIntoHost())
+#endif //!SELF_NO_HOST
+ {
+ va_list args;
+ va_start( args, fmt );
+ LogSpewValist (LF_ALWAYS, LL_ALWAYS, fmt, args);
+ }
+ else
+ {
+ // Cannot acquire the required lock, as this would call back
+ // into the host. Eat the log message.
+ }
+
+ LEAVE_SO_NOT_MAINLINE_CODE;
+}
+
+#endif // LOGGING
+
diff --git a/src/utilcode/makepath.cpp b/src/utilcode/makepath.cpp
new file mode 100644
index 0000000000..402280bd89
--- /dev/null
+++ b/src/utilcode/makepath.cpp
@@ -0,0 +1,208 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+/***
+*makepath.c - create path name from components
+*
+
+*
+*Purpose:
+* To provide support for creation of full path names from components
+*
+*******************************************************************************/
+#include "stdafx.h"
+#include "winwrap.h"
+#include "utilcode.h"
+#include "ex.h"
+
+/***
+*void _makepath() - build path name from components
+*
+*Purpose:
+* create a path name from its individual components
+*
+*Entry:
+* WCHAR *path - pointer to buffer for constructed path
+* WCHAR *drive - pointer to drive component, may or may not contain
+* trailing ':'
+* WCHAR *dir - pointer to subdirectory component, may or may not include
+* leading and/or trailing '/' or '\' characters
+* WCHAR *fname - pointer to file base name component
+* WCHAR *ext - pointer to extension component, may or may not contain
+* a leading '.'.
+*
+*Exit:
+* path - pointer to constructed path name
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+void MakePath (
+ __out_ecount (MAX_PATH) register WCHAR *path,
+ __in LPCWSTR drive,
+ __in LPCWSTR dir,
+ __in LPCWSTR fname,
+ __in LPCWSTR ext
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ register const WCHAR *p;
+ register DWORD count = 0;
+
+ /* we assume that the arguments are in the following form (although we
+ * do not diagnose invalid arguments or illegal filenames (such as
+ * names longer than 8.3 or with illegal characters in them)
+ *
+ * drive:
+ * A ; or
+ * A:
+ * dir:
+ * \top\next\last\ ; or
+ * /top/next/last/ ; or
+ * either of the above forms with either/both the leading
+ * and trailing / or \ removed. Mixed use of '/' and '\' is
+ * also tolerated
+ * fname:
+ * any valid file name
+ * ext:
+ * any valid extension (none if empty or null )
+ */
+
+ /* copy drive */
+
+ if (drive && *drive) {
+ *path++ = *drive;
+ *path++ = _T(':');
+ count += 2;
+ }
+
+ /* copy dir */
+
+ if ((p = dir)) {
+ while (*p) {
+ *path++ = *p++;
+ count++;
+
+ if (count == MAX_PATH) {
+ --path;
+ *path = _T('\0');
+ return;
+ }
+ }
+
+#ifdef _MBCS
+ if (*(p=_mbsdec(dir,p)) != _T('/') && *p != _T('\\')) {
+#else /* _MBCS */
+ // suppress warning for the following line; this is safe but would require significant code
+ // delta for prefast to understand.
+#ifdef _PREFAST_
+ #pragma warning( suppress: 26001 )
+#endif
+ if (*(p-1) != _T('/') && *(p-1) != _T('\\')) {
+#endif /* _MBCS */
+ *path++ = _T('\\');
+ count++;
+
+ if (count == MAX_PATH) {
+ --path;
+ *path = _T('\0');
+ return;
+ }
+ }
+ }
+
+ /* copy fname */
+
+ if ((p = fname)) {
+ while (*p) {
+ *path++ = *p++;
+ count++;
+
+ if (count == MAX_PATH) {
+ --path;
+ *path = _T('\0');
+ return;
+ }
+ }
+ }
+
+ /* copy ext, including 0-terminator - check to see if a '.' needs
+ * to be inserted.
+ */
+
+ if ((p = ext)) {
+ if (*p && *p != _T('.')) {
+ *path++ = _T('.');
+ count++;
+
+ if (count == MAX_PATH) {
+ --path;
+ *path = _T('\0');
+ return;
+ }
+ }
+
+ while ((*path++ = *p++)) {
+ count++;
+
+ if (count == MAX_PATH) {
+ --path;
+ *path = _T('\0');
+ return;
+ }
+ }
+ }
+ else {
+ /* better add the 0-terminator */
+ *path = _T('\0');
+ }
+}
+
+#if !defined(FEATURE_CORECLR)
+static LPCWSTR g_wszProcessExePath = NULL;
+
+HRESULT GetProcessExePath(LPCWSTR *pwszProcessExePath)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CONSISTENCY_CHECK(CheckPointer(pwszProcessExePath));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ if (g_wszProcessExePath == NULL)
+ {
+ NewArrayHolder<WCHAR> wszProcName = new (nothrow) WCHAR[_MAX_PATH];
+ IfNullRet(wszProcName);
+
+ DWORD cchProcName = WszGetModuleFileName(NULL, wszProcName, _MAX_PATH);
+ if (cchProcName == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ if (InterlockedCompareExchangeT(&g_wszProcessExePath, const_cast<LPCWSTR>(wszProcName.GetValue()), NULL) == NULL)
+ {
+ wszProcName.SuppressRelease();
+ }
+ }
+ _ASSERTE(g_wszProcessExePath != NULL);
+ _ASSERTE(SUCCEEDED(hr));
+
+ *pwszProcessExePath = g_wszProcessExePath;
+ return hr;
+}
+#endif
+
diff --git a/src/utilcode/md5.cpp b/src/utilcode/md5.cpp
new file mode 100644
index 0000000000..5b316122ee
--- /dev/null
+++ b/src/utilcode/md5.cpp
@@ -0,0 +1,293 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+// md5.cpp
+//
+
+//
+
+#include "stdafx.h"
+
+#include <stdlib.h>
+#include "md5.h"
+#include "contract.h"
+
+void MD5::Init(BOOL fConstructed)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // These two fields are read only, and so initialization thereof can be
+ // omitted on the second and subsequent hashes using this same instance.
+ //
+ if (!fConstructed)
+ {
+ memset(m_padding, 0, 64);
+ m_padding[0]=0x80;
+ }
+
+ m_cbitHashed = 0;
+ m_cbData = 0;
+ u.m_a = 0x67452301; // magic
+ u.m_b = 0xefcdab89; // ... constants
+ u.m_c = 0x98badcfe; // ... per
+ u.m_d = 0x10325476; // .. RFC1321
+ }
+
+
+void MD5::HashMore(const void* pvInput, ULONG cbInput)
+// Hash the additional data into the state
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ const BYTE* pbInput = (const BYTE*)pvInput;
+
+ m_cbitHashed += (((ULONGLONG)cbInput) <<3);
+
+ ULONG cbRemaining = 64 - m_cbData;
+ if (cbInput < cbRemaining)
+ {
+ // It doesn't fill up the buffer, so just store it
+ memcpy(&m_data[m_cbData], pbInput, cbInput);
+ m_cbData += cbInput;
+ }
+ else
+ {
+ // It does fill up the buffer. Fill up all that it will take
+ memcpy(&m_data[m_cbData], pbInput, cbRemaining);
+
+ // Hash the now-full buffer
+ MD5Transform(m_state, (ULONG*)&m_data[0]);
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:22019) // Suppress this OACR warning 22019:
+ // 'cbInput-=cbRemaining' may be greater than 'cbInput'. This can be caused by integer underflow.
+ // This could yield an incorrect loop index 'cbInput>=64'
+ // We only enter the else clause here if cbInput >= cbRemaining
+#endif
+ cbInput -= cbRemaining;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ pbInput += cbRemaining;
+
+ // Hash the data in 64-byte runs, starting just after what we've copied
+ while (cbInput >= 64)
+ {
+ MD5Transform(m_state, (ULONG*)pbInput);
+ pbInput += 64;
+ cbInput -= 64;
+ }
+
+ // Store the tail of the input into the buffer
+ memcpy(&m_data[0], pbInput, cbInput);
+ m_cbData = cbInput;
+ }
+ }
+
+
+void MD5::GetHashValue(MD5HASHDATA* phash)
+// Finalize the hash by appending the necessary padding and length count. Then
+// return the final hash value.
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ union {
+ ULONGLONG cbitHashed;
+ BYTE rgb[8];
+ }u;
+
+ // Remember how many bits there were in the input data
+ u.cbitHashed = m_cbitHashed;
+
+ // Calculate amount of padding needed. Enough so total byte count hashed is 56 mod 64
+ ULONG cbPad = (m_cbData < 56 ? 56-m_cbData : 120-m_cbData);
+
+ // Hash the padding
+ HashMore(&m_padding[0], cbPad);
+
+ // Hash the (before padding) bit length
+ HashMore(&u.rgb[0], 8);
+
+ // Return the hash value
+ memcpy(phash, &this->u.m_a, 16);
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // ROTATE_LEFT should be a macro that updates its first operand
+ // with its present value rotated left by the amount of its
+ // second operand, which is always a constant.
+ //
+ // One way to portably do it would be
+ //
+ // #define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+ // #define ROTATE_LEFT(x,n) (x) = ROL(x,n)
+ //
+ // but our compiler has an intrinsic!
+
+ #define ROTATE_LEFT(x,n) (x) = _lrotl(x,n)
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Constants used in each of the various rounds
+
+ #define MD5_S11 7
+ #define MD5_S12 12
+ #define MD5_S13 17
+ #define MD5_S14 22
+ #define MD5_S21 5
+ #define MD5_S22 9
+ #define MD5_S23 14
+ #define MD5_S24 20
+ #define MD5_S31 4
+ #define MD5_S32 11
+ #define MD5_S33 16
+ #define MD5_S34 23
+ #define MD5_S41 6
+ #define MD5_S42 10
+ #define MD5_S43 15
+ #define MD5_S44 21
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // The core twiddle functions
+
+// #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) // the function per the standard
+ #define F(x, y, z) ((((z) ^ (y)) & (x)) ^ (z)) // an alternate encoding
+
+// #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) // the function per the standard
+ #define G(x, y, z) ((((x) ^ (y)) & (z)) ^ (y)) // an alternate encoding
+
+ #define H(x, y, z) ((x) ^ (y) ^ (z))
+
+ #define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+ #define AC(ac) ((ULONG)(ac))
+
+ ////////////////////////////////////////////////////////////////
+
+ #define FF(a, b, c, d, x, s, ac) { \
+ (a) += F (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ ////////////////////////////////////////////////////////////////
+
+ #define GG(a, b, c, d, x, s, ac) { \
+ (a) += G (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ ////////////////////////////////////////////////////////////////
+
+ #define HH(a, b, c, d, x, s, ac) { \
+ (a) += H (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ ////////////////////////////////////////////////////////////////
+
+ #define II(a, b, c, d, x, s, ac) { \
+ (a) += I (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ void __stdcall MD5Transform(ULONG state[4], const ULONG* data)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ ULONG a=state[0];
+ ULONG b=state[1];
+ ULONG c=state[2];
+ ULONG d=state[3];
+
+ // Round 1
+ FF (a, b, c, d, data[ 0], MD5_S11, 0xd76aa478); // 1
+ FF (d, a, b, c, data[ 1], MD5_S12, 0xe8c7b756); // 2
+ FF (c, d, a, b, data[ 2], MD5_S13, 0x242070db); // 3
+ FF (b, c, d, a, data[ 3], MD5_S14, 0xc1bdceee); // 4
+ FF (a, b, c, d, data[ 4], MD5_S11, 0xf57c0faf); // 5
+ FF (d, a, b, c, data[ 5], MD5_S12, 0x4787c62a); // 6
+ FF (c, d, a, b, data[ 6], MD5_S13, 0xa8304613); // 7
+ FF (b, c, d, a, data[ 7], MD5_S14, 0xfd469501); // 8
+ FF (a, b, c, d, data[ 8], MD5_S11, 0x698098d8); // 9
+ FF (d, a, b, c, data[ 9], MD5_S12, 0x8b44f7af); // 10
+ FF (c, d, a, b, data[10], MD5_S13, 0xffff5bb1); // 11
+ FF (b, c, d, a, data[11], MD5_S14, 0x895cd7be); // 12
+ FF (a, b, c, d, data[12], MD5_S11, 0x6b901122); // 13
+ FF (d, a, b, c, data[13], MD5_S12, 0xfd987193); // 14
+ FF (c, d, a, b, data[14], MD5_S13, 0xa679438e); // 15
+ FF (b, c, d, a, data[15], MD5_S14, 0x49b40821); // 16
+
+ // Round 2
+ GG (a, b, c, d, data[ 1], MD5_S21, 0xf61e2562); // 17
+ GG (d, a, b, c, data[ 6], MD5_S22, 0xc040b340); // 18
+ GG (c, d, a, b, data[11], MD5_S23, 0x265e5a51); // 19
+ GG (b, c, d, a, data[ 0], MD5_S24, 0xe9b6c7aa); // 20
+ GG (a, b, c, d, data[ 5], MD5_S21, 0xd62f105d); // 21
+ GG (d, a, b, c, data[10], MD5_S22, 0x2441453); // 22
+ GG (c, d, a, b, data[15], MD5_S23, 0xd8a1e681); // 23
+ GG (b, c, d, a, data[ 4], MD5_S24, 0xe7d3fbc8); // 24
+ GG (a, b, c, d, data[ 9], MD5_S21, 0x21e1cde6); // 25
+ GG (d, a, b, c, data[14], MD5_S22, 0xc33707d6); // 26
+ GG (c, d, a, b, data[ 3], MD5_S23, 0xf4d50d87); // 27
+ GG (b, c, d, a, data[ 8], MD5_S24, 0x455a14ed); // 28
+ GG (a, b, c, d, data[13], MD5_S21, 0xa9e3e905); // 29
+ GG (d, a, b, c, data[ 2], MD5_S22, 0xfcefa3f8); // 30
+ GG (c, d, a, b, data[ 7], MD5_S23, 0x676f02d9); // 31
+ GG (b, c, d, a, data[12], MD5_S24, 0x8d2a4c8a); // 32
+
+ // Round 3
+ HH (a, b, c, d, data[ 5], MD5_S31, 0xfffa3942); // 33
+ HH (d, a, b, c, data[ 8], MD5_S32, 0x8771f681); // 34
+ HH (c, d, a, b, data[11], MD5_S33, 0x6d9d6122); // 35
+ HH (b, c, d, a, data[14], MD5_S34, 0xfde5380c); // 36
+ HH (a, b, c, d, data[ 1], MD5_S31, 0xa4beea44); // 37
+ HH (d, a, b, c, data[ 4], MD5_S32, 0x4bdecfa9); // 38
+ HH (c, d, a, b, data[ 7], MD5_S33, 0xf6bb4b60); // 39
+ HH (b, c, d, a, data[10], MD5_S34, 0xbebfbc70); // 40
+ HH (a, b, c, d, data[13], MD5_S31, 0x289b7ec6); // 41
+ HH (d, a, b, c, data[ 0], MD5_S32, 0xeaa127fa); // 42
+ HH (c, d, a, b, data[ 3], MD5_S33, 0xd4ef3085); // 43
+ HH (b, c, d, a, data[ 6], MD5_S34, 0x4881d05); // 44
+ HH (a, b, c, d, data[ 9], MD5_S31, 0xd9d4d039); // 45
+ HH (d, a, b, c, data[12], MD5_S32, 0xe6db99e5); // 46
+ HH (c, d, a, b, data[15], MD5_S33, 0x1fa27cf8); // 47
+ HH (b, c, d, a, data[ 2], MD5_S34, 0xc4ac5665); // 48
+
+ // Round 4
+ II (a, b, c, d, data[ 0], MD5_S41, 0xf4292244); // 49
+ II (d, a, b, c, data[ 7], MD5_S42, 0x432aff97); // 50
+ II (c, d, a, b, data[14], MD5_S43, 0xab9423a7); // 51
+ II (b, c, d, a, data[ 5], MD5_S44, 0xfc93a039); // 52
+ II (a, b, c, d, data[12], MD5_S41, 0x655b59c3); // 53
+ II (d, a, b, c, data[ 3], MD5_S42, 0x8f0ccc92); // 54
+ II (c, d, a, b, data[10], MD5_S43, 0xffeff47d); // 55
+ II (b, c, d, a, data[ 1], MD5_S44, 0x85845dd1); // 56
+ II (a, b, c, d, data[ 8], MD5_S41, 0x6fa87e4f); // 57
+ II (d, a, b, c, data[15], MD5_S42, 0xfe2ce6e0); // 58
+ II (c, d, a, b, data[ 6], MD5_S43, 0xa3014314); // 59
+ II (b, c, d, a, data[13], MD5_S44, 0x4e0811a1); // 60
+ II (a, b, c, d, data[ 4], MD5_S41, 0xf7537e82); // 61
+ II (d, a, b, c, data[11], MD5_S42, 0xbd3af235); // 62
+ II (c, d, a, b, data[ 2], MD5_S43, 0x2ad7d2bb); // 63
+ II (b, c, d, a, data[ 9], MD5_S44, 0xeb86d391); // 64
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ } \ No newline at end of file
diff --git a/src/utilcode/memorypool.cpp b/src/utilcode/memorypool.cpp
new file mode 100644
index 0000000000..5330c4b3dc
--- /dev/null
+++ b/src/utilcode/memorypool.cpp
@@ -0,0 +1,318 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h"
+
+#include "memorypool.h"
+#include "ex.h"
+
+size_t MemoryPool::GetSize()
+{
+ LIMITED_METHOD_CONTRACT;
+ size_t retval=0;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ retval+=(BYTE*)block->elementsEnd-(BYTE*)block->elements;
+ block = block->next;
+ }
+ return retval;
+}
+
+#ifndef DACCESS_COMPILE
+
+BOOL MemoryPool::AddBlock(SIZE_T elementCount)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return FALSE;);
+ } CONTRACTL_END;
+
+ //
+ // Check for arithmetic overflow
+ //
+ S_SIZE_T cbBlockSize = S_SIZE_T(elementCount) * S_SIZE_T(m_elementSize);
+ S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(Block)) + cbBlockSize;
+ if (cbBlockSize.IsOverflow() || cbAllocSize.IsOverflow())
+ return FALSE;
+
+ //
+ // Allocate the new block.
+ //
+
+ Block *block = (Block *) new (nothrow) BYTE [cbAllocSize.Value()];
+
+ if (block == NULL)
+ return FALSE;
+
+ //
+ // Chain all elements together for the free list
+ //
+
+ _ASSERTE(m_freeList == NULL);
+ Element **prev = &m_freeList;
+
+ Element *e = block->elements;
+ Element *eEnd = (Element *) ((BYTE*) block->elements + elementCount*m_elementSize);
+ while (e < eEnd)
+ {
+ *prev = e;
+ prev = &e->next;
+#if _DEBUG
+ DeadBeef(e);
+#endif
+ e = (Element*) ((BYTE*)e + m_elementSize);
+ }
+
+ *prev = NULL;
+
+ //
+ // Initialize the other block fields & link the block into the block list
+ //
+
+ block->elementsEnd = e;
+ block->next = m_blocks;
+ m_blocks = block;
+
+ return TRUE;
+}
+
+void MemoryPool::DeadBeef(Element *element)
+{
+#if _DEBUG
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ int *i = &element->deadBeef;
+ int *iEnd = (int*) ((BYTE*)element + m_elementSize);
+ while (i < iEnd)
+ *i++ = (int) 0xdeadbeef;
+#else
+ LIMITED_METHOD_CONTRACT;
+#endif
+}
+
+MemoryPool::MemoryPool(SIZE_T elementSize, SIZE_T initGrowth, SIZE_T initCount)
+ : m_elementSize(elementSize),
+ m_growCount(initGrowth),
+ m_blocks(NULL),
+ m_freeList(NULL)
+{
+ CONTRACTL {
+ if (initCount) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(elementSize >= sizeof(Element));
+ _ASSERTE((elementSize & ((sizeof(PVOID)-1))) == 0);
+
+ if (initCount > 0)
+ AddBlock(initCount);
+}
+
+MemoryPool::~MemoryPool()
+{
+ LIMITED_METHOD_CONTRACT;
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ Block *next = block->next;
+ delete [] (BYTE*) block;
+ block = next;
+ }
+}
+
+BOOL MemoryPool::IsElement(void *element)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ if (element >= block->elements &&
+ element < block->elementsEnd )
+ {
+ return ((BYTE *)element - (BYTE*)block->elements) % m_elementSize == 0;
+ }
+ block = block->next;
+ }
+
+ return FALSE;
+}
+
+BOOL MemoryPool::IsAllocatedElement(void *element)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ if (!IsElement(element))
+ return FALSE;
+
+ //
+ // Now, make sure the element isn't
+ // in the free list.
+ //
+
+#if _DEBUG
+ //
+ // In a debug build, all objects on the free list
+ // will be marked with deadbeef. This means that
+ // if the object is not deadbeef, it's not on the
+ // free list.
+ //
+ // This check will give us decent performance in
+ // a debug build for FreeElement, since we
+ // always expect to return TRUE in that case.
+ //
+
+ if (((Element*)element)->deadBeef != (int) 0xdeadBeef)
+ return TRUE;
+#endif
+
+ Element *f = m_freeList;
+ while (f != NULL)
+ {
+ if (f == element)
+ return FALSE;
+ f = f->next;
+ }
+
+#if _DEBUG
+ //
+ // We should never get here in a debug build, because
+ // all free elements should be deadbeefed.
+ //
+ _ASSERTE(!"Unreachable");
+#endif
+
+ return TRUE;
+}
+
+void *MemoryPool::AllocateElement()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ void *element = AllocateElementNoThrow();
+ if (element == NULL)
+ ThrowOutOfMemory();
+
+ return element;
+}
+
+void *MemoryPool::AllocateElementNoThrow()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT( return FALSE; );
+ } CONTRACTL_END;
+
+ void *element = m_freeList;
+
+ if (element == NULL)
+ {
+ if (!AddBlock(m_growCount))
+ return NULL;
+
+ m_growCount *= 2;
+ element = m_freeList;
+ }
+
+ // if we come there means that addblock succeeded and m_freelist isn't null anymore
+ PREFIX_ASSUME(m_freeList!= NULL);
+ m_freeList = m_freeList->next;
+
+ return element;
+}
+
+void MemoryPool::FreeElement(void *element)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+#if _DEBUG // don't want to do this assert in a non-debug build; it is expensive
+ _ASSERTE(IsAllocatedElement(element));
+#endif
+
+ Element *e = (Element *) element;
+
+#if _DEBUG
+ DeadBeef(e);
+#endif
+
+ e->next = m_freeList;
+ m_freeList = e;
+}
+
+void MemoryPool::FreeAllElements()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ Block *next = block->next;
+ delete [] block;
+ block = next;
+ }
+
+ m_freeList = NULL;
+ m_blocks = NULL;
+}
+
+
+
+MemoryPool::Iterator::Iterator(MemoryPool *pool)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ //
+ // Warning! This only works if you haven't freed
+ // any elements.
+ //
+
+ m_next = pool->m_blocks;
+ m_e = NULL;
+ m_eEnd = NULL;
+ m_end = (BYTE*) pool->m_freeList;
+ m_size = pool->m_elementSize;
+}
+
+BOOL MemoryPool::Iterator::Next()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_e == m_eEnd
+ || (m_e == m_end && m_end != NULL))
+ {
+ if (m_next == NULL)
+ return FALSE;
+ m_e = (BYTE*) m_next->elements;
+ m_eEnd = (BYTE*) m_next->elementsEnd;
+ m_next = m_next->next;
+ if (m_e == m_end)
+ return FALSE;
+ }
+
+ m_e += m_size;
+
+ return TRUE;
+}
+
+#endif
diff --git a/src/utilcode/namespaceutil.cpp b/src/utilcode/namespaceutil.cpp
new file mode 100644
index 0000000000..ac09443760
--- /dev/null
+++ b/src/utilcode/namespaceutil.cpp
@@ -0,0 +1,680 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// NamespaceUtil.cpp
+//
+
+//
+// Helpers for converting namespace separators.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "corhdr.h"
+#include "corhlpr.h"
+#include "sstring.h"
+#include "utilcode.h"
+
+#ifndef _ASSERTE
+#define _ASSERTE(foo)
+#endif
+
+#include "nsutilpriv.h"
+
+
+//*****************************************************************************
+// Determine how many chars large a fully qualified name would be given the
+// two parts of the name. The return value includes room for every character
+// in both names, as well as room for the separator and a final terminator.
+//*****************************************************************************
+int ns::GetFullLength( // Number of chars in full name.
+ const WCHAR *szNameSpace, // Namspace for value.
+ const WCHAR *szName) // Name of value.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ int iLen = 1; // Null terminator.
+ if (szNameSpace)
+ iLen += (int)wcslen(szNameSpace);
+ if (szName)
+ iLen += (int)wcslen(szName);
+ if (szNameSpace && *szNameSpace && szName && *szName)
+ ++iLen;
+ return iLen;
+} //int ns::GetFullLength()
+
+int ns::GetFullLength( // Number of chars in full name.
+ LPCUTF8 szNameSpace, // Namspace for value.
+ LPCUTF8 szName) // Name of value.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ int iLen = 1;
+ if (szNameSpace)
+ iLen += (int)strlen(szNameSpace);
+ if (szName)
+ iLen += (int)strlen(szName);
+ if (szNameSpace && *szNameSpace && szName && *szName)
+ ++iLen;
+ return iLen;
+} //int ns::GetFullLength()
+
+
+//*****************************************************************************
+// Scan the string from the rear looking for the first valid separator. If
+// found, return a pointer to it. Else return null. This code is smart enough
+// to skip over special sequences, such as:
+// a.b..ctor
+// ^
+// |
+// The ".ctor" is considered one token.
+//*****************************************************************************
+WCHAR *ns::FindSep( // Pointer to separator or null.
+ const WCHAR *szPath) // The path to look in.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ _ASSERTE(szPath);
+ WCHAR *ptr = (WCHAR*)wcsrchr(szPath, NAMESPACE_SEPARATOR_WCHAR);
+ if((ptr == NULL) || (ptr == szPath)) return NULL;
+ if(*(ptr - 1) == NAMESPACE_SEPARATOR_WCHAR) // here ptr is at least szPath+1
+ --ptr;
+ return ptr;
+} //WCHAR *ns::FindSep()
+
+//<TODO>@todo: this isn't dbcs safe if this were ansi, but this is utf8. Still an issue?</TODO>
+LPUTF8 ns::FindSep( // Pointer to separator or null.
+ LPCUTF8 szPath) // The path to look in.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ _ASSERTE(szPath);
+ LPUTF8 ptr = const_cast<LPUTF8>(strrchr(szPath, NAMESPACE_SEPARATOR_CHAR));
+ if((ptr == NULL) || (ptr == szPath)) return NULL;
+ if(*(ptr - 1) == NAMESPACE_SEPARATOR_CHAR) // here ptr is at least szPath+1
+ --ptr;
+ return ptr;
+} //LPUTF8 ns::FindSep()
+
+
+
+//*****************************************************************************
+// Take a path and find the last separator (nsFindSep), and then replace the
+// separator with a '\0' and return a pointer to the name. So for example:
+// a.b.c
+// becomes two strings "a.b" and "c" and the return value points to "c".
+//*****************************************************************************
+WCHAR *ns::SplitInline( // Pointer to name portion.
+ __inout __inout_z WCHAR *szPath) // The path to split.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ WCHAR *ptr = ns::FindSep(szPath);
+ if (ptr)
+ {
+ *ptr = 0;
+ ++ptr;
+ }
+ return ptr;
+} // WCHAR *ns::SplitInline()
+
+LPUTF8 ns::SplitInline( // Pointer to name portion.
+ __inout __inout_z LPUTF8 szPath) // The path to split.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPUTF8 ptr = ns::FindSep(szPath);
+ if (ptr)
+ {
+ *ptr = 0;
+ ++ptr;
+ }
+ return ptr;
+} // LPUTF8 ns::SplitInline()
+
+void ns::SplitInline(
+ __inout __inout_z LPWSTR szPath, // Path to split.
+ LPCWSTR &szNameSpace, // Return pointer to namespace.
+ LPCWSTR &szName) // Return pointer to name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ WCHAR *ptr = SplitInline(szPath);
+ if (ptr)
+ {
+ szNameSpace = szPath;
+ szName = ptr;
+ }
+ else
+ {
+ szNameSpace = 0;
+ szName = szPath;
+ }
+} // void ns::SplitInline()
+
+void ns::SplitInline(
+ __inout __inout_z LPUTF8 szPath, // Path to split.
+ LPCUTF8 &szNameSpace, // Return pointer to namespace.
+ LPCUTF8 &szName) // Return pointer to name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPUTF8 ptr = SplitInline(szPath);
+ if (ptr)
+ {
+ szNameSpace = szPath;
+ szName = ptr;
+ }
+ else
+ {
+ szNameSpace = 0;
+ szName = szPath;
+ }
+} // void ns::SplitInline()
+
+
+//*****************************************************************************
+// Split the last parsable element from the end of the string as the name,
+// the first part as the namespace.
+//*****************************************************************************
+int ns::SplitPath( // true ok, false trunction.
+ const WCHAR *szPath, // Path to split.
+ __out_ecount(cchNameSpace) WCHAR *szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ __out_ecount(cchName) WCHAR *szName, // Output for name.
+ int cchName) // Max chars for output.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ const WCHAR *ptr = ns::FindSep(szPath);
+ size_t iLen = (ptr) ? ptr - szPath : 0;
+ size_t iCopyMax;
+ int brtn = true;
+ if (szNameSpace && cchNameSpace)
+ {
+ _ASSERTE(cchNameSpace > 1);
+ iCopyMax = cchNameSpace - 1;
+ iCopyMax = min(iCopyMax, iLen);
+ wcsncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax);
+ szNameSpace[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchNameSpace)
+ brtn = false;
+ }
+
+ if (szName && cchName)
+ {
+ _ASSERTE(cchName > 1);
+ iCopyMax = cchName - 1;
+ if (ptr)
+ ++ptr;
+ else
+ ptr = szPath;
+ iLen = (int)wcslen(ptr);
+ iCopyMax = min(iCopyMax, iLen);
+ wcsncpy_s(szName, cchName, ptr, iCopyMax);
+ szName[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchName)
+ brtn = false;
+ }
+ return brtn;
+} // int ns::SplitPath()
+
+
+int ns::SplitPath( // true ok, false trunction.
+ LPCUTF8 szPath, // Path to split.
+ __out_ecount_opt (cchNameSpace) LPUTF8 szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ __out_ecount_opt (cchName) LPUTF8 szName, // Output for name.
+ int cchName) // Max chars for output.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPCUTF8 ptr = ns::FindSep(szPath);
+ size_t iLen = (ptr) ? ptr - szPath : 0;
+ size_t iCopyMax;
+ int brtn = true;
+ if (szNameSpace && cchNameSpace)
+ {
+ _ASSERTE(cchNameSpace > 1);
+ iCopyMax = cchNameSpace-1;
+ iCopyMax = min(iCopyMax, iLen);
+ strncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax);
+ szNameSpace[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchNameSpace)
+ brtn = false;
+ }
+
+ if (szName && cchName)
+ {
+ _ASSERTE(cchName > 1);
+ iCopyMax = cchName-1;
+ if (ptr)
+ ++ptr;
+ else
+ ptr = szPath;
+ iLen = (int)strlen(ptr);
+ iCopyMax = min(iCopyMax, iLen);
+ strncpy_s(szName, cchName, ptr, iCopyMax);
+ szName[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchName)
+ brtn = false;
+ }
+ return brtn;
+} // int ns::SplitPath()
+
+
+//*****************************************************************************
+// Take two values and put them together in a fully qualified path using the
+// correct separator.
+//*****************************************************************************
+int ns::MakePath( // true ok, false truncation.
+ __out_ecount(cchChars) WCHAR *szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ const WCHAR *szNameSpace, // Namespace.
+ const WCHAR *szName) // Name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ if (szOut)
+ *szOut = 0;
+ else
+ return false;
+
+ if (szNameSpace && *szNameSpace != W('\0'))
+ {
+ if (wcsncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE)
+ return false;
+
+ // Add namespace separator if a non-empty name was supplied
+ if (szName && *szName != W('\0'))
+ {
+ if (wcsncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_WSTR, _TRUNCATE) == STRUNCATE)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (szName && *szName)
+ {
+ if (wcsncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE)
+ return false;
+ }
+
+ return true;
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false truncation.
+ __out_ecount(cchChars) LPUTF8 szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szNameSpace, // Namespace.
+ LPCUTF8 szName) // Name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ if (szOut)
+ *szOut = 0;
+ else
+ return false;
+
+ if (szNameSpace && *szNameSpace != W('\0'))
+ {
+ if (strncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE)
+ return false;
+
+ // Add namespace separator if a non-empty name was supplied
+ if (szName && *szName != W('\0'))
+ {
+ if (strncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_STR, _TRUNCATE) == STRUNCATE)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (szName && *szName)
+ {
+ if (strncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE)
+ return false;
+ }
+
+ return true;
+
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false truncation.
+ __out_ecount(cchChars) WCHAR *szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szNamespace, // Namespace.
+ LPCUTF8 szName) // Name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ if (szOut)
+ *szOut = 0;
+ else
+ return false;
+
+ if (szNamespace != NULL && *szNamespace != '\0')
+ {
+ if (cchChars < 2)
+ return false;
+
+ int count;
+
+ // We use cBuffer - 2 to account for the '.' and at least a 1 character name below.
+ count = WszMultiByteToWideChar(CP_UTF8, 0, szNamespace, -1, szOut, cchChars-2);
+ if (count == 0)
+ return false; // Supply a bigger buffer!
+
+ // buffer access is bounded: WszMultiByteToWideChar returns 0 if access doesn't fit in range
+#ifdef _PREFAST_
+ #pragma warning( suppress: 26015 )
+#endif
+ szOut[count-1] = NAMESPACE_SEPARATOR_WCHAR;
+ szOut += count;
+ cchChars -= count;
+ }
+
+ if (((cchChars == 0) && (szName != NULL) && (*szName != '\0')) ||
+ (WszMultiByteToWideChar(CP_UTF8, 0, szName, -1, szOut, cchChars) == 0))
+ return false; // supply a bigger buffer!
+ return true;
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ LPCUTF8 szNameSpace, // Namespace for name.
+ LPCUTF8 szName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iLen = 2;
+ if (szNameSpace)
+ iLen += (int)strlen(szNameSpace);
+ if (szName)
+ iLen += (int)strlen(szName);
+ LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen);
+ if (!szOut)
+ return false;
+ return ns::MakePath(szOut, iLen, szNameSpace, szName);
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false out of memory
+ CQuickArray<WCHAR> &qa, // Where to put results.
+ LPCUTF8 szNameSpace, // Namespace for name.
+ LPCUTF8 szName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iLen = 2;
+ if (szNameSpace)
+ iLen += (int)strlen(szNameSpace);
+ if (szName)
+ iLen += (int)strlen(szName);
+ WCHAR *szOut = (WCHAR *) qa.AllocNoThrow(iLen);
+ if (!szOut)
+ return false;
+ return ns::MakePath(szOut, iLen, szNameSpace, szName);
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ const WCHAR *szNameSpace, // Namespace for name.
+ const WCHAR *szName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iLen = 2;
+ if (szNameSpace)
+ iLen += (int)wcslen(szNameSpace);
+ if (szName)
+ iLen += (int)wcslen(szName);
+ WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR));
+ if (!szOut)
+ return false;
+ return ns::MakePath(szOut, iLen, szNameSpace, szName);
+} // int ns::MakePath()
+
+void ns::MakePath( // throws on out of memory
+ SString &ssBuf, // Where to put results.
+ const SString &ssNameSpace, // Namespace for name.
+ const SString &ssName) // Final part of name.
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ ssBuf.Clear();
+
+ if (!ssNameSpace.IsEmpty())
+ {
+ if (ssName.IsEmpty())
+ {
+ ssBuf.Set(ssNameSpace);
+ }
+ else
+ {
+ SString s(SString::Literal, NAMESPACE_SEPARATOR_WSTR);
+ ssBuf.Set(ssNameSpace, s);
+ }
+ }
+
+ if (!ssName.IsEmpty())
+ {
+ ssBuf.Append(ssName);
+ }
+}
+
+bool ns::MakeAssemblyQualifiedName( // true ok, false truncation
+ __out_ecount(dwBuffer) WCHAR* pBuffer, // Buffer to recieve the results
+ int dwBuffer, // Number of characters total in buffer
+ const WCHAR *szTypeName, // Namespace for name.
+ int dwTypeName, // Number of characters (not including null)
+ const WCHAR *szAssemblyName, // Final part of name.
+ int dwAssemblyName) // Number of characters (not including null)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (dwBuffer < 2)
+ return false;
+
+ int iCopyMax = 0;
+ _ASSERTE(pBuffer);
+ *pBuffer = NULL;
+
+ if (szTypeName && *szTypeName != W('\0'))
+ {
+ _ASSERTE(dwTypeName > 0);
+ iCopyMax = min(dwBuffer-1, dwTypeName);
+ wcsncpy_s(pBuffer, dwBuffer, szTypeName, iCopyMax);
+ dwBuffer -= iCopyMax;
+ }
+
+ if (szAssemblyName && *szAssemblyName != W('\0'))
+ {
+
+ if(dwBuffer < ASSEMBLY_SEPARATOR_LEN)
+ return false;
+
+ for(DWORD i = 0; i < ASSEMBLY_SEPARATOR_LEN; i++)
+ pBuffer[iCopyMax+i] = ASSEMBLY_SEPARATOR_WSTR[i];
+
+ dwBuffer -= ASSEMBLY_SEPARATOR_LEN;
+ if(dwBuffer == 0)
+ return false;
+
+ int iCur = iCopyMax + ASSEMBLY_SEPARATOR_LEN;
+ _ASSERTE(dwAssemblyName > 0);
+ iCopyMax = min(dwBuffer-1, dwAssemblyName);
+ wcsncpy_s(pBuffer + iCur, dwBuffer, szAssemblyName, iCopyMax);
+ pBuffer[iCur + iCopyMax] = W('\0');
+
+ if (iCopyMax < dwAssemblyName)
+ return false;
+ }
+ else {
+ if(dwBuffer == 0) {
+ PREFIX_ASSUME(iCopyMax > 0);
+ pBuffer[iCopyMax-1] = W('\0');
+ return false;
+ }
+ else
+ pBuffer[iCopyMax] = W('\0');
+ }
+
+ return true;
+} // int ns::MakePath()
+
+bool ns::MakeAssemblyQualifiedName( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ const WCHAR *szTypeName, // Namespace for name.
+ const WCHAR *szAssemblyName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iTypeName = 0;
+ int iAssemblyName = 0;
+ if (szTypeName)
+ iTypeName = (int)wcslen(szTypeName);
+ if (szAssemblyName)
+ iAssemblyName = (int)wcslen(szAssemblyName);
+
+ int iLen = ASSEMBLY_SEPARATOR_LEN + iTypeName + iAssemblyName + 1; // Space for null terminator
+ WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR));
+ if (!szOut)
+ return false;
+
+ bool ret;
+ ret = ns::MakeAssemblyQualifiedName(szOut, iLen, szTypeName, iTypeName, szAssemblyName, iAssemblyName);
+ _ASSERTE(ret);
+ return true;
+}
+
+int ns::MakeNestedTypeName( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ LPCUTF8 szEnclosingName, // Full name for enclosing type
+ LPCUTF8 szNestedName) // Full name for nested type
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ _ASSERTE(szEnclosingName && szNestedName);
+ int iLen = 2;
+ iLen += (int)strlen(szEnclosingName);
+ iLen += (int)strlen(szNestedName);
+ LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen);
+ if (!szOut)
+ return false;
+ return ns::MakeNestedTypeName(szOut, iLen, szEnclosingName, szNestedName);
+} // int ns::MakeNestedTypeName()
+
+int ns::MakeNestedTypeName( // true ok, false truncation.
+ __out_ecount (cchChars) LPUTF8 szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szEnclosingName, // Full name for enclosing type
+ LPCUTF8 szNestedName) // Full name for nested type
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ int iCopyMax = 0, iLen;
+ int brtn = true;
+ *szOut = 0;
+
+ iLen = (int)strlen(szEnclosingName);
+ iCopyMax = min(cchChars-1, iLen);
+ strncpy_s(szOut, cchChars, szEnclosingName, iCopyMax);
+
+ if (iLen >= cchChars)
+ brtn = false;
+
+ szOut[iCopyMax] = NESTED_SEPARATOR_CHAR;
+ int iCur = iCopyMax+1; // iCopyMax characters + nested_separator_char
+ cchChars -= iCur;
+ if(cchChars == 0)
+ return false;
+
+ iLen = (int)strlen(szNestedName);
+ iCopyMax = min(cchChars-1, iLen);
+ strncpy_s(&szOut[iCur], cchChars, szNestedName, iCopyMax);
+ szOut[iCur + iCopyMax] = 0;
+
+ if (iLen >= cchChars)
+ brtn = false;
+
+ return brtn;
+} // int ns::MakeNestedTypeName()
+
+void ns::MakeNestedTypeName( // throws on out of memory
+ SString &ssBuf, // output path for name.
+ const SString &ssEnclosingName, // Full name for enclosing type
+ const SString &ssNestedName) // Full name for nested type
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ ssBuf.Clear();
+
+ ssBuf.Append(ssEnclosingName);
+ ssBuf.Append(NESTED_SEPARATOR_WCHAR);
+ ssBuf.Append(ssNestedName);
+}
+
diff --git a/src/utilcode/newapis.cpp b/src/utilcode/newapis.cpp
new file mode 100644
index 0000000000..8d2af7bbc5
--- /dev/null
+++ b/src/utilcode/newapis.cpp
@@ -0,0 +1,1429 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+////////////////////////////////////////////////////////////////////////////
+//
+// File: newapis.cpp
+//
+
+
+// Purpose: functions that need to be emulated on downlevel platforms.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "newapis.h"
+#ifdef ENABLE_DOWNLEVEL_FOR_NLS
+#include "downlevel.h"
+#endif
+
+#include "utilcode.h"
+#include "sortversioning.h"
+
+namespace NewApis
+{
+
+
+#if defined(ENABLE_DOWNLEVEL_FOR_NLS)
+
+ FARPROC GetProcAddressForLocaleApi(__in LPCSTR lpProcName, __in_opt FARPROC pFnDownlevelFallback)
+ {
+ _ASSERTE(lpProcName != NULL);
+
+ FARPROC result = NULL;
+
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+
+ // First try to use the function defined in the culture dll
+ // if we are running on a platform prior to Win7
+ if(!IsWindows7Platform())
+ {
+ // only need to load the culture dll's handle once then we can hold onto it
+ static HMODULE hCulture = NULL;
+ // if we haven't loaded the culture dll yet
+ if (hCulture == NULL)
+ {
+ UtilCode::LoadLibraryShim(MAKEDLLNAME_W(W("culture")), NULL, 0, &hCulture);
+ }
+
+ // make sure we were successful before using the handle
+ if (hCulture != NULL)
+ {
+ result=GetProcAddress(hCulture,lpProcName);
+ }
+ }
+#endif // !FEATURE_CORECLR && !CROSSGEN_COMPILE
+
+ // next try the kernel
+ if(result==NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod!=NULL)
+ {
+ result=GetProcAddress(hMod,lpProcName);
+ }
+ }
+
+ // failing all that, use the fallback provided
+ if(result==NULL)
+ {
+ result = pFnDownlevelFallback;
+ }
+
+ return result;
+ }
+
+#endif // ENABLE_DOWNLEVEL_FOR_NLS
+
+ __success(return > 0) int
+ GetSystemDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName)
+ {
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::GetSystemDefaultLocaleName(lpLocaleName, cchLocaleName);
+#else
+ typedef int (WINAPI *PFNGetSystemDefaultLocaleName)(LPWSTR, int);
+ static PFNGetSystemDefaultLocaleName pFNGetSystemDefaultLocaleName=NULL;
+ if (pFNGetSystemDefaultLocaleName == NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ // TODO: NLS Arrowhead - We should always fallback to the Downlevel APIs if the kernel APIs aren't found,
+ // regardless of error reason
+ if(hMod==NULL)
+ return 0;
+ pFNGetSystemDefaultLocaleName=(PFNGetSystemDefaultLocaleName)GetProcAddress(hMod,"GetSystemDefaultLocaleName");
+ if(pFNGetSystemDefaultLocaleName==NULL)
+ {
+ if(GetLastError() == ERROR_PROC_NOT_FOUND)
+ pFNGetSystemDefaultLocaleName=DownLevel::GetSystemDefaultLocaleName;
+ else
+ return 0;
+ }
+ }
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return pFNGetSystemDefaultLocaleName(lpLocaleName,cchLocaleName);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+#endif
+ };
+
+ __success(return == TRUE) BOOL
+ GetUserPreferredUILanguages (__in DWORD dwFlags, __out PULONG pulNumLanguages, __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer, __in PULONG pcchLanguagesBuffer)
+ {
+#ifdef ENABLE_DOWNLEVEL_FOR_NLS
+ typedef DWORD (WINAPI *PFNGetUserPreferredUILanguages)(ULONG, PULONG, LPWSTR, PULONG);
+ static PFNGetUserPreferredUILanguages pFNGetUserPreferredUILanguages=NULL;
+ if (pFNGetUserPreferredUILanguages == NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod==NULL)
+ return FALSE;
+ pFNGetUserPreferredUILanguages=(PFNGetUserPreferredUILanguages)GetProcAddress(hMod,"GetUserPreferredUILanguages");
+ if(pFNGetUserPreferredUILanguages==NULL)
+ {
+ if(GetLastError() == ERROR_PROC_NOT_FOUND)
+ pFNGetUserPreferredUILanguages=DownLevel::GetUserPreferredUILanguages;
+ else
+ return FALSE;
+ }
+ }
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ BOOL res = pFNGetUserPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
+ if(res == TRUE)
+ return res;
+
+ //fallback to thread preferred langs
+ return GetThreadPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+#else
+ return ::GetUserPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
+#endif
+ };
+
+
+
+ __success(return != 0) int
+ GetUserDefaultLocaleName(__out_ecount(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName)
+ {
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::GetUserDefaultLocaleName(lpLocaleName, cchLocaleName);
+#else
+ typedef int (WINAPI *PFNGetUserDefaultLocaleName)(LPWSTR, int);
+ static PFNGetUserDefaultLocaleName pFNGetUserDefaultLocaleName=NULL;
+ if (pFNGetUserDefaultLocaleName == NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod==NULL)
+ return 0;
+ pFNGetUserDefaultLocaleName=(PFNGetUserDefaultLocaleName)GetProcAddress(hMod,"GetUserDefaultLocaleName");
+ if(pFNGetUserDefaultLocaleName==NULL)
+ {
+ if(GetLastError() == ERROR_PROC_NOT_FOUND)
+ pFNGetUserDefaultLocaleName=DownLevel::GetUserDefaultLocaleName;
+ else
+ return 0;
+ }
+ }
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return pFNGetUserDefaultLocaleName(lpLocaleName,cchLocaleName);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+#endif
+ }
+
+ // Call GetLocaleInfoEx and see if the OS knows about it.
+ // Note that GetLocaleInfoEx has variations:
+ // * Pre-Vista it fails and has to go downlevel
+ // * Vista succeeds, but not for neutrals
+ // * Win7 succeeds for all locales.
+ // * Mac does ???
+ //
+ // The caller is expected to call with a specific locale (non-neutral) on Windows < windows 7,
+ // except for LOCALE_INEUTRAL and LOCALE_SPARENT, which downlevel.cpp handles
+ //
+ __success(return != 0) int
+ GetLocaleInfoEx (__in LPCWSTR lpLocaleName, __in LCTYPE LCType, __out_ecount_opt(cchData) LPWSTR lpLCData, __in int cchData)
+ {
+ _ASSERTE((lpLCData == NULL && cchData == 0) || (lpLCData != NULL && cchData > 0));
+ // ComNlsInfo::nativeInitCultureData calls GetLocaleInfoEx with LcType LOCALE_SNAME
+ // to determine if this is a valid culture. We shouldn't assert in this case, but
+ // all others we should.
+ _ASSERTE(LCType == LOCALE_SNAME || NotLeakingFrameworkOnlyCultures(lpLocaleName));
+ int retVal;
+
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::GetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData);
+#else
+ typedef int (WINAPI *PFNGetLocaleInfoEx)(LPCWSTR, LCTYPE, LPWSTR, int);
+ static PFNGetLocaleInfoEx pFNGetLocaleInfoEx=NULL;
+ if (pFNGetLocaleInfoEx== NULL)
+ {
+ pFNGetLocaleInfoEx=(PFNGetLocaleInfoEx)GetProcAddressForLocaleApi(
+ "GetLocaleInfoEx",
+ (FARPROC)DownLevel::GetLocaleInfoEx);
+ }
+ retVal = pFNGetLocaleInfoEx(lpLocaleName,LCType,lpLCData,cchData);
+
+ // Do fallback if we didn't find anything yet
+ if (retVal == 0)
+ retVal = DownLevel::UplevelFallback::GetLocaleInfoEx(lpLocaleName,0,LCType,lpLCData,cchData);
+#endif
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return retVal;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+ }
+
+ __success(return != 0) int
+ GetDateFormatEx(__in LPCWSTR lpLocaleName, __in DWORD dwFlags, __in_opt CONST SYSTEMTIME* lpDate, __in_opt LPCWSTR lpFormat,
+ __out_ecount(cchDate) LPWSTR lpDateStr, __in int cchDate, __in_opt LPCWSTR lpCalendar)
+ {
+ _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName));
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::GetDateFormatEx(lpLocaleName, dwFlags, lpDate, lpFormat, lpDateStr, cchDate, lpCalendar);
+#else
+ typedef int (WINAPI *PFNGetDateFormatEx)(LPCWSTR, DWORD, CONST SYSTEMTIME*, LPCWSTR, LPWSTR,int, LPCWSTR);
+ static PFNGetDateFormatEx pFNGetDateFormatEx=NULL;
+ if (pFNGetDateFormatEx== NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod==NULL)
+ return 0;
+ pFNGetDateFormatEx=(PFNGetDateFormatEx)GetProcAddress(hMod,"GetDateFormatEx");
+ if(pFNGetDateFormatEx==NULL)
+ {
+ if(GetLastError() == ERROR_PROC_NOT_FOUND)
+ pFNGetDateFormatEx=DownLevel::GetDateFormatEx;
+ else
+ return 0;
+ }
+ }
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return pFNGetDateFormatEx(lpLocaleName,dwFlags,lpDate,lpFormat,lpDateStr,cchDate,lpCalendar);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+#endif
+ }
+
+#if defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ static BOOL AvoidVistaTurkishBug = FALSE;
+
+ __success(return != NULL)
+ inline
+ FARPROC GetSystemProcAddressForSortingApi(
+ __in LPCSTR lpProcName,
+ __in_opt FARPROC pFnDownlevelFallback)
+ {
+ _ASSERTE(lpProcName != NULL);
+
+ FARPROC result = NULL;
+
+ // try the kernel
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod!=NULL)
+ {
+ result=GetProcAddress(hMod,lpProcName);
+ }
+
+ if (IsVistaPlatform()) AvoidVistaTurkishBug = TRUE;
+
+ // failing all that, use the fallback provided
+ if(result==NULL)
+ {
+ result = pFnDownlevelFallback;
+ }
+
+ return result;
+ }
+
+ __success(return != NULL)
+ FARPROC GetProcAddressForSortingApi(
+ __in LPCSTR lpProcName,
+ __in FARPROC lpSortDllProcedure,
+ __in_opt FARPROC pFnDownlevelFallback,
+ __in_opt CONST NLSVERSIONINFO * lpVersionInformation)
+ {
+ _ASSERTE(lpProcName != NULL);
+ _ASSERTE(lpSortDllProcedure != NULL);
+
+ FARPROC result = NULL;
+
+ // Below windows 8 we have to try the sorting dll
+ if (!RunningOnWin8() && SortVersioning::IsAvailableVersion(lpVersionInformation))
+ {
+ result=lpSortDllProcedure;
+ }
+
+ if(result == NULL)
+ {
+ result = GetSystemProcAddressForSortingApi(lpProcName, pFnDownlevelFallback);
+ }
+ return result;
+ }
+#endif // ENABLE_DOWNLEVEL_FOR_NLS
+
+
+#if defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ //
+ // Vista handle tr-TR and az-Latn-AZ incorrectly with the sorting APIs that takes locale names
+ // work around the problem by using the sorting name instead.
+ //
+
+ LPWSTR GetLingusticLocaleName(__in LPWSTR pLocaleName, __in DWORD dwFlags)
+ {
+ _ASSERTE(IsVistaPlatform());
+
+ // If the localeName is NULL, then we are using an OS SortHandle and don't need to fix up
+ // anything.
+ if (pLocaleName == NULL)
+ {
+ return pLocaleName;
+ }
+
+ if ((dwFlags & CASING_BITS))
+ {
+ if (_wcsicmp(pLocaleName, TURKISH_LOCALE_NAME) == 0)
+ return TURKISH_SORTING_LOCALE_NAME;
+
+ if (_wcsicmp(pLocaleName, AZERBAIJAN_LOCALE_NAME) == 0)
+ return AZERBAIJAN_SORTING_LOCALE_NAME;
+ }
+ return pLocaleName;
+ }
+#endif
+
+ //
+ // NOTE: We assume that we're only being called from the BCL with an explicit locale name, so we don't
+ // support the system/user default tokens used in the OS.
+ //
+ // Additionally this is only called for casing and sort keys, other functionality isn't supported.
+ //
+ int CompareStringEx(__in LPCWSTR lpLocaleName, __in DWORD dwCmpFlags, __in_ecount(cchCount1) LPCWSTR lpString1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR lpString2,
+ __in int cchCount2, __in_opt LPNLSVERSIONINFO lpVersionInformation, __in_opt LPVOID lpReserved, __in_opt LPARAM lParam )
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ PRECONDITION((lParam == 0 && CheckPointer(lpLocaleName)) || (lParam != 0 && lpLocaleName == NULL));
+ PRECONDITION(CheckPointer(lpString1));
+ PRECONDITION(CheckPointer(lpString2));
+ } CONTRACTL_END;
+
+ _ASSERTE(lpLocaleName == NULL || NotLeakingFrameworkOnlyCultures(lpLocaleName));
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return::CompareStringEx(lpLocaleName, dwCmpFlags, lpString1, cchCount1, lpString2,
+ cchCount2, lpVersionInformation, lpReserved, lParam );
+#else
+ typedef int (WINAPI *PFNCompareStringEx)(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPNLSVERSIONINFO, LPVOID, LPARAM );
+ static PFNCompareStringEx pFNCompareStringEx=NULL;
+
+ // See if we loaded our pointer already
+ if (pFNCompareStringEx == NULL)
+ {
+ pFNCompareStringEx = (PFNCompareStringEx) GetProcAddressForSortingApi(
+ "CompareStringEx",
+ (FARPROC)SortVersioning::SortCompareString,
+ (FARPROC)DownLevel::CompareStringEx,
+ lpVersionInformation);
+ }
+
+ // TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista.
+ // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx
+ LPCWSTR pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwCmpFlags) : lpLocaleName;
+ // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8
+
+ return pFNCompareStringEx(pLingLocaleName, dwCmpFlags, lpString1, cchCount1, lpString2,
+ cchCount2, lpVersionInformation, lpReserved, lParam );
+#endif
+ }
+
+ // Note that unlike the real version we always expect our callers to pass counted strings
+ // I don't think we can assert because I think it'd call this code again
+ int CompareStringOrdinal(__in_ecount(cchCount1) LPCWSTR lpString1, __in int cchCount1, __in_ecount(cchCount2) LPCWSTR lpString2, __in int cchCount2, __in BOOL bIgnoreCase)
+ {
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::CompareStringOrdinal(lpString1, cchCount1, lpString2, cchCount2, bIgnoreCase);
+#else
+ typedef int (WINAPI *PFNCompareStringOrdinal )(LPCWSTR, int, LPCWSTR, int, BOOL );
+ static PFNCompareStringOrdinal pFNCompareStringOrdinal=NULL;
+ if (pFNCompareStringOrdinal == NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod != NULL)
+ {
+ // Grab the OS proc if its there
+ pFNCompareStringOrdinal=(PFNCompareStringOrdinal)GetProcAddress(hMod, "CompareStringOrdinal");
+ }
+ if (pFNCompareStringOrdinal == NULL)
+ {
+ // Regardless of the reason why, if we can't find the API just use the downlevel version
+ pFNCompareStringOrdinal=DownLevel::CompareStringOrdinal;
+ }
+ }
+ return pFNCompareStringOrdinal(lpString1, cchCount1, lpString2, cchCount2, bIgnoreCase);
+#endif
+ }
+
+// The invariant locale should always match the system for
+// upper and lower casing
+inline BOOL IsInvariantCasing(__in LPCWSTR lpLocaleName, __in DWORD dwMapFlags)
+{
+ _ASSERTE(lpLocaleName);
+
+ if(lpLocaleName[0] == NULL)
+ {
+ if(dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+ //
+ // NOTE: We assume that we're only being called from the BCL with an explicit locale name, so we don't
+ // support the system/user default tokens used in the OS.
+ //
+ // Additionally this is only called for casing and sort keys, other functionality isn't supported.
+ //
+ __success(return != 0)
+ int LCMapStringEx (__in LPCWSTR lpLocaleName, __in DWORD dwMapFlags, __in_ecount(cchSrc) LPCWSTR lpSrcStr, __in int cchSrc,
+ __out_xcount_opt(cchDest) LPWSTR lpDestStr, __in int cchDest, __in_opt LPNLSVERSIONINFO lpVersionInformation, __in_opt LPVOID lpReserved, __in_opt LPARAM lParam )
+ {
+ int retVal = 0;
+ // Note: We should only be calling this for casing or sort keys
+ _ASSERTE((dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE | LCMAP_TITLECASE | LCMAP_SORTKEY | (RunningOnWin8() ? (LCMAP_SORTHANDLE | LCMAP_HASH) : 0))) != 0);
+
+ // Need to have a name or sort node
+ _ASSERTE(lpLocaleName == NULL && lParam != 0 || lParam == 0 && lpLocaleName != NULL);
+
+ _ASSERTE(lpLocaleName == NULL || NotLeakingFrameworkOnlyCultures(lpLocaleName));
+
+ // Can't use the system token, which starts with an illegal !
+ _ASSERTE(lpLocaleName == NULL || lpLocaleName[0] != W('!'));
+
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::LCMapStringEx (lpLocaleName, dwMapFlags, lpSrcStr, cchSrc,
+ lpDestStr, cchDest, lpVersionInformation, lpReserved, lParam );
+#else
+ typedef int (WINAPI *PFNLCMapStringEx )(LPCWSTR, DWORD, LPCWSTR, int, LPWSTR, int, LPNLSVERSIONINFO, LPVOID, LPARAM);
+
+ PFNLCMapStringEx pLcMapStringEx;
+ LPCWSTR pLingLocaleName;
+ if(lpLocaleName == NULL || IsInvariantCasing(lpLocaleName, dwMapFlags)){
+ static PFNLCMapStringEx pFNSystemLCMapStringEx=NULL;
+ if (pFNSystemLCMapStringEx == NULL)
+ {
+ pFNSystemLCMapStringEx = (PFNLCMapStringEx) GetSystemProcAddressForSortingApi(
+ "LCMapStringEx",
+ (FARPROC)DownLevel::LCMapStringEx);
+ }
+ pLcMapStringEx = pFNSystemLCMapStringEx;
+ pLingLocaleName = lpLocaleName;
+ }
+ else
+ {
+ static PFNLCMapStringEx pFNLCMapStringEx=NULL;
+ // See if we still need to find which function to use
+ if (pFNLCMapStringEx == NULL)
+ {
+ pFNLCMapStringEx = (PFNLCMapStringEx) GetProcAddressForSortingApi(
+ "LCMapStringEx",
+ (FARPROC)SortVersioning::LCMapStringEx,
+ (FARPROC)DownLevel::LCMapStringEx,
+ lpVersionInformation);
+ }
+ pLcMapStringEx = pFNLCMapStringEx;
+
+ // TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista.
+ // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx
+ pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwMapFlags) : lpLocaleName;
+ // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8
+ }
+
+ retVal = pLcMapStringEx(pLingLocaleName, dwMapFlags, lpSrcStr, cchSrc,
+ lpDestStr, cchDest, lpVersionInformation, lpReserved, lParam);
+ if (retVal == 0)
+ retVal = DownLevel::UplevelFallback::LCMapStringEx(lpLocaleName, dwMapFlags, lpSrcStr, cchSrc,
+ lpDestStr, cchDest, lpVersionInformation, lpReserved, lParam);
+#endif
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return retVal;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+
+ __inline BOOL IsLowerAsciiString(__in_ecount(cchCount) LPCWSTR lpString, __in int cchCount)
+ {
+ int count = cchCount;
+ LPCWSTR pStr = lpString;
+ __range(0,10) int cch;
+ int value = 0;
+
+ while (count > 0)
+ {
+ cch = min(count, 10);
+ switch (cch)
+ {
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26010) // Prefast - Potential read overflow of null terminated buffer using expression 'pStr[9]'
+#endif // _PREFAST_
+ case 10: value |= (int) pStr[9]; __fallthrough; // fall through
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ case 9: value |= (int) pStr[8]; __fallthrough; // fall through
+ case 8: value |= (int) pStr[7]; __fallthrough; // fall through
+ case 7: value |= (int) pStr[6]; __fallthrough; // fall through
+ case 6: value |= (int) pStr[5]; __fallthrough; // fall through
+ case 5: value |= (int) pStr[4]; __fallthrough; // fall through
+ case 4: value |= (int) pStr[3]; __fallthrough; // fall through
+ case 3: value |= (int) pStr[2]; __fallthrough; // fall through
+ case 2: value |= (int) pStr[1]; __fallthrough; // fall through
+ case 1: value |= (int) pStr[0]; __fallthrough; // fall through
+ }
+
+ if (value >= 0x80)
+ {
+ return FALSE;
+ }
+
+ count -= cch;
+ pStr += cch;
+ }
+
+ return TRUE;
+ }
+
+ INT32 FastIndexOfString(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength)
+ {
+ if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1)
+ {
+ return -1;
+ }
+
+ INT32 startIndex = 0;
+ INT32 endIndex = sourceLength - 1;
+
+ int endPattern = endIndex - patternLength + 1;
+
+ if (endPattern<0) {
+ return -1;
+ }
+
+ if (patternLength == 0) {
+ return startIndex;
+ }
+
+ WCHAR patternChar0 = pattern[0];
+ for (int ctrSrc = startIndex; ctrSrc<=endPattern; ctrSrc++) {
+ if (source[ctrSrc] != patternChar0)
+ continue;
+ int ctrPat;
+ for (ctrPat = 1; ctrPat < patternLength; ctrPat++) {
+ if (source[ctrSrc + ctrPat] != pattern[ctrPat]) break;
+ }
+ if (ctrPat == patternLength) {
+ return (ctrSrc);
+ }
+ }
+
+ return (-1);
+ }
+
+ INT32 FastIndexOfStringInsensitive(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength)
+ {
+ if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1)
+ {
+ return -1;
+ }
+
+ INT32 startIndex = 0;
+ INT32 endIndex = sourceLength - 1;
+
+ WCHAR srcChar;
+ WCHAR patChar;
+
+ int endPattern = endIndex - patternLength + 1;
+
+ if (endPattern<0) {
+ return -1;
+ }
+
+ if (patternLength == 0) {
+ return startIndex;
+ }
+
+ WCHAR pattern0 = pattern[0];
+ if (pattern0>='A' && pattern0<='Z') {
+ pattern0|=0x20;
+ }
+
+ for (int ctrSrc = startIndex; ctrSrc<=endPattern; ctrSrc++) {
+ srcChar = source[ctrSrc];
+ if (srcChar>='A' && srcChar<='Z') {
+ srcChar|=0x20;
+ }
+ if (srcChar != pattern0)
+ continue;
+
+ int ctrPat;
+ for (ctrPat = 1; (ctrPat < patternLength); ctrPat++) {
+ srcChar = source[ctrSrc + ctrPat];
+ if (srcChar>='A' && srcChar<='Z') {
+ srcChar|=0x20;
+ }
+ patChar = pattern[ctrPat];
+ if (patChar>='A' && patChar<='Z') {
+ patChar|=0x20;
+ }
+ if (srcChar!=patChar) {
+ break;
+ }
+ }
+
+ if (ctrPat == patternLength) {
+ return (ctrSrc);
+ }
+ }
+
+ return (-1);
+ }
+
+ // Works backwards, starting at startIndex and ending at endIndex
+ INT32 FastLastIndexOfString(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength)
+ {
+ if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1)
+ {
+ return -1;
+ }
+
+ INT32 startIndex = sourceLength - 1;
+ INT32 endIndex = 0;
+
+ //startIndex is the greatest index into the string.
+ int startPattern = startIndex - patternLength + 1;
+
+ if (startPattern < 0) {
+ return (-1);
+ }
+
+ if (patternLength == 0) {
+ return startIndex;
+ }
+
+ WCHAR patternChar0 = pattern[0];
+ for (int ctrSrc = startPattern; ctrSrc >= endIndex; ctrSrc--) {
+ if (source[ctrSrc] != patternChar0)
+ continue;
+ int ctrPat;
+ for (ctrPat = 1; ctrPat<patternLength; ctrPat++) {
+ if (source[ctrSrc+ctrPat] != pattern[ctrPat]) break;
+ }
+ if (ctrPat == patternLength) {
+ return (ctrSrc);
+ }
+ }
+
+ return (-1);
+ }
+
+ // Works backwards, starting at startIndex and ending at endIndex
+ INT32 FastLastIndexOfStringInsensitive(__in_ecount(sourceLength) const WCHAR *source, __in INT32 sourceLength, __in_ecount(patternLength) const WCHAR *pattern, __in INT32 patternLength)
+ {
+ if (source == NULL || pattern == NULL || patternLength < 0 || sourceLength < 1)
+ {
+ return -1;
+ }
+
+ INT32 startIndex = sourceLength - 1;
+ INT32 endIndex = 0;
+
+ //startIndex is the greatest index into the string.
+ int startPattern = startIndex - patternLength + 1;
+
+ if (startPattern < 0) {
+ return (-1);
+ }
+
+ if (patternLength == 0) {
+ return startIndex;
+ }
+
+ WCHAR srcChar;
+ WCHAR patChar;
+ WCHAR pattern0 = pattern[0];
+ if (pattern0>='A' && pattern0<='Z') {
+ pattern0|=0x20;
+ }
+
+
+ for (int ctrSrc = startPattern; ctrSrc >= endIndex; ctrSrc--) {
+ srcChar = source[ctrSrc];
+ if (srcChar>='A' && srcChar<='Z') {
+ srcChar|=0x20;
+ }
+ if (srcChar != pattern0)
+ continue;
+
+ int ctrPat;
+ for (ctrPat = 1; ctrPat<patternLength; ctrPat++) {
+ srcChar = source[ctrSrc+ctrPat];
+ if (srcChar>='A' && srcChar<='Z') {
+ srcChar|=0x20;
+ }
+ patChar = pattern[ctrPat];
+ if (patChar>='A' && patChar<='Z') {
+ patChar|=0x20;
+ }
+ if (srcChar!=patChar) {
+ break;
+ }
+ }
+ if (ctrPat == patternLength) {
+ return (ctrSrc);
+ }
+ }
+
+ return (-1);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // IndexOfString
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ int IndexOfString( __in LPCWSTR lpLocaleName,
+ __in_ecount(cchCount1) LPCWSTR pString1, // String to search in
+ __in int cchCount1, // length of pString1
+ __in_ecount(cchCount2) LPCWSTR pString2, // String we're looking for
+ __in int cchCount2, // length of pString2
+ __in DWORD dwFlags, // search flags
+ __in BOOL startWith) // true if we need to check for prefix case
+ {
+ int iRetVal = -1;
+
+ //
+ // Check the ranges.
+ //
+ if (cchCount1 == 0)
+ {
+ if (cchCount2 == 0)
+ iRetVal = 0;
+ // else iRetVal = -1 (not found)
+ goto lExit;
+ }
+
+ //
+ // See if we have an empty string 2.
+ //
+ if (cchCount2 == 0)
+ {
+ iRetVal = 0;
+ goto lExit;
+ }
+
+ //
+ // Search for the character in the string.
+ //
+
+ if (dwFlags == COMPARE_OPTIONS_ORDINAL)
+ {
+ iRetVal = FastIndexOfString(pString1, cchCount1, pString2, cchCount2);
+ goto lExit;
+ }
+ //For dwFlags, 0 is the default, 1 is ignore case, we can handle both.
+ // TODO: NLS Arrowhead -This isn't really right, custom locales could start with en- and have different sort behavior
+
+ if (((dwFlags & ~CASING_BITS) == 0) && IS_FAST_COMPARE_LOCALE(lpLocaleName))
+ {
+ if (IsLowerAsciiString(pString1, cchCount1) && IsLowerAsciiString(pString2, cchCount2))
+ {
+ if ((dwFlags & (LINGUISTIC_IGNORECASE | NORM_IGNORECASE)) == 0)
+ iRetVal = FastIndexOfString(pString1, cchCount1, pString2, cchCount2);
+ else
+ iRetVal = FastIndexOfStringInsensitive(pString1, cchCount1, pString2, cchCount2);
+ goto lExit;
+ }
+ }
+
+ _ASSERTE(iRetVal==-1);
+ int result;
+
+ // Some things to think about, depending on the options passed in:
+ //
+ // LINGUISTIC_IGNORECASE - Can't cause length changes since casing is always changing to the same length
+ // NORM_IGNORECASE - Can't cause length changes since casing is always changing to the same length
+ // NORM_LINGUISTIC_CASING - Can't cause length changes since casing is always changing to the same length
+ // NORM_IGNOREKANATYPE - A 1:1 mapping, so the lengths don't change
+ // NORM_IGNOREWIDTH - A 1:1 mapping (full & half width), so lengths don't change
+ // SORT_STRINGSORT - No impact on search size - special treatment for - and '
+ // LINGUISTIC_IGNOREDIACRITIC - Terrible because both strings could be all diacritics, except for the last character.
+ // NORM_IGNORENONSPACE -Terrible because both strings could all be non-spacing characters, except for 1 somewhere.
+ // NORM_IGNORESYMBOLS - Terrible because both strings could be all symbols
+ // Compressions/Expansions - Either string may have compressions or expansions impacting the size needing searched
+ // for default table cultures (including invariant) there're only expansions, and those only expand 2X worst case.
+
+ for (int iOffset=0; iRetVal == -1 && iOffset<cchCount1; iOffset++)
+ {
+ // Because of compressions/expansions/ignorable characters we can't just use the known length, but need to consider the entire remainder of the string.
+ // TODO: NLS: the nested loop is extremely slow. oledbtest.exe can't finish even overnight. (Doing invariant ignore case)
+ for (int iLength=1; iLength<=cchCount1 - iOffset; iLength++)
+ {
+ result = NewApis::CompareStringEx(lpLocaleName, dwFlags, pString2, cchCount2, &pString1[iOffset], iLength, NULL, NULL, 0);
+ if (result == CSTR_EQUAL)
+ {
+ iRetVal = iOffset;
+ break;
+ }
+ else if (result == 0)
+ {
+ // return value of 0 indicates failure and error value is supposed to be set.
+ // shouldn't ever really happen
+ _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx! This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole.");
+ }
+ }
+ }
+
+ lExit:
+ if (startWith && iRetVal != 0)
+ {
+ iRetVal = -1;
+ }
+
+ return iRetVal;
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // LastIndexOfString
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ int LastIndexOfString( __in LPCWSTR lpLocaleName,
+ __in_ecount(cchCount1) LPCWSTR pString1, // String to search in
+ __in int cchCount1, // length of pString1
+ __in_ecount(cchCount2) LPCWSTR pString2, // String we're looking for
+ __in int cchCount2, // length of pString2
+ __in DWORD dwFlags,
+ __in BOOL endWith) // check suffix case
+ {
+ INT32 iRetVal = -1;
+ BOOL comparedOrdinal = FALSE;
+
+ // Check for empty strings
+ if (cchCount1 == 0)
+ {
+ if (cchCount2 == 0)
+ iRetVal = 0;
+ // else iRetVal = -1 (not found)
+ goto lExit;
+ }
+
+ //
+ // See if we have an empty string 2.
+ //
+ if (cchCount2 == 0)
+ {
+ iRetVal = 0;
+ goto lExit;
+ }
+
+ //
+ // Search for the character in the string.
+ //<TODO>
+ // @ToDo: Should read the nls data tables directly to make this
+ // much faster and to handle composite characters.
+ //</TODO>
+
+ if (dwFlags == COMPARE_OPTIONS_ORDINAL)
+ {
+ iRetVal = FastLastIndexOfString(pString1, cchCount1, pString2, cchCount2);
+ comparedOrdinal = TRUE;
+ goto lExit;
+ }
+
+ //For dwFlags, 0 is the default, 1 is ignore case, we can handle both.
+ // TODO: NLS Arrowhead -This isn't really right, custom locales could start with en- and have different sort behavior
+ if (((dwFlags & ~CASING_BITS) == 0) && IS_FAST_COMPARE_LOCALE(lpLocaleName))
+ {
+ if (IsLowerAsciiString(pString1, cchCount1) && IsLowerAsciiString(pString2, cchCount2))
+ {
+ if ((dwFlags & (LINGUISTIC_IGNORECASE | NORM_IGNORECASE)) == 0)
+ iRetVal = FastLastIndexOfString(pString1, cchCount1, pString2, cchCount2);
+ else
+ iRetVal = FastLastIndexOfStringInsensitive(pString1, cchCount1, pString2, cchCount2);
+ comparedOrdinal = TRUE;
+ goto lExit;
+ }
+ }
+
+ _ASSERTE(iRetVal==-1);
+ // TODO: Cleanup like IndexOfString
+ for (int iOffset=0; iRetVal == -1 && iOffset>-cchCount1; iOffset--)
+ {
+ for (int iLength=1; iLength<=cchCount1 + iOffset; iLength++)
+ {
+ if (NewApis::CompareStringEx(lpLocaleName, dwFlags, pString2, cchCount2, &pString1[cchCount1 + iOffset - iLength], iLength, NULL, NULL, 0) == CSTR_EQUAL)
+ {
+ iRetVal= cchCount1 + iOffset - iLength;
+ break;
+ }
+ }
+ }
+ lExit:
+
+ if (endWith && iRetVal>=0)
+ {
+ if (comparedOrdinal && (cchCount1 - iRetVal != cchCount2)) // optimize here to avoid calling CompareString
+ {
+ iRetVal = -1;
+ }
+ else if (NewApis::CompareStringEx(lpLocaleName, dwFlags, pString2, cchCount2, &pString1[iRetVal], cchCount1 - iRetVal, NULL, NULL, 0) != CSTR_EQUAL)
+ {
+ iRetVal = -1;
+ }
+ }
+
+ return iRetVal;
+ }
+
+ //
+ // NOTE: We assume that we're only being called from the BCL with an explicit locale name, so we don't
+ // support the system/user default tokens used in the OS.
+ //
+ // Additionally this is only called for casing and sort keys, other functionality isn't supported.
+ //
+ int FindNLSStringEx(__in LPCWSTR lpLocaleName,
+ __in DWORD dwFindNLSStringFlags,
+ __in_ecount(cchSource) LPCWSTR lpStringSource,
+ __in int cchSource,
+ __in_ecount(cchValue) LPCWSTR lpStringValue,
+ __in int cchValue,
+ __out_opt LPINT pcchFound,
+ __in_opt LPNLSVERSIONINFO lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ _ASSERTE(lpLocaleName == NULL || NotLeakingFrameworkOnlyCultures(lpLocaleName));
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::FindNLSStringEx(lpLocaleName, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound,
+ lpVersionInformation, lpReserved, lParam );
+#else
+ typedef int (WINAPI *PFNFindNLSStringEx )(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPINT, LPNLSVERSIONINFOEX, LPVOID, LPARAM );
+ static PFNFindNLSStringEx pFNFindNLSStringEx=NULL;
+
+ // See if we still need to figure out which function to call
+ if (pFNFindNLSStringEx == NULL)
+ {
+ pFNFindNLSStringEx = (PFNFindNLSStringEx) GetProcAddressForSortingApi(
+ "FindNLSStringEx",
+ (FARPROC)SortVersioning::SortFindString,
+ (FARPROC)DownLevel::FindNLSStringEx,
+ lpVersionInformation);
+ }
+
+ // TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista.
+ // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx
+ LPCWSTR pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwFindNLSStringFlags) : lpLocaleName;
+ // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8
+
+ int cchFound; // we need to get the length even if the caller doesn't care about it (see below)
+ int result = pFNFindNLSStringEx(pLingLocaleName, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound,
+ lpVersionInformation, lpReserved, lParam );
+ // When searching from end with an empty pattern (either empty string or all ignored characters)
+ // a match is found (result != -1)
+ // Currently we get a result == 0 but we are hoping this will change
+ // with Win7 to be the length of the source string (thus pointing past-the-end)
+ // and the length of the match (cchFound) will be 0
+ // For compatibility, we need to return the index of the last character (or 0 if the source is empty)
+ if((dwFindNLSStringFlags & FIND_FROMEND) &&
+ result != -1 &&
+ cchFound == 0 &&
+ cchSource != 0)
+ {
+ result = cchSource - 1;
+ }
+
+ // if the caller cares about the length, give it to them
+ if(pcchFound != NULL)
+ {
+ *pcchFound = cchFound;
+ }
+
+ return result;
+#endif
+ }
+
+ __success(return != 0) int
+ GetCalendarInfoEx(__in LPCWSTR lpLocaleName, __in CALID Calendar, __in_opt LPCWSTR pReserved, __in CALTYPE CalType, __out_ecount_opt(cchData) LPWSTR lpCalData, __in int cchData, __out_opt LPDWORD lpValue )
+ {
+
+ _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName));
+ if ( (lpCalData != NULL && cchData == 0) || (lpCalData == NULL && cchData > 0) )
+ {
+ _ASSERTE(FALSE);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ if ((CalType & CAL_RETURN_NUMBER))
+ {
+ // If CAL_RETURN_NUMBER, lpValue must be non-null and lpCalData must be null
+ if (lpValue == NULL || lpCalData != NULL)
+ {
+ _ASSERTE(FALSE);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ }
+
+ int retVal = 0;
+
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::GetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CalType, lpCalData, cchData, lpValue );
+#else
+ typedef int (WINAPI *PFNGetCalendarInfoEx )(LPCWSTR, CALID, LPCWSTR, CALTYPE, LPWSTR, int, LPDWORD );
+ static PFNGetCalendarInfoEx pFNGetCalendarInfoEx=NULL;
+ if (pFNGetCalendarInfoEx== NULL)
+ {
+ pFNGetCalendarInfoEx=(PFNGetCalendarInfoEx)GetProcAddressForLocaleApi(
+ "GetCalendarInfoEx",
+ (FARPROC)DownLevel::GetCalendarInfoEx);
+ }
+ retVal = pFNGetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CalType, lpCalData, cchData, lpValue );
+
+ // Do fallback if we didn't find anything yet
+ if (retVal == 0)
+ retVal = DownLevel::UplevelFallback::GetCalendarInfoEx(lpLocaleName, Calendar, pReserved, CalType, lpCalData, cchData, lpValue );
+#endif
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return retVal;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+
+ __success(return != 0)
+ int LCIDToLocaleName(__in LCID Locale, __out_ecount_opt(cchName) LPWSTR lpName, __in int cchName, __in DWORD dwFlags)
+ {
+ int retVal = 0;
+
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::LCIDToLocaleName(Locale, lpName, cchName, dwFlags);
+#else
+
+#ifdef FEATURE_CORECLR
+ retVal = DownLevel::LCIDToLocaleName(Locale, lpName,cchName,dwFlags);
+#endif // FEATURE_CORECLR
+
+ typedef int (WINAPI *PFNLCIDToLocaleName)(LCID, LPWSTR,int ,DWORD);
+ static PFNLCIDToLocaleName pFNLCIDToLocaleName=NULL;
+ if (retVal == 0)
+ {
+ if (pFNLCIDToLocaleName==NULL)
+ {
+ pFNLCIDToLocaleName=(PFNLCIDToLocaleName)GetProcAddressForLocaleApi(
+ "LCIDToLocaleName",
+ (FARPROC)DownLevel::LCIDToLocaleName);
+
+ }
+
+ // Try with the allow neutral flag (will fail in Vista, but
+ // Downlevel::LCIDToLocaleName knows all vista locales)
+ retVal = pFNLCIDToLocaleName(Locale, lpName, cchName, dwFlags | LOCALE_ALLOW_NEUTRAL_NAMES);
+ if(retVal == 0)
+ {
+ // in case we are using OS, it could have a problem with the above flag; retry without it
+ retVal = pFNLCIDToLocaleName(Locale, lpName, cchName, dwFlags);
+ }
+ }
+#endif // ENABLE_DOWNLEVEL_FOR_NLS
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return retVal;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+
+ LCID LocaleNameToLCID(__in_opt LPCWSTR lpName , __in DWORD dwFlags)
+ {
+ LCID retVal = 0;
+
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::LocaleNameToLCID(lpName, dwFlags);
+#else
+#ifdef FEATURE_CORECLR
+ retVal = DownLevel::LocaleNameToLCID(lpName, dwFlags);
+#endif // FEATURE_CORECLR
+
+ typedef int (WINAPI *PFNLocaleNameToLCID)(LPCWSTR,DWORD);
+ static PFNLocaleNameToLCID pFNLocaleNameToLCID=NULL;
+
+ if (retVal == 0)
+ {
+ if (pFNLocaleNameToLCID==NULL)
+ {
+ pFNLocaleNameToLCID=(PFNLocaleNameToLCID)GetProcAddressForLocaleApi(
+ "LocaleNameToLCID",
+ (FARPROC)DownLevel::LocaleNameToLCID);
+
+ }
+
+ // Try with the allow neutral flag (will fail in Vista, but
+ // Downlevel::LocaleNametoLCID knows all vista locales)
+ retVal = pFNLocaleNameToLCID(lpName, dwFlags | LOCALE_ALLOW_NEUTRAL_NAMES);
+ if(retVal == 0)
+ {
+ // in case we are using OS, it could have a problem with the above flag; retry without it
+ retVal = pFNLocaleNameToLCID(lpName, dwFlags);
+ }
+ }
+#endif // ENABLE_DOWNLEVEL_FOR_NLS
+
+ return retVal;
+ }
+
+ __success(return != 0) BOOL
+ EnumDateFormatsExEx (DATEFMT_ENUMPROCEXEX lpDateFmtEnumProcExEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam)
+ {
+ _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName));
+ int retVal = 0;
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::EnumDateFormatsExEx (lpDateFmtEnumProcExEx, lpLocaleName, dwFlags, lParam);
+#else
+ typedef int (WINAPI *PFNEnumDateFormatsExEx)(DATEFMT_ENUMPROCEXEX, LPCWSTR, DWORD, LPARAM);
+
+ static PFNEnumDateFormatsExEx pFNEnumDateFormatsExEx=NULL;
+ if (pFNEnumDateFormatsExEx==NULL)
+ {
+ pFNEnumDateFormatsExEx=(PFNEnumDateFormatsExEx)GetProcAddressForLocaleApi(
+ "EnumDateFormatsExEx",
+ (FARPROC)DownLevel::LegacyCallbacks::EnumDateFormatsExEx);
+ }
+
+ retVal = pFNEnumDateFormatsExEx(lpDateFmtEnumProcExEx, lpLocaleName, dwFlags, lParam);
+ if (retVal == 0)
+ {
+ retVal = DownLevel::LegacyCallbacks::EnumDateFormatsExEx(lpDateFmtEnumProcExEx, lpLocaleName, dwFlags, lParam);
+ }
+#endif
+ return retVal;
+ }
+
+ __success(return != 0)
+ BOOL EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX lpTimeFmtEnumProcEx, LPCWSTR lpLocaleName, DWORD dwFlags, LPARAM lParam)
+ {
+ _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName));
+ int retVal = 0;
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::EnumTimeFormatsEx(lpTimeFmtEnumProcEx, lpLocaleName, dwFlags, lParam);
+#else
+ typedef int (WINAPI *PFNEnumTimeFormatsEx)(TIMEFMT_ENUMPROCEX, LPCWSTR, DWORD, LPARAM);
+
+ static PFNEnumTimeFormatsEx pFNEnumTimeFormatsEx=NULL;
+ if (pFNEnumTimeFormatsEx==NULL)
+ {
+ pFNEnumTimeFormatsEx=(PFNEnumTimeFormatsEx)GetProcAddressForLocaleApi(
+ "EnumTimeFormatsEx",
+ (FARPROC)DownLevel::LegacyCallbacks::EnumTimeFormatsEx);
+ }
+
+ retVal = pFNEnumTimeFormatsEx(lpTimeFmtEnumProcEx, lpLocaleName, dwFlags, lParam);
+ if (retVal == 0)
+ {
+ retVal = DownLevel::LegacyCallbacks::EnumTimeFormatsEx(lpTimeFmtEnumProcEx, lpLocaleName, dwFlags, lParam);
+ }
+#endif
+ return retVal;
+ }
+
+ __success(return != 0)
+ BOOL EnumCalendarInfoExEx(CALINFO_ENUMPROCEXEX pCalInfoEnumProcExEx, LPCWSTR lpLocaleName, CALID Calendar, CALTYPE CalType, LPARAM lParam)
+ {
+ _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName));
+ int retVal = 0;
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::EnumCalendarInfoExEx (pCalInfoEnumProcExEx, lpLocaleName, Calendar, NULL, CalType, lParam);
+#else
+ typedef int (WINAPI *PFNEnumCalendarInfoExEx)(CALINFO_ENUMPROCEXEX, LPCWSTR, CALID, LPCWSTR, CALTYPE, LPARAM);
+
+ static PFNEnumCalendarInfoExEx pFNEnumCalendarInfoExEx=NULL;
+ if (pFNEnumCalendarInfoExEx==NULL)
+ {
+ pFNEnumCalendarInfoExEx=(PFNEnumCalendarInfoExEx)GetProcAddressForLocaleApi(
+ "EnumCalendarInfoExEx",
+ (FARPROC)DownLevel::LegacyCallbacks::EnumCalendarInfoExEx);
+ }
+
+ retVal = pFNEnumCalendarInfoExEx(pCalInfoEnumProcExEx, lpLocaleName, Calendar, NULL, CalType, lParam);
+ if (retVal == 0)
+ {
+ retVal = DownLevel::LegacyCallbacks::EnumCalendarInfoExEx(pCalInfoEnumProcExEx, lpLocaleName, Calendar, NULL, CalType, lParam);
+ }
+#endif
+
+ return retVal;
+ }
+
+ // This function exists is in server 2003 and above
+ // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now
+ BOOL IsNLSDefinedString(__in NLS_FUNCTION Function, __in DWORD dwFlags, __in_opt LPNLSVERSIONINFOEX lpVersionInfo, __in LPCWSTR lpString, __in int cchStr )
+ {
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::IsNLSDefinedString(Function, dwFlags, lpVersionInfo, lpString, cchStr);
+#else
+ typedef int (WINAPI *PFNIsNLSDefinedString)(NLS_FUNCTION, DWORD, LPNLSVERSIONINFO, LPCWSTR, int);
+
+ static PFNIsNLSDefinedString pFNIsNLSDefinedString=NULL;
+
+ // See if we still need to find our function
+ if (pFNIsNLSDefinedString== NULL)
+ {
+ pFNIsNLSDefinedString = (PFNIsNLSDefinedString) GetProcAddressForSortingApi(
+ "IsNLSDefinedString",
+ (FARPROC)SortVersioning::SortIsDefinedString,
+ (FARPROC)DownLevel::IsNLSDefinedString,
+ lpVersionInfo);
+ }
+
+ // Call the appropriate function and return
+ return pFNIsNLSDefinedString(Function, dwFlags, (LPNLSVERSIONINFO)lpVersionInfo, lpString, cchStr);
+#endif
+ }
+
+
+#if !defined(FEATURE_CORECLR)
+ BOOL GetNlsVersionEx(__in NLS_FUNCTION Function, __in LPCWSTR lpLocaleName, __inout LPNLSVERSIONINFOEX lpVersionInfo)
+ {
+
+ typedef BOOL (WINAPI *PFNGetNLSVersionEx)(NLS_FUNCTION, LPCWSTR, LPNLSVERSIONINFOEX);
+
+ static PFNGetNLSVersionEx pFNGetNLSVersionEx=NULL;
+
+ // See if we still need to find our function
+ if (pFNGetNLSVersionEx == NULL)
+ {
+ // We only call this on Win8 and above, so this should always work.
+ pFNGetNLSVersionEx = (PFNGetNLSVersionEx) GetSystemProcAddressForSortingApi("GetNLSVersionEx", NULL);
+ }
+
+ _ASSERTE(pFNGetNLSVersionEx != NULL);
+
+ return pFNGetNLSVersionEx(Function, lpLocaleName, lpVersionInfo);
+ }
+#endif
+
+ // This is a Windows 7 and above function
+ // This returns the "specific" locale from an input name, ie: "en" returns "en-US",
+ // although note that it should always succeed!(returning "" neutral if nothing else)
+ __success(return != 0)
+ int ResolveLocaleName(__in LPCWSTR lpNameToResolve, __in_ecount_opt(cchLocaleName) LPWSTR lpLocaleName, __in int cchLocaleName)
+ {
+ _ASSERTE(NotLeakingFrameworkOnlyCultures(lpLocaleName));
+ int retVal = 0;
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ retVal = ::ResolveLocaleName(lpNameToResolve, lpLocaleName, cchLocaleName);
+#else
+ typedef int (WINAPI *PFNResolveLocaleName)(LPCWSTR, LPWSTR, int);
+
+ static PFNResolveLocaleName pFNResolveLocaleName=NULL;
+ if (pFNResolveLocaleName == NULL)
+ {
+ pFNResolveLocaleName =(PFNResolveLocaleName) GetProcAddressForLocaleApi(
+ "ResolveLocaleName",
+ (FARPROC)DownLevel::ResolveLocaleName);
+ }
+ retVal = pFNResolveLocaleName(lpNameToResolve, lpLocaleName, cchLocaleName);
+ if (retVal == 0)
+ {
+ retVal = DownLevel::ResolveLocaleName(lpNameToResolve, lpLocaleName, cchLocaleName);
+ }
+#endif
+
+ return retVal;
+ }
+
+ __success(return == TRUE) BOOL
+ GetThreadPreferredUILanguages(__in DWORD dwFlags,
+ __out PULONG pulNumLanguages,
+ __out_ecount_opt(*pcchLanguagesBuffer) PWSTR pwszLanguagesBuffer,
+ __inout PULONG pcchLanguagesBuffer)
+ {
+
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::GetThreadPreferredUILanguages(dwFlags, pulNumLanguages,pwszLanguagesBuffer,pcchLanguagesBuffer);
+#else
+ typedef int (WINAPI *PFNGetThreadPreferredUILanguages)(DWORD, PULONG,PWSTR,PULONG);
+ static PFNGetThreadPreferredUILanguages pFNGetThreadPreferredUILanguages=NULL;
+ if (pFNGetThreadPreferredUILanguages == NULL)
+ {
+ HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
+ if(hMod==NULL)
+ return 0;
+ pFNGetThreadPreferredUILanguages=(PFNGetThreadPreferredUILanguages)GetProcAddress(hMod,"GetThreadPreferredUILanguages");
+ if(pFNGetThreadPreferredUILanguages==NULL)
+ {
+ if(GetLastError() == ERROR_PROC_NOT_FOUND)
+ pFNGetThreadPreferredUILanguages=DownLevel::GetThreadPreferredUILanguages;
+ else
+ return 0;
+ }
+ }
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return pFNGetThreadPreferredUILanguages(dwFlags, pulNumLanguages,pwszLanguagesBuffer,pcchLanguagesBuffer);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+#endif
+ }
+
+ __success(return != 0)
+ BOOL WINAPI EnumSystemLocalesEx(
+ __in LOCALE_ENUMPROCEX lpLocaleEnumProc,
+ __in DWORD dwFlags,
+ __in LPARAM lParam,
+ __in_opt LPVOID lpReserved)
+ {
+#if !defined(ENABLE_DOWNLEVEL_FOR_NLS)
+ return ::EnumSystemLocalesEx(lpLocaleEnumProc, dwFlags, lParam, lpReserved);
+#else
+ typedef BOOL (WINAPI *PFNEnumSystemLocalesEx)(
+ LOCALE_ENUMPROCEX lpLocaleEnumProc,
+ DWORD dwFlags,
+ LPARAM lParam,
+ LPVOID lpReserved);
+ static PFNEnumSystemLocalesEx pFNEnumSystemLocalesEx=NULL;
+ if (pFNEnumSystemLocalesEx== NULL)
+ {
+ pFNEnumSystemLocalesEx=(PFNEnumSystemLocalesEx)GetProcAddressForLocaleApi(
+ "EnumSystemLocalesEx",
+ NULL);
+ if (pFNEnumSystemLocalesEx == NULL)
+ {
+
+ return FALSE;
+ }
+ }
+ BOOL result = pFNEnumSystemLocalesEx(lpLocaleEnumProc, dwFlags, lParam, lpReserved);
+
+ {
+ if(result == FALSE
+ && GetLastError() == ERROR_BADDB
+ && IsWindows7Platform())
+ {
+ HKEY hKey;
+ if (::RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ W("SYSTEM\\CurrentControlSet\\Control\\Nls\\ExtendedLocale"),
+ 0,
+ KEY_READ,
+ &hKey) == ERROR_SUCCESS)
+ {
+ ::RegCloseKey(hKey);
+ }
+ else
+ {
+ result = TRUE;
+ }
+ }
+ }
+ return result;
+#endif
+ }
+}
+
+
+
+
+
diff --git a/src/utilcode/opinfo.cpp b/src/utilcode/opinfo.cpp
new file mode 100644
index 0000000000..a7d71dbfc2
--- /dev/null
+++ b/src/utilcode/opinfo.cpp
@@ -0,0 +1,129 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/***************************************************************************/
+/* OpInfo.cpp */
+/***************************************************************************/
+
+#include "stdafx.h"
+#include <cor.h> // for debugMacros.h
+#include "debugmacros.h" // for ASSERTE
+#include "opinfo.h"
+
+
+OpInfo::OpInfoData OpInfo::table[] = {
+
+#ifdef _MSC_VER
+#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) \
+ { s, args + type, FLOW_ ## ctrl, pop, push, c },
+#else
+#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) \
+ { s, (OPCODE_FORMAT) (args + type), FLOW_ ## ctrl, pop, push, c },
+#endif
+
+ // Kind of a workaround, get the prefixes (IInternal) to return InlineOpcode instead of InlineNone
+#define IInternal (InlineOpcode - InlineNone)
+#define IMacro 0
+#define IPrimitive 0
+#define IAnnotation 0
+#define IObjModel 0
+#define IPrefix 0
+
+#define Pop0 0
+#define Pop1 1
+#define PopI 1
+#define PopI4 1
+#define PopR4 1
+#define PopI8 1
+#define PopR8 1
+#define PopRef 1
+#define VarPop -1
+
+#define Push0 0
+#define Push1 1
+#define PushI 1
+#define PushI4 1
+#define PushR4 1
+#define PushI8 1
+#define PushR8 1
+#define PushRef 1
+#define VarPush -1
+
+#include "opcode.def"
+#undef OPDEF
+};
+
+
+/***************************************************************************/
+/* parse instruction at 'instrPtr', into its opcode (OpInfo), and its
+ (inline)args, 'args' 'instrPtr' is updated */
+
+/***************************************************************************/
+const BYTE* OpInfo::fetch(const BYTE* instrPtr, OpArgsVal* args) {
+
+ data = &table[*instrPtr++];
+AGAIN:
+ _ASSERTE(data - table == data->opcode);
+ switch(data->format) {
+ case InlineNone:
+ break;
+ case InlineOpcode:
+ _ASSERTE(*instrPtr + 256 < (int) (sizeof(table) / sizeof(OpInfoData)));
+ data = &table[256 + *instrPtr++];
+ goto AGAIN;
+
+ case ShortInlineVar:
+ args->i = *instrPtr; instrPtr +=1;
+ break;
+ case InlineVar:
+ args->i = GET_UNALIGNED_VAL16(instrPtr); instrPtr +=2;
+ break;
+ case ShortInlineI:
+ case ShortInlineBrTarget:
+ args->i = *instrPtr; instrPtr +=1;
+ break;
+ case ShortInlineR: {
+ DWORD f = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4;
+ args->r = *((float*) (&f));
+ }
+ break;
+ case InlineRVA:
+ case InlineI:
+ case InlineMethod:
+ case InlineField:
+ case InlineType:
+ case InlineString:
+ case InlineSig:
+ case InlineTok:
+ case InlineBrTarget:
+ args->i = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4;
+ break;
+ case InlineI8:
+ args->i8 = GET_UNALIGNED_VAL64(instrPtr); instrPtr +=8;
+ break;
+ case InlineR: {
+ __int64 d = GET_UNALIGNED_VAL64(instrPtr); instrPtr +=8;
+ instrPtr += 8;
+ args->r = *((double*) (&d));
+ } break;
+ case InlineSwitch:
+ args->switch_.count = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4;
+ args->switch_.targets = (int*) instrPtr; instrPtr += (4 * args->switch_.count);
+ break;
+ case InlinePhi:
+ args->phi.count = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=1;
+ args->phi.vars = (unsigned short*) instrPtr; instrPtr += (2 * args->phi.count);
+ break;
+ default:
+#ifdef _DEBUG
+ _ASSERTE(!"BadType");
+#else
+ __assume(0); // we are really certain the default case does not happen
+#endif
+ break;
+ }
+ return(instrPtr);
+}
+
diff --git a/src/utilcode/outstring.cpp b/src/utilcode/outstring.cpp
new file mode 100644
index 0000000000..e79d70775e
--- /dev/null
+++ b/src/utilcode/outstring.cpp
@@ -0,0 +1,182 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*****************************************************************/
+/* OutString.cpp */
+/*****************************************************************/
+/* A simple, lightweight, character output stream, with very few
+ external dependancies (like sprintf ... ) */
+
+/*
+ Date : 2/1/99 */
+/*****************************************************************/
+
+#include "stdafx.h"
+#include "outstring.h"
+
+
+/*****************************************************************/
+// print out 'count' instances of the character 'c'
+OutString& OutString::pad(size_t count, char c) {
+ if (cur+count > end)
+ Realloc(count);
+ memset(cur, c, count);
+ cur = cur + count;
+ return(*this);
+}
+
+/*****************************************************************/
+// prints out a decimal representation
+OutString& OutString::operator<<(double d) {
+
+ if (d == 0.0) {
+ *this << "0.0";
+ return *this;
+ }
+
+ if (d < 0) {
+ d = -d;
+ *this << '-';
+ }
+
+ // compute the exponent
+ int exponent = 0;
+ while (d > 10.0) {
+ d /= 10;
+ exponent++;
+ if (exponent > 500) { // avoids a possible infinite loop
+ *this << "INF";
+ return *this;
+ }
+ }
+ while (d < 1.0) {
+ d *= 10;
+ --exponent;
+ if (exponent < -500) { // avoids a possible infinite loop
+ *this << "0.0";
+ return *this;
+ }
+ }
+
+ // we now have a normalized d (between 1 and 10)
+ double delta = .5E-10;
+ d += delta; // round to the precision we are displaying
+
+ unsigned trailingZeros = 0;
+ for(unsigned i = 0; i < 10; i++) {
+ int digit = (int) d;
+ d = (d - digit) * 10; // ISSUE: does roundoff ever bite us here?
+
+ if (digit == 0) // defer printing traiing zeros
+ trailingZeros++;
+ else {
+ if (trailingZeros > 0) {
+ this->pad(trailingZeros, '0');
+ trailingZeros = 0;
+ }
+ *this << (char) ('0' + digit);
+ }
+ if (i == 0)
+ *this << '.';
+
+ }
+ if (exponent != 0) {
+ *this << 'E';
+ *this << exponent;
+ }
+ return(*this);
+}
+
+/*****************************************************************/
+// prints out a decimal representation
+OutString& OutString::dec(int i, size_t minWidth) {
+ char buff[12]; // big enough for any number (10 digits, - sign, null term)
+ char* ptr = &buff[11];
+ *ptr = 0;
+
+ unsigned val = i;
+ if (i < 0)
+ val = -i; // note this happens to also work for minint!
+
+ for(;;) {
+ if (val < 10) {
+ *--ptr = '0' + val;
+ break;
+ }
+ *--ptr = '0' + (val % 10);
+ val = val / 10;
+ }
+
+ if (i < 0)
+ *--ptr = '-';
+
+ size_t len = &buff[11] - ptr; // length of string
+ if (len < minWidth)
+ pad(minWidth-len, ' ');
+
+ *this << ptr;
+ return(*this);
+}
+
+/*****************************************************************/
+OutString& OutString::hex(unsigned __int64 i, int minWidth, unsigned flags) {
+
+ unsigned hi = unsigned(i >> 32);
+ unsigned low = unsigned(i);
+
+ if (hi != 0) {
+ minWidth -= 8;
+ hex(hi, minWidth, flags); // print upper bits
+ flags = zeroFill;
+ minWidth = 8;
+ }
+ return hex(low, minWidth, flags); // print lower bits
+}
+
+/*****************************************************************/
+OutString& OutString::hex(unsigned i, int minWidth, unsigned flags) {
+ char buff[12]; // big enough for any number
+ char* ptr = &buff[11];
+ *ptr = 0;
+
+ static const char digits[] = "0123456789ABCDEF";
+
+ for(;;) {
+ if (i < 16) {
+ *--ptr = digits[i];
+ break;
+ }
+ *--ptr = digits[(i % 16)];
+ i = i / 16;
+ }
+
+ size_t len = &buff[11] - ptr; // length of string
+ if (flags & put0x) {
+ if (flags & zeroFill)
+ *this << "0x";
+ else
+ *--ptr = 'x', *--ptr = '0';
+ len += 2;
+ }
+
+ if (len < (size_t)minWidth)
+ pad(minWidth-len, (flags & zeroFill) ? '0' : ' ');
+
+ *this << ptr;
+ return(*this);
+}
+
+/*****************************************************************/
+void OutString::Realloc(size_t neededSpace) {
+ size_t oldSize = cur-start;
+ size_t newSize = (oldSize + neededSpace) * 3 / 2 + 32;
+ char* oldBuff = start;
+ start = new char[newSize+1];
+ memcpy(start, oldBuff, oldSize);
+ cur = &start[oldSize];
+ end = &start[newSize];
+ delete [] oldBuff;
+}
+
diff --git a/src/utilcode/pedecoder.cpp b/src/utilcode/pedecoder.cpp
new file mode 100644
index 0000000000..049276ba5b
--- /dev/null
+++ b/src/utilcode/pedecoder.cpp
@@ -0,0 +1,2938 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// --------------------------------------------------------------------------------
+// PEDecoder.cpp
+//
+
+// --------------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "ex.h"
+#include "pedecoder.h"
+#include "mdcommon.h"
+#include "nibblemapmacros.h"
+
+CHECK PEDecoder::CheckFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(HasContents());
+
+ if (HasNTHeaders())
+ {
+ CHECK(CheckNTHeaders());
+
+ if (HasCorHeader())
+ {
+ CHECK(CheckCorHeader());
+
+#if !defined(FEATURE_MIXEDMODE) && !defined(FEATURE_PREJIT)
+ CHECK(IsILOnly());
+#endif
+
+ if (IsILOnly() && !HasReadyToRunHeader())
+ CHECK(CheckILOnly());
+
+ if (HasNativeHeader())
+ CHECK(CheckNativeHeader());
+
+ CHECK(CheckWillCreateGuardPage());
+ }
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckNTFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckCORFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+
+ CHECK_OK;
+}
+
+
+CHECK PEDecoder::CheckILFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+ CHECK(!HasNativeHeader());
+
+ CHECK_OK;
+}
+
+
+CHECK PEDecoder::CheckILOnlyFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+ CHECK(IsILOnly());
+ CHECK(!HasNativeHeader());
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckNativeFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+#ifdef FEATURE_PREJIT
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+ CHECK(!IsILOnly());
+ CHECK(HasNativeHeader());
+#else // FEATURE_PREJIT
+ CHECK(false);
+#endif // FEATURE_PREJIT
+
+ CHECK_OK;
+}
+
+BOOL PEDecoder::HasNTHeaders() const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ PRECONDITION(HasContents());
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ // Check for a valid DOS header
+
+ if (m_size < sizeof(IMAGE_DOS_HEADER))
+ RETURN FALSE;
+
+ IMAGE_DOS_HEADER* pDOS = PTR_IMAGE_DOS_HEADER(m_base);
+
+ {
+ if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)
+ || (DWORD) pDOS->e_lfanew == VAL32(0))
+ {
+ RETURN FALSE;
+ }
+
+ // Check for integer overflow
+ S_SIZE_T cbNTHeaderEnd(S_SIZE_T(static_cast<SIZE_T>(VAL32(pDOS->e_lfanew))) +
+ S_SIZE_T(sizeof(IMAGE_NT_HEADERS)));
+ if (cbNTHeaderEnd.IsOverflow())
+ {
+ RETURN FALSE;
+ }
+
+ // Now check for a valid NT header
+ if (m_size < cbNTHeaderEnd.Value())
+ {
+ RETURN FALSE;
+ }
+ }
+
+ IMAGE_NT_HEADERS *pNT = PTR_IMAGE_NT_HEADERS(m_base + VAL32(pDOS->e_lfanew));
+
+ if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
+ RETURN FALSE;
+
+ if (pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ {
+ if (pNT->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER32)))
+ RETURN FALSE;
+ }
+ else if (pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ {
+ // on 64 bit we can promote this
+ if (pNT->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER64)))
+ RETURN FALSE;
+
+ // Check for integer overflow
+ S_SIZE_T cbNTHeaderEnd(S_SIZE_T(static_cast<SIZE_T>(VAL32(pDOS->e_lfanew))) +
+ S_SIZE_T(sizeof(IMAGE_NT_HEADERS64)));
+
+ if (cbNTHeaderEnd.IsOverflow())
+ {
+ RETURN FALSE;
+ }
+
+ // Now check for a valid NT header
+ if (m_size < cbNTHeaderEnd.Value())
+ {
+ RETURN FALSE;
+ }
+
+ }
+ else
+ RETURN FALSE;
+
+ // Go ahead and cache NT header since we already found it.
+ const_cast<PEDecoder *>(this)->m_pNTHeaders = dac_cast<PTR_IMAGE_NT_HEADERS>(pNT);
+
+ RETURN TRUE;
+}
+
+CHECK PEDecoder::CheckNTHeaders() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ PRECONDITION(HasContents());
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ // Only check once per file
+ if (m_flags & FLAG_NT_CHECKED)
+ CHECK_OK;
+
+ CHECK(HasNTHeaders());
+
+ IMAGE_NT_HEADERS *pNT = FindNTHeaders();
+
+ CHECK((pNT->FileHeader.Characteristics & VAL16(IMAGE_FILE_SYSTEM)) == 0);
+
+ CHECK(CheckAlignment(VAL32(pNT->OptionalHeader.FileAlignment)));
+ CHECK(CheckAlignment(VAL32(pNT->OptionalHeader.SectionAlignment)));
+
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.FileAlignment), 512));
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), VAL32(pNT->OptionalHeader.FileAlignment)));
+
+ // INVESTIGATE: this doesn't seem to be necessary on Win64 - why??
+ //CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), OS_PAGE_SIZE));
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), 0x1000)); // for base relocs logic
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SizeOfImage), VAL32(pNT->OptionalHeader.SectionAlignment)));
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SizeOfHeaders), VAL32(pNT->OptionalHeader.FileAlignment)));
+
+ // Data directories will be validated later on.
+ PTR_IMAGE_DATA_DIRECTORY pDataDirectories = NULL;
+
+ if (Has32BitNTHeaders())
+ {
+ IMAGE_NT_HEADERS32* pNT32=GetNTHeaders32();
+ CHECK(CheckAligned(VAL32(pNT32->OptionalHeader.ImageBase), 0x10000));
+ CHECK((VAL32(pNT32->OptionalHeader.SizeOfStackCommit) <= VAL32(pNT32->OptionalHeader.SizeOfStackReserve)));
+ CHECK((VAL32(pNT32->OptionalHeader.SizeOfHeapCommit) <= VAL32(pNT32->OptionalHeader.SizeOfHeapReserve)));
+ pDataDirectories = dac_cast<PTR_IMAGE_DATA_DIRECTORY>(
+ dac_cast<TADDR>(pNT32) + offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory));
+ }
+ else
+ {
+ IMAGE_NT_HEADERS64* pNT64=GetNTHeaders64();
+ CHECK(CheckAligned(VAL64(pNT64->OptionalHeader.ImageBase), 0x10000));
+ CHECK((VAL64(pNT64->OptionalHeader.SizeOfStackCommit) <= VAL64(pNT64->OptionalHeader.SizeOfStackReserve)));
+ CHECK((VAL64(pNT64->OptionalHeader.SizeOfHeapCommit) <= VAL64(pNT64->OptionalHeader.SizeOfHeapReserve)));
+ pDataDirectories = dac_cast<PTR_IMAGE_DATA_DIRECTORY>(
+ dac_cast<TADDR>(pNT64) + offsetof(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory));
+ }
+
+ // @todo: this is a bit awkward here, it would be better to make this assertion on
+ // PEDecoder instantiation. However, we don't necessarily have the NT headers there (in fact
+ // they might not exist.)
+
+ if (IsMapped())
+ {
+ // Ideally we would require the layout address to honor the section alignment constraints.
+ // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this
+ // case, we can only guarantee OS page alignment (which after all, is good enough.)
+ CHECK(CheckAligned(m_base, OS_PAGE_SIZE));
+ }
+
+ // @todo: check NumberOfSections for overflow of SizeOfHeaders
+
+ UINT32 currentAddress = 0;
+ UINT32 currentOffset = 0;
+
+ CHECK(CheckSection(currentAddress, 0, VAL32(pNT->OptionalHeader.SizeOfHeaders),
+ currentOffset, 0, VAL32(pNT->OptionalHeader.SizeOfHeaders)));
+
+ currentAddress=currentOffset=VAL32(pNT->OptionalHeader.SizeOfHeaders);
+
+ PTR_IMAGE_SECTION_HEADER section = FindFirstSection(pNT);
+ PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(pNT->FileHeader.NumberOfSections);
+
+ CHECK(sectionEnd >= section);
+
+
+ while (section < sectionEnd)
+ {
+
+ //
+ // NOTE: the if condition is becuase of a design issue in the CLR and OS loader's remapping
+ // of PE32 headers to PE32+. Because IMAGE_NT_HEADERS64 is bigger than IMAGE_NT_HEADERS32,
+ // the remapping will expand this part of the header and push out the following
+ // IMAGE_SECTION_HEADER entries. When IMAGE_DOS_HEADER::e_lfanew is large enough (size is
+ // proportional to the number of tools used to produce the inputs to the C++ linker, and
+ // has become larger when producing some WinMD files) this can push the last section header
+ // beyond the boundary set by IMAGE_NT_HEADERS::OptionalHeader.SizeOfHeaders (e.g., this
+ // was recently seen where the unaligned size of the headers was 0x1f8 and SizeOfHeaders was
+ // 0x200, and the header remapping resulted in new headers size of 0x208). To compensate
+ // for this issue (it would be quite difficult to fix in the remapping code; see Dev11 430008)
+ // we assume that when the image is mapped that the needed validation has already been done.
+ //
+
+ if (!IsMapped())
+ {
+ CHECK(CheckBounds(dac_cast<PTR_CVOID>(pNT),VAL32(pNT->OptionalHeader.SizeOfHeaders),
+ section,sizeof(IMAGE_SECTION_HEADER)));
+ }
+
+ // Check flags
+ // Only allow a small list of characteristics
+ CHECK(!(section->Characteristics &
+ ~(VAL32((IMAGE_SCN_CNT_CODE |
+ IMAGE_SCN_CNT_INITIALIZED_DATA |
+ IMAGE_SCN_CNT_UNINITIALIZED_DATA|
+ IMAGE_SCN_MEM_DISCARDABLE |
+ IMAGE_SCN_MEM_NOT_CACHED |
+ IMAGE_SCN_MEM_NOT_PAGED |
+ IMAGE_SCN_MEM_EXECUTE |
+ IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE |
+ // allow shared sections for all images for now.
+ // we'll constrain this in CheckILOnly
+ IMAGE_SCN_MEM_SHARED)))));
+
+ // we should not allow writable code sections, check if both flags are set
+ CHECK((section->Characteristics & VAL32((IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_WRITE))) !=
+ VAL32((IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_WRITE)));
+
+ CHECK(CheckSection(currentAddress, VAL32(section->VirtualAddress), VAL32(section->Misc.VirtualSize),
+ currentOffset, VAL32(section->PointerToRawData), VAL32(section->SizeOfRawData)));
+
+ currentAddress = VAL32(section->VirtualAddress)
+ + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(pNT->OptionalHeader.SectionAlignment));
+ currentOffset = VAL32(section->PointerToRawData) + VAL32(section->SizeOfRawData);
+
+ section++;
+ }
+
+ // Now check that the COR data directory is either NULL, or exists entirely in one section.
+ {
+ PTR_IMAGE_DATA_DIRECTORY pCORDataDir = pDataDirectories + IMAGE_DIRECTORY_ENTRY_COMHEADER;
+ CHECK(CheckRva(VAL32(pCORDataDir->VirtualAddress), VAL32(pCORDataDir->Size), 0, NULL_OK));
+ }
+
+ // @todo: verify directory entries
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_NT_CHECKED;
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, COUNT_T addressSize,
+ COUNT_T previousOffsetEnd, COUNT_T offsetStart, COUNT_T offsetSize) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ // Fetch the NT header
+ IMAGE_NT_HEADERS *pNT = FindNTHeaders();
+
+ // OS will zero pad a mapped file up to file alignment size - some images rely on this
+ // COUNT_T alignedSize = AlignUp(m_size, VAL32(pNT->OptionalHeader.FileAlignment));
+ COUNT_T alignedSize = IsMapped() ? AlignUp(m_size, VAL32(pNT->OptionalHeader.FileAlignment)) : m_size;
+
+ // Check to make sure that our memory is big enough to cover the stated range.
+ // Note that this check is only required if we have a non-flat image.
+ if (IsMapped())
+ CHECK(alignedSize >= VAL32(pNT->OptionalHeader.SizeOfImage));
+
+ // Check expected alignments
+ CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment)));
+ CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment)));
+ CHECK(CheckAligned(offsetSize, VAL32(pNT->OptionalHeader.FileAlignment)));
+
+ // addressSize is typically not aligned, so we align it for purposes of checks.
+ COUNT_T alignedAddressSize = AlignUp(addressSize, VAL32(pNT->OptionalHeader.SectionAlignment));
+ CHECK(addressSize <= alignedAddressSize);
+
+ // Check overflow
+ CHECK(CheckOverflow(addressStart, alignedAddressSize));
+ CHECK(CheckOverflow(offsetStart, offsetSize));
+
+ // Make sure we don't overlap the previous section
+ CHECK(addressStart >= previousAddressEnd
+ && (offsetSize == 0
+ || offsetStart >= previousOffsetEnd));
+
+ // Make sure we don't overrun the end of the mapped image
+ CHECK(addressStart + alignedAddressSize <= VAL32(pNT->OptionalHeader.SizeOfImage));
+
+ // Make sure we don't overrun the end of the file (only relevant if we're not mapped, otherwise
+ // we don't know the file size, as it's not declared in the headers.)
+ if (!IsMapped())
+ CHECK(offsetStart + offsetSize <= alignedSize);
+
+ // Make sure the data doesn't overrun the virtual address space
+ CHECK(offsetSize <= alignedAddressSize);
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckDirectoryEntry(int entry, int forbiddenFlags, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(entry < IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
+ PRECONDITION(HasDirectoryEntry(entry));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckDirectory(GetDirectoryEntry(entry), forbiddenFlags, ok));
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckPointer(pDir));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckRva(VAL32(pDir->VirtualAddress), VAL32(pDir->Size), forbiddenFlags, ok));
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckRva(RVA rva, COUNT_T size, int forbiddenFlags, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ if (rva == 0)
+ {
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ IMAGE_SECTION_HEADER *section = RvaToSection(rva);
+
+ CHECK(section != NULL);
+
+ CHECK(CheckBounds(VAL32(section->VirtualAddress),
+ // AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment)),
+ (UINT)VAL32(section->Misc.VirtualSize),
+ rva, size));
+ if(!IsMapped())
+ {
+ CHECK(CheckBounds(VAL32(section->VirtualAddress), VAL32(section->SizeOfRawData), rva, size));
+ }
+
+ if (forbiddenFlags!=0)
+ CHECK((section->Characteristics & VAL32(forbiddenFlags))==0);
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckRva(RVA rva, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ if (rva == 0)
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ else
+ CHECK(RvaToSection(rva) != NULL);
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckOffset(COUNT_T fileOffset, COUNT_T size, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (fileOffset == 0)
+ {
+ CHECK_MSG(ok == NULL_OK, "zero fileOffset illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ IMAGE_SECTION_HEADER *section = OffsetToSection(fileOffset);
+
+ CHECK(section != NULL);
+
+ CHECK(CheckBounds(section->PointerToRawData, section->SizeOfRawData,
+ fileOffset, size));
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckOffset(COUNT_T fileOffset, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (fileOffset == NULL)
+ CHECK_MSG(ok == NULL_OK, "Null pointer illegal");
+ else
+ {
+ CHECK(OffsetToSection(fileOffset) != NULL);
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckData(const void *data, COUNT_T size, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (data == NULL)
+ {
+ CHECK_MSG(ok == NULL_OK, "NULL pointer illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ CHECK(CheckUnderflow(data, m_base));
+ CHECK((UINT_PTR) (((BYTE *) data) - ((BYTE *) m_base)) <= COUNT_T_MAX);
+
+ if (IsMapped())
+ CHECK(CheckRva((COUNT_T) ((BYTE *) data - (BYTE *) m_base), size));
+ else
+ CHECK(CheckOffset((COUNT_T) ((BYTE *) data - (BYTE *) m_base), size));
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckData(const void *data, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (data == NULL)
+ CHECK_MSG(ok == NULL_OK, "Null pointer illegal");
+ else
+ {
+ CHECK(CheckUnderflow(data, m_base));
+ CHECK((UINT_PTR) (((BYTE *) data) - ((BYTE *) m_base)) <= COUNT_T_MAX);
+
+ if (IsMapped())
+ CHECK(CheckRva((COUNT_T) ((BYTE *) data - (BYTE *) m_base)));
+ else
+ CHECK(CheckOffset((COUNT_T) ((BYTE *) data - (BYTE *) m_base)));
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckInternalAddress(SIZE_T address, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ if (address == 0)
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ else
+ CHECK(RvaToSection(InternalAddressToRva(address)) != NULL);
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckInternalAddress(SIZE_T address, COUNT_T size, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ if (address == 0)
+ {
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ CHECK(CheckRva(InternalAddressToRva(address), size));
+ }
+
+ CHECK_OK;
+}
+
+RVA PEDecoder::InternalAddressToRva(SIZE_T address) const
+{
+ CONTRACT(RVA)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckRva(RETVAL));
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ if (m_flags & FLAG_RELOCATED)
+ {
+ // Address has been fixed up
+ RETURN (RVA) ((BYTE *) address - (BYTE *) m_base);
+ }
+ else
+ {
+ // Address has not been fixed up
+ RETURN (RVA) (address - (SIZE_T) GetPreferredBase());
+ }
+}
+
+// Returns a pointer to the named section or NULL if not found.
+// The name should include the starting "." as well.
+IMAGE_SECTION_HEADER *PEDecoder::FindSection(LPCSTR sectionName) const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(sectionName != NULL);
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ SO_TOLERANT;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ // Ensure that the section name length is valid
+ SIZE_T iSectionNameLength = strlen(sectionName);
+ if ((iSectionNameLength < 1) || (iSectionNameLength > IMAGE_SIZEOF_SHORT_NAME))
+ {
+ _ASSERTE(!"Invalid section name!");
+ RETURN NULL;
+ }
+
+ // Get the start and ends of the sections
+ PTR_IMAGE_SECTION_HEADER pSection = FindFirstSection(FindNTHeaders());
+ _ASSERTE(pSection != NULL);
+ PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+ _ASSERTE(pSectionEnd != NULL);
+
+ BOOL fFoundSection = FALSE;
+
+ // Loop thru the sections and see if we got the section we are interested in
+ while (pSection < pSectionEnd)
+ {
+ // Is this the section we are looking for?
+ if (strncmp(sectionName, (char*)pSection->Name, iSectionNameLength) == 0)
+ {
+ // We found our section - break out of the loop
+ fFoundSection = TRUE;
+ break;
+ }
+
+ // Move to the next section
+ pSection++;
+ }
+
+ if (TRUE == fFoundSection)
+ RETURN pSection;
+ else
+ RETURN NULL;
+}
+
+IMAGE_SECTION_HEADER *PEDecoder::RvaToSection(RVA rva) const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ PTR_IMAGE_SECTION_HEADER section = dac_cast<PTR_IMAGE_SECTION_HEADER>(FindFirstSection(FindNTHeaders()));
+ PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ while (section < sectionEnd)
+ {
+ if (rva < (VAL32(section->VirtualAddress)
+ + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment))))
+ {
+ if (rva < VAL32(section->VirtualAddress))
+ RETURN NULL;
+ else
+ {
+ RETURN section;
+ }
+ }
+
+ section++;
+ }
+
+ RETURN NULL;
+}
+
+IMAGE_SECTION_HEADER *PEDecoder::OffsetToSection(COUNT_T fileOffset) const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ PTR_IMAGE_SECTION_HEADER section = dac_cast<PTR_IMAGE_SECTION_HEADER>(FindFirstSection(FindNTHeaders()));
+ PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ while (section < sectionEnd)
+ {
+ if (fileOffset < section->PointerToRawData + section->SizeOfRawData)
+ {
+ if (fileOffset < section->PointerToRawData)
+ RETURN NULL;
+ else
+ RETURN section;
+ }
+
+ section++;
+ }
+
+ RETURN NULL;
+}
+
+TADDR PEDecoder::GetRvaData(RVA rva, IsNullOK ok /*= NULL_NOT_OK*/) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckRva(rva, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if ((rva == 0)&&(ok == NULL_NOT_OK))
+ RETURN NULL;
+
+ RVA offset;
+ if (IsMapped())
+ offset = rva;
+ else
+ {
+ // !!! check for case where rva is in padded portion of segment
+ offset = RvaToOffset(rva);
+ }
+
+ RETURN( m_base + offset );
+}
+
+RVA PEDecoder::GetDataRva(const TADDR data) const
+{
+ CONTRACT(RVA)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckData((void *)data, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (data == NULL)
+ RETURN 0;
+
+ COUNT_T offset = (COUNT_T) (data - m_base);
+ if (IsMapped())
+ RETURN offset;
+ else
+ RETURN OffsetToRva(offset);
+}
+
+BOOL PEDecoder::PointerInPE(PTR_CVOID data) const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ TADDR taddrData = dac_cast<TADDR>(data);
+ TADDR taddrBase = dac_cast<TADDR>(m_base);
+
+ if (this->IsMapped())
+ {
+ return taddrBase <= taddrData && taddrData < taddrBase + GetVirtualSize();
+ }
+ else
+ {
+ return taddrBase <= taddrData && taddrData < taddrBase + GetSize();
+ }
+}
+
+TADDR PEDecoder::GetOffsetData(COUNT_T fileOffset, IsNullOK ok /*= NULL_NOT_OK*/) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckOffset(fileOffset, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if ((fileOffset == 0)&&(ok == NULL_NOT_OK))
+ RETURN NULL;
+
+ RETURN GetRvaData(OffsetToRva(fileOffset));
+}
+
+//-------------------------------------------------------------------------------
+// Lifted from "..\md\inc\mdfileformat.h"
+// (cannot #include it here because it references lot of other stuff)
+#define STORAGE_MAGIC_SIG 0x424A5342 // BSJB
+struct STORAGESIGNATURE
+{
+ ULONG lSignature; // "Magic" signature.
+ USHORT iMajorVer; // Major file version.
+ USHORT iMinorVer; // Minor file version.
+ ULONG iExtraData; // Offset to next structure of information
+ ULONG iVersionString; // Length of version string
+};
+typedef STORAGESIGNATURE UNALIGNED * PSTORAGESIGNATURE;
+typedef DPTR(STORAGESIGNATURE UNALIGNED) PTR_STORAGESIGNATURE;
+
+
+struct STORAGEHEADER
+{
+ BYTE fFlags; // STGHDR_xxx flags.
+ BYTE pad;
+ USHORT iStreams; // How many streams are there.
+};
+typedef STORAGEHEADER UNALIGNED * PSTORAGEHEADER;
+typedef DPTR(STORAGEHEADER UNALIGNED) PTR_STORAGEHEADER;
+
+
+struct STORAGESTREAM
+{
+ ULONG iOffset; // Offset in file for this stream.
+ ULONG iSize; // Size of the file.
+ char rcName[32]; // Start of name, null terminated.
+};
+typedef STORAGESTREAM UNALIGNED * PSTORAGESTREAM;
+typedef DPTR(STORAGESTREAM UNALIGNED) PTR_STORAGESTREAM;
+
+
+// if the stream's name is shorter than 32 bytes (incl.zero terminator),
+// the size of storage stream header is less than sizeof(STORAGESTREAM)
+// and is padded to 4-byte alignment
+inline PTR_STORAGESTREAM NextStorageStream(PTR_STORAGESTREAM pSS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC;
+ TADDR pc = dac_cast<TADDR>(pSS);
+ pc += (sizeof(STORAGESTREAM) - 32 /*sizeof(STORAGESTREAM::rcName)*/ + strlen(pSS->rcName)+1+3)&~3;
+ return PTR_STORAGESTREAM(pc);
+}
+//-------------------------------------------------------------------------------
+
+
+CHECK PEDecoder::CheckCorHeader() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+ if (m_flags & FLAG_COR_CHECKED)
+ CHECK_OK;
+
+ CHECK(CheckNTHeaders());
+
+ CHECK(HasCorHeader());
+
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_COMHEADER);
+
+ CHECK(CheckDirectory(pDir, IMAGE_SCN_MEM_WRITE, NULL_NOT_OK));
+
+ CHECK(VAL32(pDir->Size) >= sizeof(IMAGE_COR20_HEADER));
+
+ IMAGE_SECTION_HEADER *section = RvaToSection(VAL32(pDir->VirtualAddress));
+ CHECK(section != NULL);
+ CHECK((section->Characteristics & VAL32(IMAGE_SCN_MEM_READ))!=0);
+
+ CHECK(CheckRva(VAL32(pDir->VirtualAddress), sizeof(IMAGE_COR20_HEADER)));
+
+ IMAGE_COR20_HEADER *pCor = GetCorHeader();
+
+ //CHECK(((ULONGLONG)pCor & 0x3)==0);
+
+ // If the file is COM+ 1.0, which by definition has nothing the runtime can
+ // use, or if the file requires a newer version of this engine than us,
+ // it cannot be run by this engine.
+ CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR);
+
+ CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, HasNativeHeader() ? NULL_OK : NULL_NOT_OK));
+ CHECK(CheckDirectory(&pCor->Resources, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pCor->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pCor->CodeManagerTable, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pCor->VTableFixups, 0, NULL_OK));
+ CHECK(CheckDirectory(&pCor->ExportAddressTableJumps, 0, NULL_OK));
+ CHECK(CheckDirectory(&pCor->ManagedNativeHeader, 0, NULL_OK));
+
+ CHECK(VAL32(pCor->cb) >= offsetof(IMAGE_COR20_HEADER, ManagedNativeHeader) + sizeof(IMAGE_DATA_DIRECTORY));
+
+ DWORD validBits = COMIMAGE_FLAGS_ILONLY
+ | COMIMAGE_FLAGS_32BITREQUIRED
+ | COMIMAGE_FLAGS_TRACKDEBUGDATA
+ | COMIMAGE_FLAGS_STRONGNAMESIGNED
+ | COMIMAGE_FLAGS_NATIVE_ENTRYPOINT
+ | COMIMAGE_FLAGS_IL_LIBRARY
+ | COMIMAGE_FLAGS_32BITPREFERRED;
+
+ CHECK((pCor->Flags&VAL32(~validBits)) == 0);
+
+ // Pure IL images should not have VTable fixups or EAT jumps
+ if (IsILOnly())
+ {
+ CHECK(pCor->VTableFixups.Size == VAL32(0));
+ CHECK(pCor->ExportAddressTableJumps.Size == VAL32(0));
+ CHECK(!(pCor->Flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)));
+ //@TODO: If not an exe, check that EntryPointToken is mdNil
+ }
+ else
+ {
+ if (pCor->Flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT))
+ {
+ CHECK(CheckRva(VAL32(IMAGE_COR20_HEADER_FIELD(*pCor,EntryPointToken))));
+ }
+ }
+
+ // Strong name signed images should have a signature
+ if (IsStrongNameSigned())
+ CHECK(HasStrongNameSignature());
+
+ // IL library files (really a misnomer - these are native images) only
+ // may have a native image header
+ if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0)
+ {
+ CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0);
+ }
+
+ // Metadata header checks
+ IMAGE_DATA_DIRECTORY *pDirMD = &pCor->MetaData;
+ COUNT_T ctMD = (COUNT_T)VAL32(pDirMD->Size);
+ TADDR pcMD = (TADDR)GetDirectoryData(pDirMD);
+
+ if(pcMD != NULL)
+ {
+ // Storage signature checks
+ CHECK(ctMD >= sizeof(STORAGESIGNATURE));
+ PTR_STORAGESIGNATURE pStorageSig = PTR_STORAGESIGNATURE((TADDR)pcMD);
+ COUNT_T ctMDStreamSize = ctMD; // Store MetaData stream size for later usage
+
+
+ CHECK(VAL32(pStorageSig->lSignature) == STORAGE_MAGIC_SIG);
+ COUNT_T ctSSig;
+ CHECK(ClrSafeInt<COUNT_T>::addition(sizeof(STORAGESIGNATURE), (COUNT_T)VAL32(pStorageSig->iVersionString), ctSSig));
+ CHECK(ctMD > ctSSig);
+
+ // Storage header checks
+ pcMD += ctSSig;
+
+ PTR_STORAGEHEADER pSHdr = PTR_STORAGEHEADER((TADDR)pcMD);
+
+
+ ctMD -= ctSSig;
+ CHECK(ctMD >= sizeof(STORAGEHEADER));
+ pcMD = dac_cast<TADDR>(pSHdr) + sizeof(STORAGEHEADER);
+ ctMD -= sizeof(STORAGEHEADER);
+ WORD nStreams = VAL16(pSHdr->iStreams);
+
+ // Storage streams checks (pcMD is a target pointer, so watch out)
+ PTR_STORAGESTREAM pStr = PTR_STORAGESTREAM((TADDR)pcMD);
+ PTR_STORAGESTREAM pSSOutOfRange =
+ PTR_STORAGESTREAM((TADDR)(pcMD + ctMD));
+ size_t namelen;
+ WORD iStr;
+ PTR_STORAGESTREAM pSS;
+ for(iStr = 1, pSS = pStr; iStr <= nStreams; iStr++)
+ {
+ CHECK(pSS < pSSOutOfRange);
+ CHECK(pSS + 1 <= pSSOutOfRange);
+
+ for(namelen=0; (namelen<32)&&(pSS->rcName[namelen]!=0); namelen++);
+ CHECK((0 < namelen)&&(namelen < 32));
+
+ // Is it ngen image?
+ if (!HasNativeHeader())
+ {
+ // Forbid HOT_MODEL_STREAM for non-ngen images
+ CHECK(strcmp(pSS->rcName, HOT_MODEL_STREAM_A) != 0);
+ }
+
+ pcMD = dac_cast<TADDR>(NextStorageStream(pSS));
+ ctMD -= (COUNT_T)(pcMD - dac_cast<TADDR>(pSS));
+
+ pSS = PTR_STORAGESTREAM((TADDR)pcMD);
+ }
+
+ // At this moment, pcMD is pointing past the last stream header
+ // and ctMD contains total size left for streams per se
+ // Now, check the offsets and sizes of streams
+ COUNT_T ctStreamsBegin = (COUNT_T)(pcMD - dac_cast<TADDR>(pStorageSig)); // min.possible offset
+ COUNT_T ctSS, ctSSbegin, ctSSend = 0;
+ for(iStr = 1, pSS = pStr; iStr <= nStreams; iStr++,pSS = NextStorageStream(pSS))
+ {
+ ctSSbegin = (COUNT_T)VAL32(pSS->iOffset);
+ CHECK(ctStreamsBegin <= ctSSbegin);
+ CHECK(ctSSbegin < ctMDStreamSize);
+
+ ctSS = (COUNT_T)VAL32(pSS->iSize);
+ CHECK(ctMD >= ctSS);
+ CHECK(ClrSafeInt<COUNT_T>::addition(ctSSbegin, ctSS, ctSSend));
+ CHECK(ctSSend <= ctMDStreamSize);
+ ctMD -= ctSS;
+
+ // Check stream overlap
+ PTR_STORAGESTREAM pSSprior;
+ for(pSSprior=pStr; pSSprior < pSS; pSSprior = NextStorageStream(pSSprior))
+ {
+ COUNT_T ctSSprior_end = 0;
+ CHECK(ClrSafeInt<COUNT_T>::addition((COUNT_T)VAL32(pSSprior->iOffset), (COUNT_T)VAL32(pSSprior->iSize), ctSSprior_end));
+ CHECK((ctSSbegin >= ctSSprior_end)||(ctSSend <= (COUNT_T)VAL32(pSSprior->iOffset)));
+ }
+ }
+ } //end if(pcMD != NULL)
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_COR_CHECKED;
+
+ CHECK_OK;
+}
+
+#if !defined(FEATURE_CORECLR)
+#define WHIDBEY_SP2_VERSION_MAJOR 2
+#define WHIDBEY_SP2_VERSION_MINOR 0
+#define WHIDBEY_SP2_VERSION_BUILD 50727
+#define WHIDBEY_SP2_VERSION_PRIVATE_BUILD 3053
+#endif // !defined(FEATURE_CORECLR)
+
+
+// This function exists to provide compatibility between two different native image
+// (NGEN) formats. In particular, the manifest metadata blob and the full metadata
+// blob swapped locations from 3.5RTM to 3.5SP1. The logic here is to look at the
+// runtime version embedded in the native image, to determine which format it is.
+IMAGE_DATA_DIRECTORY *PEDecoder::GetMetaDataHelper(METADATA_SECTION_TYPE type) const
+{
+ CONTRACT(IMAGE_DATA_DIRECTORY *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+#ifdef FEATURE_PREJIT
+ PRECONDITION(type == METADATA_SECTION_FULL || type == METADATA_SECTION_MANIFEST);
+ PRECONDITION(type != METADATA_SECTION_MANIFEST || HasNativeHeader());
+#else // FEATURE_PREJIT
+ PRECONDITION(type == METADATA_SECTION_FULL);
+#endif // FEATURE_PREJIT
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDirRet = &GetCorHeader()->MetaData;
+
+#ifdef FEATURE_PREJIT
+ // For backward compatibility reasons, we must be able to locate the metadata in all v2 native images as
+ // well as current version of native images. This is needed by mdbg.exe for SxS debugging scenarios.
+ // Specifically, mdbg.exe can be used to debug v2 managed applications, and need to be able to find
+ // metadata in v2 native images. Therefore, the location of the data we need to locate the metadata must
+ // never be moved. Here are some asserts to ensure that.
+ // IMAGE_COR20_HEADER should be stable since it is defined in ECMA. Verify a coupld of fields we use:
+ _ASSERTE(offsetof(IMAGE_COR20_HEADER, MetaData) == 8);
+ _ASSERTE(offsetof(IMAGE_COR20_HEADER, ManagedNativeHeader) == 64);
+ // We use a couple of fields in CORCOMPILE_HEADER.
+ _ASSERTE(offsetof(CORCOMPILE_HEADER, VersionInfo) == 40);
+ _ASSERTE(offsetof(CORCOMPILE_HEADER, ManifestMetaData) == 88);
+ // And we use four version fields in CORCOMPILE_VERSION_INFO.
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionMajor) == 4);
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionMinor) == 6);
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionBuildNumber) == 8);
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionPrivateBuildNumber) == 10);
+
+ // Visual Studio took dependency on crossgen /CreatePDB returning COR_E_NI_AND_RUNTIME_VERSION_MISMATCH
+ // when crossgen and the native image come from different runtimes. In order to reach error path that returns
+ // COR_E_NI_AND_RUNTIME_VERSION_MISMATCH in this case, size of CORCOMPILE_HEADER has to remain constant to pass earlier
+ // checks that lead to different error codes. See Windows Phone Blue Bug #45406 for details.
+ _ASSERTE(sizeof(CORCOMPILE_HEADER) == 160 + sizeof(TADDR));
+
+ // Handle NGEN format; otherwise, there is only one MetaData section in the
+ // COR_HEADER and so the value of pDirRet is correct
+ if (HasNativeHeader())
+ {
+#ifdef FEATURE_CORECLR
+
+ if (type == METADATA_SECTION_MANIFEST)
+ pDirRet = &GetNativeHeader()->ManifestMetaData;
+
+#else // FEATURE_CORECLR
+
+ IMAGE_DATA_DIRECTORY *pDirNativeHeader = &GetNativeHeader()->ManifestMetaData;
+
+ // This code leverages the fact that pre-Whidbey SP2 private build numbers can never
+ // be greater than Whidbey SP2 private build number, because otherwise major setup
+ // issues would arise. To prevent this, it is standard to bump the private build
+ // number up a significant amount for SPs, as was the case between Whidbey SP1 and
+ // Whidbey SP2.
+ //
+ // Since we could be reading an older version of native image, we tell
+ // GetNativeVersionInfoMaybeNull to skip checking the native header.
+ CORCOMPILE_VERSION_INFO *pVerInfo = GetNativeVersionInfoMaybeNull(true);
+ bool fIsPreWhidbeySP2 = false;
+
+ // If pVerInfo is NULL, we assume that we're in an NGEN compilation domain and that
+ // the information has not yet been written. Since an NGEN compilation domain running
+ // in the v4.0 runtime can only complie v4.0 native images, we'll assume the default
+ // fIsPreWhidbeySP2 value (false) is correct.
+ if (pVerInfo != NULL && pVerInfo->wVersionMajor <= WHIDBEY_SP2_VERSION_MAJOR)
+ {
+ if (pVerInfo->wVersionMajor < WHIDBEY_SP2_VERSION_MAJOR)
+ fIsPreWhidbeySP2 = true;
+ else if (pVerInfo->wVersionMajor == WHIDBEY_SP2_VERSION_MAJOR)
+ {
+ // If the sp2 minor version isn't 0, we need this logic:
+ // if (pVerInfo->wVersionMinor < WHIDBEY_SP2_VERSION_MINOR)
+ // fIsPreWhidbeySP2 = true;
+ // else
+ // However, if it is zero, with that logic we get a warning about the comparison
+ // of an unsigned variable to zero always being false.
+ _ASSERTE(WHIDBEY_SP2_VERSION_MINOR == 0);
+
+ if (pVerInfo->wVersionMinor == WHIDBEY_SP2_VERSION_MINOR)
+ {
+ if (pVerInfo->wVersionBuildNumber < WHIDBEY_SP2_VERSION_BUILD)
+ fIsPreWhidbeySP2 = true;
+ else if (pVerInfo->wVersionBuildNumber == WHIDBEY_SP2_VERSION_BUILD)
+ {
+ if (pVerInfo->wVersionPrivateBuildNumber < WHIDBEY_SP2_VERSION_PRIVATE_BUILD)
+ fIsPreWhidbeySP2 = true;
+ }
+ }
+ }
+ }
+
+ // In pre-Whidbey SP2, pDirRet points to manifest and pDirNativeHeader points to full.
+ if (fIsPreWhidbeySP2)
+ {
+ if (type == METADATA_SECTION_FULL)
+ pDirRet = pDirNativeHeader;
+ }
+ // In Whidbey SP2 and later, pDirRet points to full and pDirNativeHeader points to manifest.
+ else
+ {
+ if (type == METADATA_SECTION_MANIFEST)
+ pDirRet = pDirNativeHeader;
+ }
+
+#endif // FEATURE_CORECLR
+
+ }
+
+#endif // FEATURE_PREJIT
+
+ RETURN pDirRet;
+}
+
+PTR_CVOID PEDecoder::GetMetadata(COUNT_T *pSize) const
+{
+ CONTRACT(PTR_CVOID)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = GetMetaDataHelper(METADATA_SECTION_FULL);
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir));
+}
+
+const void *PEDecoder::GetResources(COUNT_T *pSize) const
+{
+ CONTRACT(const void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (void *)GetDirectoryData(pDir);
+}
+
+CHECK PEDecoder::CheckResource(COUNT_T offset) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCorHeader());
+ }
+ CONTRACT_CHECK_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;
+
+ CHECK(CheckOverflow(VAL32(pDir->VirtualAddress), offset));
+
+ RVA rva = VAL32(pDir->VirtualAddress) + offset;
+
+ // Make sure we have at least enough data for a length
+ CHECK(CheckRva(rva, sizeof(DWORD)));
+
+ // Make sure resource is within resource section
+ CHECK(CheckBounds(VAL32(pDir->VirtualAddress), VAL32(pDir->Size),
+ rva + sizeof(DWORD), GET_UNALIGNED_VAL32((LPVOID)GetRvaData(rva))));
+
+ CHECK_OK;
+}
+
+const void *PEDecoder::GetResource(COUNT_T offset, COUNT_T *pSize) const
+{
+ CONTRACT(const void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;
+
+ // 403571: Prefix complained correctly about need to always perform rva check
+ if (CheckResource(offset) == FALSE)
+ return NULL;
+
+ void * resourceBlob = (void *)GetRvaData(VAL32(pDir->VirtualAddress) + offset);
+ // Holds if CheckResource(offset) == TRUE
+ PREFIX_ASSUME(resourceBlob != NULL);
+
+ if (pSize != NULL)
+ *pSize = GET_UNALIGNED_VAL32(resourceBlob);
+
+ RETURN (const void *) ((BYTE*)resourceBlob+sizeof(DWORD));
+}
+
+BOOL PEDecoder::HasManagedEntryPoint() const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ ULONG flags = GetCorHeader()->Flags;
+ return (!(flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) &&
+ (!IsNilToken(GetEntryPointToken())));
+}
+
+ULONG PEDecoder::GetEntryPointToken() const
+{
+ CONTRACT(ULONG)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN VAL32(IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken));
+}
+
+IMAGE_COR_VTABLEFIXUP *PEDecoder::GetVTableFixups(COUNT_T *pCount) const
+{
+ CONTRACT(IMAGE_COR_VTABLEFIXUP *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->VTableFixups;
+
+ if (pCount != NULL)
+ *pCount = VAL32(pDir->Size)/sizeof(IMAGE_COR_VTABLEFIXUP);
+
+ RETURN PTR_IMAGE_COR_VTABLEFIXUP(GetDirectoryData(pDir));
+}
+
+CHECK PEDecoder::CheckILOnly() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (m_flags & FLAG_IL_ONLY_CHECKED)
+ CHECK_OK;
+
+ CHECK(CheckCorHeader());
+
+
+ // Allow only verifiable directories.
+
+ static int s_allowedBitmap =
+ ((1 << (IMAGE_DIRECTORY_ENTRY_IMPORT )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_RESOURCE )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_SECURITY )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_BASERELOC)) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_DEBUG )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_IAT )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_COMHEADER)));
+
+
+
+
+ for (UINT32 entry=0; entry<GetNumberOfRvaAndSizes(); ++entry)
+ {
+ if (Has32BitNTHeaders())
+ CheckBounds(dac_cast<PTR_CVOID>(&GetNTHeaders32()->OptionalHeader),
+ GetNTHeaders32()->FileHeader.SizeOfOptionalHeader,
+ dac_cast<PTR_CVOID>(GetNTHeaders32()->OptionalHeader.DataDirectory + entry),
+ sizeof(IMAGE_DATA_DIRECTORY));
+ else
+ CheckBounds(dac_cast<PTR_CVOID>(&GetNTHeaders64()->OptionalHeader),
+ GetNTHeaders32()->FileHeader.SizeOfOptionalHeader,
+ dac_cast<PTR_CVOID>(GetNTHeaders64()->OptionalHeader.DataDirectory + entry),
+ sizeof(IMAGE_DATA_DIRECTORY));
+
+ if (HasDirectoryEntry(entry))
+ {
+ CHECK((s_allowedBitmap & (1 << entry)) != 0);
+ if (entry!=IMAGE_DIRECTORY_ENTRY_SECURITY) //ignored by OS loader
+ CHECK(CheckDirectoryEntry(entry,IMAGE_SCN_MEM_SHARED,NULL_NOT_OK));
+ }
+ }
+ if (HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) ||
+ HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC) ||
+ FindNTHeaders()->OptionalHeader.AddressOfEntryPoint != 0)
+ {
+ // When the image is LoadLibrary'd, we whack the import, IAT directories and the entrypoint. We have to relax
+ // the verification for mapped images. Ideally, we would only do it for a post-LoadLibrary image.
+ if (!IsMapped() || (HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) || HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC)))
+ {
+ CHECK(CheckILOnlyImportDlls());
+ CHECK(CheckILOnlyBaseRelocations());
+ }
+
+#ifdef _TARGET_X86_
+ if (!IsMapped())
+ {
+ CHECK(CheckILOnlyEntryPoint());
+ }
+#endif
+ }
+
+ // Check some section characteristics
+ IMAGE_NT_HEADERS *pNT = FindNTHeaders();
+ IMAGE_SECTION_HEADER *section = FindFirstSection(pNT);
+ IMAGE_SECTION_HEADER *sectionEnd = section + VAL16(pNT->FileHeader.NumberOfSections);
+ while (section < sectionEnd)
+ {
+ // Don't allow shared sections for IL-only images
+ CHECK(!(section->Characteristics & IMAGE_SCN_MEM_SHARED));
+
+ // Be sure that we have some access to the section. Note that this test assumes that
+ // execute or write permissions will also let us read the section. If that is found to be an
+ // incorrect assumption, this will need to be modified.
+ CHECK((section->Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) != 0);
+ section++;
+ }
+
+ // For EXE, check that OptionalHeader.Win32VersionValue is zero. When this value is non-zero, GetVersionEx
+ // returns PE supplied values, rather than native OS values; the runtime relies on knowing the actual
+ // OS version.
+ if (!IsDll())
+ {
+ CHECK(GetWin32VersionValue() == 0);
+ }
+
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_IL_ONLY_CHECKED;
+
+ CHECK_OK;
+}
+
+
+CHECK PEDecoder::CheckILOnlyImportDlls() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ // The only allowed DLL Imports are MscorEE.dll:_CorExeMain,_CorDllMain
+
+#ifdef _WIN64
+ // On win64, when the image is LoadLibrary'd, we whack the import and IAT directories. We have to relax
+ // the verification for mapped images. Ideally, we would only do it for a post-LoadLibrary image.
+ if (IsMapped() && !HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT))
+ CHECK_OK;
+#endif
+
+ CHECK(HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT));
+ CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_SCN_MEM_WRITE));
+
+ // Get the import directory entry
+ PIMAGE_DATA_DIRECTORY pDirEntryImport = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT);
+ CHECK(pDirEntryImport != NULL);
+ PREFIX_ASSUME(pDirEntryImport != NULL);
+
+ // There should be space for 2 entries. (mscoree and NULL)
+ CHECK(VAL32(pDirEntryImport->Size) >= (2 * sizeof(IMAGE_IMPORT_DESCRIPTOR)));
+
+ // Get the import data
+ PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR) GetDirectoryData(pDirEntryImport);
+ CHECK(pID != NULL);
+ PREFIX_ASSUME(pID != NULL);
+
+ // Entry 0: ILT, Name, IAT must be be non-null. Forwarder, DateTime should be NULL.
+ CHECK( IMAGE_IMPORT_DESC_FIELD(pID[0], Characteristics) != 0
+ && pID[0].TimeDateStamp == 0
+ && (pID[0].ForwarderChain == 0 || pID[0].ForwarderChain == static_cast<ULONG>(-1))
+ && pID[0].Name != 0
+ && pID[0].FirstThunk != 0);
+
+ // Entry 1: must be all nulls.
+ CHECK( IMAGE_IMPORT_DESC_FIELD(pID[1], Characteristics) == 0
+ && pID[1].TimeDateStamp == 0
+ && pID[1].ForwarderChain == 0
+ && pID[1].Name == 0
+ && pID[1].FirstThunk == 0);
+
+ // Ensure the RVA of the name plus its length is valid for this image
+ UINT nameRVA = VAL32(pID[0].Name);
+ CHECK(CheckRva(nameRVA, (COUNT_T) sizeof("mscoree.dll")));
+
+ // Make sure the name is equal to mscoree
+ CHECK(SString::_stricmp( (char *)GetRvaData(nameRVA), "mscoree.dll") == 0);
+
+ // Check the Hint/Name table.
+ CHECK(CheckILOnlyImportByNameTable(VAL32(IMAGE_IMPORT_DESC_FIELD(pID[0], OriginalFirstThunk))));
+
+ // The IAT needs to be checked only for size.
+ CHECK(CheckRva(VAL32(pID[0].FirstThunk), 2*sizeof(UINT32)));
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckILOnlyImportByNameTable(RVA rva) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ // Check if we have enough space to hold 2 DWORDS
+ CHECK(CheckRva(rva, 2*sizeof(UINT32)));
+
+ UINT32 UNALIGNED *pImportArray = (UINT32 UNALIGNED *) GetRvaData(rva);
+
+ CHECK(GET_UNALIGNED_VAL32(&pImportArray[0]) != 0);
+ CHECK(GET_UNALIGNED_VAL32(&pImportArray[1]) == 0);
+
+ UINT32 importRVA = GET_UNALIGNED_VAL32(&pImportArray[0]);
+
+ // First bit Set implies Ordinal lookup
+ CHECK((importRVA & 0x80000000) == 0);
+
+#define DLL_NAME "_CorDllMain"
+#define EXE_NAME "_CorExeMain"
+
+ static_assert_no_msg(sizeof(DLL_NAME) == sizeof(EXE_NAME));
+
+ // Check if we have enough space to hold 2 bytes +
+ // _CorExeMain or _CorDllMain and a NULL char
+ CHECK(CheckRva(importRVA, offsetof(IMAGE_IMPORT_BY_NAME, Name) + sizeof(DLL_NAME)));
+
+ IMAGE_IMPORT_BY_NAME *import = (IMAGE_IMPORT_BY_NAME*) GetRvaData(importRVA);
+
+ CHECK(SString::_stricmp((char *) import->Name, DLL_NAME) == 0 || _stricmp((char *) import->Name, EXE_NAME) == 0);
+
+ CHECK_OK;
+}
+
+#ifdef _TARGET_X86_
+// jmp dword ptr ds:[XXXX]
+#define JMP_DWORD_PTR_DS_OPCODE { 0xFF, 0x25 }
+#define JMP_DWORD_PTR_DS_OPCODE_SIZE 2 // Size of opcode
+#define JMP_SIZE 6 // Size of opcode + operand
+#endif
+
+CHECK PEDecoder::CheckILOnlyBaseRelocations() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC))
+ {
+ // We require base relocs for dlls.
+ CHECK(!IsDll());
+
+ CHECK((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) != 0);
+ }
+ else
+ {
+ CHECK((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) == 0);
+
+ CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_SCN_MEM_WRITE));
+
+ IMAGE_DATA_DIRECTORY *pRelocDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+
+ IMAGE_SECTION_HEADER *section = RvaToSection(VAL32(pRelocDir->VirtualAddress));
+ CHECK(section != NULL);
+ CHECK((section->Characteristics & VAL32(IMAGE_SCN_MEM_READ))!=0);
+
+ IMAGE_BASE_RELOCATION *pReloc = (IMAGE_BASE_RELOCATION *)
+ GetRvaData(VAL32(pRelocDir->VirtualAddress));
+
+ // 403569: PREfix correctly complained about pReloc being possibly NULL
+ CHECK(pReloc != NULL);
+ CHECK(VAL32(pReloc->SizeOfBlock) == VAL32(pRelocDir->Size));
+
+ UINT16 *pRelocEntry = (UINT16 *) (pReloc + 1);
+ UINT16 *pRelocEntryEnd = (UINT16 *) ((BYTE *) pReloc + VAL32(pReloc->SizeOfBlock));
+ if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_IA64))
+ {
+ // Exactly 2 Reloc records, both IMAGE_REL_BASED_DIR64
+ CHECK(VAL32(pReloc->SizeOfBlock) >= (sizeof(IMAGE_BASE_RELOCATION)+2*sizeof(UINT16)));
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12));
+ pRelocEntry++;
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12));
+ }
+ else
+ {
+ // Only one Reloc record is expected
+ CHECK(VAL32(pReloc->SizeOfBlock) >= (sizeof(IMAGE_BASE_RELOCATION)+sizeof(UINT16)));
+ if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_AMD64))
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12));
+ else
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_HIGHLOW << 12));
+ }
+
+ while (++pRelocEntry < pRelocEntryEnd)
+ {
+ // NULL padding entries are allowed
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == IMAGE_REL_BASED_ABSOLUTE);
+ }
+ }
+
+ CHECK_OK;
+}
+
+#ifdef _TARGET_X86_
+CHECK PEDecoder::CheckILOnlyEntryPoint() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint != 0);
+
+ if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_I386))
+ {
+ // EntryPoint should be a jmp dword ptr ds:[XXXX] instruction.
+ // XXXX should be RVA of the first and only entry in the IAT.
+
+ CHECK(CheckRva(VAL32(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint), JMP_SIZE));
+
+ BYTE *stub = (BYTE *) GetRvaData(VAL32(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint));
+
+ static const BYTE s_DllOrExeMain[] = JMP_DWORD_PTR_DS_OPCODE;
+
+ // 403570: prefix complained about stub being possibly NULL.
+ // Unsure here. PREFIX_ASSUME might be also correct as indices are
+ // verified in the above CHECK statement.
+ CHECK(stub != NULL);
+ CHECK(memcmp(stub, s_DllOrExeMain, JMP_DWORD_PTR_DS_OPCODE_SIZE) == 0);
+
+ // Verify target of jump - it should be first entry in the IAT.
+
+ PIMAGE_IMPORT_DESCRIPTOR pID =
+ (PIMAGE_IMPORT_DESCRIPTOR) GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_IMPORT);
+
+ UINT32 va = * (UINT32 *) (stub + JMP_DWORD_PTR_DS_OPCODE_SIZE);
+
+ CHECK(VAL32(pID[0].FirstThunk) == (va - (SIZE_T) GetPreferredBase()));
+ }
+
+ CHECK_OK;
+}
+#endif // _TARGET_X86_
+
+#ifndef DACCESS_COMPILE
+
+void PEDecoder::LayoutILOnly(void *base, BOOL allowFullPE) const
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(allowFullPE || CheckILOnlyFormat());
+ PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage)));
+ // Ideally we would require the layout address to honor the section alignment constraints.
+ // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this
+ // case, we can only guarantee OS page alignment (which after all, is good enough.)
+ PRECONDITION(CheckAligned((SIZE_T)base, OS_PAGE_SIZE));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // We're going to copy everything first, and write protect what we need to later.
+
+ // First, copy headers
+ CopyMemory(base, (void *)m_base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders));
+
+ // Now, copy all sections to appropriate virtual address
+
+ IMAGE_SECTION_HEADER *sectionStart = IMAGE_FIRST_SECTION(FindNTHeaders());
+ IMAGE_SECTION_HEADER *sectionEnd = sectionStart + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ IMAGE_SECTION_HEADER *section = sectionStart;
+ while (section < sectionEnd)
+ {
+ // Raw data may be less than section size if tail is zero, but may be more since VirtualSize is
+ // not padded.
+ DWORD size = min(VAL32(section->SizeOfRawData), VAL32(section->Misc.VirtualSize));
+
+ CopyMemory((BYTE *) base + VAL32(section->VirtualAddress), (BYTE *) m_base + VAL32(section->PointerToRawData), size);
+
+ // Note that our memory is zeroed already, so no need to initialize any tail.
+
+ section++;
+ }
+
+ // Apply write protection to copied headers
+ DWORD oldProtection;
+ if (!ClrVirtualProtect((void *) base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders),
+ PAGE_READONLY, &oldProtection))
+ ThrowLastError();
+
+ // Finally, apply proper protection to copied sections
+ section = sectionStart;
+ while (section < sectionEnd)
+ {
+ // Add appropriate page protection.
+ if ((section->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) == 0)
+ {
+ if (!ClrVirtualProtect((void *) ((BYTE *)base + VAL32(section->VirtualAddress)),
+ VAL32(section->Misc.VirtualSize),
+ PAGE_READONLY, &oldProtection))
+ ThrowLastError();
+ }
+
+ section++;
+ }
+
+ RETURN;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+#ifndef FEATURE_PAL
+void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize /*=NULL*/) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(IsMapped());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ if (pSize != NULL)
+ *pSize = 0;
+
+ HMODULE hModule = (HMODULE) dac_cast<TADDR>(GetBase());
+
+ // Use the Win32 functions to decode the resources
+
+ HRSRC hResource = WszFindResourceEx(hModule, lpType, lpName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
+ if (!hResource)
+ return NULL;
+
+ HGLOBAL hLoadedResource = ::LoadResource(hModule, hResource);
+ if (!hLoadedResource)
+ return NULL;
+
+ PVOID pResource = ::LockResource(hLoadedResource);
+ if (!pResource)
+ return NULL;
+
+ if (pSize != NULL)
+ *pSize = ::SizeofResource(hModule, hResource);
+
+ return pResource;
+}
+#endif // FEATURE_PAL
+
+BOOL PEDecoder::HasNativeHeader() const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+#ifdef FEATURE_PREJIT
+ // Pretend that ready-to-run images do not have native header
+ RETURN (((GetCorHeader()->Flags & VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) != 0) && !HasReadyToRunHeader());
+#else
+ RETURN FALSE;
+#endif
+}
+
+CHECK PEDecoder::CheckNativeHeader() const
+{
+ CONTRACT_CHECK
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACT_CHECK_END;
+
+#ifdef FEATURE_PREJIT
+ if (m_flags & FLAG_NATIVE_CHECKED)
+ CHECK_OK;
+
+ CHECK(CheckCorHeader());
+
+ CHECK(HasNativeHeader());
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader;
+
+ CHECK(CheckDirectory(pDir));
+ CHECK(VAL32(pDir->Size) == sizeof(CORCOMPILE_HEADER));
+
+#if 0
+ // We want to be sure to not trigger these checks when loading a native
+ // image in a retail build
+
+ // And we do not want to trigger these checks in debug builds either to avoid debug/retail behavior
+ // differences.
+
+ PTR_CORCOMPILE_HEADER pHeader = PTR_CORCOMPILE_HEADER((TADDR)GetDirectoryData(pDir));
+
+ CHECK(CheckDirectory(&pHeader->EEInfoTable));
+ CHECK(pHeader->EEInfoTable.Size == sizeof(CORCOMPILE_EE_INFO_TABLE));
+
+ CHECK(CheckDirectory(&pHeader->HelperTable, 0, NULL_OK));
+ // @todo: verify helper table size
+
+ CHECK(CheckDirectory(&pHeader->ImportSections, 0, NULL_OK));
+ // @todo verify import sections
+
+ CHECK(CheckDirectory(&pHeader->ImportTable, 0, NULL_OK));
+ // @todo verify import table
+
+ CHECK(CheckDirectory(&pHeader->VersionInfo, 0, NULL_OK)); // no version header for precompiled netmodules
+ CHECK(pHeader->VersionInfo.Size == 0
+ || (pHeader->VersionInfo.Size == sizeof(CORCOMPILE_VERSION_INFO) &&
+ // Sanity check that we are not just pointing to zeroed-out memory
+ ((CORCOMPILE_VERSION_INFO*)PTR_READ(GetDirectoryData(&pHeader->VersionInfo), sizeof(CORCOMPILE_VERSION_INFO)))->wOSMajorVersion != 0));
+
+ CHECK(CheckDirectory(&pHeader->Dependencies, 0, NULL_OK)); // no version header for precompiled netmodules
+ CHECK(pHeader->Dependencies.Size % sizeof(CORCOMPILE_DEPENDENCY) == 0);
+
+ CHECK(CheckDirectory(&pHeader->DebugMap, 0, NULL_OK));
+ CHECK(pHeader->DebugMap.Size % sizeof(CORCOMPILE_DEBUG_RID_ENTRY) == 0);
+
+ // GetPersistedModuleImage()
+ CHECK(CheckDirectory(&pHeader->ModuleImage));
+ CHECK(pHeader->ModuleImage.Size > 0); // sizeof(Module) if we knew it here
+
+ CHECK(CheckDirectory(&pHeader->CodeManagerTable));
+ CHECK(pHeader->CodeManagerTable.Size == sizeof(CORCOMPILE_CODE_MANAGER_ENTRY));
+
+ PTR_CORCOMPILE_CODE_MANAGER_ENTRY pEntry = PTR_CORCOMPILE_CODE_MANAGER_ENTRY((TADDR)GetDirectoryData(&pHeader->CodeManagerTable));
+ CHECK(CheckDirectory(&pEntry->HotCode, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pEntry->Code, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pEntry->ColdCode, IMAGE_SCN_MEM_WRITE, NULL_OK));
+
+ CHECK(CheckDirectory(&pHeader->ProfileDataList, 0, NULL_OK));
+ CHECK(pHeader->ProfileDataList.Size >= sizeof(CORCOMPILE_METHOD_PROFILE_LIST)
+ || pHeader->ProfileDataList.Size == 0);
+
+#endif
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_NATIVE_CHECKED;
+
+#else // FEATURE_PREJIT
+ CHECK(false);
+#endif // FEATURE_PREJIT
+
+ CHECK_OK;
+}
+
+READYTORUN_HEADER * PEDecoder::FindReadyToRunHeader() const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader;
+
+ if (VAL32(pDir->Size) >= sizeof(READYTORUN_HEADER) && CheckDirectory(pDir))
+ {
+ PTR_READYTORUN_HEADER pHeader = PTR_READYTORUN_HEADER((TADDR)GetDirectoryData(pDir));
+ if (pHeader->Signature == READYTORUN_SIGNATURE)
+ {
+ const_cast<PEDecoder *>(this)->m_pReadyToRunHeader = pHeader;
+ return pHeader;
+ }
+ }
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_HAS_NO_READYTORUN_HEADER;
+ return NULL;
+}
+
+//
+// code:PEDecoder::CheckILMethod and code:PEDecoder::ComputeILMethodSize really belong to
+// file:..\inc\corhlpr.cpp. Unfortunately, corhlpr.cpp is public header file that cannot be
+// properly DACized and have other dependencies on the rest of the CLR.
+//
+
+typedef DPTR(COR_ILMETHOD_TINY) PTR_COR_ILMETHOD_TINY;
+typedef DPTR(COR_ILMETHOD_FAT) PTR_COR_ILMETHOD_FAT;
+typedef DPTR(COR_ILMETHOD_SECT_SMALL) PTR_COR_ILMETHOD_SECT_SMALL;
+typedef DPTR(COR_ILMETHOD_SECT_FAT) PTR_COR_ILMETHOD_SECT_FAT;
+
+CHECK PEDecoder::CheckILMethod(RVA rva)
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ //
+ // Incrementaly validate that the entire IL method body is within the bounds of the image
+ //
+
+ // We need to have at least the tiny header
+ CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_TINY)));
+
+ TADDR pIL = GetRvaData(rva);
+
+ PTR_COR_ILMETHOD_TINY pMethodTiny = PTR_COR_ILMETHOD_TINY(pIL);
+
+ if (pMethodTiny->IsTiny())
+ {
+ // Tiny header has no optional sections - we are done.
+ CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_TINY) + pMethodTiny->GetCodeSize()));
+ CHECK_OK;
+ }
+
+ //
+ // Fat header
+ //
+
+ CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_FAT)));
+
+ PTR_COR_ILMETHOD_FAT pMethodFat = PTR_COR_ILMETHOD_FAT(pIL);
+
+ CHECK(pMethodFat->IsFat());
+
+ S_UINT32 codeEnd = S_UINT32(4) * S_UINT32(pMethodFat->GetSize()) + S_UINT32(pMethodFat->GetCodeSize());
+ CHECK(!codeEnd.IsOverflow());
+
+ // Check minimal size of the header
+ CHECK(pMethodFat->GetSize() >= (sizeof(COR_ILMETHOD_FAT) / 4));
+
+ CHECK(CheckRva(rva, codeEnd.Value()));
+
+ if (!pMethodFat->More())
+ {
+ CHECK_OK;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::GetSect
+ TADDR pSect = AlignUp(pIL + codeEnd.Value(), 4);
+
+ //
+ // Optional sections following the code
+ //
+
+ for (;;)
+ {
+ CHECK(CheckRva(rva, UINT32(pSect - pIL) + sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL)));
+
+ PTR_COR_ILMETHOD_SECT_SMALL pSectSmall = PTR_COR_ILMETHOD_SECT_SMALL(pSect);
+
+ UINT32 sectSize;
+
+ if (pSectSmall->IsSmall())
+ {
+ sectSize = pSectSmall->DataSize;
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_SMALL::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL));
+ }
+ else
+ {
+ CHECK(CheckRva(rva, UINT32(pSect - pIL) + sizeof(IMAGE_COR_ILMETHOD_SECT_FAT)));
+
+ PTR_COR_ILMETHOD_SECT_FAT pSectFat = PTR_COR_ILMETHOD_SECT_FAT(pSect);
+
+ sectSize = pSectFat->GetDataSize();
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_FAT::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT));
+ }
+
+ // Section has to be non-empty to avoid infinite loop below
+ CHECK(sectSize > 0);
+
+ S_UINT32 sectEnd = S_UINT32(UINT32(pSect - pIL)) + S_UINT32(sectSize);
+ CHECK(!sectEnd.IsOverflow());
+
+ CHECK(CheckRva(rva, sectEnd.Value()));
+
+ if (!pSectSmall->More())
+ {
+ CHECK_OK;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::Next
+ pSect = AlignUp(pIL + sectEnd.Value(), 4);
+ }
+}
+
+//
+// Compute size of IL blob. Assumes that the IL is within the bounds of the image - make sure
+// to call code:PEDecoder::CheckILMethod before calling this method.
+//
+// code:PEDecoder::ComputeILMethodSize is DACized duplicate of code:COR_ILMETHOD_DECODER::GetOnDiskSize.
+// code:MethodDesc::GetILHeader contains debug-only check that ensures that both implementations
+// are in sync.
+//
+
+SIZE_T PEDecoder::ComputeILMethodSize(TADDR pIL)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Mirror flow of code:PEDecoder::CheckILMethod, except for the range checks
+ //
+
+ PTR_COR_ILMETHOD_TINY pMethodTiny = PTR_COR_ILMETHOD_TINY(pIL);
+
+ if (pMethodTiny->IsTiny())
+ {
+ return sizeof(IMAGE_COR_ILMETHOD_TINY) + pMethodTiny->GetCodeSize();
+ }
+
+ PTR_COR_ILMETHOD_FAT pMethodFat = PTR_COR_ILMETHOD_FAT(pIL);
+
+ UINT32 codeEnd = 4 * pMethodFat->GetSize() + pMethodFat->GetCodeSize();
+
+ if (!pMethodFat->More())
+ {
+ return codeEnd;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::GetSect
+ TADDR pSect = AlignUp(pIL + codeEnd, 4);
+
+ for (;;)
+ {
+ PTR_COR_ILMETHOD_SECT_SMALL pSectSmall = PTR_COR_ILMETHOD_SECT_SMALL(pSect);
+
+ UINT32 sectSize;
+
+ if (pSectSmall->IsSmall())
+ {
+ sectSize = pSectSmall->DataSize;
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_SMALL::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL));
+ }
+ else
+ {
+ PTR_COR_ILMETHOD_SECT_FAT pSectFat = PTR_COR_ILMETHOD_SECT_FAT(pSect);
+
+ sectSize = pSectFat->GetDataSize();
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_FAT::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT));
+ }
+
+ UINT32 sectEnd = UINT32(pSect - pIL) + sectSize;
+
+ if (!pSectSmall->More() || (sectSize == 0))
+ {
+ return sectEnd;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::Next
+ pSect = AlignUp(pIL + sectEnd, 4);
+ }
+}
+
+//
+// GetDebugDirectoryEntry - return the debug directory entry at the specified index
+//
+// Arguments:
+// index The 0-based index of the entry to return. Usually this is just 0,
+// but there can be multiple debug directory entries in a PE file.
+//
+// Return value:
+// A pointer to the IMAGE_DEBUG_DIRECTORY in the PE file for the specified index,
+// or NULL if it doesn't exist.
+//
+// Note that callers on untrusted input are required to validate the debug directory
+// first by calling CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG) (possibly
+// indirectly via one of the CheckILOnly* functions).
+//
+PTR_IMAGE_DEBUG_DIRECTORY PEDecoder::GetDebugDirectoryEntry(UINT index) const
+{
+ CONTRACT(PTR_IMAGE_DEBUG_DIRECTORY)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG))
+ {
+ RETURN NULL;
+ }
+
+ // Get a pointer to the contents and size of the debug directory
+ // Also validates (in CHK builds) that this is all within one section, which the
+ // caller should have already validated if they don't trust the context of this PE file.
+ COUNT_T cbDebugDir;
+ TADDR taDebugDir = GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugDir);
+
+ // Check if the specified directory entry exists (based on the size of the directory)
+ // Note that the directory size should be an even multiple of the entry size, but we
+ // just round-down because we need to be resiliant (without asserting) to corrupted /
+ // fuzzed PE files.
+ UINT cNumEntries = cbDebugDir / sizeof(IMAGE_DEBUG_DIRECTORY);
+ if (index >= cNumEntries)
+ {
+ RETURN NULL; // index out of range
+ }
+
+ // Get the debug directory entry at the specified index.
+ PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = dac_cast<PTR_IMAGE_DEBUG_DIRECTORY>(taDebugDir);
+ pDebugEntry += index; // offset from the first entry to the requested entry
+ RETURN pDebugEntry;
+}
+
+
+#ifdef FEATURE_PREJIT
+
+CORCOMPILE_EE_INFO_TABLE *PEDecoder::GetNativeEEInfoTable() const
+{
+ CONTRACT(CORCOMPILE_EE_INFO_TABLE *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->EEInfoTable;
+
+ // 403523: PREFIX correctly complained here. Fixed GetDirectoryData.
+ RETURN PTR_CORCOMPILE_EE_INFO_TABLE(GetDirectoryData(pDir));
+}
+
+
+void *PEDecoder::GetNativeHelperTable(COUNT_T *pSize) const
+{
+ CONTRACT(void *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->HelperTable;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (void *)GetDirectoryData(pDir);
+}
+
+CORCOMPILE_VERSION_INFO *PEDecoder::GetNativeVersionInfoMaybeNull(bool skipCheckNativeHeader) const
+{
+ CONTRACT(CORCOMPILE_VERSION_INFO *)
+ {
+ PRECONDITION(skipCheckNativeHeader || CheckNativeHeader());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->VersionInfo;
+
+ RETURN PTR_CORCOMPILE_VERSION_INFO(GetDirectoryData(pDir));
+}
+
+CORCOMPILE_VERSION_INFO *PEDecoder::GetNativeVersionInfo() const
+{
+ CONTRACT(CORCOMPILE_VERSION_INFO *)
+ {
+ POSTCONDITION(CheckPointer(RETVAL));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ RETURN GetNativeVersionInfoMaybeNull();
+}
+
+BOOL PEDecoder::HasNativeDebugMap() const
+{
+ CONTRACT(BOOL)
+ {
+ PRECONDITION(CheckNativeHeader());
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ // 403522: Prefix complained correctly here.
+ CORCOMPILE_HEADER *pNativeHeader = GetNativeHeader();
+ if (!pNativeHeader)
+ RETURN FALSE;
+ else
+ RETURN (VAL32(pNativeHeader->DebugMap.VirtualAddress) != 0);
+}
+
+TADDR PEDecoder::GetNativeDebugMap(COUNT_T *pSize) const
+{
+ CONTRACT(TADDR)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->DebugMap;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (GetDirectoryData(pDir));
+}
+
+Module *PEDecoder::GetPersistedModuleImage(COUNT_T *pSize) const
+{
+ CONTRACT(Module *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ModuleImage;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (Module *) GetDirectoryData(pDir);
+}
+
+CHECK PEDecoder::CheckNativeHeaderVersion() const
+{
+ CONTRACT_CHECK
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader;
+ CHECK(VAL32(pDir->Size) == sizeof(CORCOMPILE_HEADER));
+
+ CORCOMPILE_HEADER *pNativeHeader = GetNativeHeader();
+ CHECK(pNativeHeader->Signature == CORCOMPILE_SIGNATURE);
+ CHECK(pNativeHeader->MajorVersion == CORCOMPILE_MAJOR_VERSION);
+ CHECK(pNativeHeader->MinorVersion == CORCOMPILE_MINOR_VERSION);
+
+ CHECK_OK;
+}
+
+CORCOMPILE_CODE_MANAGER_ENTRY *PEDecoder::GetNativeCodeManagerTable() const
+{
+ CONTRACT(CORCOMPILE_CODE_MANAGER_ENTRY *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ POSTCONDITION(CheckPointer(RETVAL));
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->CodeManagerTable;
+
+ RETURN PTR_CORCOMPILE_CODE_MANAGER_ENTRY(GetDirectoryData(pDir));
+}
+
+PCODE PEDecoder::GetNativeHotCode(COUNT_T * pSize) const
+{
+ CONTRACT(PCODE)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->HotCode;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+PCODE PEDecoder::GetNativeCode(COUNT_T * pSize) const
+{
+ CONTRACT(PCODE)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->Code;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+PCODE PEDecoder::GetNativeColdCode(COUNT_T * pSize) const
+{
+ CONTRACT(PCODE)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->ColdCode;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+
+CORCOMPILE_METHOD_PROFILE_LIST *PEDecoder::GetNativeProfileDataList(COUNT_T * pSize) const
+{
+ CONTRACT(CORCOMPILE_METHOD_PROFILE_LIST *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ProfileDataList;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN PTR_CORCOMPILE_METHOD_PROFILE_LIST(GetDirectoryData(pDir));
+}
+
+
+
+PTR_CVOID PEDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const
+{
+ CONTRACT(PTR_CVOID)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNativeHeader());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // TBD - may not store metadata for IJW
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = GetMetaDataHelper(METADATA_SECTION_MANIFEST);
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir));
+}
+
+CHECK PEDecoder::CheckNativeImportFromIndex(COUNT_T index) const
+{
+ CONTRACT_CHECK
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK_MSG(index >= 0 && index < GetNativeImportTableCount(), "Bad Native Import Index");
+
+ CHECK_OK;
+}
+
+COUNT_T PEDecoder::GetNativeImportTableCount() const
+{
+ CONTRACT(COUNT_T)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportTable;
+
+ RETURN (VAL32(pDir->Size) / sizeof(CORCOMPILE_IMPORT_TABLE_ENTRY));
+}
+
+CORCOMPILE_IMPORT_TABLE_ENTRY *PEDecoder::GetNativeImportFromIndex(COUNT_T index) const
+{
+ CONTRACT(CORCOMPILE_IMPORT_TABLE_ENTRY *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckNativeImportFromIndex(index));
+ POSTCONDITION(CheckPointer(RETVAL));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportTable;
+
+ CORCOMPILE_IMPORT_TABLE_ENTRY *pEntry
+ = (CORCOMPILE_IMPORT_TABLE_ENTRY *) GetDirectoryData(pDir);
+
+ RETURN pEntry + index;
+}
+
+PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSections(COUNT_T *pCount) const
+{
+ CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections;
+
+ if (pCount != NULL)
+ *pCount = VAL32(pDir->Size) / sizeof(CORCOMPILE_IMPORT_SECTION);
+
+ RETURN dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir));
+}
+
+PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSectionFromIndex(COUNT_T index) const
+{
+ CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections;
+
+ _ASSERTE(VAL32(pDir->Size) % sizeof(CORCOMPILE_IMPORT_SECTION) == 0);
+ _ASSERTE(index * sizeof(CORCOMPILE_IMPORT_SECTION) < VAL32(pDir->Size));
+
+ RETURN dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir)) + index;
+}
+
+PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSectionForRVA(RVA rva) const
+{
+ CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections;
+
+ _ASSERTE(VAL32(pDir->Size) % sizeof(CORCOMPILE_IMPORT_SECTION) == 0);
+
+ PTR_CORCOMPILE_IMPORT_SECTION pSections = dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir));
+ PTR_CORCOMPILE_IMPORT_SECTION pEnd = dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(dac_cast<TADDR>(pSections) + VAL32(pDir->Size));
+
+ for (PTR_CORCOMPILE_IMPORT_SECTION pSection = pSections; pSection < pEnd; pSection++)
+ {
+ if (rva >= VAL32(pSection->Section.VirtualAddress) && rva < VAL32(pSection->Section.VirtualAddress) + VAL32(pSection->Section.Size))
+ RETURN pSection;
+ }
+
+ RETURN NULL;
+}
+
+TADDR PEDecoder::GetStubsTable(COUNT_T *pSize) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->StubsData;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ return (GetDirectoryData(pDir));
+}
+
+TADDR PEDecoder::GetVirtualSectionsTable(COUNT_T *pSize) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->VirtualSectionsTable;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ return (GetDirectoryData(pDir));
+}
+
+#endif // FEATURE_PREJIT
+
+// Get the SizeOfStackReserve and SizeOfStackCommit from the PE file that was used to create
+// the calling process (.exe file).
+void PEDecoder::GetEXEStackSizes(SIZE_T *PE_SizeOfStackReserve, SIZE_T *PE_SizeOfStackCommit) const
+{
+ CONTRACTL {
+ PRECONDITION(!IsDll()); // This routine should only be called for EXE files.
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ * PE_SizeOfStackReserve = GetSizeOfStackReserve();
+ * PE_SizeOfStackCommit = GetSizeOfStackCommit();
+}
+
+CHECK PEDecoder::CheckWillCreateGuardPage() const
+{
+ CONTRACT_CHECK
+ {
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (!IsDll())
+ {
+ SIZE_T sizeReservedStack = 0;
+ SIZE_T sizeCommitedStack = 0;
+
+ GetEXEStackSizes(&sizeReservedStack, &sizeCommitedStack);
+
+ CHECK(ThreadWillCreateGuardPage(sizeReservedStack, sizeCommitedStack));
+
+ }
+
+ CHECK_OK;
+}
+
+BOOL PEDecoder::HasNativeEntryPoint() const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCorHeader());
+ } CONTRACTL_END;
+
+ ULONG flags = GetCorHeader()->Flags;
+ return ((flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) &&
+ (IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken) != VAL32(0)));
+}
+
+void *PEDecoder::GetNativeEntryPoint() const
+{
+ CONTRACT (void *) {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(HasNativeEntryPoint());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ } CONTRACT_END;
+
+ RETURN ((void *) GetRvaData((RVA)VAL32(IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken))));
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+PEDecoder::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ bool enumThis)
+{
+ SUPPORTS_DAC;
+ if (enumThis)
+ {
+ DAC_ENUM_DTHIS();
+ }
+
+ DacEnumMemoryRegion((TADDR)m_base, sizeof(IMAGE_DOS_HEADER));
+ m_pNTHeaders.EnumMem();
+ m_pCorHeader.EnumMem();
+ m_pNativeHeader.EnumMem();
+ m_pReadyToRunHeader.EnumMem();
+
+ if (HasNTHeaders())
+ {
+ // resource file does not have NT Header.
+ //
+ // we also need to write out section header.
+ DacEnumMemoryRegion(dac_cast<TADDR>(FindFirstSection()), sizeof(IMAGE_SECTION_HEADER) * GetNumberOfSections());
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+// --------------------------------------------------------------------------------
+
+#ifdef _DEBUG
+
+// This is a stress mode to force DLLs to be relocated.
+// This is particularly useful for hardbinding of ngen images as we
+// embed pointers into other hardbound ngen dependencies.
+
+BOOL PEDecoder::GetForceRelocs()
+{
+ WRAPPER_NO_CONTRACT;
+
+ static ConfigDWORD forceRelocs;
+ return (forceRelocs.val(CLRConfig::INTERNAL_ForceRelocs) != 0);
+}
+
+BOOL PEDecoder::ForceRelocForDLL(LPCWSTR lpFileName)
+{
+ // Use static contracts to avoid recursion, as the dynamic contracts
+ // do WszLoadLibrary(MSCOREE_SHIM_W).
+#ifdef _DEBUG
+ STATIC_CONTRACT_NOTHROW; \
+ ANNOTATION_DEBUG_ONLY; \
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
+ ANNOTATION_FN_SO_NOT_MAINLINE;
+#endif
+
+#if defined(DACCESS_COMPILE) || defined(FEATURE_PAL)
+ return TRUE;
+#else
+
+ CONTRACT_VIOLATION(SOToleranceViolation);
+
+ // Contracts in ConfigDWORD do WszLoadLibrary(MSCOREE_SHIM_W).
+ // This check prevents recursion.
+ if (wcsstr(lpFileName, MSCOREE_SHIM_W) != 0)
+ return TRUE;
+
+ if (!GetForceRelocs())
+ return TRUE;
+
+ BOOL fSuccess = FALSE;
+ PBYTE hndle = NULL;
+ PEDecoder pe;
+ void* pPreferredBase;
+ COUNT_T nVirtualSize;
+
+ HANDLE hFile = WszCreateFile(lpFileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ goto ErrExit;
+
+ HANDLE hMap = WszCreateFileMapping(hFile,
+ NULL,
+ SEC_IMAGE | PAGE_READONLY,
+ 0,
+ 0,
+ NULL);
+ CloseHandle(hFile);
+
+ if (hMap == NULL)
+ goto ErrExit;
+
+ hndle = (PBYTE)MapViewOfFile(hMap,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0);
+ CloseHandle(hMap);
+
+ if (!hndle)
+ goto ErrExit;
+
+ pe.Init(hndle);
+
+ pPreferredBase = (void*)pe.GetPreferredBase();
+ nVirtualSize = pe.GetVirtualSize();
+
+ UnmapViewOfFile(hndle);
+ hndle = NULL;
+
+ // Reserve the space so nobody can use it. A potential bug is likely to
+ // result in a plain AV this way. It is not a good idea to use the original
+ // mapping for the reservation since since it would lock the file on the disk.
+ if (!ClrVirtualAlloc(pPreferredBase, nVirtualSize, MEM_RESERVE, PAGE_NOACCESS))
+ goto ErrExit;
+
+ fSuccess = TRUE;
+
+ErrExit:
+ if (hndle != NULL)
+ UnmapViewOfFile(hndle);
+
+ return fSuccess;
+
+#endif // DACCESS_COMPILE || FEATURE_PAL
+}
+
+#endif // _DEBUG
+
+//
+// MethodSectionIterator class is used to iterate hot (or) cold method section in an ngen image.
+// Also used to iterate over jitted methods in the code heap
+//
+MethodSectionIterator::MethodSectionIterator(const void *code, SIZE_T codeSize,
+ const void *codeTable, SIZE_T codeTableSize)
+{
+ //For DAC builds,we'll read the table one DWORD at a time. Note that m_code IS
+ //NOT a host pointer.
+ m_codeTableStart = PTR_DWORD(TADDR(codeTable));
+ m_codeTable = m_codeTableStart;
+ _ASSERTE((codeTableSize % sizeof(DWORD)) == 0);
+ m_codeTableEnd = m_codeTableStart + (codeTableSize / sizeof(DWORD));
+ m_code = (BYTE *) code;
+ m_current = NULL;
+
+
+ if (m_codeTable < m_codeTableEnd)
+ {
+ m_dword = *m_codeTable++;
+ m_index = 0;
+ }
+ else
+ {
+ m_index = NIBBLES_PER_DWORD;
+ }
+}
+
+BOOL MethodSectionIterator::Next()
+{
+ while (m_codeTable < m_codeTableEnd || m_index < (int)NIBBLES_PER_DWORD)
+ {
+ while (m_index++ < (int)NIBBLES_PER_DWORD)
+ {
+ int nibble = (m_dword & HIGHEST_NIBBLE_MASK)>>HIGHEST_NIBBLE_BIT;
+ m_dword <<= NIBBLE_SIZE;
+
+ if (nibble != 0)
+ {
+ // We have found a method start
+ m_current = m_code + ((nibble-1)*CODE_ALIGN);
+ m_code += BYTES_PER_BUCKET;
+ return TRUE;
+ }
+
+ m_code += BYTES_PER_BUCKET;
+ }
+
+ if (m_codeTable < m_codeTableEnd)
+ {
+ m_dword = *m_codeTable++;
+ m_index = 0;
+ }
+ }
+ return FALSE;
+}
diff --git a/src/utilcode/peinformation.cpp b/src/utilcode/peinformation.cpp
new file mode 100644
index 0000000000..e2261dd70a
--- /dev/null
+++ b/src/utilcode/peinformation.cpp
@@ -0,0 +1,194 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// --------------------------------------------------------------------------------
+// PEInformation.cpp
+//
+
+// --------------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "peinformation.h"
+
+#if defined(FEATURE_FUSION) && !defined(DACCESS_COMPILE)
+
+extern BOOL g_fWow64Process; // Wow64 Process
+
+PEKIND GetCurrentRealProcessorPEKIND()
+{
+ PEKIND curProcessorPEKind = TargetNativePEKIND();
+
+#ifdef _TARGET_X86_
+ if (g_fWow64Process)
+ {
+ SYSTEM_INFO si = {0};
+
+ GetNativeSystemInfo(&si);
+ switch (si.wProcessorArchitecture)
+ {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ curProcessorPEKind = peAMD64;
+ break;
+ default:
+ _ASSERTE(FALSE);
+ curProcessorPEKind = peInvalid;
+ break;
+ }
+ }
+#endif // _TARGET_X86_
+
+ return curProcessorPEKind;
+}
+
+HRESULT RuntimeIsValidAssemblyOnThisPlatform_CheckProcessorArchitecture(PEKIND processorArchitecture, BOOL bForInstall)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ // MSIL / legacy images always allowed
+ if (IsPEMSIL(processorArchitecture) || (processorArchitecture == peNone))
+ {
+ goto Exit;
+ }
+ else if (IsPE32(processorArchitecture))
+ {
+#ifdef _TARGET_ARM_
+ // ARM can use only native ones
+ if (processorArchitecture != TargetNativePEKIND())
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+
+#else //!_TARGET_ARM_
+ //ARM assemblies can be installed only on ARM
+ if (processorArchitecture == peARM)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+#endif //!_TARGET_ARM_
+
+ if (bForInstall)
+ {
+ goto Exit;
+ }
+ else
+ {
+ // won't allow bind to x86 while in 64 bit process.
+ if (!IsProcess32())
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ }
+ goto Exit;
+ }
+ }
+ // 64 bit images must match processor type
+ else if(IsPE64(processorArchitecture))
+ {
+ if (!IsProcess32() && (processorArchitecture == TargetNativePEKIND()))
+ {
+ goto Exit;
+ }
+ else if (bForInstall && (GetCurrentRealProcessorPEKIND() == processorArchitecture))
+ {
+ goto Exit;
+ }
+ }
+
+ // Everything else, fails match
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+
+Exit:
+ return hr;
+}
+#endif // FEATURE_FUSION && !DACCESS_COMPILE
+
+HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, PEKIND * pPeKind)
+{
+ return TranslatePEToArchitectureType(CLRPeKind, dwImageType, 0, pPeKind);
+}
+
+HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, DWORD dwAssemblyFlags, PEKIND * pPeKind)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pPeKind != NULL);
+
+ if (CLRPeKind == peNot)
+ { // Not a PE. Shouldn't ever get here.
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+ else if (IsAfPA_NoPlatform(dwAssemblyFlags))
+ {
+ *pPeKind = peNone;
+ goto Exit;
+ }
+ else
+ {
+ if ((CLRPeKind & peILonly) &&
+ !(CLRPeKind & pe32Plus) &&
+ !(CLRPeKind & pe32BitRequired) &&
+ (dwImageType == IMAGE_FILE_MACHINE_I386))
+ {
+ // Processor-agnostic (MSIL)
+ *pPeKind = peMSIL;
+ }
+ else if (CLRPeKind & pe32Plus)
+ {
+ // 64-bit
+
+ if (CLRPeKind & pe32BitRequired)
+ {
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+
+ // Regardless of whether ILONLY is set or not, the architecture
+ // is the machine type.
+
+ if (dwImageType == IMAGE_FILE_MACHINE_IA64)
+ {
+ *pPeKind = peIA64;
+ }
+ else if (dwImageType == IMAGE_FILE_MACHINE_AMD64)
+ {
+ *pPeKind = peAMD64;
+ }
+ else
+ { // We don't support other architectures
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+ }
+ else
+ {
+ // 32-bit, non-agnostic
+
+ if (dwImageType == IMAGE_FILE_MACHINE_I386)
+ {
+ *pPeKind = peI386;
+ }
+ else if (dwImageType == IMAGE_FILE_MACHINE_ARMNT)
+ {
+ *pPeKind = peARM;
+ }
+ else
+ { // Not supported
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+ }
+ }
+
+Exit:
+ return hr;
+}
diff --git a/src/utilcode/perflog.cpp b/src/utilcode/perflog.cpp
new file mode 100644
index 0000000000..14e4737d13
--- /dev/null
+++ b/src/utilcode/perflog.cpp
@@ -0,0 +1,327 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h"
+#include "perflog.h"
+#include "jitperf.h"
+#include <limits.h>
+
+//=============================================================================
+// ALL THE PERF LOG CODE IS COMPILED ONLY IF THE ENABLE_PERF_LOG WAS DEFINED.
+// ENABLE_PERF_LOGis defined if GOLDEN or DISABLE_PERF_LOG is not defined.
+#if defined (ENABLE_PERF_LOG)
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Widechar strings representing the units in UnitOfMeasure. *** Keep in sync ***
+// with the array defined in PerfLog.cpp
+const wchar_t * const wszUnitOfMeasureDescr[MAX_UNITS_OF_MEASURE] =
+{
+ W(""),
+ W("sec"),
+ W("Bytes"),
+ W("KBytes"),
+ W("KBytes/sec"),
+ W("cycles")
+};
+
+//-----------------------------------------------------------------------------
+// Widechar strings representing the "direction" property of above units.
+// *** Keep in sync *** with the array defined in PerfLog.cpp
+// "Direction" property is false if an increase in the value of the counter indicates
+// a degrade.
+// "Direction" property is true if an increase in the value of the counter indicates
+// an improvement.
+const wchar_t * const wszIDirection[MAX_UNITS_OF_MEASURE] =
+{
+ W("false"),
+ W("false"),
+ W("false"),
+ W("false"),
+ W("true"),
+ W("false")
+};
+
+//-----------------------------------------------------------------------------
+// Initialize static variables of the PerfLog class.
+bool PerfLog::m_perfLogInit = false;
+wchar_t PerfLog::m_wszOutStr_1[];
+DWORD PerfLog::m_dwWriteByte = 0;
+int PerfLog::m_fLogPerfData = 0;
+HANDLE PerfLog::m_hPerfLogFileHandle = 0;
+bool PerfLog::m_perfAutomationFormat = false;
+bool PerfLog::m_commaSeparatedFormat = false;
+
+//-----------------------------------------------------------------------------
+// Initliaze perf logging. Must be called before calling PERFLOG (x)...
+void PerfLog::PerfLogInitialize()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Make sure we are called only once.
+ if (m_perfLogInit)
+ {
+ return;
+ }
+
+ // First check for special cases:
+
+#if defined(ENABLE_JIT_PERF)
+ // Checks the JIT_PERF_OUTPUT env var and sets g_fJitPerfOn.
+ InitJitPerf();
+#endif
+
+#ifdef WS_PERF
+ // Private working set perf stats
+ InitWSPerf();
+#endif // WS_PERF
+
+ // Put other special cases here.
+
+ // <TODO>@TODO agk: clean this logic a bit</TODO>
+ // Special cases considered. Now turn on loggin if any of above want logging
+ // or if PERF_OUTPUT says so.
+
+ wchar_t lpszValue[2];
+ // Read the env var PERF_OUTPUT and if set continue.
+ m_fLogPerfData = WszGetEnvironmentVariable (W("PERF_OUTPUT"), lpszValue, sizeof(lpszValue)/sizeof(lpszValue[0]));
+
+#if defined(ENABLE_JIT_PERF)
+ if (!m_fLogPerfData)
+ {
+ // Make sure that JIT perf was not requested.
+ if (!g_fJitPerfOn)
+ return;
+
+ // JIT perf stats are needed so set the flags also.
+ m_fLogPerfData = 1;
+ }
+#endif
+
+ // See if we want to output to the database
+ wchar_t _lpszValue[11];
+ DWORD _cchValue = 10; // 11 - 1
+ _cchValue = WszGetEnvironmentVariable (W("PerfOutput"), _lpszValue, _cchValue);
+ if (_cchValue && (wcscmp (_lpszValue, W("DBase")) == 0))
+ m_perfAutomationFormat = true;
+ if (_cchValue && (wcscmp (_lpszValue, W("CSV")) == 0))
+ m_commaSeparatedFormat = true;
+
+ if (PerfAutomationFormat() || CommaSeparatedFormat())
+ {
+ // Hardcoded file name for spitting the perf auotmation formatted perf data. Open
+ // the file here for writing and close in PerfLogDone().
+ m_hPerfLogFileHandle = WszCreateFile (
+#ifdef PLATFORM_UNIX
+ L"/tmp/PerfData.dat",
+#else
+ W("C:\\PerfData.dat"),
+#endif
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ 0,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+
+ // check return value
+ if(m_hPerfLogFileHandle == INVALID_HANDLE_VALUE)
+ {
+ m_fLogPerfData = 0;
+ goto ErrExit;
+ }
+
+ // Make sure we append to the file. <TODO>@TODO agk: Is this necessary?</TODO>
+ if(SetFilePointer (m_hPerfLogFileHandle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER )
+ {
+ CloseHandle (m_hPerfLogFileHandle);
+ m_fLogPerfData = 0;
+ goto ErrExit;
+ }
+ }
+
+ m_perfLogInit = true;
+
+ErrExit:
+ return;
+}
+
+// Wrap up...
+void PerfLog::PerfLogDone()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if defined(ENABLE_JIT_PERF)
+ DoneJitPerfStats();
+#endif
+
+#ifdef WS_PERF
+ // Private working set perf
+ OutputWSPerfStats();
+#endif // WS_PERF
+
+ if (CommaSeparatedFormat())
+ {
+ if (0 == WriteFile (m_hPerfLogFileHandle, "\n", (DWORD)strlen("\n"), &m_dwWriteByte, NULL))
+ printf("ERROR: Could not write to perf log.\n");
+ }
+
+ if (PerfLoggingEnabled())
+ CloseHandle (m_hPerfLogFileHandle);
+}
+
+void PerfLog::OutToStdout(__in_z const wchar_t *wszName, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ wchar_t wszOutStr_2[PRINT_STR_LEN];
+
+ if (wszDescr)
+ _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W(" (%s)\n"), wszDescr);
+ else
+ _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("\n"));
+
+ printf("%S", m_wszOutStr_1);
+ printf("%S", wszOutStr_2);
+}
+
+void PerfLog::OutToPerfFile(__in_z const wchar_t *wszName, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ char szPrintStr[PRINT_STR_LEN];
+
+ if (CommaSeparatedFormat())
+ {
+ if (WszWideCharToMultiByte (CP_ACP, 0, m_wszOutStr_1, -1, szPrintStr, PRINT_STR_LEN-1, 0, 0) ) {
+ if (0 == WriteFile (m_hPerfLogFileHandle, szPrintStr, (DWORD)strlen(szPrintStr), &m_dwWriteByte, NULL))
+ printf("ERROR: Could not write to perf log.\n");
+ }
+ else
+ wprintf(W("ERROR: Could not do string conversion.\n"));
+ }
+ else
+ {
+ wchar_t wszOutStr_2[PRINT_STR_LEN];
+
+ // workaround. The formats for ExecTime is slightly different from a custom value.
+ if (wcscmp(wszName, W("ExecTime")) == 0)
+ _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("ExecUnitDescr=%s\nExecIDirection=%s\n"), wszDescr, wszIDirection[unit]);
+ else
+ {
+ if (wszDescr)
+ _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s Descr=%s\n%s Unit Descr=None\n%s IDirection=%s\n"), wszName, wszDescr, wszName, wszName, wszIDirection[unit]);
+ else
+ _snwprintf_s(wszOutStr_2, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s Descr=None\n%s Unit Descr=None\n%s IDirection=%s\n"), wszName, wszName, wszName, wszIDirection[unit]);
+ }
+
+ // Write both pieces to the file.
+ if(WszWideCharToMultiByte (CP_ACP, 0, m_wszOutStr_1, -1, szPrintStr, PRINT_STR_LEN-1, 0, 0) ) {
+ if (0 == WriteFile (m_hPerfLogFileHandle, szPrintStr, (DWORD)strlen(szPrintStr), &m_dwWriteByte, NULL))
+ printf("ERROR: Could not write to perf log.\n");
+ }
+ else
+ wprintf(W("ERROR: Could not do string conversion.\n"));
+
+ if(WszWideCharToMultiByte (CP_ACP, 0, wszOutStr_2, -1, szPrintStr, PRINT_STR_LEN-1, 0, 0)) {
+ if (0 == WriteFile (m_hPerfLogFileHandle, szPrintStr, (DWORD)strlen(szPrintStr), &m_dwWriteByte, NULL))
+ printf("ERROR: Could not write to perf log.\n");
+ }
+ else
+ wprintf(W("ERROR: Could not do string conversion.\n"));
+ }
+}
+
+// Output stats in pretty print to stdout and perf automation format to file
+// handle m_hPerfLogFileHandle
+void PerfLog::Log(__in_z const wchar_t *wszName, UINT64 val, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Format the output into two pieces: The first piece is formatted here, rest in OutToStdout.
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%-30s%12.3I64u %s"), wszName, val, wszUnitOfMeasureDescr[unit]);
+ OutToStdout (wszName, unit, wszDescr);
+
+ // Format the output into two pieces: The first piece is formatted here, rest in OutToPerfFile
+ if (CommaSeparatedFormat())
+ {
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s;%0.3I64u;"), wszName, val);
+ OutToPerfFile (wszName, unit, wszDescr);
+ }
+
+ if (PerfAutomationFormat())
+ {
+ // workaround, Special case for ExecTime. since the format is slightly different than for custom value.
+ if (wcscmp(wszName, W("ExecTime")) == 0)
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3I64u\nExecUnit=%s\n"), wszName, val, wszUnitOfMeasureDescr[unit]);
+ else
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3I64u\n%s Unit=%s\n"), wszName, val, wszName, wszUnitOfMeasureDescr[unit]);
+ OutToPerfFile (wszName, unit, wszDescr);
+ }
+}
+
+// Output stats in pretty print to stdout and perf automation format to file
+// handle m_hPerfLogFileHandle
+void PerfLog::Log(__in_z const wchar_t *wszName, double val, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Format the output into two pieces: The first piece is formatted here, rest in OutToStdout.
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%-30s%12.3g %s"), wszName, val, wszUnitOfMeasureDescr[unit]);
+ OutToStdout (wszName, unit, wszDescr);
+
+ // Format the output into two pieces: The first piece is formatted here, rest in OutToPerfFile
+ if (CommaSeparatedFormat())
+ {
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s;%0.3g;"), wszName, val);
+ OutToPerfFile (wszName, unit, wszDescr);
+ }
+
+ if (PerfAutomationFormat())
+ {
+ // workaround, Special case for ExecTime. since the format is slightly different than for custom value.
+ if (wcscmp(wszName, W("ExecTime")) == 0)
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3g\nExecUnit=%s\n"), wszName, val, wszUnitOfMeasureDescr[unit]);
+ else
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3g\n%s Unit=%s\n"), wszName, val, wszName, wszUnitOfMeasureDescr[unit]);
+ OutToPerfFile (wszName, unit, wszDescr);
+ }
+}
+
+// Output stats in pretty print to stdout and perf automation format to file
+// handle m_hPerfLogFileHandle
+void PerfLog::Log(__in_z const wchar_t *wszName, UINT32 val, UnitOfMeasure unit, __in_opt const wchar_t *wszDescr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Format the output into two pieces: The first piece is formatted here, rest in OutToStdout.
+
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%-30s%12d %s"), wszName, val, wszUnitOfMeasureDescr[unit]);
+ OutToStdout (wszName, unit, wszDescr);
+
+ // Format the output into two pieces: The first piece is formatted here, rest in OutToPerfFile
+ if (CommaSeparatedFormat())
+ {
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s;%d;"), wszName, val);
+ OutToPerfFile (wszName, unit, wszDescr);
+ }
+
+ if (PerfAutomationFormat())
+ {
+ // workaround, Special case for ExecTime. since the format is slightly different than for custom value.
+ if (wcscmp(wszName, W("ExecTime")) == 0)
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3d\nExecUnit=%s\n"), wszName, val, wszUnitOfMeasureDescr[unit]);
+ else
+ _snwprintf_s(m_wszOutStr_1, PRINT_STR_LEN, PRINT_STR_LEN - 1, W("%s=%0.3d\n%s Unit=%s\n"), wszName, val, wszName, wszUnitOfMeasureDescr[unit]);
+ OutToPerfFile (wszName, unit, wszDescr);
+ }
+}
+
+
+//=============================================================================
+// ALL THE PERF LOG CODE IS COMPILED ONLY IF THE ENABLE_PERF_LOG WAS DEFINED.
+#endif // ENABLE_PERF_LOG
+//=============================================================================
+
diff --git a/src/utilcode/posterror.cpp b/src/utilcode/posterror.cpp
new file mode 100644
index 0000000000..0ae48aab71
--- /dev/null
+++ b/src/utilcode/posterror.cpp
@@ -0,0 +1,404 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// PostErrors.cpp
+//
+// This module contains the error handling/posting code for the engine. It
+// is assumed that all methods may be called by a dispatch client, and therefore
+// errors are always posted using IErrorInfo.
+//
+
+//*****************************************************************************
+#include "stdafx.h" // Standard header.
+
+#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
+
+#include <utilcode.h> // Utility helpers.
+#include <corerror.h>
+#include "../dlls/mscorrc/resource.h"
+#include "ex.h"
+
+#include <posterror.h>
+
+#if !defined(lengthof)
+#define lengthof(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+// Local prototypes.
+HRESULT FillErrorInfo(LPCWSTR szMsg, DWORD dwHelpContext);
+
+//*****************************************************************************
+// Function that we'll expose to the outside world to fire off the shutdown method
+//*****************************************************************************
+#ifdef SHOULD_WE_CLEANUP
+void ShutdownCompRC()
+{
+ CCompRC::ShutdownDefaultResourceDll();
+}
+#endif /* SHOULD_WE_CLEANUP */
+
+void GetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId)
+{
+ WRAPPER_NO_CONTRACT;
+ CCompRC::GetDefaultCallbacks(
+ fpGetThreadUICultureNames,
+ fpGetThreadUICultureId
+ );
+}
+//*****************************************************************************
+// Set callbacks to get culture info
+//*****************************************************************************
+void SetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId // TODO: Don't rely on the LCID, only the name
+)
+{
+ WRAPPER_NO_CONTRACT;
+ CCompRC::SetDefaultCallbacks(
+ fpGetThreadUICultureNames,
+ fpGetThreadUICultureId
+ );
+
+}
+
+//*****************************************************************************
+// Public function to load a resource string
+//*****************************************************************************
+STDAPI UtilLoadStringRC(
+ UINT iResourceID,
+ __out_ecount(iMax) LPWSTR szBuffer,
+ int iMax,
+ int bQuiet
+)
+{
+ WRAPPER_NO_CONTRACT;
+ return UtilLoadResourceString(bQuiet? CCompRC::Optional : CCompRC::Required,iResourceID, szBuffer, iMax);
+}
+
+HRESULT UtilLoadResourceString(CCompRC::ResourceCategory eCategory, UINT iResourceID, __out_ecount (iMax) LPWSTR szBuffer, int iMax)
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ HRESULT retVal = E_OUTOFMEMORY;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+ SString::Startup();
+ EX_TRY
+ {
+ CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll();
+
+ if (pResourceDLL != NULL)
+ {
+ retVal = pResourceDLL->LoadString(eCategory, iResourceID, szBuffer, iMax);
+ }
+ }
+ EX_CATCH
+ {
+ // Catch any errors and return E_OUTOFMEMORY;
+ retVal = E_OUTOFMEMORY;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ END_SO_INTOLERANT_CODE;
+
+ return retVal;
+}
+
+#ifdef FEATURE_USE_LCID
+STDAPI UtilLoadStringRCEx(
+ LCID lcid,
+ UINT iResourceID,
+ __out_ecount(iMax) LPWSTR szBuffer,
+ int iMax,
+ int bQuiet,
+ int *pcwchUsed
+)
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ HRESULT retVal = E_OUTOFMEMORY;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+ EX_TRY
+ {
+ SString::Startup();
+ CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll();
+
+ if (pResourceDLL != NULL)
+ {
+ retVal = pResourceDLL->LoadString(bQuiet? CCompRC::Optional : CCompRC::Required,lcid, iResourceID, szBuffer, iMax, pcwchUsed);
+ }
+ }
+ EX_CATCH
+ {
+ // Catch any errors and return E_OUTOFMEMORY;
+ retVal = E_OUTOFMEMORY;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ END_SO_INTOLERANT_CODE;
+
+ return retVal;
+}
+#endif //FEATURE_USE_LCID
+
+//*****************************************************************************
+// Format a Runtime Error message.
+//*****************************************************************************
+HRESULT __cdecl FormatRuntimeErrorVa(
+ __inout_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format.
+ ULONG cchMsg, // Size of buffer, characters.
+ HRESULT hrRpt, // The HR to report.
+ va_list marker) // Optional args.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcBuf[512]; // Resource string.
+ HRESULT hr;
+
+ // Ensure nul termination.
+ *rcMsg = W('\0');
+
+ // If this is one of our errors or if it is simply a resource ID, then grab the error from the rc file.
+ if ((HRESULT_FACILITY(hrRpt) == FACILITY_URT) || (HIWORD(hrRpt) == 0))
+ {
+ hr = UtilLoadStringRC(LOWORD(hrRpt), rcBuf, NumItems(rcBuf), true);
+ if (hr == S_OK)
+ {
+ _vsnwprintf_s(rcMsg, cchMsg, _TRUNCATE, rcBuf, marker);
+ }
+ }
+ // Otherwise it isn't one of ours, so we need to see if the system can
+ // find the text for it.
+ else
+ {
+#ifdef FEATURE_USE_LCID
+ if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, hrRpt, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/))
+#else
+ if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, hrRpt, 0,
+ rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/))
+#endif
+ {
+ hr = S_OK;
+
+ // System messages contain a trailing \r\n, which we don't want normally.
+ size_t iLen = wcslen(rcMsg);
+ if (iLen > 3 && rcMsg[iLen - 2] == '\r' && rcMsg[iLen - 1] == '\n')
+ rcMsg[iLen - 2] = '\0';
+ }
+ else
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ // If we failed to find the message anywhere, then issue a hard coded message.
+ if (FAILED(hr))
+ {
+ _snwprintf_s(rcMsg, cchMsg, _TRUNCATE, W("Common Language Runtime Internal error: 0x%08x"), hrRpt);
+ DEBUG_STMT(DbgWriteEx(rcMsg));
+ }
+
+ return hrRpt;
+} // FormatRuntimeErrorVa
+
+//*****************************************************************************
+// Format a Runtime Error message, varargs.
+//*****************************************************************************
+HRESULT __cdecl FormatRuntimeError(
+ __out_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format.
+ ULONG cchMsg, // Size of buffer, characters.
+ HRESULT hrRpt, // The HR to report.
+ ...) // Optional args.
+{
+ WRAPPER_NO_CONTRACT;
+ va_list marker; // User text.
+ va_start(marker, hrRpt);
+ hrRpt = FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker);
+ va_end(marker);
+ return hrRpt;
+}
+
+#ifdef FEATURE_COMINTEROP
+//*****************************************************************************
+// Create, fill out and set an error info object. Note that this does not fill
+// out the IID for the error object; that is done elsewhere.
+//*****************************************************************************
+HRESULT FillErrorInfo( // Return status.
+ LPCWSTR szMsg, // Error message.
+ DWORD dwHelpContext) // Help context.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ICreateErrorInfo *pICreateErr; // Error info creation Iface pointer.
+ IErrorInfo *pIErrInfo = NULL; // The IErrorInfo interface.
+ HRESULT hr; // Return status.
+
+ // Get the ICreateErrorInfo pointer.
+ hr = S_OK;
+ EX_TRY
+ {
+ hr = CreateErrorInfo(&pICreateErr);
+ }
+ EX_CATCH
+ {
+ hr = GET_EXCEPTION()->GetHR();
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (FAILED(hr))
+ return (hr);
+
+ // Set message text description.
+ if (FAILED(hr = pICreateErr->SetDescription((LPWSTR) szMsg)))
+ goto Exit1;
+
+ // suppress PreFast warning about passing literal string to non-const API.
+ // This API (ICreateErrorInfo::SetHelpFile) is documented to take a const argument, but
+ // we can't put const in the signature because it would break existing implementors of
+ // the API.
+#ifdef _PREFAST_
+#pragma prefast(push)
+#pragma warning(disable:6298)
+#endif
+
+ // Set the help file and help context.
+ //<TODO>@todo: we don't have a help file yet.</TODO>
+ if (FAILED(hr = pICreateErr->SetHelpFile(const_cast<wchar_t*>(W("complib.hlp")))) ||
+ FAILED(hr = pICreateErr->SetHelpContext(dwHelpContext)))
+ goto Exit1;
+
+#ifdef _PREFAST_
+#pragma prefast(pop)
+#endif
+
+ // Get the IErrorInfo pointer.
+ if (FAILED(hr = pICreateErr->QueryInterface(IID_IErrorInfo, (PVOID *) &pIErrInfo)))
+ goto Exit1;
+
+ // Save the error and release our local pointers.
+ {
+ // If we get here, we have loaded oleaut32.dll.
+ CONTRACT_VIOLATION(ThrowsViolation);
+ SetErrorInfo(0L, pIErrInfo);
+ }
+
+Exit1:
+ pICreateErr->Release();
+ if (pIErrInfo) {
+ pIErrInfo->Release();
+ }
+ return hr;
+}
+#endif // FEATURE_COMINTEROP
+
+//*****************************************************************************
+// This function will post an error for the client. If the LOWORD(hrRpt) can
+// be found as a valid error message, then it is loaded and formatted with
+// the arguments passed in. If it cannot be found, then the error is checked
+// against FormatMessage to see if it is a system error. System errors are
+// not formatted so no add'l parameters are required. If any errors in this
+// process occur, hrRpt is returned for the client with no error posted.
+//*****************************************************************************
+extern "C"
+HRESULT __cdecl PostErrorVA( // Returned error.
+ HRESULT hrRpt, // Reported error.
+ va_list marker) // Error arguments.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ ENTRY_POINT;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_COMINTEROP
+
+ const DWORD cchMsg = 4096;
+ WCHAR *rcMsg = (WCHAR*)alloca(cchMsg * sizeof(WCHAR)); // Error message.
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Return warnings without text.
+ if (!FAILED(hrRpt))
+ goto ErrExit;
+
+ // If we are already out of memory or out of stack or the thread is in some bad state,
+ // we don't want throw gasoline on the fire by calling ErrorInfo stuff below (which can
+ // trigger a delayload of oleaut32.dll). We don't need to embellish transient errors
+ // so just return this without text.
+ if (Exception::IsTransient(hrRpt))
+ {
+ goto ErrExit;
+ }
+
+ // Format the error.
+ FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker);
+
+ // Turn the error into a posted error message. If this fails, we still
+ // return the original error message since a message caused by our error
+ // handling system isn't going to give you a clue about the original error.
+ hr = FillErrorInfo(rcMsg, LOWORD(hrRpt));
+ _ASSERTE(hr == S_OK);
+
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+
+#endif // FEATURE_COMINTEROP
+
+ return (hrRpt);
+} // PostErrorVA
+
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
+
+//*****************************************************************************
+// This function will post an error for the client. If the LOWORD(hrRpt) can
+// be found as a valid error message, then it is loaded and formatted with
+// the arguments passed in. If it cannot be found, then the error is checked
+// against FormatMessage to see if it is a system error. System errors are
+// not formatted so no add'l parameters are required. If any errors in this
+// process occur, hrRpt is returned for the client with no error posted.
+//*****************************************************************************
+extern "C"
+HRESULT __cdecl PostError(
+ HRESULT hrRpt, // Reported error.
+ ...) // Error arguments.
+{
+#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
+ WRAPPER_NO_CONTRACT;
+ va_list marker; // User text.
+ va_start(marker, hrRpt);
+ hrRpt = PostErrorVA(hrRpt, marker);
+ va_end(marker);
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
+ return hrRpt;
+}
diff --git a/src/utilcode/prettyprintsig.cpp b/src/utilcode/prettyprintsig.cpp
new file mode 100644
index 0000000000..928cfbb88c
--- /dev/null
+++ b/src/utilcode/prettyprintsig.cpp
@@ -0,0 +1,1004 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//*****************************************************************************
+// This code supports formatting a method and it's signature in a friendly
+// and consistent format.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "prettyprintsig.h"
+#include "utilcode.h"
+#include "metadata.h"
+#include "corpriv.h"
+
+/***********************************************************************/
+// Null-terminates the string held in "out"
+
+static WCHAR* asStringW(CQuickBytes *out)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + 1)))
+ return 0;
+ WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize);
+ *cur = 0;
+ return((WCHAR*) out->Ptr());
+} // static WCHAR* asStringW()
+
+// Null-terminates the string held in "out"
+
+static CHAR* asStringA(CQuickBytes *out)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + 1)))
+ return 0;
+ CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize);
+ *cur = 0;
+ return((CHAR*) out->Ptr());
+} // static CHAR* asStringA()
+
+/***********************************************************************/
+// Appends the str to "out"
+// The string held in "out" is not NULL-terminated. asStringW() needs to
+// be called for the NULL-termination
+
+static HRESULT appendStrW(CQuickBytes *out, const WCHAR* str)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ SIZE_T len = wcslen(str) * sizeof(WCHAR);
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + len)))
+ return E_OUTOFMEMORY;
+ WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize);
+ memcpy(cur, str, len);
+ // Note no trailing null!
+ return S_OK;
+} // static HRESULT appendStrW()
+
+// Appends the str to "out"
+// The string held in "out" is not NULL-terminated. asStringA() needs to
+// be called for the NULL-termination
+
+static HRESULT appendStrA(CQuickBytes *out, const CHAR* str)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ SIZE_T len = strlen(str) * sizeof(CHAR);
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + len)))
+ return E_OUTOFMEMORY;
+ CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize);
+ memcpy(cur, str, len);
+ // Note no trailing null!
+ return S_OK;
+} // static HRESULT appendStrA()
+
+
+static HRESULT appendStrNumW(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ WCHAR buff[32];
+ swprintf_s(buff, 32, W("%d"), num);
+ return appendStrW(out, buff);
+} // static HRESULT appendStrNumW()
+
+static HRESULT appendStrNumA(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CHAR buff[32];
+ sprintf_s(buff, 32, "%d", num);
+ return appendStrA(out, buff);
+} // static HRESULT appendStrNumA()
+
+static HRESULT appendStrHexW(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ WCHAR buff[32];
+ swprintf_s(buff, 32, W("%08X"), num);
+ return appendStrW(out, buff);
+} // static HRESULT appendStrHexW()
+
+static HRESULT appendStrHexA(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CHAR buff[32];
+ sprintf_s(buff, 32, "%08X", num);
+ return appendStrA(out, buff);
+} // static HRESULT appendStrHexA()
+
+/***********************************************************************/
+
+LPCWSTR PrettyPrintSigWorker(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const WCHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI); // Import api to use.
+
+//*****************************************************************************
+//*****************************************************************************
+// pretty prints 'type' to the buffer 'out' returns a poitner to the next type,
+// or 0 on a format failure
+
+static PCCOR_SIGNATURE PrettyPrintType(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ size_t typeLen, // Maximum length of the type
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI) // ptr to IMDInternal class with ComSig
+{
+ mdToken tk;
+ const WCHAR * str;
+ WCHAR rcname[MAX_CLASS_NAME];
+ HRESULT hr;
+ unsigned __int8 elt = *typePtr++;
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen;
+
+ switch(elt)
+ {
+ case ELEMENT_TYPE_VOID:
+ str = W("void");
+ goto APPEND;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ str = W("bool");
+ goto APPEND;
+
+ case ELEMENT_TYPE_CHAR:
+ str = W("wchar");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I1:
+ str = W("int8");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U1:
+ str = W("unsigned int8");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I2:
+ str = W("int16");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U2:
+ str = W("unsigned int16");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I4:
+ str = W("int32");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U4:
+ str = W("unsigned int32");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I8:
+ str = W("int64");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U8:
+ str = W("unsigned int64");
+ goto APPEND;
+
+ case ELEMENT_TYPE_R4:
+ str = W("float32");
+ goto APPEND;
+
+ case ELEMENT_TYPE_R8:
+ str = W("float64");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U:
+ str = W("unsigned int");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I:
+ str = W("int");
+ goto APPEND;
+
+ case ELEMENT_TYPE_OBJECT:
+ str = W("class System.Object");
+ goto APPEND;
+
+ case ELEMENT_TYPE_STRING:
+ str = W("class System.String");
+ goto APPEND;
+
+ case ELEMENT_TYPE_CANON_ZAPSIG:
+ str = W("class System.__Canon");
+ goto APPEND;
+
+ case ELEMENT_TYPE_TYPEDBYREF:
+ str = W("refany");
+ goto APPEND;
+
+ APPEND:
+ appendStrW(out, str);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ str = W("value class ");
+ goto DO_CLASS;
+
+ case ELEMENT_TYPE_CLASS:
+ str = W("class ");
+ goto DO_CLASS;
+
+ DO_CLASS:
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ appendStrW(out, str);
+ rcname[0] = 0;
+ str = rcname;
+
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ hr = pIMDI->GetTypeRefProps(tk, 0, rcname, NumItems(rcname), 0);
+ }
+ else if (TypeFromToken(tk) == mdtTypeDef)
+ {
+ hr = pIMDI->GetTypeDefProps(tk, rcname, NumItems(rcname), 0, 0, 0);
+ }
+ else
+ {
+ _ASSERTE(!"Unknown token type encountered in signature.");
+ str = W("<UNKNOWN>");
+ }
+
+ appendStrW(out, str);
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ appendStrW(out, W("[]"));
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ unsigned rank = CorSigUncompressData(typePtr);
+ PREFIX_ASSUME(rank <= 0xffffff);
+
+ // <TODO>TODO what is the syntax for the rank 0 case? </TODO>
+ if (rank == 0)
+ {
+ appendStrW(out, W("[??]"));
+ }
+ else
+ {
+ _ASSERTE(rank != 0);
+ int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
+ int* sizes = &lowerBounds[rank];
+ memset(lowerBounds, 0, sizeof(int)*2*rank);
+
+ unsigned numSizes = CorSigUncompressData(typePtr);
+ _ASSERTE(numSizes <= rank);
+ unsigned int i;
+ for(i =0; i < numSizes; i++)
+ sizes[i] = CorSigUncompressData(typePtr);
+
+ unsigned numLowBounds = CorSigUncompressData(typePtr);
+ _ASSERTE(numLowBounds <= rank);
+ for(i = 0; i < numLowBounds; i++)
+ lowerBounds[i] = CorSigUncompressData(typePtr);
+
+ appendStrW(out, W("["));
+ for(i = 0; i < rank; i++)
+ {
+ if (sizes[i] != 0 && lowerBounds[i] != 0)
+ {
+ if (lowerBounds[i] == 0)
+ appendStrNumW(out, sizes[i]);
+ else
+ {
+ appendStrNumW(out, lowerBounds[i]);
+ appendStrW(out, W("..."));
+ if (sizes[i] != 0)
+ appendStrNumW(out, lowerBounds[i] + sizes[i] + 1);
+ }
+ }
+ if (i < rank-1)
+ appendStrW(out, W(","));
+ }
+ appendStrW(out, W("]"));
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_MVAR:
+ appendStrW(out, W("!!"));
+ appendStrNumW(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ appendStrW(out, W("!"));
+ appendStrNumW(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ unsigned ntypars = CorSigUncompressData(typePtr);
+ appendStrW(out, W("<"));
+ for (unsigned i = 0; i < ntypars; i++)
+ {
+ if (i > 0)
+ appendStrW(out, W(","));
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ }
+ appendStrW(out, W(">"));
+ }
+ break;
+
+ case ELEMENT_TYPE_MODULE_ZAPSIG:
+ appendStrW(out, W("[module#"));
+ appendStrNumW(out, CorSigUncompressData(typePtr));
+ appendStrW(out, W(", token#"));
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ appendStrHexW(out, tk);
+ appendStrW(out, W("]"));
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ appendStrW(out, W("fnptr "));
+ PrettyPrintSigWorker(typePtr, (typeEnd - typePtr), W(""), out, pIMDI);
+ break;
+
+ case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG:
+ case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+ appendStrW(out, W("native "));
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ break;
+
+ // Modifiers or depedant types
+ case ELEMENT_TYPE_PINNED:
+ str = W(" pinned");
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_PTR:
+ str = W("*");
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_BYREF:
+ str = W("&");
+ goto MODIFIER;
+
+ MODIFIER:
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ appendStrW(out, str);
+ break;
+
+ default:
+ case ELEMENT_TYPE_SENTINEL:
+ case ELEMENT_TYPE_END:
+ _ASSERTE(!"Unknown Type");
+ return(typePtr);
+ break;
+ }
+ return(typePtr);
+} // static PCCOR_SIGNATURE PrettyPrintType()
+
+//*****************************************************************************
+// Converts a com signature to a text signature.
+//
+// Note that this function DOES NULL terminate the result signature string.
+//*****************************************************************************
+LPCWSTR PrettyPrintSigLegacy(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ unsigned typeLen, // length of type
+ const WCHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI) // Import api to use.
+{
+ return PrettyPrintSigWorker(typePtr, typeLen, name, out, pIMDI);
+} // LPCWSTR PrettyPrintSigLegacy()
+
+LPCWSTR PrettyPrintSigWorker(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const WCHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI) // Import api to use.
+{
+ out->Shrink(0);
+ unsigned numTyArgs = 0;
+ unsigned numArgs;
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen; // End of the signature.
+
+ if (name != 0) // 0 means a local var sig
+ {
+ // get the calling convention out
+ unsigned callConv = CorSigUncompressData(typePtr);
+
+ if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ if (name != 0 && *name != 0)
+ {
+ appendStrW(out, W(" "));
+ appendStrW(out, name);
+ }
+ return(asStringW(out));
+ }
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ appendStrW(out, W("instance "));
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ appendStrW(out, W("generic "));
+ numTyArgs = CorSigUncompressData(typePtr);
+ }
+
+ static const WCHAR * const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] =
+ {
+ W(""),
+ W("unmanaged cdecl "),
+ W("unmanaged stdcall "),
+ W("unmanaged thiscall "),
+ W("unmanaged fastcall "),
+ W("vararg "),
+ W("<error> "),
+ W("<error> "),
+ W(""),
+ W(""),
+ W(""),
+ W("native vararg ")
+ };
+
+ if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX)
+ {
+ appendStrW(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]);
+ }
+
+
+ numArgs = CorSigUncompressData(typePtr);
+ // do return type
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+
+ }
+ else
+ {
+ numArgs = CorSigUncompressData(typePtr);
+ }
+
+ if (name != 0 && *name != 0)
+ {
+ appendStrW(out, W(" "));
+ appendStrW(out, name);
+ }
+ appendStrW(out, W("("));
+
+ bool needComma = false;
+
+ while (numArgs)
+ {
+ if (typePtr >= typeEnd)
+ break;
+
+ if (*typePtr == ELEMENT_TYPE_SENTINEL)
+ {
+ if (needComma)
+ appendStrW(out, W(","));
+ appendStrW(out, W("..."));
+ typePtr++;
+ }
+ else
+ {
+ if (numArgs <= 0)
+ break;
+ if (needComma)
+ appendStrW(out, W(","));
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ --numArgs;
+ }
+ needComma = true;
+ }
+ appendStrW(out, W(")"));
+ return (asStringW(out));
+} // LPCWSTR PrettyPrintSigWorker()
+
+
+// Internal implementation of PrettyPrintSig().
+
+HRESULT PrettyPrintSigWorkerInternal(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const CHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMDInternalImport * pIMDI); // Import api to use.
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+//*****************************************************************************
+//*****************************************************************************
+// pretty prints 'type' to the buffer 'out' returns a poitner to the next type,
+// or 0 on a format failure
+
+__checkReturn
+static HRESULT PrettyPrintTypeA(
+ PCCOR_SIGNATURE &typePtr, // type to convert,
+ size_t typeLen, // Maximum length of the type.
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI) // ptr to IMDInternal class with ComSig
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ mdToken tk; // A type's token.
+ const CHAR *str; // Temporary string.
+ LPCUTF8 pNS; // A type's namespace.
+ LPCUTF8 pN; // A type's name.
+ HRESULT hr; // A result.
+
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen; // End of the signature.
+ unsigned __int8 elt = *typePtr++;
+
+ switch(elt) {
+ case ELEMENT_TYPE_VOID:
+ str = "void";
+ goto APPEND;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ str = "bool";
+ goto APPEND;
+
+ case ELEMENT_TYPE_CHAR:
+ str = "wchar";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I1:
+ str = "int8";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U1:
+ str = "unsigned int8";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I2:
+ str = "int16";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U2:
+ str = "unsigned int16";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I4:
+ str = "int32";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U4:
+ str = "unsigned int32";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I8:
+ str = "int64";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U8:
+ str = "unsigned int64";
+ goto APPEND;
+
+ case ELEMENT_TYPE_R4:
+ str = "float32";
+ goto APPEND;
+
+ case ELEMENT_TYPE_R8:
+ str = "float64";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U:
+ str = "unsigned int";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I:
+ str = "int";
+ goto APPEND;
+
+ case ELEMENT_TYPE_OBJECT:
+ str = "class System.Object";
+ goto APPEND;
+
+ case ELEMENT_TYPE_STRING:
+ str = "class System.String";
+ goto APPEND;
+
+ case ELEMENT_TYPE_CANON_ZAPSIG:
+ str = "class System.__Canon";
+ goto APPEND;
+
+ case ELEMENT_TYPE_TYPEDBYREF:
+ str = "refany";
+ goto APPEND;
+
+ APPEND:
+ IfFailGo(appendStrA(out, str));
+ break;
+
+ case ELEMENT_TYPE_INTERNAL:
+ void* pMT;
+ pMT = *((void* UNALIGNED *)typePtr);
+ typePtr += sizeof(void*);
+ CHAR tempBuffer[64];
+ sprintf_s(tempBuffer, 64, "pMT: %p", pMT);
+ IfFailGo(appendStrA(out, tempBuffer));
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ str = "value class ";
+ goto DO_CLASS;
+
+ case ELEMENT_TYPE_CLASS:
+ str = "class ";
+ goto DO_CLASS;
+
+ case ELEMENT_TYPE_CMOD_REQD:
+ str = "required_modifier ";
+ goto DO_CLASS;
+
+ case ELEMENT_TYPE_CMOD_OPT:
+ str = "optional_modifier ";
+ goto DO_CLASS;
+
+ DO_CLASS:
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ IfFailGo(appendStrA(out, str));
+ str = "<UNKNOWN>";
+
+ if (TypeFromToken(tk) == mdtTypeSpec)
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE sig;
+ IfFailGo(pIMDI->GetSigFromToken(tk, &cSig, &sig));
+ IfFailGo(PrettyPrintTypeA(sig, cSig, out, pIMDI));
+ }
+ else
+ {
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ //<TODO>@consider: assembly name?</TODO>
+ if (FAILED(pIMDI->GetNameOfTypeRef(tk, &pNS, &pN)))
+ {
+ pNS = pN = "Invalid TypeRef record";
+ }
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tk) == mdtTypeDef);
+ if (FAILED(pIMDI->GetNameOfTypeDef(tk, &pN, &pNS)))
+ {
+ pNS = pN = "Invalid TypeDef record";
+ }
+ }
+
+ if (pNS && *pNS)
+ {
+ IfFailGo(appendStrA(out, pNS));
+ IfFailGo(appendStrA(out, NAMESPACE_SEPARATOR_STR));
+ }
+ IfFailGo(appendStrA(out, pN));
+ }
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ IfFailGo(appendStrA(out, "[]"));
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ unsigned rank = CorSigUncompressData(typePtr);
+ PREFIX_ASSUME(rank <= 0xffffff);
+ // <TODO>TODO what is the syntax for the rank 0 case? </TODO>
+ if (rank == 0)
+ {
+ IfFailGo(appendStrA(out, "[??]"));
+ }
+ else
+ {
+ _ASSERTE(rank != 0);
+ int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
+ int* sizes = &lowerBounds[rank];
+ memset(lowerBounds, 0, sizeof(int)*2*rank);
+
+ unsigned numSizes = CorSigUncompressData(typePtr);
+ _ASSERTE(numSizes <= rank);
+ unsigned int i;
+ for(i =0; i < numSizes; i++)
+ sizes[i] = CorSigUncompressData(typePtr);
+
+ unsigned numLowBounds = CorSigUncompressData(typePtr);
+ _ASSERTE(numLowBounds <= rank);
+ for(i = 0; i < numLowBounds; i++)
+ lowerBounds[i] = CorSigUncompressData(typePtr);
+
+ IfFailGo(appendStrA(out, "["));
+ for(i = 0; i < rank; i++)
+ {
+ if (sizes[i] != 0 && lowerBounds[i] != 0)
+ {
+ if (lowerBounds[i] == 0)
+ appendStrNumA(out, sizes[i]);
+ else
+ {
+ appendStrNumA(out, lowerBounds[i]);
+ IfFailGo(appendStrA(out, "..."));
+ if (sizes[i] != 0)
+ appendStrNumA(out, lowerBounds[i] + sizes[i] + 1);
+ }
+ }
+ if (i < rank-1)
+ IfFailGo(appendStrA(out, ","));
+ }
+ IfFailGo(appendStrA(out, "]"));
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_MVAR:
+ IfFailGo(appendStrA(out, "!!"));
+ appendStrNumA(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ IfFailGo(appendStrA(out, "!"));
+ appendStrNumA(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ unsigned ntypars = CorSigUncompressData(typePtr);
+ IfFailGo(appendStrA(out, "<"));
+ for (unsigned i = 0; i < ntypars; i++)
+ {
+ if (i > 0)
+ IfFailGo(appendStrA(out, ","));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ }
+ IfFailGo(appendStrA(out, ">"));
+ }
+ break;
+
+ case ELEMENT_TYPE_MODULE_ZAPSIG:
+ IfFailGo(appendStrA(out, "[module#"));
+ appendStrNumA(out, CorSigUncompressData(typePtr));
+ IfFailGo(appendStrA(out, ", token#"));
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ IfFailGo(appendStrHexA(out, tk));
+ IfFailGo(appendStrA(out, "]"));
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ IfFailGo(appendStrA(out, "fnptr "));
+ IfFailGo(PrettyPrintSigWorkerInternal(typePtr, (typeEnd - typePtr), "", out,pIMDI));
+ break;
+
+ case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG:
+ case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+ IfFailGo(appendStrA(out, "native "));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ break;
+
+ // Modifiers or dependent types
+ case ELEMENT_TYPE_PINNED:
+ str = " pinned";
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_PTR:
+ str = "*";
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_BYREF:
+ str = "&";
+ goto MODIFIER;
+
+ MODIFIER:
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ IfFailGo(appendStrA(out, str));
+ break;
+
+ default:
+ case ELEMENT_TYPE_SENTINEL:
+ case ELEMENT_TYPE_END:
+ hr = E_INVALIDARG;
+ break;
+ }
+ ErrExit:
+ return hr;
+} // PrettyPrintTypeA
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Converts a com signature to a text signature.
+//
+// Note that this function DOES NULL terminate the result signature string.
+//*****************************************************************************
+HRESULT PrettyPrintSigInternalLegacy(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ unsigned typeLen, // length of type
+ const CHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMDInternalImport * pIMDI) // Import api to use.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ return PrettyPrintSigWorkerInternal(typePtr, typeLen, name, out, pIMDI);
+} // HRESULT PrettyPrintSigInternalLegacy()
+
+HRESULT PrettyPrintSigWorkerInternal(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const CHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMDInternalImport * pIMDI) // Import api to use.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ unsigned numArgs; // Count of arugments to function, or count of local vars.
+ unsigned numTyArgs = 0;
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen;
+ bool needComma = false;
+
+ out->Shrink(0);
+
+ if (name != 0) // 0 means a local var sig
+ {
+ // get the calling convention out
+ unsigned callConv = CorSigUncompressData(typePtr);
+
+ if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ if (name != 0 && *name != 0)
+
+ {
+ IfFailGo(appendStrA(out, " "));
+ IfFailGo(appendStrA(out, name));
+ }
+ goto ErrExit;
+ }
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ IfFailGo(appendStrA(out, "instance "));
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ IfFailGo(appendStrA(out, "generic "));
+ numTyArgs = CorSigUncompressData(typePtr);
+ }
+
+ static const CHAR* const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] =
+ {
+ "",
+ "unmanaged cdecl ",
+ "unmanaged stdcall ",
+ "unmanaged thiscall ",
+ "unmanaged fastcall ",
+ "vararg ",
+ "<error> ",
+ "<error> ",
+ "",
+ "",
+ "",
+ "native vararg "
+ };
+
+ if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX)
+ {
+ appendStrA(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]);
+ }
+
+ numArgs = CorSigUncompressData(typePtr);
+ // do return type
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ }
+ else
+ {
+ numArgs = CorSigUncompressData(typePtr);
+ }
+
+ if (name != 0 && *name != 0)
+ {
+ IfFailGo(appendStrA(out, " "));
+ IfFailGo(appendStrA(out, name));
+ }
+ IfFailGo(appendStrA(out, "("));
+
+ while (numArgs)
+ {
+ if (typePtr >= typeEnd)
+ break;
+
+ if (*typePtr == ELEMENT_TYPE_SENTINEL)
+ {
+ if (needComma)
+ IfFailGo(appendStrA(out, ","));
+ IfFailGo(appendStrA(out, "..."));
+ ++typePtr;
+ }
+ else
+ {
+ if (needComma)
+ IfFailGo(appendStrA(out, ","));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ --numArgs;
+ }
+ needComma = true;
+ }
+ IfFailGo(appendStrA(out, ")"));
+ if (asStringA(out) == 0)
+ IfFailGo(E_OUTOFMEMORY);
+
+ ErrExit:
+ return hr;
+} // HRESULT PrettyPrintSigWorkerInternal()
diff --git a/src/utilcode/rangetree.cpp b/src/utilcode/rangetree.cpp
new file mode 100644
index 0000000000..028ebd9769
--- /dev/null
+++ b/src/utilcode/rangetree.cpp
@@ -0,0 +1,520 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+#include "stdafx.h"
+
+#include "rangetree.h"
+
+#ifndef DACCESS_COMPILE
+
+void RangeTree::Node::Init(SIZE_T rangeStart, SIZE_T rangeEnd
+ DEBUGARG(DWORD ord))
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(rangeEnd >= rangeStart);
+
+ start = rangeStart;
+ end = rangeEnd;
+
+ mask = GetRangeCommonMask(start, end);
+
+ IsIntermediate(FALSE);
+#ifdef _DEBUG
+ ordinal = ord;
+#endif
+
+ children[0] = NULL;
+ children[1] = NULL;
+}
+
+
+RangeTree::RangeTree() :
+ m_root(NULL), m_pool(sizeof(Node), 16, 16)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+#ifdef _DEBUG
+ m_nodeCount = 0;
+#endif
+}
+
+RangeTree::Node *RangeTree::Lookup(SIZE_T address) const
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ Node *node = m_root;
+
+ //
+ // When comparing an address to a node,
+ // there are 5 possibilities:
+ // * the node is null - no match
+ // * the address doesn't contain the prefix m - no match
+ // * the address is inside the node's range (and necessarily
+ // contains the prefix m) - match
+ // * the address is less than the range (and necessarily
+ // has the prefix m0) - traverse the zero child
+ // * the address is greater than the range (and necessarily
+ // has the prefix m1) - traverse the one child
+ //
+
+ while (node != NULL
+ && (address < node->start || address >= node->end))
+ {
+ //
+ // See if the address has prefix m.
+ //
+
+ if ((address & node->mask) != (node->start & node->mask))
+ return NULL;
+
+ //
+ // Determine which subnode to look in.
+ //
+
+ node = *node->Child(address);
+ }
+
+ if (node != NULL && node->IsIntermediate())
+ node = NULL;
+
+ return node;
+}
+
+RangeTree::Node *RangeTree::LookupEndInclusive(SIZE_T address)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ //
+ // Lookup an address which may be the ending range
+ // of a node. In order for this to make sense, it
+ // must be the case that address is never the starting
+ // address of the node. (Otherwise there is an
+ // ambiguity when 2 nodes are adjacent.)
+ //
+
+ Node *result = Lookup(address-1);
+
+ if ((result != NULL) && (address >= result->start)
+ && (address <= result->end))
+ return result;
+ else
+ return NULL;
+}
+
+HRESULT RangeTree::AddNode(Node *addNode, SIZE_T start, SIZE_T end)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ } CONTRACTL_END;
+
+ addNode->Init(start, end DEBUGARG(++m_nodeCount));
+
+ Node **nodePtr = &m_root;
+
+ while (TRUE)
+ {
+ Node *node = *nodePtr;
+
+ //
+ // See if we can live here
+ //
+
+ if (node == NULL)
+ {
+ *nodePtr = addNode;
+ return S_OK;
+ }
+
+ //
+ // Decide if we are a child of the
+ // current node, or it is a child
+ // of us, or neither.
+ //
+
+ SIZE_T diffBits = start ^ node->start;
+
+ // See if the nodes are disjoint
+ if (diffBits & (node->mask & addNode->mask))
+ {
+ // We need to construct a intermediate node to be the parent of these two.
+
+ // AddIntermediateNode throws to indicate OOM. We need to either
+ // propagate this behavior upward or convert the exception here.
+ CONTRACT_VIOLATION(ThrowsViolation);
+ *nodePtr = AddIntermediateNode(node, addNode);
+ // <TODO> data structure is hosed at this point if we get an exception - should
+ // we undo the operation?</TODO>
+
+ return S_OK;
+ }
+ else
+ {
+ SIZE_T maskDiff = node->mask ^ addNode->mask;
+
+ if (maskDiff == 0)
+ {
+ // Masks are the same size, ranges overlap.
+ // This must be an intermediate node or we have a problem.
+ if (!node->IsIntermediate())
+ return E_INVALIDARG;
+
+ // Replace the intermediate node with this one.
+ addNode->children[0] = node->children[0];
+ addNode->children[1] = node->children[1];
+ *nodePtr = addNode;
+ FreeIntermediate(node);
+
+ return S_OK;
+ }
+
+ // Make sure the range doesn't intersect.
+ if (end > node->start && start < node->end)
+ return E_INVALIDARG;
+
+ else if (addNode->mask & maskDiff)
+ {
+ // added node's mask is bigger - it should be the child
+ nodePtr = node->Child(start);
+ }
+ else
+ {
+ // existing node's mask is bigger - it should be the child
+ *nodePtr = addNode;
+ nodePtr = addNode->Child(node->start);
+ addNode = node;
+ }
+ }
+ }
+}
+
+HRESULT RangeTree::RemoveNode(Node *removeNode)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ Node **nodePtr = &m_root;
+
+ while (TRUE)
+ {
+ Node *node = *nodePtr;
+
+ _ASSERTE(node != NULL);
+
+ if (node == removeNode)
+ {
+ if (node->children[0] == NULL)
+ *nodePtr = node->children[1];
+ else if (node->children[1] == NULL)
+ *nodePtr = node->children[0];
+ else
+ {
+ *nodePtr = AddIntermediateNode(node->children[0],
+ node->children[1]);
+ }
+
+ return S_OK;
+ }
+ else if (node->IsIntermediate())
+ {
+ if (node->children[0] == removeNode)
+ {
+ if (removeNode->children[0] == NULL && removeNode->children[1] == NULL)
+ {
+ *nodePtr = node->children[1];
+ FreeIntermediate(node);
+ return S_OK;
+ }
+ }
+ else if (node->children[1] == removeNode)
+ {
+ if (removeNode->children[0] == NULL && removeNode->children[1] == NULL)
+ {
+ *nodePtr = node->children[0];
+ FreeIntermediate(node);
+ return S_OK;
+ }
+ }
+ }
+
+ nodePtr = node->Child(removeNode->start);
+ }
+}
+
+void RangeTree::Iterate(IterationCallback pCallback, void *context)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_root != NULL)
+ IterateNode(m_root, pCallback, context);
+}
+
+void RangeTree::IterateNode(Node *node, IterationCallback pCallback, void *context)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (node->children[0] != NULL)
+ IterateNode(node->children[0], pCallback, context);
+
+ if (!node->IsIntermediate())
+ pCallback(node, context);
+
+ if (node->children[1] != NULL)
+ IterateNode(node->children[1], pCallback, context);
+}
+
+void RangeTree::IterateRange(SIZE_T start, SIZE_T end, IterationCallback pCallback, void *context)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_root != NULL)
+ IterateRangeNode(m_root, start, end, GetRangeCommonMask(start, end),
+ pCallback, context);
+}
+
+void RangeTree::IterateRangeNode(Node *node, SIZE_T start, SIZE_T end,
+ SIZE_T mask, IterationCallback pCallback, void *context)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Compute which bits are different between the two start ranges
+ SIZE_T diffBits = start ^ node->start;
+
+ // See if the nodes are disjoint
+ if (diffBits & (node->mask & mask))
+ {
+ return;
+ }
+ else
+ {
+ if (node->children[0] != NULL)
+ IterateRangeNode(node->children[0], start, end, mask, pCallback, context);
+
+ if (!node->IsIntermediate()
+ && (end > node->start && start < node->end))
+ (*pCallback)(node, context);
+
+ if (node->children[1] != NULL)
+ IterateRangeNode(node->children[1], start, end, mask, pCallback, context);
+ }
+}
+
+
+BOOL RangeTree::Overlaps(SIZE_T start, SIZE_T end)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ if (m_root != NULL)
+ return OverlapsNode(m_root, start, end, GetRangeCommonMask(start, end));
+ else
+ return FALSE;
+}
+
+BOOL RangeTree::OverlapsNode(Node *node, SIZE_T start, SIZE_T end, SIZE_T mask)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ //
+ // Decide if we are a child of the
+ // current node, or it is a child
+ // of us, or neither.
+ //
+
+ SIZE_T diffBits = start ^ node->start;
+
+ // See if the nodes are disjoint
+ if (diffBits & (node->mask & mask))
+ {
+ return FALSE;
+ }
+ else
+ {
+ if (!node->IsIntermediate()
+ && (end > node->start && start < node->end))
+ return TRUE;
+
+ if (node->children[0] != NULL && OverlapsNode(node->children[0], start, end, mask)
+ || node->children[1] != NULL && OverlapsNode(node->children[1], start, end, mask))
+ return TRUE;
+
+ return FALSE;
+ }
+}
+
+
+void RangeTree::RemoveRange(SIZE_T start, SIZE_T end)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ if (m_root != NULL)
+ RemoveRangeNode(&m_root, start, end, GetRangeCommonMask(start, end));
+}
+
+
+void RangeTree::RemoveRangeNode(Node **nodePtr, SIZE_T start, SIZE_T end, SIZE_T mask)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ Node *node = *nodePtr;
+
+ // Compute which bits are different between the two start ranges
+ SIZE_T diffBits = start ^ node->start;
+
+ // See if the nodes are disjoint
+ if (diffBits & (node->mask & mask))
+ {
+ // do nothing
+ }
+ else
+ {
+ // First, remove from children
+ if (node->children[0] != NULL)
+ RemoveRangeNode(&node->children[0], start, end, mask);
+ if (node->children[1] != NULL)
+ RemoveRangeNode(&node->children[1], start, end, mask);
+
+ // Now, remove this node if necessary.
+ if (node->IsIntermediate())
+ {
+ if (node->children[0] == NULL)
+ {
+ *nodePtr = node->children[0];
+ FreeIntermediate(node);
+ }
+ else if (node->children[1] == NULL)
+ {
+ *nodePtr = node->children[1];
+ FreeIntermediate(node);
+ }
+ }
+ else if (end > node->start && start < node->end)
+ {
+ if (node->children[0] == NULL)
+ *nodePtr = node->children[1];
+ else if (node->children[1] == NULL)
+ *nodePtr = node->children[0];
+ else
+ {
+
+ // AddIntermediateNode throws to indicate OOM. We need to either
+ // propagate this behavior upward or convert the exception here.
+ CONTRACT_VIOLATION(ThrowsViolation);
+ *nodePtr = AddIntermediateNode(node->children[0],
+ node->children[1]);
+ }
+ }
+ }
+}
+
+
+SIZE_T RangeTree::GetRangeCommonMask(SIZE_T start, SIZE_T end)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Compute which bits are the different
+
+ SIZE_T diff = start ^ end;
+
+ // Fill up bits to the right until all are set
+
+ diff |= (diff>>1);
+ diff |= (diff>>2);
+ diff |= (diff>>4);
+ diff |= (diff>>8);
+ diff |= (diff>>16);
+
+#if (POINTER_BITS > 32)
+ diff |= (diff>>32);
+#endif
+
+ // flip bits to form high mask
+ return ~diff;
+}
+
+RangeTree::Node *RangeTree::AddIntermediateNode(Node *node0,
+ Node *node1)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ SIZE_T mask = GetRangeCommonMask(node0->start,
+ node1->start);
+
+ _ASSERTE((mask & ~node0->mask) == 0);
+ _ASSERTE((mask & ~node1->mask) == 0);
+ _ASSERTE((node0->start & mask) == (node1->start & mask));
+ _ASSERTE((node0->start & mask) == (node1->start & mask));
+
+ SIZE_T middle = (node0->start & mask) + (~mask>>1);
+
+ Node *intermediate = AllocateIntermediate();
+
+ intermediate->start = middle;
+ intermediate->end = middle+1;
+ intermediate->mask = mask;
+ intermediate->IsIntermediate(TRUE);
+#ifdef _DEBUG
+ intermediate->ordinal = ++m_nodeCount;
+#endif
+
+ int less = (node0->start < node1->start);
+
+ intermediate->children[!less] = node0;
+ intermediate->children[less] = node1;
+
+ return intermediate;
+}
+
+RangeTree::Node *RangeTree::AllocateIntermediate()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ return (RangeTree::Node *) m_pool.AllocateElement();
+}
+
+void RangeTree::FreeIntermediate(Node *node)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ m_pool.FreeElement(node);
+}
+
+#endif
diff --git a/src/utilcode/registrywrapper.cpp b/src/utilcode/registrywrapper.cpp
new file mode 100644
index 0000000000..e48fb0941c
--- /dev/null
+++ b/src/utilcode/registrywrapper.cpp
@@ -0,0 +1,360 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// File: registrywrapper.cpp
+//
+
+//
+// Wrapper around Win32 Registry Functions allowing redirection of .Net
+// Framework root registry location
+//
+// Notes on Offline Ngen Implementation:
+//
+// This implementation redirects file accesses to the GAC, NIC, framework directory
+// and registry accesses to root store and fusion.
+// Essentially, if we open a file or reg key directly from the CLR, we redirect it
+// into the mounted VHD specified in the COMPLUS config values.
+//
+// Terminology:
+// Host Machine - The machine running a copy of windows that mounts a VHD to
+// compile the assemblies within. This is the build machine in the build lab.
+//
+// Target Machine - The VHD that gets mounted inside the host. We compile
+// native images storing them inside the target. This is the freshly build
+// copy of windows in the build lab.
+//
+// The OS itself pulls open all manner of registry keys and files as side-effects
+// of our API calls. Here is a list of things the redirection implementation does
+// not cover:
+//
+// - COM
+// We use COM in Ngen to create and communicate with worker processes. In
+// order to marshal arguments between ngen and worker, mscoree.tlb is loaded.
+// The COM system from the loaded and running OS is used, which means the host
+// machine's mscoree.tlb gets loaded for marshalling arguments for the CLR
+// running on the target machine. In the next release (4.5) the ngen interfaces
+// don't expect to change for existing ngen operations. If new functionality
+// is added, new interfaces would be added, but existing ones will not be
+// altered since we have a high compat bar with an inplace release. mscoree.tlb
+// also contains interfaces for mscoree shim and gchost which again we expect to
+// remain compatible in this next product release. In order to fix this, we will
+// need support from Windows for using the COM system on another copy of Windows.
+// - Registry Accesses under
+// - HKLM\software[\Wow6432Node]\policies\microsoft : SQM, Cryptography, MUI, codeidentifiers, appcompat, RPC
+// - HKLM\software[\Wow6432Node]\RPC,OLE,COM and under these keys
+// - HKLM\Software\Microsoft\Cryptography and under
+// - HKLM\Software\Microsoft\SQMClient
+// - HKLM\Software[\Wow6432Node]\Microsoft\Windows\Windows Error Reporting\WMR and under
+//
+// These locations are not accessed directly by the CLR, but looked up by Windows
+// as part of other API calls. It is safer that we are accessing these
+// on the host machine since they correspond with the actively running copy of
+// Windows. If we could somehow redirect these to the target VM, we would have
+// Windows 7/2K8 OS looking up its config keys from a Win8 registry. If Windows
+// has made incompatible changes here, such as moving the location or redefining
+// values, we would break.
+// - Accesses from C:\Windows\System32 and C:\Windows\Syswow64 and HKCU
+// HKCU does not contain any .Net Framework settings (Microsoft\.NETFramework
+// is empty).
+// There are various files accessed from C:\Windows\System32 and these are a
+// function of the OS loader. We load an executable and it automatically
+// pulls in kernel32.dll, for example. This should not be a problem for running
+// the CLR, since v4.5 will run on Win2K8, and for offline ngen compilation, we
+// will not be using the new Win8 APIs for AppX. We had considered setting
+// the PATH to point into the target machine's System32 directory, but the
+// Windows team advised us that would break pretty quickly due to Api-sets
+// having their version numbers rev'd and the Win2k8 host machine not having
+// the ability to load them.
+//
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "registrywrapper.h"
+#include "clrconfig.h"
+#include "strsafe.h"
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) && !defined(FEATURE_CORECLR)
+
+static LPCWSTR kRegistryRootKey = W("SOFTWARE\\");
+static const HKEY kRegistryRootHive = HKEY_LOCAL_MACHINE;
+static LPCWSTR kWow6432Node = W("Wow6432Node\\");
+static LPCWSTR kMicrosoftDotNetFrameworkPolicy = W("Microsoft\\.NETFramework\\Policy");
+static LPCWSTR wszHKLM = W("HKLM\\");
+static WCHAR * g_registryRoot = NULL;
+static volatile bool g_initialized = false;
+static const WCHAR BACKSLASH_CHARACTER[] = W("\\");
+
+//
+// Called the first time we use ClrRegCreateKeyEx or ClrRegOpenKeyEx to determine if the registry redirection
+// config value has been set. If so, we parse it into g_registryRoot
+// If the config string is mal-formed, we don't set the global variable.
+//
+HRESULT ParseRegistryRootConfigValue()
+{
+ if (!IsNgenOffline())
+ {
+ return ERROR_SUCCESS;
+ }
+
+ CLRConfigStringHolder configValue(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RegistryRoot));
+ // Since IsNgenOffline returned true, this better not be NULL
+ if (configValue == NULL)
+ return ERROR_BAD_ARGUMENTS;
+
+ if (_wcsnicmp(configValue, wszHKLM, wcslen(wszHKLM)) != 0)
+ {
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ // The rest of the string is the location of the redirected registry key
+ LPWSTR configValueKey = (LPWSTR)configValue;
+ configValueKey = _wcsninc(configValueKey, wcslen(wszHKLM));
+
+ size_t len = wcslen(configValueKey) + 1;
+
+ bool appendBackslash = false;
+ if (configValueKey[wcslen(configValueKey) - 1] != W('\\'))
+ {
+ appendBackslash = true;
+ len += wcslen(BACKSLASH_CHARACTER);
+ }
+ g_registryRoot = new (nothrow) WCHAR[len];
+
+ if (g_registryRoot == NULL)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy_s(g_registryRoot, len, configValueKey);
+ if (appendBackslash)
+ {
+ StringCchCat(g_registryRoot, len, BACKSLASH_CHARACTER);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+//
+// This is our check that the target machine is 32/64bit
+// If 32bit only, there will be no Wow6432Node key
+// If 64bit, there will be a Wow6432Node key
+//
+HRESULT CheckWow6432NodeNetFxExists(bool * fResult)
+{
+ static bool bInitialized = false;
+ static bool bNodeExists = false;
+ HRESULT hr = ERROR_SUCCESS;
+
+ if (!bInitialized)
+ {
+ size_t redirectedLength = wcslen(g_registryRoot) + wcslen(kWow6432Node) + wcslen(kMicrosoftDotNetFrameworkPolicy) + 1;
+ LPWSTR lpRedirectedKey = new (nothrow) WCHAR[redirectedLength];
+
+ if (lpRedirectedKey == NULL)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy_s(lpRedirectedKey, redirectedLength, g_registryRoot);
+ StringCchCat(lpRedirectedKey, redirectedLength, kWow6432Node);
+ StringCchCat(lpRedirectedKey, redirectedLength, kMicrosoftDotNetFrameworkPolicy);
+
+ HKEY hkey;
+ hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, lpRedirectedKey, 0, KEY_READ, &hkey);
+ bNodeExists = hr == ERROR_SUCCESS;
+
+ if (hr == ERROR_FILE_NOT_FOUND)
+ {
+ hr = ERROR_SUCCESS;
+ }
+
+ if (hkey)
+ {
+ RegCloseKey(hkey);
+ }
+ bInitialized = true;
+ }
+ *fResult = bNodeExists;
+ return hr;
+}
+
+HRESULT CheckUseWow6432Node(REGSAM samDesired, bool * fResult)
+{
+ HRESULT hr = ERROR_SUCCESS;
+ bool fWow6432NodeExists = false;
+
+ hr = CheckWow6432NodeNetFxExists(&fWow6432NodeExists);
+ if (hr == ERROR_SUCCESS)
+ {
+ if (!fWow6432NodeExists)
+ {
+ *fResult = false;
+ } else
+ {
+#if defined(_WIN64)
+ *fResult = false;
+#else
+ *fResult = true;
+#endif
+ // If KEY_WOW64_64KEY or KEY_WOW64_32KEY are set, override
+ // If both are set, the actual registry call will fail with ERROR_INVALID_PARAMETER
+ // so no worries here
+ if (samDesired & KEY_WOW64_64KEY)
+ {
+ *fResult = false;
+ } else if (samDesired & KEY_WOW64_32KEY)
+ {
+ *fResult = true;
+ }
+ }
+ }
+ return hr;
+}
+
+//
+// lpRedirectedKey is allocated and returned from this method. Caller owns the new string and
+// should release
+//
+HRESULT RedirectKey(REGSAM samDesired, HKEY hKey, LPCWSTR lpKey, __deref_out_z LPWSTR * lpRedirectedKey)
+{
+ if (hKey != kRegistryRootHive || _wcsnicmp(lpKey, kRegistryRootKey, wcslen(kRegistryRootKey)) != 0)
+ {
+ return ERROR_SUCCESS;
+ }
+
+ LPCWSTR lpRootStrippedKey = lpKey + wcslen(kRegistryRootKey);
+ size_t redirectedLength = wcslen(g_registryRoot) + wcslen(lpRootStrippedKey) + 1;
+
+ bool bUseWow = false;
+ HRESULT hr = CheckUseWow6432Node(samDesired, &bUseWow);
+
+ if (hr != ERROR_SUCCESS)
+ {
+ return hr;
+ }
+
+ if (bUseWow)
+ {
+ redirectedLength += wcslen(kWow6432Node);
+ }
+
+ *lpRedirectedKey = new (nothrow) WCHAR[redirectedLength];
+
+ if (*lpRedirectedKey == NULL)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy_s(*lpRedirectedKey, redirectedLength, g_registryRoot);
+ if (bUseWow)
+ {
+ StringCchCat(*lpRedirectedKey, redirectedLength, kWow6432Node);
+ }
+
+ StringCchCat(*lpRedirectedKey, redirectedLength, lpRootStrippedKey);
+
+ return ERROR_SUCCESS;
+}
+
+LONG ClrRegCreateKeyEx(
+ HKEY hKey,
+ LPCWSTR lpSubKey,
+ DWORD Reserved,
+ __in_opt LPWSTR lpClass,
+ DWORD dwOptions,
+ REGSAM samDesired,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ PHKEY phkResult,
+ LPDWORD lpdwDisposition
+ )
+{
+ HRESULT hr;
+ if (!g_initialized)
+ {
+ hr = ParseRegistryRootConfigValue();
+ if (hr != ERROR_SUCCESS)
+ return hr;
+ g_initialized = true;
+ }
+
+ if (g_registryRoot != NULL)
+ {
+ NewArrayHolder<WCHAR> lpRedirectedKey(NULL);
+ hr = RedirectKey(samDesired, hKey, lpSubKey, &lpRedirectedKey);
+ // We don't want to touch the registry if we're low on memory and can't redirect properly. It could lead
+ // to use reading and writing to two separate registries and corrupting both in the process
+ if (hr != ERROR_SUCCESS)
+ return hr;
+ LPCWSTR lpKeyToUse = lpRedirectedKey == NULL ? lpSubKey : lpRedirectedKey;
+ return RegCreateKeyExW(hKey, lpKeyToUse, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
+ }
+ return RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
+} // ClrRegCreateKeyEx
+
+LONG ClrRegOpenKeyEx(
+ HKEY hKey,
+ LPCWSTR lpSubKey,
+ DWORD ulOptions,
+ REGSAM samDesired,
+ PHKEY phkResult
+ )
+{
+ HRESULT hr;
+ if (!g_initialized)
+ {
+ hr = ParseRegistryRootConfigValue();
+ if (hr != ERROR_SUCCESS)
+ return hr;
+ g_initialized = true;
+ }
+
+ if (g_registryRoot != NULL)
+ {
+ NewArrayHolder<WCHAR> lpRedirectedKey(NULL);
+ hr = RedirectKey(samDesired, hKey, lpSubKey, &lpRedirectedKey);
+ // We don't want to touch the registry if we're low on memory and can't redirect properly. It could lead
+ // to use reading and writing to two separate registries and corrupting both in the process
+ if (hr != ERROR_SUCCESS)
+ return hr;
+ LPCWSTR lpKeyToUse = lpRedirectedKey == NULL ? lpSubKey : lpRedirectedKey;
+ return RegOpenKeyExW(hKey, lpKeyToUse, ulOptions, samDesired, phkResult);
+ }
+ return RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
+}
+
+//
+// Determine whether we're running Offline Ngen for Build Lab based on the settings of several Config Values.
+//
+bool IsNgenOffline()
+{
+ static volatile bool fInitialized = false;
+ static volatile bool fNgenOffline = false;
+
+ if (!fInitialized)
+ {
+ REGUTIL::CORConfigLevel kCorConfigLevel = static_cast<REGUTIL::CORConfigLevel>(REGUTIL::COR_CONFIG_ENV);
+ CLRConfigStringHolder pwzConfigInstallRoot = REGUTIL::GetConfigString_DontUse_(CLRConfig::INTERNAL_InstallRoot,
+ TRUE /* Prepend Complus */,
+ kCorConfigLevel,
+ FALSE /* fUsePerfCache */);
+ CLRConfigStringHolder pwzConfigNicPath(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_NicPath));
+ CLRConfigStringHolder pwzRegistryRoot(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RegistryRoot));
+ CLRConfigStringHolder pwzConfigAssemblyPath(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_AssemblyPath));
+ CLRConfigStringHolder pwzConfigAssemblyPath2(CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_AssemblyPath2));
+ if (pwzConfigInstallRoot != NULL &&
+ pwzConfigNicPath != NULL &&
+ pwzConfigAssemblyPath != NULL &&
+ pwzConfigAssemblyPath2 != NULL &&
+ pwzRegistryRoot != NULL)
+ {
+ fNgenOffline = true;
+ }
+ fInitialized = true;
+ }
+
+ return fNgenOffline;
+}
+
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES && !FEATURE_CORECLR
diff --git a/src/utilcode/regutil.cpp b/src/utilcode/regutil.cpp
new file mode 100644
index 0000000000..6b000288a3
--- /dev/null
+++ b/src/utilcode/regutil.cpp
@@ -0,0 +1,1496 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// regutil.cpp
+//
+
+//
+// This module contains a set of functions that can be used to access the
+// registry.
+//
+//*****************************************************************************
+
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "mscoree.h"
+#include "sstring.h"
+
+#define COMPLUS_PREFIX W("COMPlus_")
+#define LEN_OF_COMPLUS_PREFIX 8
+
+#if (!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(DEBUG)) && !defined(FEATURE_PAL)
+#define ALLOW_REGISTRY
+#endif
+
+#undef WszRegCreateKeyEx
+#undef WszRegOpenKeyEx
+#undef WszRegOpenKey
+#define WszRegCreateKeyEx RegCreateKeyExW
+#define WszRegOpenKeyEx RegOpenKeyExW
+#define WszRegOpenKey(hKey, wszSubKey, phkRes) RegOpenKeyExW(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes)
+
+//*****************************************************************************
+// Reads from the environment setting
+//*****************************************************************************
+LPWSTR REGUTIL::EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ WCHAR buff[64];
+
+ if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0)))
+ {
+ return(0);
+ }
+
+#ifdef ALLOW_REGISTRY
+ if (fPrependCOMPLUS)
+ {
+ if (!EnvCacheValueNameSeenPerhaps(name))
+ return 0;
+ }
+#endif // ALLOW_REGISTRY
+
+ if (fPrependCOMPLUS)
+ {
+ wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX);
+ }
+ else
+ {
+ *buff = 0;
+ }
+
+ wcscat_s(buff, _countof(buff), name);
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+ int len = WszGetEnvironmentVariable(buff, 0, 0);
+
+ if (len == 0)
+ {
+ return(0);
+ }
+
+ // If we can't get memory to return the string, then will simply pretend we didn't find it.
+ WCHAR * ret = new (nothrow) WCHAR [len];
+ if (ret != NULL)
+ {
+ WszGetEnvironmentVariable(buff, ret, len);
+ }
+
+ return ret;
+}
+
+#ifdef ALLOW_REGISTRY
+
+#ifndef FEATURE_CORECLR
+
+//*****************************************************************************
+// Gives the use the ability to turn on/off REGUTIL's ability to read from
+// the registry. This is useful in mscoree.dll on startup, in order to avoid
+// loading advapi32 and rpcrt4 until we're ready for them
+//*****************************************************************************
+void REGUTIL::AllowRegistryUse(BOOL fAllowUse)
+{
+ s_fUseRegistry = fAllowUse;
+}// AllowRegistryUse
+
+#endif // !FEATURE_CORECLR
+
+#endif // ALLOW_REGISTRY
+
+
+BOOL REGUTIL::UseRegistry()
+{
+#if !defined(ALLOW_REGISTRY)
+ return TRUE;
+#else
+ return s_fUseRegistry;
+#endif
+}// UseRegistry
+
+//*****************************************************************************
+// Reads a DWORD from the COR configuration according to the level specified
+// Returns back defValue if the key cannot be found
+//*****************************************************************************
+DWORD REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC_HOST_ONLY;
+
+ ULONGLONG result;
+ GetConfigInteger(name, defValue, &result, TRUE, level, fPrependCOMPLUS);
+
+ return (DWORD)result;
+}
+
+#define uniwcst(val, endptr, base) (fGetDWORD ? wcstoul(val, endptr, base) : _wcstoui64(val, endptr, base))
+
+//
+// Look up a dword config value, and write the result to the DWORD passed in by reference.
+//
+// Return value:
+// * E_FAIL if the value is not found. (result is assigned the default value)
+// * S_OK if the value is found. (result is assigned the value that was found)
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+// * result - Pointer to the output DWORD.
+//
+// static
+HRESULT REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, __out DWORD * result, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ ULONGLONG ullResult;
+ HRESULT hr = GetConfigInteger(name, defValue, &ullResult, TRUE, level, fPrependCOMPLUS);
+ *result = (DWORD)ullResult;
+ return hr;
+}
+
+ULONGLONG REGUTIL::GetConfigULONGLONG_DontUse_(LPCWSTR name, ULONGLONG defValue, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC_HOST_ONLY;
+
+ ULONGLONG result;
+ GetConfigInteger(name, defValue, &result, FALSE, level, fPrependCOMPLUS);
+
+ return result;
+}
+
+// This function should really be refactored to return the string from the environment and let the caller decide
+// what to convert it to; and return the buffer read from the reg call.
+// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as
+// a 32-bit number. When we have the _wcstoui64 API on MAC we will use uniwcst instead of wcstoul.
+HRESULT REGUTIL::GetConfigInteger(LPCWSTR name, ULONGLONG defValue, __out ULONGLONG * result, BOOL fGetDWORD, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC_HOST_ONLY;
+
+ ULONGLONG rtn;
+ ULONGLONG ret = 0;
+ DWORD type = 0;
+ HKEY userKey;
+ HKEY machineKey;
+ DWORD size = 4;
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+ if (level & COR_CONFIG_ENV)
+ {
+ WCHAR* val = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first
+ if (val != 0) {
+ errno = 0;
+ LPWSTR endPtr;
+ rtn = uniwcst(val, &endPtr, 16); // treat it has hex
+ BOOL fSuccess = ((errno != ERANGE) && (endPtr != val));
+ delete[] val;
+
+ if (fSuccess) // success
+ {
+ *result = rtn;
+ return (S_OK);
+ }
+ }
+ }
+
+ // Early out if no registry access, simplifies following code.
+ //
+ if (!UseRegistry() || !(level & COR_CONFIG_REGISTRY))
+ {
+ *result = defValue;
+ return (E_FAIL);
+ }
+
+#ifdef ALLOW_REGISTRY
+ // Probe the config cache to see if there is any point
+ // probing the registry; if not, don't bother.
+ //
+ if (!RegCacheValueNameSeenPerhaps(name))
+ {
+ *result = defValue;
+ return (E_FAIL);
+ }
+#endif // ALLOW_REGISTRY
+
+ if (level & COR_CONFIG_USER)
+ {
+#ifdef ALLOW_REGISTRY
+ {
+ LONG retVal = ERROR_SUCCESS;
+ BOOL bCloseHandle = FALSE;
+ userKey = s_hUserFrameworkKey;
+
+ if (userKey == INVALID_HANDLE_VALUE)
+ {
+ retVal = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey);
+ bCloseHandle = TRUE;
+ }
+
+ if (retVal == ERROR_SUCCESS)
+ {
+ rtn = WszRegQueryValueEx(userKey, name, 0, &type, (LPBYTE)&ret, &size);
+
+ if (bCloseHandle)
+ VERIFY(!RegCloseKey(userKey));
+
+ if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD)))
+ {
+ *result = ret;
+ return (S_OK);
+ }
+ }
+ }
+#endif // ALLOW_REGISTRY
+ }
+
+ if (level & COR_CONFIG_MACHINE)
+ {
+#ifdef ALLOW_REGISTRY
+ {
+ LONG retVal = ERROR_SUCCESS;
+ BOOL bCloseHandle = FALSE;
+ machineKey = s_hMachineFrameworkKey;
+
+ if (machineKey == INVALID_HANDLE_VALUE)
+ {
+ retVal = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey);
+ bCloseHandle = TRUE;
+ }
+
+ if (retVal == ERROR_SUCCESS)
+ {
+ rtn = WszRegQueryValueEx(machineKey, name, 0, &type, (LPBYTE)&ret, &size);
+
+ if (bCloseHandle)
+ VERIFY(!RegCloseKey(machineKey));
+
+ if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD)))
+ {
+ *result = ret;
+ return (S_OK);
+ }
+ }
+ }
+#endif // ALLOW_REGISTRY
+ }
+
+ *result = defValue;
+ return (E_FAIL);
+}
+
+#define FUSION_REGISTRY_KEY_W W("Software\\Microsoft\\Fusion")
+
+//*****************************************************************************
+// Reads a string from the COR configuration according to the level specified
+// The caller is responsible for deallocating the returned string by
+// calling code:REGUTIL::FreeConfigString or using a code:ConfigStringHolder
+//*****************************************************************************
+
+LPWSTR REGUTIL::GetConfigString_DontUse_(LPCWSTR name, BOOL fPrependCOMPLUS, CORConfigLevel level, BOOL fUsePerfCache)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+#ifdef ALLOW_REGISTRY
+ HRESULT lResult;
+ RegKeyHolder userKey = NULL;
+ RegKeyHolder machineKey = NULL;
+ RegKeyHolder fusionKey = NULL;
+ DWORD type;
+ DWORD size;
+#endif // ALLOW_REGISTRY
+ NewArrayHolder<WCHAR> ret(NULL);
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+ if (level & COR_CONFIG_ENV)
+ {
+ ret = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first
+ if (ret != 0) {
+ if (*ret != 0)
+ {
+ ret.SuppressRelease();
+ return(ret);
+ }
+ ret.Clear();
+ }
+ }
+
+ // Early out if no registry access, simplifies following code.
+ //
+ if (!UseRegistry() || !(level & COR_CONFIG_REGISTRY))
+ {
+ return(ret);
+ }
+
+#ifdef ALLOW_REGISTRY
+ // Probe the config cache to see if there is any point
+ // probing the registry; if not, don't bother.
+ //
+ if (fUsePerfCache && !RegCacheValueNameSeenPerhaps(name))
+ return ret;
+#endif // ALLOW_REGISTRY
+
+ if (level & COR_CONFIG_USER)
+ {
+#ifdef ALLOW_REGISTRY
+ BOOL bUsingCachedKey = FALSE;
+
+ if (s_hUserFrameworkKey != INVALID_HANDLE_VALUE)
+ {
+ bUsingCachedKey = TRUE;
+ userKey = s_hUserFrameworkKey;
+ }
+
+ if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey) == ERROR_SUCCESS)
+ {
+ BOOL bReturn = FALSE;
+ if (WszRegQueryValueEx(userKey, name, 0, &type, 0, &size) == ERROR_SUCCESS &&
+ type == REG_SZ)
+ {
+ ret = (LPWSTR) new (nothrow) BYTE [size];
+ if (ret)
+ {
+ ret[0] = W('\0');
+ lResult = WszRegQueryValueEx(userKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ {
+ ret.SuppressRelease();
+ }
+ }
+ bReturn = TRUE;
+ }
+
+ if (bUsingCachedKey)
+ userKey.SuppressRelease();
+
+ if (bReturn)
+ return ret;
+ }
+
+#endif // ALLOW_REGISTRY
+ }
+
+ if (level & COR_CONFIG_MACHINE)
+ {
+#ifdef ALLOW_REGISTRY
+ BOOL bUsingCachedKey = FALSE;
+
+ if (s_hMachineFrameworkKey != INVALID_HANDLE_VALUE)
+ {
+ bUsingCachedKey = TRUE;
+ machineKey = s_hMachineFrameworkKey;
+ }
+
+ if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey) == ERROR_SUCCESS)
+ {
+ BOOL bReturn = FALSE;
+ if (WszRegQueryValueEx(machineKey, name, 0, &type, 0, &size) == ERROR_SUCCESS &&
+ type == REG_SZ)
+ {
+ ret = (LPWSTR) new (nothrow) BYTE [size];
+ if (ret)
+ {
+ ret[0] = W('\0');
+ lResult = WszRegQueryValueEx(machineKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ {
+ ret.SuppressRelease();
+ }
+ }
+ bReturn = TRUE;
+ }
+
+ if (bUsingCachedKey)
+ machineKey.SuppressRelease();
+
+ if (bReturn)
+ return ret;
+ }
+
+#endif // ALLOW_REGISTRY
+ }
+
+ if (level & COR_CONFIG_FUSION)
+ {
+#ifdef ALLOW_REGISTRY
+ if ((WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FUSION_REGISTRY_KEY_W, 0, KEY_READ, &fusionKey) == ERROR_SUCCESS) &&
+ (WszRegQueryValueEx(fusionKey, name, 0, &type, 0, &size) == ERROR_SUCCESS) &&
+ type == REG_SZ)
+ {
+ ret = (LPWSTR) new (nothrow) BYTE [size];
+ if (!ret)
+ {
+ return NULL;
+ }
+ ret[0] = W('\0');
+ lResult = WszRegQueryValueEx(fusionKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ ret.SuppressRelease();
+ return(ret);
+ }
+#endif // ALLOW_REGISTRY
+ }
+
+ return NULL;
+}
+
+//*****************************************************************************
+// Deallocation function for code:REGUTIL::GetConfigString_DontUse_
+//
+// Notes:
+// Use a code:ConfigStringHolder to automatically call this.
+//*****************************************************************************
+void REGUTIL::FreeConfigString(__in_z LPWSTR str)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ delete [] str;
+}
+
+//*****************************************************************************
+// Reads a BIT flag from the COR configuration according to the level specified
+// Returns back defValue if the key cannot be found
+//*****************************************************************************
+DWORD REGUTIL::GetConfigFlag_DontUse_(LPCWSTR name, DWORD bitToSet, BOOL defValue)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return(GetConfigDWORD_DontUse_(name, defValue) != 0 ? bitToSet : 0);
+}
+
+
+#ifdef ALLOW_REGISTRY
+
+#ifndef FEATURE_CORECLR
+
+//*****************************************************************************
+// Open's the given key and returns the value desired. If the key or value is
+// not found, then the default is returned.
+//*****************************************************************************
+long REGUTIL::GetLong( // Return value from registry or default.
+ LPCTSTR szName, // Name of value to get.
+ long iDefault, // Default value to return if not found.
+ LPCTSTR szKey, // Name of key, NULL==default.
+ HKEY hKeyVal) // What key to work on.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ long iValue; // The value to read.
+ DWORD iType; // Type of value to get.
+ DWORD iSize; // Size of buffer.
+ HKEY hKey; // Key for the registry entry.
+
+ _ASSERTE(UseRegistry());
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+ // Open the key if it is there.
+ if (ERROR_SUCCESS != WszRegOpenKeyEx(hKeyVal, (szKey) ? szKey : FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hKey))
+ return (iDefault);
+
+ // Read the key value if found.
+ iType = REG_DWORD;
+ iSize = sizeof(long);
+ if (ERROR_SUCCESS != WszRegQueryValueEx(hKey, szName, NULL,
+ &iType, (LPBYTE)&iValue, &iSize) || iType != REG_DWORD)
+ iValue = iDefault;
+
+ // We're done with the key now.
+ VERIFY(!RegCloseKey(hKey));
+ return (iValue);
+}
+
+
+// Opens or creates desired reg key, then writes iValue
+//*****************************************************************************
+long REGUTIL::SetOrCreateLong( // Return value from registry or default.
+ LPCTSTR szName, // Name of value to get.
+ long iValue, // Value to set.
+ LPCTSTR szKey, // Name of key, NULL==default.
+ HKEY hKeyVal) // What key to work on.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(UseRegistry());
+
+ long lRtn; // Return code.
+ HKEY hKey; // Key for the registry entry.
+
+
+ // Open the key if it is there, else create it
+ if (WszRegCreateKeyEx(hKeyVal,
+ (szKey) ? szKey : FRAMEWORK_REGISTRY_KEY_W,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &hKey,
+ NULL) != ERROR_SUCCESS)
+ {
+ return (-1);
+ }
+
+ // Read the key value if found.
+ lRtn = WszRegSetValueEx(hKey, szName, NULL, REG_DWORD, (const BYTE *) &iValue, sizeof(DWORD));
+
+ // We're done with the key now.
+ VERIFY(!RegCloseKey(hKey));
+ return (lRtn);
+}
+
+
+//*****************************************************************************
+// Open's the given key and returns the value desired. If the key or value is
+// not found, then the default is returned.
+//*****************************************************************************
+long REGUTIL::SetLong( // Return value from registry or default.
+ LPCTSTR szName, // Name of value to get.
+ long iValue, // Value to set.
+ LPCTSTR szKey, // Name of key, NULL==default.
+ HKEY hKeyVal) // What key to work on.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(UseRegistry());
+
+ long lRtn; // Return code.
+ HKEY hKey; // Key for the registry entry.
+
+ // Open the key if it is there.
+ if (ERROR_SUCCESS != WszRegOpenKey(hKeyVal, (szKey) ? szKey : FRAMEWORK_REGISTRY_KEY_W, &hKey))
+ return (-1);
+
+ // Read the key value if found.
+ lRtn = WszRegSetValueEx(hKey, szName, NULL, REG_DWORD, (const BYTE *) &iValue, sizeof(DWORD));
+
+ // We're done with the key now.
+ VERIFY(!RegCloseKey(hKey));
+ return (lRtn);
+}
+
+//*****************************************************************************
+// Set an entry in the registry of the form:
+// HKEY_CLASSES_ROOT\szKey\szSubkey = szValue. If szSubkey or szValue are
+// NULL, omit them from the above expression.
+//*****************************************************************************
+BOOL REGUTIL::SetKeyAndValue( // TRUE or FALSE.
+ LPCTSTR szKey, // Name of the reg key to set.
+ LPCTSTR szSubkey, // Optional subkey of szKey.
+ LPCTSTR szValue) // Optional value for szKey\szSubkey.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(UseRegistry());
+
+ size_t nLen = _tcslen(szKey) + 1;
+ if (szSubkey)
+ nLen += (_tcslen(szSubkey) + 1);
+
+ NewArrayHolder<TCHAR> rcKey = new (nothrow) TCHAR[nLen]; // Buffer for the full key name.
+ if (rcKey == NULL)
+ return FALSE;
+
+ HKEY hKey = NULL; // Handle to the new reg key.
+
+ // Init the key with the base key name.
+ _tcscpy_s(rcKey, nLen, szKey);
+
+ // Append the subkey name (if there is one).
+ if (szSubkey != NULL)
+ {
+ _tcscat_s(rcKey, nLen, _T("\\"));
+ _tcscat_s(rcKey, nLen, szSubkey);
+ }
+
+ // Create the registration key.
+ if (WszRegCreateKeyEx(HKEY_CLASSES_ROOT, rcKey, 0, NULL,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
+ &hKey, NULL) != ERROR_SUCCESS)
+ return(FALSE);
+
+ // Set the value (if there is one).
+ if (szValue != NULL)
+ if( WszRegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *) szValue,
+ (Wszlstrlen(szValue)+1) * sizeof(TCHAR)) != ERROR_SUCCESS ) {
+ VERIFY(!RegCloseKey(hKey));
+ return(FALSE);
+ }
+
+ VERIFY(!RegCloseKey(hKey));
+ return(TRUE);
+}
+
+
+//*****************************************************************************
+// Delete an entry in the registry of the form:
+// HKEY_CLASSES_ROOT\szKey\szSubkey.
+//*****************************************************************************
+LONG REGUTIL::DeleteKey( // TRUE or FALSE.
+ LPCTSTR szKey, // Name of the reg key to set.
+ LPCTSTR szSubkey) // Subkey of szKey.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(UseRegistry());
+
+ size_t nLen = _tcslen(szKey) + 1;
+ if (szSubkey)
+ nLen += (_tcslen(szSubkey) + 1);
+
+ NewArrayHolder<TCHAR> rcKey = new (nothrow) TCHAR[nLen]; // Buffer for the full key name.
+ if (rcKey == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // Init the key with the base key name.
+ _tcscpy_s(rcKey, nLen, szKey);
+
+ // Append the subkey name (if there is one).
+ if (szSubkey != NULL)
+ {
+ _tcscat_s(rcKey, nLen, _T("\\"));
+ _tcscat_s(rcKey, nLen, szSubkey);
+ }
+
+ // Delete the registration key.
+ return WszRegDeleteKey(HKEY_CLASSES_ROOT, rcKey);
+}
+
+
+//*****************************************************************************
+// Open the key, create a new keyword and value pair under it.
+//*****************************************************************************
+BOOL REGUTIL::SetRegValue( // Return status.
+ LPCTSTR szKeyName, // Name of full key.
+ LPCTSTR szKeyword, // Name of keyword.
+ LPCTSTR szValue) // Value of keyword.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(UseRegistry());
+
+ HKEY hKey; // Handle to the new reg key.
+
+ // Create the registration key.
+ if (WszRegCreateKeyEx(HKEY_CLASSES_ROOT, szKeyName, 0, NULL,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
+ &hKey, NULL) != ERROR_SUCCESS)
+ return (FALSE);
+
+ // Set the value (if there is one).
+ if (szValue != NULL)
+ if( WszRegSetValueEx(hKey, szKeyword, 0, REG_SZ, (BYTE *)szValue,
+ (Wszlstrlen(szValue)+1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
+ VERIFY(!RegCloseKey(hKey));
+ return(FALSE);
+ }
+
+ VERIFY(!RegCloseKey(hKey));
+ return (TRUE);
+}
+
+
+//*****************************************************************************
+// Does standard registration of a CoClass with a progid.
+//*****************************************************************************
+HRESULT REGUTIL::RegisterCOMClass( // Return code.
+ REFCLSID rclsid, // Class ID.
+ LPCTSTR szDesc, // Description of the class.
+ LPCTSTR szProgIDPrefix, // Prefix for progid.
+ int iVersion, // Version # for progid.
+ LPCTSTR szClassProgID, // Class progid.
+ LPCTSTR szThreadingModel, // What threading model to use.
+ LPCTSTR szModule, // Path to class.
+ HINSTANCE hInst, // Handle to module being registered
+ LPCTSTR szAssemblyName, // Optional Assembly,
+ LPCTSTR szVersion, // Optional Runtime version (directory containing runtime)
+ BOOL fExternal, // flag - External to mscoree.
+ BOOL fRelativePath) // flag - Relative path in szModule
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcCLSID[256]; // CLSID\\szID.
+ WCHAR rcInproc[_MAX_PATH+64]; // CLSID\\InprocServer32
+ WCHAR rcProgID[256]; // szProgIDPrefix.szClassProgID
+ WCHAR rcIndProgID[256]; // rcProgID.iVersion
+ WCHAR rcShim[_MAX_PATH];
+ HRESULT hr;
+
+ // Format the prog ID values.
+ VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID));
+
+ VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%d"), rcIndProgID, iVersion));
+
+ // Do the initial portion.
+ if (FAILED(hr = RegisterClassBase(rclsid, szDesc, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID))))
+ return (hr);
+
+ VERIFY(_snwprintf_s(rcInproc, _countof(rcInproc), _TRUNCATE, W("%s\\%s"), rcCLSID, W("InprocServer32")));
+
+ if (!fExternal){
+ SetKeyAndValue(rcCLSID, W("InprocServer32"), szModule);
+ }
+ else{
+ LPCTSTR pSep = szModule;
+ if (!fRelativePath && szModule) {
+ pSep = wcsrchr(szModule, W('\\'));
+ if(pSep == NULL)
+ pSep = szModule;
+ else
+ pSep++;
+ }
+ HMODULE hMod = WszLoadLibrary(W("mscoree.dll"));
+ if (!hMod)
+ return E_FAIL;
+
+ DWORD ret;
+ VERIFY(ret = WszGetModuleFileName(hMod, rcShim, NumItems(rcShim)));
+ FreeLibrary(hMod);
+ if( !ret )
+ return E_FAIL;
+
+ // Set the server path.
+ SetKeyAndValue(rcCLSID, W("InprocServer32"), rcShim);
+ if(pSep)
+ SetKeyAndValue(rcCLSID, W("Server"), pSep);
+
+ if(szAssemblyName) {
+ SetRegValue(rcInproc, W("Assembly"), szAssemblyName);
+ SetRegValue(rcInproc, W("Class"), rcIndProgID);
+ }
+ }
+
+ // Set the runtime version, it needs to be passed in from the outside
+ if(szVersion != NULL) {
+ LPCTSTR pSep2 = NULL;
+ LPTSTR pSep1 = const_cast<LPTSTR>(wcsrchr(szVersion, W('\\')));
+ if(pSep1 != NULL) {
+ *pSep1 = '\0';
+ pSep2 = wcsrchr(szVersion, W('\\'));
+ if (!pSep2)
+ pSep2 = szVersion;
+ else
+ pSep2 = pSep2++; // exclude '\\'
+ }
+ else
+ pSep2 = szVersion;
+
+ size_t bufLen = wcslen(rcInproc)+wcslen(pSep2)+2;
+ WCHAR* rcVersion = new (nothrow) WCHAR[bufLen];
+ if(rcVersion==NULL)
+ return (E_OUTOFMEMORY);
+ wcscpy_s(rcVersion, bufLen, rcInproc);
+ wcscat_s(rcVersion, bufLen, W("\\"));
+ wcscat_s(rcVersion, bufLen, pSep2);
+ SetRegValue(rcVersion, W("ImplementedInThisVersion"), W(""));
+ delete[] rcVersion;
+
+ if(pSep1 != NULL)
+ *pSep1 = W('\\');
+ }
+
+ // Add the threading model information.
+ SetRegValue(rcInproc, W("ThreadingModel"), szThreadingModel);
+ return (S_OK);
+}
+
+
+
+//*****************************************************************************
+// Does standard registration of a CoClass with a progid.
+// NOTE: This is the non-side-by-side execution version.
+//*****************************************************************************
+HRESULT REGUTIL::RegisterCOMClass( // Return code.
+ REFCLSID rclsid, // Class ID.
+ LPCTSTR szDesc, // Description of the class.
+ LPCTSTR szProgIDPrefix, // Prefix for progid.
+ int iVersion, // Version # for progid.
+ LPCTSTR szClassProgID, // Class progid.
+ LPCTSTR szThreadingModel, // What threading model to use.
+ LPCTSTR szModule, // Path to class.
+ BOOL bInprocServer) // Whether we register the server as inproc or local
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcCLSID[256]; // CLSID\\szID.
+ WCHAR rcInproc[_MAX_PATH+64]; // CLSID\\InprocServer32
+ WCHAR rcProgID[256]; // szProgIDPrefix.szClassProgID
+ WCHAR rcIndProgID[256]; // rcProgID.iVersion
+ HRESULT hr;
+
+ // Format the prog ID values.
+ VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID));
+
+ VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%d"), rcIndProgID, iVersion));
+
+ // Do the initial portion.
+ if (FAILED(hr = RegisterClassBase(rclsid, szDesc, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID))))
+ return (hr);
+
+ WCHAR *szServerType = bInprocServer ? W("InprocServer32") : W("LocalServer32");
+
+ // Set the server path.
+ SetKeyAndValue(rcCLSID, szServerType , szModule);
+
+ // Add the threading model information.
+ VERIFY(_snwprintf_s(rcInproc, _countof(rcInproc), _TRUNCATE, W("%s\\%s"), rcCLSID, szServerType));
+
+ SetRegValue(rcInproc, W("ThreadingModel"), szThreadingModel);
+ return (S_OK);
+}
+
+
+
+//*****************************************************************************
+// Register the basics for a in proc server.
+//*****************************************************************************
+HRESULT REGUTIL::RegisterClassBase( // Return code.
+ REFCLSID rclsid, // Class ID we are registering.
+ LPCTSTR szDesc, // Class description.
+ LPCTSTR szProgID, // Class prog ID.
+ LPCTSTR szIndepProgID, // Class version independant prog ID.
+ __out_ecount(cchOutCLSID) LPTSTR szOutCLSID, // CLSID formatted in character form.
+ DWORD cchOutCLSID) // Out CLS ID buffer size
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ TCHAR szID[64]; // The class ID to register.
+
+ // Create some base key strings.
+ GuidToLPWSTR(rclsid, szID, NumItems(szID));
+
+ size_t nLen = _tcslen(_T("CLSID\\")) + _tcslen( szID) + 1;
+ if( cchOutCLSID < nLen )
+ return E_INVALIDARG;
+
+ _tcscpy_s(szOutCLSID, cchOutCLSID, W("CLSID\\"));
+ _tcscat_s(szOutCLSID, cchOutCLSID, szID);
+
+ // Create ProgID keys.
+ SetKeyAndValue(szProgID, NULL, szDesc);
+ SetKeyAndValue(szProgID, W("CLSID"), szID);
+
+ // Create VersionIndependentProgID keys.
+ SetKeyAndValue(szIndepProgID, NULL, szDesc);
+ SetKeyAndValue(szIndepProgID, W("CurVer"), szProgID);
+ SetKeyAndValue(szIndepProgID, W("CLSID"), szID);
+
+ // Create entries under CLSID.
+ SetKeyAndValue(szOutCLSID, NULL, szDesc);
+ SetKeyAndValue(szOutCLSID, W("ProgID"), szProgID);
+ SetKeyAndValue(szOutCLSID, W("VersionIndependentProgID"), szIndepProgID);
+ SetKeyAndValue(szOutCLSID, W("NotInsertable"), NULL);
+ return (S_OK);
+}
+
+
+
+//*****************************************************************************
+// Unregister the basic information in the system registry for a given object
+// class.
+//*****************************************************************************
+HRESULT REGUTIL::UnregisterCOMClass( // Return code.
+ REFCLSID rclsid, // Class ID we are registering.
+ LPCTSTR szProgIDPrefix, // Prefix for progid.
+ int iVersion, // Version # for progid.
+ LPCTSTR szClassProgID, // Class progid.
+ BOOL fExternal) // flag - External to mscoree.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcCLSID[64]; // CLSID\\szID.
+ WCHAR rcProgID[128]; // szProgIDPrefix.szClassProgID
+ WCHAR rcIndProgID[128]; // rcProgID.iVersion
+
+ // Format the prog ID values.
+ VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID));
+
+ VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%d"), rcProgID, iVersion));
+
+ UnregisterClassBase(rclsid, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID));
+ DeleteKey(rcCLSID, W("InprocServer32"));
+ if (fExternal){
+ DeleteKey(rcCLSID, W("Server"));
+ DeleteKey(rcCLSID, W("Version"));
+ }
+ GuidToLPWSTR(rclsid, rcCLSID, NumItems(rcCLSID));
+ DeleteKey(W("CLSID"), rcCLSID);
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// Unregister the basic information in the system registry for a given object
+// class.
+// NOTE: This is the non-side-by-side execution version.
+//*****************************************************************************
+HRESULT REGUTIL::UnregisterCOMClass( // Return code.
+ REFCLSID rclsid, // Class ID we are registering.
+ LPCTSTR szProgIDPrefix, // Prefix for progid.
+ int iVersion, // Version # for progid.
+ LPCTSTR szClassProgID) // Class progid.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcCLSID[64]; // CLSID\\szID.
+ WCHAR rcProgID[128]; // szProgIDPrefix.szClassProgID
+ WCHAR rcIndProgID[128]; // rcProgID.iVersion
+
+ // Format the prog ID values.
+ VERIFY(_snwprintf_s(rcProgID, _countof(rcProgID), _TRUNCATE, W("%s.%s"), szProgIDPrefix, szClassProgID));
+
+ VERIFY(_snwprintf_s(rcIndProgID, _countof(rcIndProgID), _TRUNCATE, W("%s.%d"), rcProgID, iVersion));
+
+ UnregisterClassBase(rclsid, rcProgID, rcIndProgID, rcCLSID, NumItems(rcCLSID));
+ DeleteKey(rcCLSID, W("InprocServer32"));
+ DeleteKey(rcCLSID, W("LocalServer32"));
+
+ GuidToLPWSTR(rclsid, rcCLSID, NumItems(rcCLSID));
+ DeleteKey(W("CLSID"), rcCLSID);
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// Delete the basic settings for an inproc server.
+//*****************************************************************************
+HRESULT REGUTIL::UnregisterClassBase( // Return code.
+ REFCLSID rclsid, // Class ID we are registering.
+ LPCTSTR szProgID, // Class prog ID.
+ LPCTSTR szIndepProgID, // Class version independant prog ID.
+ __out_ecount(cchOutCLSID) LPTSTR szOutCLSID, // Return formatted class ID here.
+ DWORD cchOutCLSID) // Out CLS ID buffer size
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ TCHAR szID[64]; // The class ID to register.
+
+ // Create some base key strings.
+ GuidToLPWSTR(rclsid, szID, NumItems(szID));
+ size_t nLen = _tcslen(_T("CLSID\\")) + _tcslen( szID) + 1;
+ if( cchOutCLSID < nLen )
+ return E_INVALIDARG;
+
+ _tcscpy_s(szOutCLSID, cchOutCLSID, W("CLSID\\"));
+ _tcscat_s(szOutCLSID, cchOutCLSID, szID);
+
+ // Delete the version independant prog ID settings.
+ DeleteKey(szIndepProgID, W("CurVer"));
+ DeleteKey(szIndepProgID, W("CLSID"));
+ WszRegDeleteKey(HKEY_CLASSES_ROOT, szIndepProgID);
+
+ // Delete the prog ID settings.
+ DeleteKey(szProgID, W("CLSID"));
+ WszRegDeleteKey(HKEY_CLASSES_ROOT, szProgID);
+
+ // Delete the class ID settings.
+ DeleteKey(szOutCLSID, W("ProgID"));
+ DeleteKey(szOutCLSID, W("VersionIndependentProgID"));
+ DeleteKey(szOutCLSID, W("NotInsertable"));
+ WszRegDeleteKey(HKEY_CLASSES_ROOT, szOutCLSID);
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// Register a type library.
+//*****************************************************************************
+HRESULT REGUTIL::RegisterTypeLib( // Return code.
+ REFGUID rtlbid, // TypeLib ID we are registering.
+ int iVersion, // Typelib version.
+ LPCTSTR szDesc, // TypeLib description.
+ LPCTSTR szModule) // Path to the typelib.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR szID[64]; // The typelib ID to register.
+ WCHAR szTLBID[256]; // TypeLib\\szID.
+ WCHAR szHelpDir[_MAX_PATH];
+ WCHAR szDrive[_MAX_DRIVE];
+ WCHAR szDir[_MAX_DIR];
+ WCHAR szVersion[64];
+ LPWSTR szTmp;
+
+ // Create some base key strings.
+ GuidToLPWSTR(rtlbid, szID, NumItems(szID));
+
+ _tcscpy_s(szTLBID, _countof(szTLBID), W("TypeLib\\"));
+ _tcscat_s(szTLBID, _countof(szTLBID), szID);
+
+ VERIFY(_snwprintf_s(szVersion, _countof(szVersion), _TRUNCATE, W("%d.0"), iVersion));
+
+ // Create Typelib keys.
+ SetKeyAndValue(szTLBID, NULL, NULL);
+ SetKeyAndValue(szTLBID, szVersion, szDesc);
+ _tcscat_s(szTLBID, _countof(szTLBID), W("\\"));
+ _tcscat_s(szTLBID, _countof(szTLBID), szVersion);
+ SetKeyAndValue(szTLBID, W("0"), NULL);
+ SetKeyAndValue(szTLBID, W("0\\win32"), szModule);
+ SetKeyAndValue(szTLBID, W("FLAGS"), W("0"));
+ SplitPath(szModule, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, NULL, 0, NULL, 0);
+ _tcscpy_s(szHelpDir, _countof(szHelpDir), szDrive);
+ if ((szTmp = CharPrev(szDir, szDir + Wszlstrlen(szDir))) != NULL)
+ *szTmp = '\0';
+ _tcscat_s(szHelpDir, _countof(szHelpDir), szDir);
+ SetKeyAndValue(szTLBID, W("HELPDIR"), szHelpDir);
+ return (S_OK);
+}
+
+
+//*****************************************************************************
+// Remove the registry keys for a type library.
+//*****************************************************************************
+HRESULT REGUTIL::UnregisterTypeLib( // Return code.
+ REFGUID rtlbid, // TypeLib ID we are registering.
+ int iVersion) // Typelib version.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR szID[64]; // The typelib ID to register.
+ WCHAR szTLBID[256]; // TypeLib\\szID.
+ WCHAR szTLBVersion[256]; // TypeLib\\szID\\szVersion
+ WCHAR szVersion[64];
+
+ // Create some base key strings.
+ GuidToLPWSTR(rtlbid, szID, NumItems(szID));
+
+ VERIFY(_snwprintf_s(szVersion, _countof(szVersion), _TRUNCATE, W("%d.0"), iVersion));
+
+ _tcscpy_s(szTLBID, _countof(szTLBID), W("TypeLib\\"));
+ _tcscat_s(szTLBID, _countof(szTLBID), szID);
+ _tcscpy_s(szTLBVersion, _countof(szTLBVersion), szTLBID);
+ _tcscat_s(szTLBVersion, _countof(szTLBVersion), W("\\"));
+ _tcscat_s(szTLBVersion, _countof(szTLBVersion), szVersion);
+
+ // Delete Typelib keys.
+ DeleteKey(szTLBVersion, W("HELPDIR"));
+ DeleteKey(szTLBVersion, W("FLAGS"));
+ DeleteKey(szTLBVersion, W("0\\win32"));
+ DeleteKey(szTLBVersion, W("0"));
+ DeleteKey(szTLBID, szVersion);
+ WszRegDeleteKey(HKEY_CLASSES_ROOT, szTLBID);
+ return (0);
+}
+
+#endif // !FEATURE_CORECLR
+
+
+//
+// ProbabilisticNameSet:
+//
+// (Used by ConfigCache, below. If used elsewhere, might justify
+// promotion to a standalone header file.)
+//
+// Represent a set of names in a small, fixed amount of storage.
+// We turn a name into a small integer, then add the integer to a bitvector.
+// An old trick we used in VC++4 minimal rebuild.
+//
+// For best results, the number of elements should be a fraction of
+// the total number of bits in 'bits'.
+//
+// Note, only the const methods are thread-safe.
+// Callers are responsible for providing their own synchronization when
+// constructing and Add'ing names to the set.
+//
+class ProbabilisticNameSet {
+public:
+ ProbabilisticNameSet()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ memset(bits, 0, sizeof(bits));
+ }
+
+ // Add a name to the set.
+ //
+ void Add(LPCWSTR name)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ unsigned i, mask;
+ GetBitIndex(name, 0, &i, &mask);
+ bits[i] |= mask;
+ }
+
+ void Add(LPCWSTR name, DWORD count)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ unsigned i, mask;
+ GetBitIndex(name, count, &i, &mask);
+ bits[i] |= mask;
+ }
+
+ // Return TRUE if a name *may have* been added to the set;
+ // return FALSE if the name *definitely* was NOT ever added to the set.
+ //
+ BOOL MayContain(LPCWSTR name) const
+ {
+ WRAPPER_NO_CONTRACT;
+
+ unsigned i, mask;
+ GetBitIndex(name, 0, &i, &mask);
+ return !!(bits[i] & mask);
+ }
+
+private:
+ static const unsigned cbitSet = 256U;
+ static const unsigned cbitWord = 8U*sizeof(unsigned);
+ unsigned bits[cbitSet/cbitWord];
+
+ // Return the word index and bit mask corresponding to the bitvector member
+ // addressed by the *case-insensitive* hash of the given name.
+ //
+ void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const
+ {
+ LIMITED_METHOD_CONTRACT;
+ unsigned hash;
+ if (count > 0)
+ hash = HashiStringNKnownLower80(name, count) % cbitSet;
+ else
+ hash = HashiStringKnownLower80(name) % cbitSet;
+ *pi = hash / cbitWord;
+ *pmask = (1U << (hash % cbitWord));
+ }
+
+};
+
+
+// From the Win32 SDK docs:
+// Registry Element Size Limits
+// ...
+// The maximum size of a value name is as follows:
+// Windows Server 2003 and Windows XP: 16,383 characters
+// Windows 2000: 260 ANSI characters or 16,383 Unicode characters.
+// Windows Me/98/95: 255 characters
+// Despite that, we only cache value names of 80 characters or less --
+// longer names don't make sense as configuration settings names.
+//
+static const unsigned cchRegValueNameMax = 80;
+
+BOOL REGUTIL::s_fUseRegCache = FALSE;
+BOOL REGUTIL::s_fUseEnvCache = FALSE;
+HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
+HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
+BOOL REGUTIL::s_fUseRegistry = TRUE;
+static ProbabilisticNameSet regNames; // set of registry value names seen; should be
+ // a static field of REGUTIL but I don't
+ // want to expose ProbabilisticNameSet.
+static ProbabilisticNameSet envNames; // set of environment value names seen;
+
+// "Registry Configuration Cache"
+//
+// Initialize the (optional) registry config cache.
+//
+// The purpose of the cache is to avoid hundreds of registry probes
+// otherwise incurred by calls to GetConfigDWORD_DontUse_ and GetConfigString_DontUse_.
+//
+// We accomplish this by enumerating the relevant registry keys and
+// remembering the extant value names; and then by avoiding probing
+// for a name that was not seen in the enumeration (initialization) phase.
+//
+// It is optional in the sense that REGUTIL facilities like
+// GetConfigDWORD_DontUse_ and GetConfigString_DontUse_ will work fine if the cache
+// is never initialized; however, each config access then will hit
+// the registry (typically multiple times to search HKCU and HKLM).
+//
+//
+// Initialization: Enumerate these registry keys
+// HKCU Software\Microsoft\.NetFramework
+// HKLM Software\Microsoft\.NetFramework
+// for value names, and "remember" them in the ProbalisticNameSet 'names'.
+//
+// If we ever find a reg value named DisableConfigCache under any of these
+// three keys, the feature is disabled.
+//
+// This method is not thread-safe. It should only be called once.
+//
+// Perf Optimization for VSWhidbey:113373.
+//
+void REGUTIL::InitOptionalConfigCache()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ static const HKEY roots[] = { HKEY_CURRENT_USER,
+ HKEY_LOCAL_MACHINE};
+
+ LONG l = ERROR_SUCCESS; // general Win32 API error return code
+ HKEY hkey = NULL;
+
+ // No caching if the environment variable COMPLUS_DisableConfigCache is set
+ //
+ if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0)
+ goto failure;
+
+ // Enumerate each root
+ //
+ for (int i = 0; i < NumItems(roots); i++) {
+ hkey = NULL; // defensive
+ l = WszRegOpenKeyEx(roots[i], FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hkey);
+ if (l == ERROR_FILE_NOT_FOUND) {
+ // That registry key is not present.
+ // For example, installation with no HKCU\...\.NETFramework.
+ // Should be OK to proceed.
+ continue;
+ }
+#if defined(FEATURE_APPX) && !defined(DACCESS_COMPILE)
+ else if (l == ERROR_ACCESS_DENIED && AppX::IsAppXProcess()) {
+ // If we encounter access denied for the current key in AppX, ignore
+ // the failure and continue to cache the rest. Effectively this means
+ // we are caching that key as containing no values, which is correct
+ // because in the unlikely event there are values hiding underneath
+ // later attempts to access them (open the key) would also hit access
+ // denied and continue on probing other locations.
+ continue;
+ }
+#endif // FEATURE_APPX && !DACCESS_COMPILE
+ else if (l != ERROR_SUCCESS) {
+ // Something else went wrong. To be safe, don't enable the cache.
+ goto failure;
+ }
+
+ // Enumerate every value name under this key.
+ //
+ for (int j = 0; ; j++) {
+ WCHAR wszValue[cchRegValueNameMax + 1];
+ DWORD dwValueSize = NumItems(wszValue);
+ l = WszRegEnumValue(hkey, j, wszValue, &dwValueSize,
+ NULL, NULL, NULL, NULL);
+
+ if (l == ERROR_SUCCESS) {
+ // Add value name to the names cache.
+ regNames.Add(wszValue);
+ }
+ else if (l == ERROR_NO_MORE_ITEMS) {
+ // Expected case: we've considered every value under this key.
+ break;
+ }
+ else if ((l == ERROR_INSUFFICIENT_BUFFER || l == ERROR_MORE_DATA) &&
+ (dwValueSize > cchRegValueNameMax)) {
+ // Name is too long. That's OK, we don't cache such names.
+ continue;
+ }
+#if defined(FEATURE_APPX) && !defined DACCESS_COMPILE
+ else if (l == ERROR_ACCESS_DENIED && AppX::IsAppXProcess()) {
+ // As above, ignore access denied in AppX and continue on trying to cache
+ continue;
+ }
+#endif // FEATURE_APPX && !DACCESS_COMPILE
+ else {
+ // WszRegEnumValue failed OOM, or something else went wrong.
+ // To be safe, don't enable the cache.
+ goto failure;
+ }
+ }
+
+ // Save the handles to framework regkeys so that future reads dont have to
+ // open it again
+ if (roots[i] == HKEY_CURRENT_USER)
+ s_hUserFrameworkKey = hkey;
+ else if (roots[i] == HKEY_LOCAL_MACHINE)
+ s_hMachineFrameworkKey = hkey;
+ else
+ RegCloseKey(hkey);
+
+ hkey = NULL;
+ }
+
+ // Success. We've enumerated all value names under the roots;
+ // enable the REGUTIL value name config cache.
+ //
+ s_fUseRegCache = TRUE;
+
+ // Now create a cache of environment variables
+ WCHAR * wszStrings = WszGetEnvironmentStrings();
+ if (wszStrings)
+ {
+ // GetEnvironmentStrings returns pointer to a null terminated block containing
+ // null terminated strings
+ for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++)
+ {
+ WCHAR wch = towlower(*wszCurr);
+
+ // Lets only cache env variables with the COMPlus prefix only
+ if (wch == W('c'))
+ {
+ WCHAR *wszName = wszCurr;
+
+ // Look for the separator between name and value
+ while (*wszCurr && *wszCurr != W('='))
+ wszCurr++;
+
+ if (*wszCurr == W('='))
+ {
+ // Check the prefix
+ if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX))
+ {
+ wszName += LEN_OF_COMPLUS_PREFIX;
+ envNames.Add(wszName, (DWORD) (wszCurr - wszName));
+ }
+ }
+
+ }
+ // Look for current string termination
+ while (*wszCurr)
+ wszCurr++;
+
+ }
+
+ WszFreeEnvironmentStrings(wszStrings);
+ s_fUseEnvCache = TRUE;
+
+ }
+ return;
+
+failure:
+ if (hkey != NULL)
+ RegCloseKey(hkey);
+}
+
+// Return TRUE if the registry value name was seen (or might have been seen)
+// in the registry at cache initialization time;
+// return FALSE if it definitely was not seen at startup.
+//
+// If not using the config cache, return TRUE always.
+//
+// Perf Optimization for VSWhidbey:113373.
+//
+BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return !s_fUseRegCache
+ || (wcslen(name) > cchRegValueNameMax)
+ || regNames.MayContain(name);
+}
+
+BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return !s_fUseEnvCache
+ || envNames.MayContain(name);
+}
+
+#endif // ALLOW_REGISTRY
diff --git a/src/utilcode/regutilformac.cpp b/src/utilcode/regutilformac.cpp
new file mode 100644
index 0000000000..d04b1076a6
--- /dev/null
+++ b/src/utilcode/regutilformac.cpp
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+//
+
+//
+//*****************************************************************************
+
+/* The mac build does not like filenames with .CPP (in capitals) for an extension, so we need this workaround
+ so we can include REGUTIL.CPP. */
+#include "REGUTIL.CPP"
diff --git a/src/utilcode/safewrap.cpp b/src/utilcode/safewrap.cpp
new file mode 100644
index 0000000000..041ed5632c
--- /dev/null
+++ b/src/utilcode/safewrap.cpp
@@ -0,0 +1,415 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// SafeWrap.cpp
+//
+
+//
+// This file contains wrapper functions for Win32 API's that take SStrings
+// and use CLR-safe holders.
+//
+// See guidelines in SafeWrap.h for writing these APIs.
+//*****************************************************************************
+
+#include "stdafx.h" // Precompiled header key.
+#include "safewrap.h"
+#include "winwrap.h" // Header for macros and functions.
+#include "utilcode.h"
+#include "holder.h"
+#include "sstring.h"
+#include "ex.h"
+
+//-----------------------------------------------------------------------------
+// Get the current directory.
+// On success, returns true and sets 'Value' to unicode version of cur dir.
+// Throws on all failures. This should mainly be oom.
+//-----------------------------------------------------------------------------
+void ClrGetCurrentDirectory(SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Get size needed
+ DWORD lenWithNull = WszGetCurrentDirectory(0, NULL);
+
+ // Now read it for content.
+ WCHAR * pCharBuf = value.OpenUnicodeBuffer(lenWithNull);
+ DWORD lenWithoutNull = WszGetCurrentDirectory(lenWithNull, pCharBuf);
+
+ // An actual API failure in GetCurrentDirectory failure should be very rare, so we'll throw on those.
+ if (lenWithoutNull == 0)
+ {
+ value.CloseBuffer(0);
+ ThrowLastError();
+ }
+ if (lenWithoutNull != (lenWithNull - 1))
+ {
+ value.CloseBuffer(lenWithoutNull);
+
+ // must have changed underneath us.
+ ThrowHR(E_FAIL);
+ }
+
+ value.CloseBuffer(lenWithoutNull);
+}
+
+// Nothrowing wrapper.
+bool ClrGetCurrentDirectoryNoThrow(SString & value)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ bool fOk = true;
+ EX_TRY
+ {
+ ClrGetCurrentDirectory(value);
+ }
+ EX_CATCH
+ {
+ fOk = false;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ return fOk;
+}
+//-----------------------------------------------------------------------------
+// Reads an environment variable into the given SString.
+// Returns true on success, false on failure (includes if the var does not exist).
+// May throw on oom.
+//-----------------------------------------------------------------------------
+bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+
+ PRECONDITION(szEnvVarName != NULL);
+ }
+ CONTRACTL_END;
+
+ // First read it to get the needed length.
+ DWORD lenWithNull = GetEnvironmentVariableA(szEnvVarName, NULL, 0);
+ if (lenWithNull == 0)
+ {
+ return false;
+ }
+
+ // Now read it for content.
+ char * pCharBuf = value.OpenANSIBuffer(lenWithNull);
+ DWORD lenWithoutNull = GetEnvironmentVariableA(szEnvVarName, pCharBuf, lenWithNull);
+ value.CloseBuffer(lenWithoutNull);
+
+ if (lenWithoutNull != (lenWithNull - 1))
+ {
+ // Env var must have changed underneath us.
+ return false;
+ }
+ return true;
+}
+
+// Nothrowing wrapper.
+bool ClrGetEnvironmentVariableNoThrow(LPCSTR szEnvVarName, SString & value)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+
+ bool fOk = false;
+ EX_TRY
+ {
+ fOk = ClrGetEnvironmentVariable(szEnvVarName, value);
+ }
+ EX_CATCH
+ {
+ fOk = false;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ return fOk;
+}
+
+void ClrGetModuleFileName(HMODULE hModule, SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR * pCharBuf = value.OpenUnicodeBuffer(_MAX_PATH);
+ DWORD numChars = GetModuleFileNameW(hModule, pCharBuf, _MAX_PATH);
+ value.CloseBuffer(numChars);
+}
+
+bool ClrGetModuleFileNameNoThrow(HMODULE hModule, SString & value)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ bool fOk = true;
+ EX_TRY
+ {
+ ClrGetModuleFileName(hModule, value);
+ }
+ EX_CATCH
+ {
+ fOk = false;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ return fOk;
+}
+
+ClrDirectoryEnumerator::ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask /*= W("*")*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ StackSString strMask;
+ SString s(SString::Literal, W("\\"));
+ strMask.Set(pBaseDirectory, s, pMask);
+ dirHandle = WszFindFirstFile(strMask, &data);
+
+ if (dirHandle == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwLastError = GetLastError();
+
+ // We either ran out of files, or didnt encounter even a single file matching the
+ // search mask. If it is neither of these conditions, then convert the error to an exception
+ // and raise it.
+ if ((dwLastError != ERROR_FILE_NOT_FOUND) && (dwLastError != ERROR_NO_MORE_FILES))
+ ThrowLastError();
+ }
+
+ fFindNext = FALSE;
+}
+
+bool ClrDirectoryEnumerator::Next()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (dirHandle == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ for (;;)
+ {
+ if (fFindNext)
+ {
+ if (!WszFindNextFile(dirHandle, &data))
+ {
+ if (GetLastError() != ERROR_NO_MORE_FILES)
+ ThrowLastError();
+
+ return FALSE;
+ }
+ }
+ else
+ {
+ fFindNext = TRUE;
+ }
+
+ // Skip junk
+ if (wcscmp(data.cFileName, W(".")) != 0 && wcscmp(data.cFileName, W("..")) != 0)
+ return TRUE;
+ }
+}
+
+DWORD ClrReportEvent(
+ LPCWSTR pEventSource,
+ WORD wType,
+ WORD wCategory,
+ DWORD dwEventID,
+ PSID lpUserSid,
+ WORD wNumStrings,
+ LPCWSTR *lpStrings,
+ DWORD dwDataSize /*=0*/,
+ LPVOID lpRawData /*=NULL*/)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#ifndef FEATURE_PAL
+ HANDLE h = ::RegisterEventSourceW(
+ NULL, // uses local computer
+ pEventSource);
+ if (h == NULL)
+ {
+ // Return the error code to the caller so that
+ // appropriate asserts/logging can be done
+ // incase of errors like event log being full
+ return GetLastError();
+ }
+
+ // Every event id should have matching description in dlls\shim\eventmsg.mc. This allows
+ // event view to know how to display message.
+ _ASSERTE (dwEventID != 0);
+
+ // Attempt to report the event to the event log. Note that if the operation fails
+ // (for example because of low memory conditions) it isn't fatal so we can safely ignore
+ // the return code from ReportEventW.
+ BOOL ret = ::ReportEventW(
+ h, // event log handle
+ wType,
+ wCategory,
+ dwEventID,
+ lpUserSid,
+ wNumStrings,
+ dwDataSize,
+ lpStrings,
+ lpRawData);
+
+ DWORD dwRetStatus = GetLastError();
+
+ ::DeregisterEventSource(h);
+
+ return (ret == TRUE)?ERROR_SUCCESS:dwRetStatus;
+#else // FEATURE_PAL
+ // UNIXTODO: Report the event somewhere?
+ return ERROR_SUCCESS;
+#endif // FEATURE_PAL
+}
+
+// Returns ERROR_SUCCESS if succeessful in reporting to event log, or
+// Windows error code to indicate the specific error.
+DWORD ClrReportEvent(
+ LPCWSTR pEventSource,
+ WORD wType,
+ WORD wCategory,
+ DWORD dwEventID,
+ PSID lpUserSid,
+ LPCWSTR pMessage)
+{
+ return ClrReportEvent(pEventSource, wType, wCategory, dwEventID, lpUserSid, 1, &pMessage);
+}
+
+#ifndef FEATURE_PAL
+// Read a REG_SZ (null-terminated string) value from the registry. Throws.
+//
+// Arguments:
+// hKey - key to registry hive.
+// szValueName - null-terminated string for value name to lookup.
+// If this is empty, this gets the (default) value in the registry hive.
+// value - out parameter to hold registry value string contents.
+//
+// Returns:
+// value is set on success. Throws on any failure, including if the value doesn't exist
+// or if the value exists but is not a REG_SZ.
+//
+// Notes:
+// REG_SZ is a single null-terminated string in the registry.
+// This is only support on Windows because the registry is windows specific.
+void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD type;
+ DWORD numBytesData;
+
+ // Preemptively clear the string such that it's empty in any failure case.
+ value.Clear();
+
+ //
+ // Step 1: First call to find size of buffer and ensure data type is correct
+ //
+ LONG ret = WszRegQueryValueEx(
+ hKey,
+ szValueName.GetUnicode(), // NULL or "\0" represents the (default) key.
+ NULL, // reserved
+ &type, // should be REG_SZ
+ NULL, // not requesting data yet
+ &numBytesData
+ );
+
+ if (ret != ERROR_SUCCESS)
+ {
+ ThrowWin32(ret);
+ }
+
+ if (type != REG_SZ)
+ {
+ // The registry value is not a string.
+ ThrowHR(E_INVALIDARG);
+ }
+
+ // REG_SZ includes the null terminator.
+ DWORD numCharsIncludingNull = numBytesData / sizeof(WCHAR);
+
+ //
+ // Step 2: Allocate buffer to hold final result
+ //
+ WCHAR * pData = value.OpenUnicodeBuffer(numCharsIncludingNull);
+ DWORD numBytesData2 = numBytesData;
+
+
+ //
+ // Step 3: Requery to get actual contents
+ //
+ ret = WszRegQueryValueEx(
+ hKey,
+ szValueName.GetUnicode(),
+ NULL, // reserved
+ &type, // should still be REG_SZ
+ (LPBYTE) pData,
+ &numBytesData2
+ );
+
+ // This check should only fail if the registry was changed inbetween the first query
+ // and the second. In practice, that should never actually happen.
+ if ((numBytesData2 != numBytesData) || (type != REG_SZ))
+ {
+ // On error, leave string empty.
+ value.CloseBuffer(0);
+
+ ThrowHR(E_FAIL);
+ }
+
+ if (ret != ERROR_SUCCESS)
+ {
+ // On error, leave string empty.
+ value.CloseBuffer(0);
+ ThrowWin32(ret);
+ }
+
+
+ //
+ // Step 4: Close the string buffer
+ //
+ COUNT_T numCharsNoNull = numCharsIncludingNull - 1;
+ value.CloseBuffer(numCharsNoNull);
+}
+#endif // FEATURE_PAL
diff --git a/src/utilcode/sbuffer.cpp b/src/utilcode/sbuffer.cpp
new file mode 100644
index 0000000000..7d2e719135
--- /dev/null
+++ b/src/utilcode/sbuffer.cpp
@@ -0,0 +1,147 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ---------------------------------------------------------------------------
+
+//
+// Buffer.cpp
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sbuffer.h"
+#include "ex.h"
+#include "holder.h"
+#include "stdmacros.h"
+
+// Try to minimize the performance impact of contracts on CHK build.
+#if defined(_MSC_VER)
+#pragma inline_depth (20)
+#endif
+
+const DWORD g_garbageFillBuffer[GARBAGE_FILL_BUFFER_ITEMS] =
+{
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+};
+
+//----------------------------------------------------------------------------
+// ReallocateBuffer
+// Low level buffer reallocate routine
+//----------------------------------------------------------------------------
+void SBuffer::ReallocateBuffer(COUNT_T allocation, Preserve preserve)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckBufferClosed());
+ PRECONDITION(CheckAllocation(allocation));
+ PRECONDITION(allocation >= m_size);
+ POSTCONDITION(m_allocation == allocation);
+ if (allocation > 0) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ BYTE *newBuffer = NULL;
+ if (allocation > 0)
+ {
+ newBuffer = NewBuffer(allocation);
+
+ if (preserve == PRESERVE)
+ {
+ // Copy the relevant contents of the old buffer over
+ DebugMoveBuffer(newBuffer, m_buffer, m_size);
+ }
+ }
+
+ if (IsAllocated())
+ DeleteBuffer(m_buffer, m_allocation);
+
+ m_buffer = newBuffer;
+ m_allocation = allocation;
+
+ if (allocation > 0)
+ SetAllocated();
+ else
+ ClearAllocated();
+
+ ClearImmutable();
+
+ RETURN;
+}
+
+void SBuffer::Replace(const Iterator &i, COUNT_T deleteSize, COUNT_T insertSize)
+{
+ CONTRACT_VOID
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i, deleteSize));
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ COUNT_T startRange = (COUNT_T) (i.m_ptr - m_buffer);
+ // The PRECONDITION(CheckIterationRange(i, deleteSize)) should check this in
+ // contract-checking builds, but this ensures we don't integer overflow if someone
+ // passes in an aggregious deleteSize by capping it to the remaining length in the
+ // buffer.
+ if ((COUNT_T)(m_buffer + m_size - i.m_ptr) < deleteSize)
+ {
+ _ASSERTE(!"Trying to replace more bytes than are remaining in the buffer.");
+ deleteSize = (COUNT_T)(m_buffer + m_size - i.m_ptr);
+ }
+ COUNT_T endRange = startRange + deleteSize;
+ COUNT_T end = m_size;
+
+ SCOUNT_T delta = insertSize - deleteSize;
+
+ if (delta < 0)
+ {
+ // Buffer is shrinking
+
+ DebugDestructBuffer(i.m_ptr, deleteSize);
+
+ DebugMoveBuffer(m_buffer + endRange + delta,
+ m_buffer + endRange,
+ end - endRange);
+
+ Resize(m_size+delta, PRESERVE);
+
+ i.Resync(this, m_buffer + startRange);
+
+ }
+ else if (delta > 0)
+ {
+ // Buffer is growing
+
+ ResizePadded(m_size+delta);
+
+ i.Resync(this, m_buffer + startRange);
+
+ DebugDestructBuffer(i.m_ptr, deleteSize);
+
+ DebugMoveBuffer(m_buffer + endRange + delta,
+ m_buffer + endRange,
+ end - endRange);
+
+ }
+ else
+ {
+ // Buffer stays the same size. We need to DebugDestruct it first to keep
+ // the invariant that the new space is clean.
+
+ DebugDestructBuffer(i.m_ptr, insertSize);
+ }
+
+ DebugConstructBuffer(i.m_ptr, insertSize);
+
+ RETURN;
+}
+
+
diff --git a/src/utilcode/securityutil.cpp b/src/utilcode/securityutil.cpp
new file mode 100644
index 0000000000..22b99735ba
--- /dev/null
+++ b/src/utilcode/securityutil.cpp
@@ -0,0 +1,509 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "stdafx.h"
+
+#include "securityutil.h"
+#include "ex.h"
+
+#include "securitywrapper.h"
+
+// This is defined in ipcmanagerinterface.h, but including that is problematic at the moment...
+// These are the right that we will give to the global section and global events used
+// in communicating between debugger and debugee
+//
+// SECTION_ALL_ACCESS is needed for the IPC block. Unfortunately, we DACL our events and
+// IPC block identically. Or this particular right does not need to bleed into here.
+//
+#ifndef CLR_IPC_GENERIC_RIGHT
+#define CLR_IPC_GENERIC_RIGHT (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | STANDARD_RIGHTS_ALL | SECTION_ALL_ACCESS)
+#endif
+
+
+//*****************************************************************
+// static helper function
+//
+// helper to form ACL that contains AllowedACE of users of current
+// process and target process
+//
+// [IN] pid - target process id
+// [OUT] ppACL - ACL for the process
+//
+// Clean up -
+// Caller remember to call FreeACL() on *ppACL
+//*****************************************************************
+HRESULT SecurityUtil::GetACLOfPid(DWORD pid, PACL *ppACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ _ASSERTE(ppACL);
+ *ppACL = NULL;
+
+ PSID pCurrentProcessSid = NULL;
+ PSID pTargetProcessSid = NULL;
+ PSID pTargetProcessAppContainerSid = NULL;
+ DWORD cSid = 0;
+ DWORD dwAclSize = 0;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetACLOfPid on pid : 0x%08x\n",
+ pid));
+
+
+ SidBuffer sidCurrentProcess;
+ SidBuffer sidTargetProcess;
+ SidBuffer sidTargetProcessAppContainer;
+
+ // Get sid for current process.
+ EX_TRY
+ {
+ sidCurrentProcess.InitFromProcess(GetCurrentProcessId()); // throw on error.
+ pCurrentProcessSid = sidCurrentProcess.GetSid().RawSid();
+ cSid++;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+ // Get sid for target process.
+ EX_TRY
+ {
+ sidTargetProcess.InitFromProcess(pid); // throws on error.
+ pTargetProcessSid = sidTargetProcess.GetSid().RawSid();
+ cSid++;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(RethrowTerminalExceptions);
+
+ //FISHY: what is the scenario where only one of the above calls succeeds?
+ if (cSid == 0)
+ {
+ // failed to get any useful sid. Just return.
+ // need a better error.
+ //
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ hr = sidTargetProcessAppContainer.InitFromProcessAppContainerSidNoThrow(pid);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+ else if (hr == S_OK)
+ {
+ pTargetProcessAppContainerSid = sidTargetProcessAppContainer.GetSid().RawSid();
+ cSid++;
+ }
+ else if(hr == S_FALSE) //not an app container, no sid to add
+ {
+ hr = S_OK; // don't leak S_FALSE
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetACLOfPid number of sid : 0x%08x\n",
+ cSid));
+
+ // Now allocate space for ACL. First calculate the space is need to hold ACL
+ dwAclSize = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) * cSid;
+ if (pCurrentProcessSid)
+ {
+ dwAclSize += GetLengthSid(pCurrentProcessSid);
+ }
+ if (pTargetProcessSid)
+ {
+ dwAclSize += GetLengthSid(pTargetProcessSid);
+ }
+ if (pTargetProcessAppContainerSid)
+ {
+ dwAclSize += GetLengthSid(pTargetProcessAppContainerSid);
+ }
+
+ *ppACL = (PACL) new (nothrow) char[dwAclSize];
+ if (*ppACL == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+
+ // Initialize ACL
+ // add each sid to the allowed ace list
+ if (!InitializeAcl(*ppACL, dwAclSize, ACL_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ if (pCurrentProcessSid)
+ {
+ // add the current process's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pCurrentProcessSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ if (pTargetProcessSid)
+ {
+ // add the target process's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ if (pTargetProcessAppContainerSid)
+ {
+ // add the target process's AppContainer's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessAppContainerSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ // we better to form a valid ACL to return
+ _ASSERTE(IsValidAcl(*ppACL));
+exit:
+ if (FAILED(hr) && *ppACL)
+ {
+ delete [] (reinterpret_cast<char*>(ppACL));
+ }
+ return hr;
+} // SecurityUtil::GetACLOfPid
+
+
+//*****************************************************************
+// static helper function
+//
+// free the ACL allocated by SecurityUtil::GetACLOfPid
+//
+// [IN] pACL - ACL to be freed
+//
+//*****************************************************************
+void SecurityUtil::FreeACL(PACL pACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ if (pACL)
+ {
+ delete [] (reinterpret_cast<char*>(pACL));
+ }
+} // SecurityUtil::FreeACL
+
+
+//*****************************************************************
+// constructor
+//
+// [IN] pACL - ACL that this instance of SecurityUtil will held on to
+//
+//*****************************************************************
+SecurityUtil::SecurityUtil(PACL pACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ m_pACL = pACL;
+ m_pSacl = NULL;
+ m_fInitialized = false;
+}
+
+//*****************************************************************
+// destructor
+//
+// free the ACL that this instance of SecurityUtil helds on to
+//
+//*****************************************************************
+SecurityUtil::~SecurityUtil()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ FreeACL(m_pACL);
+ FreeACL(m_pSacl);
+}
+
+//*****************************************************************
+// Initialization function
+//
+// form the SecurityDescriptor that will represent the m_pACL
+//
+//*****************************************************************
+HRESULT SecurityUtil::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ if (m_pACL)
+ {
+ if (!InitializeSecurityDescriptor(&m_SD, SECURITY_DESCRIPTOR_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ if (!SetSecurityDescriptorDacl(&m_SD, TRUE, m_pACL, FALSE))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+
+ m_SA.nLength = sizeof(SECURITY_ATTRIBUTES);
+ m_SA.lpSecurityDescriptor = &m_SD;
+ m_SA.bInheritHandle = FALSE;
+ m_fInitialized = true;
+ }
+ return S_OK;
+}
+
+// ***************************************************************************
+// Initialization functions which will call the normal Init and add a
+// mandatory label entry to the sacl
+//
+// Expects hProcess to be a valid handle to the process which has the desired
+// mandatory label
+// ***************************************************************************
+HRESULT SecurityUtil::Init(HANDLE hProcess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = Init();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ NewArrayHolder<BYTE> pLabel;
+
+ hr = GetMandatoryLabelFromProcess(hProcess, &pLabel);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ TOKEN_MANDATORY_LABEL * ptml = (TOKEN_MANDATORY_LABEL *) pLabel.GetValue();
+
+ hr = SetSecurityDescriptorMandatoryLabel(ptml->Label.Sid);
+
+ return hr;
+}
+
+
+// ***************************************************************************
+// Given a process, this will put the mandatory label into a buffer and point
+// ppbLabel at the buffer.
+//
+// Caller must free ppbLabel via the array "delete []" operator
+// ***************************************************************************
+HRESULT SecurityUtil::GetMandatoryLabelFromProcess(HANDLE hProcess, LPBYTE * ppbLabel)
+{
+ *ppbLabel = NULL;
+
+ DWORD dwSize = 0;
+ HandleHolder hToken;
+ DWORD err = 0;
+
+ if(!OpenProcessToken(hProcess, TOKEN_READ, &hToken))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, NULL, 0, &dwSize))
+ {
+ err = GetLastError();
+ }
+
+ // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
+ // dwSize has the correct buffer size in it.
+ if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
+ {
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
+ if (pLabel == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // Our caller will be freeing the memory so use Extract
+ *ppbLabel = pLabel.Extract();
+
+ return S_OK;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Returns pointer inside the specified mandatory SID to the DWORD representing the
+// integrity level of the process. This DWORD will be one of the
+// SECURITY_MANDATORY_*_RID constants.
+//
+// Arguments:
+// psidIntegrityLevelLabel - [in] PSID in which to find the integrity level.
+//
+// Return Value:
+// Pointer to the RID stored in the specified SID. This RID represents the
+// integrity level of the process
+//
+
+// static
+DWORD * SecurityUtil::GetIntegrityLevelFromMandatorySID(PSID psidIntegrityLevelLabel)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
+}
+
+// Creates a mandatory label ace and sets it to be the entry in the
+// security descriptor's sacl. This assumes there are no other entries
+// in the sacl
+HRESULT SecurityUtil::SetSecurityDescriptorMandatoryLabel(PSID psidIntegrityLevelLabel)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+
+ DWORD cbSid = GetLengthSid(psidIntegrityLevelLabel);
+ DWORD cbAceStart = offsetof(SYSTEM_MANDATORY_LABEL_ACE, SidStart);
+ // We are about allocate memory for a ACL and an ACE so we need space for:
+ // 1) the ACL: sizeof(ACL)
+ // 2) the entry: the sid is of variable size, so the SYSTEM_MANDATORY_LABEL_ACE
+ // structure has only the first DWORD of the sid in its definition, to get the
+ // appropriate size we get size without SidStart and add on the actual size of the sid
+ DWORD cbSacl = sizeof(ACL) + cbAceStart + cbSid;
+
+ NewArrayHolder<BYTE> sacl = new (nothrow) BYTE[cbSacl];
+
+ m_pSacl = NULL;
+
+ if (sacl == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ ZeroMemory(sacl.GetValue(), cbSacl);
+ PACL pSacl = reinterpret_cast<ACL *>(sacl.GetValue());
+ SYSTEM_MANDATORY_LABEL_ACE * pLabelAce = reinterpret_cast<SYSTEM_MANDATORY_LABEL_ACE *>(sacl.GetValue() + sizeof(ACL));
+ PSID psid = reinterpret_cast<SID *>(&pLabelAce->SidStart);
+
+ // Our buffer looks like this now: (not drawn to scale)
+ // sacl pSacl pLabelAce psid
+ // - -
+ // | |
+ // | - -
+ // | |
+ // | | -
+ // | - |
+ // - -
+
+ DWORD dwIntegrityLevel = *(GetIntegrityLevelFromMandatorySID(psidIntegrityLevelLabel));
+
+ if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ // No need to set the integrity level unless it's lower than medium
+ return S_OK;
+ }
+
+ if(!InitializeAcl(pSacl, cbSacl, ACL_REVISION))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ pSacl->AceCount = 1;
+
+ pLabelAce->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE;
+ pLabelAce->Header.AceSize = WORD(cbAceStart + cbSid);
+ pLabelAce->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP;
+
+ memcpy(psid, psidIntegrityLevelLabel, cbSid);
+
+ if(!SetSecurityDescriptorSacl(m_SA.lpSecurityDescriptor, TRUE, pSacl, FALSE))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // No need to delete the sacl buffer, it will be deleted in the
+ // destructor of this class
+ m_pSacl = (PACL)sacl.Extract();
+ return S_OK;
+}
+
+//*****************************************************************
+// Return SECURITY_ATTRIBUTES that we form in the Init function
+//
+// No clean up is needed after calling this function. The destructor of the
+// instance will do the right thing. Note that this is designed such that
+// we minimize memory allocation, ie the SECURITY_DESCRIPTOR and
+// SECURITY_ATTRIBUTES are embedded in the SecurityUtil instance.
+//
+// Caller should not modify the returned SECURITY_ATTRIBUTES!!!
+//*****************************************************************
+HRESULT SecurityUtil::GetSA(SECURITY_ATTRIBUTES **ppSA)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(ppSA);
+
+ if (m_fInitialized == false)
+ {
+ _ASSERTE(!"Bad code path!");
+ *ppSA = NULL;
+ return E_FAIL;
+ }
+
+ *ppSA = &m_SA;
+ return S_OK;
+}
diff --git a/src/utilcode/securitywrapper.cpp b/src/utilcode/securitywrapper.cpp
new file mode 100644
index 0000000000..249fe494e1
--- /dev/null
+++ b/src/utilcode/securitywrapper.cpp
@@ -0,0 +1,842 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// File: SecurityWrapper.cpp
+//
+
+//
+// Wrapper around Win32 Security functions
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include "securitywrapper.h"
+#include "ex.h"
+#include "holder.h"
+
+
+// For GetSidFromProcess*
+#include <tlhelp32.h>
+#include "wtsapi32.h"
+
+
+//-----------------------------------------------------------------------------
+// Constructor for Sid wrapper class.
+// pSid - OS sid to wrap
+//-----------------------------------------------------------------------------
+Sid::Sid(PSID pSid)
+{
+ _ASSERTE(pSid != NULL);
+ m_pSid = pSid;
+}
+
+//-----------------------------------------------------------------------------
+// Aesthetic wrapper for Sid equality
+//-----------------------------------------------------------------------------
+bool Sid::Equals(PSID a, PSID b)
+{
+ return EqualSid(a, b) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Ctor for SidBuffer class
+//-----------------------------------------------------------------------------
+SidBuffer::SidBuffer()
+{
+ m_pBuffer = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Dtor for SidBuffer class.
+//-----------------------------------------------------------------------------
+SidBuffer::~SidBuffer()
+{
+ delete [] m_pBuffer;
+}
+
+//-----------------------------------------------------------------------------
+// Get the underlying sid
+// Caller assumes SidBuffer has been initialized.
+//-----------------------------------------------------------------------------
+Sid SidBuffer::GetSid()
+{
+ _ASSERTE(m_pBuffer != NULL);
+ Sid s((PSID) m_pBuffer);
+ return s;
+}
+
+// ----------------------------------------------------------------------------
+// Used by GetSidFromProcessWorker to determine which SID from the
+// process token to use when initializing the SID
+enum SidType
+{
+ // Use TokenOwner: the default owner SID used for newly created objects
+ kOwnerSid,
+
+ // Use TokenUser: the user account from the token
+ kUserSid,
+};
+
+// ----------------------------------------------------------------------------
+// GetSidFromProcessWorker
+//
+// Description:
+// Internal helper. Gets the SID for the given process and given sid type
+//
+// Arguments:
+// * dwProcessId - [in] Process to get SID from
+// * sidType - [in] Type of sid to get (owner or user)
+// * ppSid - [out] SID found. Caller responsible for deleting this memory.
+//
+// Return Value:
+// HRESULT indicating success / failure.
+//
+// Notes:
+// * Caller owns deleting (*ppSid) when done with the SID
+//
+
+HRESULT GetSidFromProcessWorker(DWORD dwProcessId, SidType sidType, PSID *ppSid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ TOKEN_USER *pTokUser = NULL;
+ HANDLE hProc = INVALID_HANDLE_VALUE;
+ HANDLE hToken = INVALID_HANDLE_VALUE;
+ DWORD dwRetLength;
+ LPVOID pvTokenInfo = NULL;
+ TOKEN_INFORMATION_CLASS tokenInfoClass;
+ PSID pSidFromTokenInfo = NULL;
+ DWORD cbSid;
+ PSID pSid = NULL;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcess: 0x%08x\n",
+ dwProcessId));
+
+ _ASSERTE(ppSid);
+ *ppSid = NULL;
+
+ _ASSERTE((sidType == kOwnerSid) || (sidType == kUserSid));
+ tokenInfoClass = (sidType == kOwnerSid) ? TokenOwner : TokenUser;
+
+ hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
+
+ if (hProc == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ if (!OpenProcessToken(hProc, TOKEN_QUERY, &hToken))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ // figure out the length
+ GetTokenInformation(hToken, tokenInfoClass, NULL, 0, &dwRetLength);
+ _ASSERTE(dwRetLength);
+
+ pvTokenInfo = new (nothrow) BYTE[dwRetLength];
+ if (pvTokenInfo == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+
+ if (!GetTokenInformation(hToken, tokenInfoClass, pvTokenInfo, dwRetLength, &dwRetLength))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ // Copy over the SID
+ pSidFromTokenInfo =
+ (sidType == kOwnerSid) ?
+ ((TOKEN_OWNER *) pvTokenInfo)->Owner :
+ ((TOKEN_USER *) pvTokenInfo)->User.Sid;
+ cbSid = GetLengthSid(pSidFromTokenInfo);
+ pSid = new (nothrow) BYTE[cbSid];
+ if (pSid == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ if (!CopySid(cbSid, pSid, pSidFromTokenInfo))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ *ppSid = pSid;
+ pSid = NULL;
+
+exit:
+ if (hToken != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hToken);
+ }
+ if (hProc != INVALID_HANDLE_VALUE)
+ {
+ // clean up
+ CloseHandle(hProc);
+ }
+ if (pvTokenInfo)
+ {
+ delete [] (reinterpret_cast<BYTE*>(pvTokenInfo));
+ }
+
+ if (pSid)
+ {
+ delete [] (reinterpret_cast<BYTE*>(pSid));
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcess return hr : 0x%08x\n",
+ hr));
+
+ return hr;
+}
+
+#ifndef FEATURE_CORESYSTEM
+//-----------------------------------------------------------------------------
+// get the sid of a given process id using WTSEnumerateProcesses
+// @todo: Make this function fail when WTSEnumerateProcesses is not available
+// Or is it always available on all of our platform?
+//
+// Caller remember to call delete on *ppSid
+//-----------------------------------------------------------------------------
+HRESULT GetSidFromProcessEXWorker(DWORD dwProcessId, PSID *ppSid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(ppSid));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ PWTS_PROCESS_INFOW rgProcessInfo = NULL;
+ DWORD dwNumProcesses;
+ DWORD iProc;
+ DWORD cbSid;
+ PSID pSid = NULL;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcessEx: 0x%08x\n",
+ dwProcessId));
+
+
+ *ppSid = NULL;
+ if (!WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, // use local server
+ 0, // Reserved must be zero
+ 1, // version must be 1
+ &rgProcessInfo, // Receives pointer to process list
+ &dwNumProcesses))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ for (iProc = 0; iProc < dwNumProcesses; iProc++)
+ {
+
+ if (rgProcessInfo[iProc].ProcessId == dwProcessId)
+ {
+ if (rgProcessInfo[iProc].pUserSid == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcessEx is not able to retreive SID\n"));
+
+ // if there is no Sid for the user, don't call GetLengthSid.
+ // It will crash! It is ok to return E_FAIL as caller will ignore it.
+ hr = E_FAIL;
+ goto exit;
+ }
+ cbSid = GetLengthSid(rgProcessInfo[iProc].pUserSid);
+ pSid = new (nothrow) BYTE[cbSid];
+ if (pSid == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ if (!CopySid(cbSid, pSid, rgProcessInfo[iProc].pUserSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+ else
+ {
+ // We are done. Go to exit
+ hr = S_OK;
+ }
+ }
+
+ // we already find a match. Even if we fail from memory allocation of CopySid, still
+ // goto exit.
+ goto exit;
+ }
+ }
+
+ // Walk the whole list and cannot find the matching PID
+ // Find a better error code.
+ hr = E_FAIL;
+
+exit:
+
+ if (rgProcessInfo)
+ {
+ WTSFreeMemory(rgProcessInfo);
+ }
+
+ if (FAILED(hr) && pSid)
+ {
+ delete [] (reinterpret_cast<BYTE*>(pSid));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE(pSid);
+ *ppSid = pSid;
+ }
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcessEx return hr : 0x%08x\n",
+ hr));
+
+
+ return hr;
+}
+#endif // !FEATURE_CORESYSTEM
+
+//-----------------------------------------------------------------------------
+// The functions below initialize this SidBuffer instance with a Sid from
+// the token of the specified process. The first pair use the OWNER sid from
+// the process token if possible; else use the term serv API to find the
+// USER sid from the process token. This seems a little inconsistent, but
+// remains this way for backward compatibility. The second pair consistently
+// use the USER sid (never the OWNER).
+//
+// While the USER and OWNER sid are often the same, they are not always the
+// same. For example, running a process on win2k3 server as a member of the
+// local admin group causes the USER sid to be the logged-on user, and the
+// OWNER sid to be the local admins group. At least, that's how it was on
+// Monday. Expect this to change randomly at unexpected times, as most
+// security-related behavior does.
+//-----------------------------------------------------------------------------
+
+
+// ----------------------------------------------------------------------------
+// SidBuffer::InitFromProcessNoThrow
+//
+// Description:
+// Initialize this SidBuffer instance with a Sid from the token of the specified
+// process. Use the OWNER sid from the process token if possible; else use the term
+// serv API to find the USER sid from the process token. This seems a little
+// inconsistent, but remains this way for backward compatibility.
+//
+// Arguments:
+// * pid - Process ID from which to grab the SID
+//
+// Return Value:
+// HRESULT indicating success / failure
+//
+
+HRESULT SidBuffer::InitFromProcessNoThrow(DWORD pid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_pBuffer == NULL);
+ HRESULT hr = GetSidFromProcessWorker(pid, kOwnerSid, (PSID *) &m_pBuffer);
+#ifndef FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer);
+ }
+#endif // !FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ _ASSERTE(m_pBuffer != NULL);
+ return S_OK;
+}
+
+// See code:SidBuffer::InitFromProcessNoThrow. Throws if there's an error.
+void SidBuffer::InitFromProcess(DWORD pid)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = InitFromProcessNoThrow(pid);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// SidBuffer::InitFromProcessAppContainerSidNoThrow
+//
+// Description:
+// Initialize this SidBuffer instance with the TokenAppContainerSid from
+// the process token
+//
+// Arguments:
+// * pid - Process ID from which to grab the SID
+//
+// Return Value:
+// HRESULT indicating success / failure
+// S_FALSE indicates the process isn't in an AppContainer
+//
+HRESULT SidBuffer::InitFromProcessAppContainerSidNoThrow(DWORD pid)
+{
+ HRESULT hr = S_OK;
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (hProcess == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ HANDLE hToken = NULL;
+ hr = OpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+ else
+ {
+ hr = S_OK; // not sure why, but OpenProcessToken can return S_FALSE
+ // we don't want to return S_FALSE by accident
+ }
+
+ // Define new TOKEN_INFORMATION_CLASS/ TOKEN_APPCONTAINER_INFORMATION members for Win8 since they are not in the DevDiv copy of WinSDK yet
+ typedef enum _TOKEN_INFORMATION_CLASS_WIN8 {
+ TokenIsAppContainer = TokenLogonSid + 1,
+ TokenCapabilities,
+ TokenAppContainerSid
+ } TOKEN_INFORMATION_CLASS_WIN8;
+
+ typedef struct _TOKEN_APPCONTAINER_INFORMATION
+ {
+ PSID TokenPackage;
+ } TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION;
+
+ DWORD size;
+ BOOL fIsLowBox = FALSE;
+ if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIsAppContainer, &fIsLowBox, sizeof(fIsLowBox), &size))
+ {
+ DWORD gle = GetLastError();
+ if (gle == ERROR_INVALID_PARAMETER || gle == ERROR_INVALID_FUNCTION)
+ {
+ hr = S_FALSE; // We are on an OS which doesn't understand LowBox
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(gle);
+ }
+ goto exit;
+ }
+
+ if (!fIsLowBox)
+ {
+ hr = S_FALSE;
+ goto exit;
+ }
+
+ UCHAR PackSid[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_APPCONTAINER_INFORMATION)];
+ if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenAppContainerSid, &PackSid, sizeof(PackSid), &size))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ PTOKEN_APPCONTAINER_INFORMATION pTokPack = (PTOKEN_APPCONTAINER_INFORMATION)&PackSid;
+ PSID pLowBoxPackage = pTokPack->TokenPackage;
+ DWORD dwSidLen = GetLengthSid(pLowBoxPackage);
+ m_pBuffer = new (nothrow) BYTE[dwSidLen];
+ if (m_pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+ else
+ {
+ if (!CopySid(dwSidLen, m_pBuffer, pLowBoxPackage))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ delete m_pBuffer;
+ m_pBuffer = NULL;
+ goto exit;
+ }
+ }
+
+exit:
+ if (hProcess != NULL)
+ {
+ CloseHandle(hProcess);
+ }
+ if (hToken != NULL)
+ {
+ CloseHandle(hToken);
+ }
+
+ return hr;
+}
+
+// ----------------------------------------------------------------------------
+// SidBuffer::InitFromProcessUserNoThrow
+//
+// Description:
+// Initialize this SidBuffer instance with a Sid from the token of the specified
+// process. Use the USER sid from the process token if possible; else use the term
+// serv API to find the USER sid from the process token.
+//
+// Arguments:
+// * pid - Process ID from which to grab the SID
+//
+// Return Value:
+// HRESULT indicating success / failure
+//
+
+HRESULT SidBuffer::InitFromProcessUserNoThrow(DWORD pid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_pBuffer == NULL);
+ HRESULT hr = GetSidFromProcessWorker(pid, kUserSid, (PSID *) &m_pBuffer);
+#ifndef FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer);
+ }
+#endif // !FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ _ASSERTE(m_pBuffer != NULL);
+ return S_OK;
+}
+
+// See code:SidBuffer::InitFromProcessUserNoThrow. Throws if there's an error.
+void SidBuffer::InitFromProcessUser(DWORD pid)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = InitFromProcessUserNoThrow(pid);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Ctor for Dacl class. Wraps a win32 dacl.
+//-----------------------------------------------------------------------------
+Dacl::Dacl(PACL pAcl)
+{
+ m_acl = pAcl;
+}
+
+//-----------------------------------------------------------------------------
+// Get number of ACE (Access Control Entries) in this DACL.
+//-----------------------------------------------------------------------------
+SIZE_T Dacl::GetAceCount()
+{
+ return (SIZE_T) m_acl->AceCount;
+}
+
+//-----------------------------------------------------------------------------
+// Get Raw a ACE at the given index.
+// Caller assumes index is valid (0 <= dwAceIndex < GetAceCount())
+// Throws on error (which should only be if the index is out of bounds).
+//-----------------------------------------------------------------------------
+ACE_HEADER * Dacl::GetAce(SIZE_T dwAceIndex)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ ACE_HEADER * pAce = NULL;
+ BOOL fOk = ::GetAce(m_acl, (DWORD) dwAceIndex, (LPVOID*) &pAce);
+ _ASSERTE(fOk == (pAce != NULL));
+ if (!fOk)
+ {
+ ThrowLastError();
+ }
+ return pAce;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Ctor for SecurityDescriptor
+//-----------------------------------------------------------------------------
+Win32SecurityDescriptor::Win32SecurityDescriptor()
+{
+ m_pDesc = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Dtor for security Descriptor.
+//-----------------------------------------------------------------------------
+Win32SecurityDescriptor::~Win32SecurityDescriptor()
+{
+ delete [] ((BYTE*) m_pDesc);
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Get the dacl for this security descriptor.
+//-----------------------------------------------------------------------------
+Dacl Win32SecurityDescriptor::GetDacl()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(m_pDesc != NULL);
+
+ BOOL bPresent;
+ BOOL bDaclDefaulted;
+ PACL acl;
+
+ if (GetSecurityDescriptorDacl(m_pDesc, &bPresent, &acl, &bDaclDefaulted) == 0)
+ {
+ ThrowLastError();
+ }
+ if (!bPresent)
+ {
+ // No dacl. We consider this an error because all of the objects we expect
+ // to see should be dacled. If it's not dacled, then it's a malicious user spoofing it.
+ ThrowHR(E_INVALIDARG);
+ }
+
+ Dacl d(acl);
+ return d;
+}
+
+//-----------------------------------------------------------------------------
+// Get the owner from the security descriptor.
+//-----------------------------------------------------------------------------
+HRESULT Win32SecurityDescriptor::GetOwnerNoThrow( PSID* ppSid)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(m_pDesc != NULL);
+ BOOL bOwnerDefaulted;
+
+ if( ppSid == NULL )
+ {
+ return E_INVALIDARG;
+ }
+
+ if (GetSecurityDescriptorOwner(m_pDesc, ppSid, &bOwnerDefaulted) == 0)
+ {
+ DWORD err = GetLastError();
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ return S_OK;
+}
+Sid Win32SecurityDescriptor::GetOwner()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ PSID pSid;
+ HRESULT hr = GetOwnerNoThrow( &pSid );
+ if( FAILED(hr) )
+ {
+ ThrowHR( hr );
+ }
+
+ Sid s(pSid);
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+// Initialize this instance of a SecurityDescriptor with the SD for the handle.
+// The handle must ahve READ_CONTROL permissions to do this.
+// Throws on error.
+//-----------------------------------------------------------------------------
+HRESULT Win32SecurityDescriptor::InitFromHandleNoThrow(HANDLE h)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(m_pDesc == NULL); // only init once.
+
+ DWORD cbNeeded = 0;
+
+ DWORD flags = OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ // Now get the creator's SID. First get the size of the array needed.
+ BOOL fOk = GetKernelObjectSecurity(h, flags, NULL, 0, &cbNeeded);
+ DWORD err = GetLastError();
+
+ // Caller should give us a handle for which this succeeds. First call will
+ // fail w/ InsufficientBuffer.
+ CONSISTENCY_CHECK_MSGF(fOk || (err == ERROR_INSUFFICIENT_BUFFER), ("Failed to get KernelSecurity for object handle=%p.Err=%d\n", h, err));
+
+ PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) new(nothrow) BYTE[cbNeeded];
+ if( pSD == NULL )
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (GetKernelObjectSecurity(h, flags, pSD, cbNeeded, &cbNeeded) == 0)
+ {
+ // get last error and fail out.
+ err = GetLastError();
+ delete [] ((BYTE*) pSD);
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ m_pDesc = pSD;
+ return S_OK;
+}
+void Win32SecurityDescriptor::InitFromHandle(HANDLE h)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ HRESULT hr = InitFromHandleNoThrow(h);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// We open several named kernel objects that are well-known names decorated with
+// pid of some target process (usually a debuggee).
+// Since anybody can create any kernel object with any name, we we want to make
+// sure the objects we're opening were actually created by who we think they
+// were. Each kernel object has an "owner" property which serves as the
+// fingerprints of who created the handle.
+//
+// Check if the handle owner belongs to either the process specified by the
+// pid or the current process (in case the target process is impersonating us).
+// This lets us know if the handle is spoofed.
+//
+// Parameters:
+// handle - handle for kernel object to test
+// pid - target process that it may belong to.
+//
+// Returns:
+// false- if we can verify that Owner(handle) is in the set of { Owner(Process(pid)), or Owner(this Process) }
+//
+//
+// true - Elsewise, including if we can't verify that it's false.
+//-----------------------------------------------------------------------------
+bool IsHandleSpoofed(HANDLE handle, DWORD pid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(handle != NULL);
+ _ASSERTE(pid != 0);
+ bool fIsSpoofed = true;
+
+ EX_TRY
+ {
+ // Get the owner of the kernel object referenced by the handle.
+ Win32SecurityDescriptor sdHandle;
+ sdHandle.InitFromHandle(handle);
+ Sid sidOwner(sdHandle.GetOwner());
+
+ SidBuffer sbPidOther;
+ SidBuffer sbPidThis;
+
+ // Is the object owner the "other" pid?
+ sbPidOther.InitFromProcess(pid);
+ if (Sid::Equals(sbPidOther.GetSid(), sidOwner))
+ {
+ // We now know that the kernel object was created by the user of the "other" pid.
+ // This should be the common case by far. It's not spoofed. All is well.
+ fIsSpoofed = false;
+ goto Label_Done;
+ }
+
+ // Test against our current pid if it's different than the "other" pid.
+ // This can happen if the other process impersonates us. The most common case would
+ // be if we're an admin and the other process (say some service) is impersonating Admin
+ // when it spins up the CLR.
+ DWORD pidThis = GetCurrentProcessId();
+ if (pidThis != pid)
+ {
+ sbPidThis.InitFromProcess(pidThis);
+ if (Sid::Equals(sbPidThis.GetSid(), sidOwner))
+ {
+ // The object was created by somebody pretending to be us. If they had sufficient permissions
+ // to pretend to be us, then we still trust them.
+ fIsSpoofed = false;
+ goto Label_Done;
+ }
+ }
+
+ // This should only happen if we're being attacked.
+ _ASSERTE(fIsSpoofed);
+ STRESS_LOG2(LF_CORDB, LL_INFO1000, "Security Check failed with mismatch. h=%x,pid=%x", handle, pid);
+
+Label_Done:
+ ;
+ }
+ EX_CATCH
+ {
+ // This should only happen if something goes really bad and we can't find the information.
+ STRESS_LOG2(LF_CORDB, LL_INFO1000, "Security Check failed with exception. h=%x,pid=%x", handle, pid);
+ _ASSERTE(fIsSpoofed); // should still have its default value
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return fIsSpoofed;
+}
diff --git a/src/utilcode/sigbuilder.cpp b/src/utilcode/sigbuilder.cpp
new file mode 100644
index 0000000000..0fb837d934
--- /dev/null
+++ b/src/utilcode/sigbuilder.cpp
@@ -0,0 +1,166 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+#include "stdafx.h"
+#include "sigbuilder.h"
+#include "ex.h"
+
+void SigBuilder::AppendByte(BYTE b)
+{
+ STANDARD_VM_CONTRACT;
+
+ Ensure(1);
+ m_pBuffer[m_dwLength++] = b;
+}
+
+void SigBuilder::AppendData(ULONG data)
+{
+ STANDARD_VM_CONTRACT;
+
+ //
+ // Inlined logic from CorSigCompressData
+ //
+
+ if (data <= 0x7F)
+ {
+ Ensure(1);
+ m_pBuffer[m_dwLength++] = BYTE(data);
+ return;
+ }
+
+ if (data <= 0x3FFF)
+ {
+ Ensure(2);
+
+ DWORD dwLength = m_dwLength;
+ BYTE * pBuffer = m_pBuffer;
+
+ pBuffer[dwLength] = BYTE((data >> 8) | 0x80);
+ pBuffer[dwLength+1] = BYTE(data);
+
+ m_dwLength = dwLength + 2;
+ return;
+ }
+
+ if (data <= 0x1FFFFFFF)
+ {
+ Ensure(4);
+
+ DWORD dwLength = m_dwLength;
+ BYTE * pBuffer = m_pBuffer;
+
+ pBuffer[dwLength] = BYTE((data >> 24) | 0xC0);
+ pBuffer[dwLength+1] = BYTE(data >> 16);
+ pBuffer[dwLength+2] = BYTE(data >> 8);
+ pBuffer[dwLength+3] = BYTE(data);
+
+ m_dwLength = dwLength + 4;
+ return;
+ }
+
+ // We currently can only represent to 0x1FFFFFFF.
+ ThrowHR(COR_E_OVERFLOW);
+}
+
+void SigBuilder::AppendToken(mdToken tk)
+{
+ STANDARD_VM_CONTRACT;
+
+ //
+ // Inlined logic from CorSigCompressToken
+ //
+
+ RID rid = RidFromToken(tk);
+ ULONG32 ulTyp = TypeFromToken(tk);
+
+ _ASSERTE(rid <= 0x3FFFFFF);
+ rid = (rid << 2);
+
+ // TypeDef is encoded with low bits 00
+ // TypeRef is encoded with low bits 01
+ // TypeSpec is encoded with low bits 10
+ // BaseType is encoded with low bit 11
+ //
+ if (ulTyp == g_tkCorEncodeToken[0])
+ {
+ // make the last two bits 00
+ // nothing to do
+ }
+ else if (ulTyp == g_tkCorEncodeToken[1])
+ {
+ // make the last two bits 01
+ rid |= 0x1;
+ }
+ else if (ulTyp == g_tkCorEncodeToken[2])
+ {
+ // make last two bits 0
+ rid |= 0x2;
+ }
+ else if (ulTyp == g_tkCorEncodeToken[3])
+ {
+ rid |= 0x3;
+ }
+ else
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ AppendData(rid);
+}
+
+void SigBuilder::AppendBlob(const PVOID pBlob, SIZE_T cbBlob)
+{
+ STANDARD_VM_CONTRACT;
+
+ Ensure(cbBlob);
+ memcpy(m_pBuffer + m_dwLength, pBlob, cbBlob);
+ m_dwLength += (DWORD)cbBlob;
+}
+
+void SigBuilder::Grow(SIZE_T cbMin)
+{
+ STANDARD_VM_CONTRACT;
+
+ DWORD dwNewAllocation = max(m_dwLength + (DWORD)cbMin, 2 * m_dwAllocation);
+
+ // Overflow checks
+ if (dwNewAllocation < m_dwLength || (dwNewAllocation - m_dwLength) < cbMin)
+ ThrowOutOfMemory();
+
+ BYTE * pNewAllocation = new BYTE[dwNewAllocation];
+ memcpy(pNewAllocation, m_pBuffer, m_dwLength);
+
+ BYTE * pOldAllocation = m_pBuffer;
+
+ m_pBuffer = pNewAllocation;
+ m_dwAllocation = dwNewAllocation;
+
+ if (pOldAllocation != m_prealloc)
+ delete [] pOldAllocation;
+}
+
+SigBuilder::~SigBuilder()
+{
+ if (m_pBuffer != m_prealloc)
+ delete [] m_pBuffer;
+}
+
+SigBuilder::SigBuilder(DWORD cbPreallocationSize)
+{
+ STANDARD_VM_CONTRACT;
+
+ m_dwLength = 0;
+ if (cbPreallocationSize <= sizeof(m_prealloc))
+ {
+ m_pBuffer = m_prealloc;
+ m_dwAllocation = sizeof(m_prealloc);
+ }
+ else
+ {
+ m_pBuffer = new BYTE[cbPreallocationSize];
+ m_dwAllocation = cbPreallocationSize;
+ }
+}
diff --git a/src/utilcode/sigparser.cpp b/src/utilcode/sigparser.cpp
new file mode 100644
index 0000000000..b7f683ca43
--- /dev/null
+++ b/src/utilcode/sigparser.cpp
@@ -0,0 +1,197 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//
+// sigparser.cpp
+//
+
+//
+// Signature parsing code
+//
+#include "stdafx.h"
+#include "sigparser.h"
+#include "contract.h"
+
+HRESULT SigParser::SkipExactlyOne()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ CorElementType typ;
+ HRESULT hr = GetElemType(&typ);
+
+ IfFailRet(hr);
+
+ if (!CorIsPrimitiveType(typ))
+ {
+ switch (typ)
+ {
+ default:
+ // _ASSERT(!"Illegal or unimplement type in COM+ sig.");
+ return META_E_BAD_SIGNATURE;
+ break;
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ IfFailRet(GetData(NULL)); // Skip variable number
+ break;
+ case ELEMENT_TYPE_VAR_ZAPSIG:
+ IfFailRet(GetData(NULL)); // Skip RID
+ break;
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_CANON_ZAPSIG:
+ break;
+
+ case ELEMENT_TYPE_BYREF: //fallthru
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_PINNED:
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG:
+ case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+ IfFailRet(SkipExactlyOne()); // Skip referenced type
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE: //fallthru
+ case ELEMENT_TYPE_CLASS:
+ IfFailRet(GetToken(NULL)); // Skip RID
+ break;
+
+ case ELEMENT_TYPE_MODULE_ZAPSIG:
+ IfFailRet(GetData(NULL)); // Skip index
+ IfFailRet(SkipExactlyOne()); // Skip type
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ IfFailRet(SkipSignature());
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ IfFailRet(SkipExactlyOne()); // Skip element type
+ ULONG rank;
+ IfFailRet(GetData(&rank)); // Get rank
+ if (rank)
+ {
+ ULONG nsizes;
+ IfFailRet(GetData(&nsizes)); // Get # of sizes
+ while (nsizes--)
+ {
+ IfFailRet(GetData(NULL)); // Skip size
+ }
+
+ ULONG nlbounds;
+ IfFailRet(GetData(&nlbounds)); // Get # of lower bounds
+ while (nlbounds--)
+ {
+ IfFailRet(GetData(NULL)); // Skip lower bounds
+ }
+ }
+
+ }
+ break;
+
+ case ELEMENT_TYPE_SENTINEL:
+ // Should be unreachable since GetElem strips it
+ break;
+
+ case ELEMENT_TYPE_INTERNAL:
+ IfFailRet(GetPointer(NULL));
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ IfFailRet(SkipExactlyOne()); // Skip generic type
+ ULONG argCnt;
+ IfFailRet(GetData(&argCnt)); // Get number of parameters
+ while (argCnt--)
+ {
+ IfFailRet(SkipExactlyOne()); // Skip the parameters
+ }
+ break;
+
+ }
+ }
+
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Skip only a method header signature - not the sigs of the args to the method.
+//
+HRESULT
+SigParser::SkipMethodHeaderSignature(
+ ULONG * pcArgs)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // Skip calling convention
+ ULONG uCallConv;
+ IfFailRet(GetCallingConvInfo(&uCallConv));
+
+ if ((uCallConv == IMAGE_CEE_CS_CALLCONV_FIELD) ||
+ (uCallConv == IMAGE_CEE_CS_CALLCONV_LOCAL_SIG))
+ {
+ return META_E_BAD_SIGNATURE;
+ }
+
+ // Skip type parameter count
+ if (uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ IfFailRet(GetData(NULL));
+
+ // Get arg count;
+ IfFailRet(GetData(pcArgs));
+
+ // Skip return type;
+ IfFailRet(SkipExactlyOne());
+
+ return hr;
+} // SigParser::SkipMethodHeaderSignature
+
+//---------------------------------------------------------------------------------------
+//
+// Skip a sub signature (as immediately follows an ELEMENT_TYPE_FNPTR).
+HRESULT SigParser::SkipSignature()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ ULONG cArgs;
+
+ IfFailRet(SkipMethodHeaderSignature(&cArgs));
+
+ // Skip args.
+ while (cArgs) {
+ IfFailRet(SkipExactlyOne());
+ cArgs--;
+ }
+
+ return hr;
+}
diff --git a/src/utilcode/sortversioning.cpp b/src/utilcode/sortversioning.cpp
new file mode 100644
index 0000000000..6b9f56cc8c
--- /dev/null
+++ b/src/utilcode/sortversioning.cpp
@@ -0,0 +1,1139 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+////////////////////////////////////////////////////////////////////////////
+//
+// File: SortVersioning.cpp
+//
+
+
+// Purpose: Provides access of the sort versioning functionality on
+// downlevel (pre-Win7) machines.
+//
+//
+// This is not used on CoreCLR, where we always go to the OS
+// for sorting.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "sortversioning.h"
+#include "newapis.h"
+
+#include "mscoree.h"
+#include "clrconfig.h"
+
+#define SORT_VERSION_V4 0x00060101
+#define SORT_VERSION_WHIDBEY 0x00001000
+#define SORT_VERSION_DEFAULT SORT_VERSION_V4
+#define SORT_DEFAULT_DLL_NAME MAKEDLLNAME(W("nlssorting"))
+
+namespace SortVersioning
+{
+#define SORT_HASH_TBL_SIZE 128
+
+ //
+ // Forward Declarations
+ //
+ PSORTHANDLE MakeSortHashNode(
+ __in LPCWSTR pSortName,
+ __in DWORD dwVersion);
+
+
+ PSORTHANDLE InsertSortHashNode(
+ __in PSORTHANDLE pHashN);
+
+ static PSORTHANDLE g_pSortHash[SORT_HASH_TBL_SIZE]; // Sort node hash table
+
+ static HMODULE g_hSortDefault = (HMODULE)-1;
+
+ __encoded_pointer static SORTGETHANDLE g_pDefaultGetHandle;
+ __encoded_pointer static SORTCLOSEHANDLE g_pDefaultCloseHandle;
+
+ static HMODULE g_hSortCompatV2 = (HMODULE)-1;
+
+ __encoded_pointer static SORTGETHANDLE g_pV2GetHandle;
+ __encoded_pointer static SORTCLOSEHANDLE g_pV2CloseHandle;
+
+ static HMODULE g_hSortCompatV4 = (HMODULE)-1;
+
+ __encoded_pointer static SORTGETHANDLE g_pV4GetHandle;
+ __encoded_pointer static SORTCLOSEHANDLE g_pV4CloseHandle;
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // NlsCompareInvariantNoCase
+ //
+ // This routine does fast caseless comparison without needing the tables.
+ // This helps us do the comparisons we need to load the tables :-)
+ //
+ // Returns 0 if identical, <0 if pFirst if first string sorts first.
+ //
+ // This is only intended to help with our locale name comparisons,
+ // which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
+ // compare as equal.
+ //
+ // WARNING: [\]^_` will be less than A-Z because we make everything lower
+ // case before comparing them.
+ //
+ // When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
+ // When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
+ // or when null terminators are reached, whichever happens first (strncmp-like behavior)
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ int NlsCompareInvariantNoCase(
+ LPCWSTR pFirst,
+ LPCWSTR pSecond,
+ int size,
+ BOOL bNullEnd)
+ {
+ int i=0;
+ WCHAR first;
+ WCHAR second;
+
+ for (;
+ size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
+ size--, pFirst++, pSecond++)
+ {
+ // Make them lower case
+ if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
+ if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
+
+ // Get the diff
+ i = (first - second);
+
+ // Are they the same?
+ if (i == 0)
+ continue;
+
+ // Otherwise the difference. Remember we made A-Z into lower case, so
+ // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the
+ // characters between A-Z and a-Z in ascii)
+ return i;
+ }
+
+ // When we are here, one of these holds:
+ // size == 0
+ // or one of the strings has a null terminator
+ // or both of the string reaches null terminator
+
+ if (bNullEnd || size != 0)
+ {
+ // If bNullEnd is TRUE, always check for null terminator.
+ // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
+ // than another (hense the size != 0 check).
+
+ // See if one string ended first
+ if (*pFirst != 0 || *pSecond != 0)
+ {
+ // Which one?
+ return *pFirst == 0 ? -1 : 1;
+ }
+ }
+
+ // Return our difference (0)
+ return i;
+ }
+
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // LoadSortModuleAndInvariant()
+ //
+ // Attempts to load the given dll. If that is successful, attempts to
+ // load the invariant data. If that is successful, returns the module handle
+ // and the addresses of the SortGetHandle function and SortCloseHandle function
+ //
+ // failure is indicated by returning NULL for the module handle and the
+ // function addresses
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ HMODULE LoadSortModuleAndInvariant(
+ __in LPCWSTR sDllName,
+ __in DWORD dwVersion,
+ __out SORTGETHANDLE* ppGetHandle,
+ __out SORTCLOSEHANDLE* ppCloseHandle
+ )
+ {
+ *ppGetHandle = NULL;
+ *ppCloseHandle = NULL;
+ HMODULE hSort;
+
+ if(FAILED(UtilCode::LoadLibraryShim(sDllName, NULL, NULL, &hSort)))
+ {
+ return NULL;
+ }
+
+ SORTGETHANDLE pGetHandle = (SORTGETHANDLE)GetProcAddress(hSort, "SortGetHandle");
+ SORTCLOSEHANDLE pCloseHandle = (SORTCLOSEHANDLE)GetProcAddress(hSort, "SortCloseHandle");
+
+ // If we didn't load the procs, then remember that
+ if (pCloseHandle == NULL || pGetHandle == NULL)
+ {
+ ::FreeLibrary(hSort);
+ return NULL;
+ }
+
+ // Verify that the data file's available
+ NLSVERSIONINFO sortVersion;
+ sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
+ sortVersion.dwNLSVersion = dwVersion;
+ sortVersion.dwDefinedVersion = dwVersion;
+
+ // Invariant must be there and is kinda common, so we'll just get it
+ PSORTHANDLE pSort = pGetHandle(W(""), &sortVersion, NULL);
+ if (!pSort)
+ {
+ // Yikes, invariant failed, forget about it.
+ ::FreeLibrary(hSort);
+ return NULL;
+ }
+
+ // Since we found it, may as well remember it.
+ const SORTHANDLE * const pSortInHash = InsertSortHashNode(pSort);
+
+ // If we got a different one back then free the one we added
+ if (pSortInHash != pSort && pSortInHash)
+ {
+ // We got a different one from the hash (someone beat us to the cache)
+ // so use that and discard the new one.
+ pCloseHandle(pSort);
+ }
+
+ *ppGetHandle = pGetHandle;
+ *ppCloseHandle = pCloseHandle;
+ return hSort;
+ }
+
+ // Attempts to load a Sort DLL. If this fails, the values of the __out parameters are unchanged.
+ BOOL LoadSortDllAndPublish(
+ __in LPCWSTR sDllName,
+ __in DWORD dwVersion,
+ __out __encoded_pointer SORTGETHANDLE* ppGetHandle,
+ __out __encoded_pointer SORTCLOSEHANDLE* ppCloseHandle,
+ __out HMODULE* phSortDll)
+ {
+ HMODULE hSortDll;
+ SORTGETHANDLE pGetHandle;
+ SORTCLOSEHANDLE pCloseHandle;
+
+ hSortDll = LoadSortModuleAndInvariant(sDllName, dwVersion, &pGetHandle, &pCloseHandle);
+
+ if(hSortDll != NULL) {
+ *phSortDll = hSortDll;
+ *ppGetHandle = (SORTGETHANDLE)EncodePointer(pGetHandle);
+ *ppCloseHandle = (SORTCLOSEHANDLE)EncodePointer(pCloseHandle);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // GetSortGetHandle()
+ //
+ // Get the SortGetHandle() function for the proper dll version.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
+ {
+ if(dwVersion == SORT_VERSION_DEFAULT)
+ {
+ // If we haven't tried to load the module/proc before do so now
+ if (g_hSortDefault == (HMODULE)-1)
+ {
+ LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault);
+ }
+
+ // This check is necessary because the LoadSortDllAndPublish call may have failed since some platforms
+ // won't have nlssorting.dll (e.g. Windows 8 and above).
+ if (g_hSortDefault != (HMODULE)-1)
+ {
+ return (SORTGETHANDLE)DecodePointer(g_pDefaultGetHandle);
+ }
+ }
+
+ HMODULE* pHSortModule;
+ SORTGETHANDLE* ppGetHandle;
+ SORTCLOSEHANDLE* ppCloseHandle;
+
+ if(dwVersion == SORT_VERSION_V4)
+ {
+ ppGetHandle = &g_pV4GetHandle;
+ ppCloseHandle = &g_pV4CloseHandle;
+ pHSortModule = &g_hSortCompatV4;
+ }
+ else if(dwVersion == SORT_VERSION_WHIDBEY)
+ {
+ ppGetHandle = &g_pV2GetHandle;
+ ppCloseHandle = &g_pV2CloseHandle;
+ pHSortModule = &g_hSortCompatV2;
+ }
+ else
+ {
+ // Unsupported sorting version.
+ return NULL;
+ }
+
+ if(*pHSortModule == (HMODULE) -1)
+ {
+ // get module name - the module name should be "Sort"+dwVersion.ToString("x8")+".dll"
+ WCHAR moduleName[] = W("Sort00000000.dll");
+ // replace the "00000000" with the hexadecimal of dwVersion
+ LPCWSTR hex = W("0123456789abcdef");
+ WCHAR* p = &moduleName[4+8]; // position at end of number part of dll name
+
+ unsigned int value = dwVersion;
+
+ while (value != 0 && p != moduleName) {
+ int digit = value & 0xF;
+ *--p = hex[digit];
+ value >>= 4;
+ }
+
+ if(!LoadSortDllAndPublish(&moduleName[0], dwVersion, ppGetHandle, ppCloseHandle, pHSortModule))
+ {
+ // We failed to load a versioned sort dll, try to fall back to the current version.
+ // If we haven't tried to load the module/proc before do so now
+ if (g_hSortDefault == (HMODULE)-1)
+ {
+ LoadSortDllAndPublish(SORT_DEFAULT_DLL_NAME, SORT_VERSION_DEFAULT, &g_pDefaultGetHandle, &g_pDefaultCloseHandle, &g_hSortDefault);
+ }
+
+ *pHSortModule = g_hSortDefault;
+ *ppCloseHandle = g_pDefaultCloseHandle;
+ *ppGetHandle = g_pDefaultGetHandle;
+ }
+ }
+
+ // At this point, we've either loaded a sorting dll or we've exausted all options.
+ if(*pHSortModule == (HMODULE) -1)
+ {
+ // Couldn't find anything, give up.
+ return NULL;
+ }
+ else
+ {
+ return (SORTGETHANDLE)DecodePointer(*ppGetHandle);
+ }
+
+ // Unknown version requested
+ return NULL;
+ }
+
+ void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
+ {
+ if (dwVersion == SORT_VERSION_DEFAULT)
+ {
+ SORTCLOSEHANDLE pDefaultCloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pDefaultCloseHandle);
+ if(pDefaultCloseHandle != NULL)
+ {
+ (pDefaultCloseHandle)(pSort);
+ return;
+ }
+ }
+
+ if (dwVersion == SORT_VERSION_V4)
+ {
+ SORTCLOSEHANDLE pV4CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV4CloseHandle);
+ if(pV4CloseHandle != NULL)
+ {
+ (pV4CloseHandle)(pSort);
+ return;
+ }
+ }
+
+
+ if (dwVersion == SORT_VERSION_WHIDBEY)
+ {
+ SORTCLOSEHANDLE pV2CloseHandle = (SORTCLOSEHANDLE)DecodePointer(g_pV2CloseHandle);
+ if(pV2CloseHandle != NULL)
+ {
+ (pV2CloseHandle)(pSort);
+ return;
+ }
+ }
+ }
+
+#else // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+
+ SORTGETHANDLE GetSortGetHandle(__in DWORD dwVersion)
+ {
+ return NULL;
+ }
+
+ void DoSortCloseHandle(__in DWORD dwVersion, __in PSORTHANDLE pSort)
+ {
+ }
+
+#endif // !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // GetSortHashValue
+ //
+ // Returns the hash value for given sort name & version.
+ //
+ // WARNING: This must be case insensitive. Currently we're expecting only
+ // a-z, A-Z, 0-9 & -.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ __inline __range(0, SORT_HASH_TBL_SIZE-1) int GetSortHashValue(
+ __in LPCWSTR pSortName,
+ __in DWORD dwVersion)
+ {
+ int iHash = 12; // Seed hash value
+ int iMax; // Number of characters to count (prevent problems with too-bad strings)
+
+ // Hash the string
+ if (pSortName)
+ {
+ for (iMax = 10; *pSortName != 0 && iMax != 0; pSortName++, iMax--)
+ {
+ iHash <<= 1;
+ iHash ^= ((*pSortName) & 0xdf); // 0x20 will make cases be the same (and other wierd stuff too, but we don't care about that)
+ }
+ }
+
+ // Add the version hash
+ // (the middle 2 bytes are most interesting)
+ iHash ^= dwVersion >> 8;
+
+ // Mix up our bits and hash it with 128
+ _ASSERT(SORT_HASH_TBL_SIZE == 128);
+ return (iHash + (iHash >> 8)) & 0x7f;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // InsertSortHashNode
+ //
+ // Inserts a sort hash node into the global sort hash tables. It assumes
+ // that all unused hash values in the table are pointing to NULL. If
+ // there is a collision, the new node will be added LAST in the list.
+ // (Presuming that the most often used are also the first used)
+ //
+ // We do an interlocked exchange and free the pointer if we can't add it.
+ //
+ // Warning: We stick stuff in this list, but we never remove it, so it
+ // get kind of big. Removing entries would be difficult however
+ // because it would require some sort of synchronization with the
+ // reader functions (like GetLocaleInfo), or maybe an in-use flag
+ // or spin count.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ PSORTHANDLE InsertSortHashNode(PSORTHANDLE pHashN)
+ {
+ __range(0, SORT_HASH_TBL_SIZE-1) UINT index;
+ PSORTHANDLE pSearch;
+ PSORTHANDLE* pNextToUpdate;
+
+ //
+ // Insert the hash node into the list (by name/version)
+ //
+#ifdef _PREFAST_
+#pragma warning(push)
+#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
+#endif // _PREFAST_
+ index = GetSortHashValue(pHashN->sortName, pHashN->dwNLSVersion);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+ // Get hash node
+ pSearch = g_pSortHash[index];
+
+ // Remember last pointer in case we need to add it
+ pNextToUpdate = &g_pSortHash[index];
+
+ // We'll be the last node when added
+ pHashN->pNext = NULL;
+
+ while(TRUE)
+ {
+ while (pSearch != NULL)
+ {
+ // See if we already found a node.
+ if ((pSearch->dwNLSVersion == pHashN->dwNLSVersion) &&
+ NlsCompareInvariantNoCase( pSearch->sortName, pHashN->sortName,
+ LOCALE_NAME_MAX_LENGTH, TRUE) == 0)
+ {
+ // Its the same, which is unexpected, return the old one
+ return pSearch;
+ }
+
+ pNextToUpdate = &pSearch->pNext;
+ pSearch = pSearch->pNext;
+ }
+
+ // At end, try to add our node
+ pSearch = InterlockedCompareExchangeT(pNextToUpdate, pHashN, NULL);
+
+ // If pNextToUpdate isn't NULL then another process snuck in and updated the list
+ // while we were getting ready.
+ if (pSearch == NULL)
+ {
+ // It was added, stop
+ break;
+ }
+
+ // It wasn't added, pSearch now points to a new node that snuck in, so
+ // continue and try that one. This should be really rare, even in a busy
+ // loop, so we don't try a real lock. Either
+ // a) the snuck in node is the same as pHashN, and we'll return pSearch
+ // in the first loop, or
+ // b) the snuck in node is new, in which case we'll try to readd. Very worst
+ // case we'd collide while someone added ALL of the other locales with our
+ // hash, but eventually we'd hit case a. (And there's only a couple hundred
+ // tries, so this can't lock for long.)
+ }
+
+ // Return the same one we added
+ return pHashN;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // FindSortHashNode
+ //
+ // Searches for the sort hash node for the given sort name & version.
+ // The result is returned. If none are found NULL is returned.
+ //
+ // NOTE: Call GetSortNode() which calls this.
+ //
+ // Defined as inline.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ __inline PSORTHANDLE FindSortHashNode(
+ __in LPCWSTR pSortName,
+ __in DWORD dwVersion)
+ {
+ PSORTHANDLE pHashN;
+ __range(0,SORT_HASH_TBL_SIZE-1) int index;
+
+ // Get Index
+ index = GetSortHashValue(pSortName, dwVersion);
+
+ // Get hash node
+ pHashN = g_pSortHash[index];
+
+ // Look through the list to see if one matches name and user info
+ // We're sneaky here because we know our length of our hash name string is stored
+ // just before that string.
+ while ((pHashN != NULL) &&
+ ((dwVersion != pHashN->dwNLSVersion) ||
+ (NlsCompareInvariantNoCase(pSortName, pHashN->sortName, LOCALE_NAME_MAX_LENGTH, TRUE) != 0)))
+ {
+ pHashN = pHashN->pNext;
+ }
+
+ return pHashN;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // MakeSortHashNode
+ //
+ // Builds a sort hash node and sticks it in the hash table.
+ //
+ // NOTE: Call GetSortNode() which calls this.
+ //
+ // Defined as inline.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ PSORTHANDLE MakeSortHashNode(
+ __in LPCWSTR pSortName,
+ __in DWORD dwVersion)
+ {
+ NLSVERSIONINFO sortVersion;
+
+ PSORTHANDLE pSort = NULL;
+ PSORTHANDLE pSortInHash;
+
+ // Valid locale, now we need to find out where to point this version at
+ SORTGETHANDLE pGetHandle = GetSortGetHandle(dwVersion);
+ if (pGetHandle == NULL) return NULL;
+
+ sortVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
+ sortVersion.dwNLSVersion = dwVersion;
+ sortVersion.dwDefinedVersion = dwVersion;
+
+ pSort = pGetHandle(pSortName, &sortVersion, NULL);
+
+ // If still missing, fail
+ if (pSort == NULL)
+ {
+ // Invalid sort, fail
+ return NULL;
+ }
+
+ // Now we need to add it
+ pSortInHash = InsertSortHashNode(pSort);
+
+ // If we got a different one back then free the one we added
+ if (pSortInHash != pSort && pSortInHash)
+ {
+ // We got a different one from the hash (someone beat us to the cache)
+ // so use that and discard the new one.
+ DoSortCloseHandle(dwVersion, pSort);
+ }
+
+ return pSortInHash;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // GetSortNode
+ //
+ // Get a sort hash node for the specified sort name & version
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ PSORTHANDLE GetSortNode(
+ __in LPCWSTR pSortName,
+ __in DWORD dwVersion)
+ {
+ PSORTHANDLE pSortHashN = NULL;
+
+ // WARNING: We don't bother doing the null/default/system checks
+
+ // Didn't have an obvious one, look in the hash table
+ pSortHashN = FindSortHashNode(pSortName, dwVersion);
+
+ //
+ // If the hash node does not exist, we may need to get make one
+ //
+ if (pSortHashN == NULL)
+ {
+ //
+ // Hash node does NOT exist, try to make it
+
+ //
+ pSortHashN = MakeSortHashNode(pSortName, dwVersion);
+ }
+
+ //
+ // If the hash node still does not exist, we may need to fallback to default
+ // version
+ //
+ if (pSortHashN == NULL && dwVersion != SORT_VERSION_DEFAULT)
+ {
+ return GetSortNode(pSortName, SORT_VERSION_DEFAULT);
+ }
+
+ //
+ // Return pointer to hash node
+ // (null if we still don't have one)
+ //
+ return pSortHashN;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SortNLSVersion
+ // Check for the DWORD "CompatSortNLSVersion" CLR config option.
+ //
+ // .Net 4.0 introduces sorting changes that can affect the behavior of any of the methods
+ // in CompareInfo. To mitigate against compatibility problems Applications can enable the
+ // legacy CompareInfo behavior by using the 'SortNLSVersion' configuration option
+ //
+ // There are three ways to use the configuration option:
+ //
+ // 1) Config file (MyApp.exe.config)
+ // <?xml version ="1.0"?>
+ // <configuration>
+ // <runtime>
+ // <CompatSortNLSVersion enabled="4096"/><!--0x00001000 -->
+ // </runtime>
+ // </configuration>
+ // 2) Environment variable
+ // set COMPLUS_CompatSortNLSVersion=4096
+ // 3) RegistryKey
+ // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
+ // "CompatSortNLSVersion"=dword:00001000
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ DWORD SortNLSVersion()
+ {
+#ifdef FEATURE_CORECLR
+ return SORT_VERSION_DEFAULT;
+#else
+ static bool sortNLSVersionConfigChecked = false;
+ static DWORD sortNLSVersion = SORT_VERSION_DEFAULT;
+
+ if(!sortNLSVersionConfigChecked)
+ {
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
+ sortNLSVersion = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_CompatSortNLSVersion);
+ if(sortNLSVersion == 0)
+ {
+ sortNLSVersion = SORT_VERSION_DEFAULT;
+ }
+ END_SO_INTOLERANT_CODE;
+
+ sortNLSVersionConfigChecked = true;
+ }
+ return sortNLSVersion;
+#endif // !FEATURE_CORECLR
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // VersionValue
+ //
+ // Get the version from a version blob, resolving to the default version
+ // if NULL
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ __inline DWORD VersionValue(__in_opt const NLSVERSIONINFO * const lpVersionInformation)
+ {
+
+ //
+ // If the caller passed null or zero we use the default version
+ //
+ if ((lpVersionInformation == NULL) ||
+ ((lpVersionInformation->dwNLSVersion == 0) &&
+ (lpVersionInformation->dwDefinedVersion ==0))
+ )
+ {
+ return SortNLSVersion();
+ }
+
+ // TODO: Will need to review this
+ if(((lpVersionInformation->dwNLSVersion == 0) &&
+ (lpVersionInformation->dwDefinedVersion != 0 )))
+ {
+ return lpVersionInformation->dwDefinedVersion;
+ }
+
+ return lpVersionInformation->dwNLSVersion;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SortGetSortKey
+ //
+ // Supposed to call the dll for the appropriate version. If the default
+ // version isn't available call the ordinal behavior (for minwin)
+ //
+ // Just get the sort hash node and call the worker function
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ __success(return != 0) int WINAPI SortGetSortKey(
+ __in LPCWSTR pLocaleName,
+ __in DWORD dwFlags,
+ __in_ecount(cchSrc) LPCWSTR pSrc,
+ __in int cchSrc,
+ __out_bcount_opt(cbDest) LPBYTE pDest,
+ __in int cbDest,
+ __in_opt CONST NLSVERSIONINFO *lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam
+ )
+ {
+ PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
+ return SortDllGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
+ }
+
+ // SortDllGetSortKey handles any modification to flags
+ // necessary before the actual call to the dll
+ __success(return != 0) int WINAPI SortDllGetSortKey(
+ __in PSORTHANDLE pSort,
+ __in DWORD dwFlags,
+ __in_ecount(cchSrc) LPCWSTR pSrc,
+ __in int cchSrc,
+ __out_bcount_opt(cbDest) LPBYTE pDest,
+ __in int cbDest,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam )
+ {
+ if (pSort == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ //
+ // Note that GetSortKey'll have the opposite behavior for the
+ // linguistic casing flag (eg: use flag for bad behavior, linguistic
+ // by default)
+ dwFlags ^= NORM_LINGUISTIC_CASING;
+
+ return pSort->pSortGetSortKey(pSort, dwFlags, pSrc, cchSrc, pDest, cbDest, lpReserved, lParam);
+ }
+
+ __success(return != 0) int SortDllGetHashCode(
+ __in PSORTHANDLE pSort,
+ __in DWORD dwFlags,
+ __in_ecount(cchSrc) LPCWSTR pSrc,
+ __in int cchSrc,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam )
+ {
+ if (pSort == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+ const int SortDllGetHashCodeApiIntroducedVersion = 2;
+ if(pSort->dwSHVersion < SortDllGetHashCodeApiIntroducedVersion)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return 0;
+ }
+
+ //
+ // Note that GetSortKey'll have the opposite behavior for the
+ // linguistic casing flag (eg: use flag for bad behavior, linguistic
+ // by default)
+ dwFlags ^= NORM_LINGUISTIC_CASING;
+
+ return pSort->pSortGetHashCode(pSort, dwFlags, pSrc, cchSrc, lpReserved, lParam);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SortChangeCase
+ //
+ // Supposed to call the dll for the appropriate version. If the default
+ // version isn't available call the ordinal behavior (for minwin)
+ //
+ // NOTE: The linguistic casing flags are backwards (ie: set the flag to
+ // get the non-linguistic behavior.) If we expose this then we'll
+ // need to publish the no-linguistic flag.
+ //
+ // Just get the sort hash node and call the worker function
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ __success(return != 0) int WINAPI SortChangeCase(
+ __in LPCWSTR pLocaleName,
+ __in DWORD dwFlags,
+ __in_ecount(cchSrc) LPCWSTR pSrc,
+ __in int cchSrc,
+ __out_ecount_opt(cchDest) LPWSTR pDest,
+ __in int cchDest,
+ __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ PSORTHANDLE pSort = GetSortNode(pLocaleName, VersionValue(lpVersionInformation));
+ return SortDllChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
+ }
+
+ // SortDllChangeCase handles any modification to flags
+ // necessary before the actual call to the dll
+ __success(return != 0) int WINAPI SortDllChangeCase(
+ __in PSORTHANDLE pSort,
+ __in DWORD dwFlags,
+ __in_ecount(cchSrc) LPCWSTR pSrc,
+ __in int cchSrc,
+ __out_ecount_opt(cchDest) LPWSTR pDest,
+ __in int cchDest,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ if (pSort == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ // Note that Change Case'll have the opposite behavior for the
+ // linguistic casing flag (eg: use flag for bad behavior, linguistic
+ // by default)
+ dwFlags ^= LCMAP_LINGUISTIC_CASING;
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // prefast - Possible postcondition violation due to failure to null terminate string
+#endif // _PREFAST_
+ return pSort->pSortChangeCase(pSort, dwFlags, pSrc, cchSrc, pDest, cchDest, lpReserved, lParam);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SortCompareString
+ //
+ // Supposed to call the dll for the appropriate version. If the default
+ // version isn't available call the ordinal behavior (for minwin)
+ //
+ // Just get the sort hash node and call the worker function
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ __success(return != 0) int WINAPI SortCompareString(
+ __in LPCWSTR lpLocaleName,
+ __in DWORD dwCmpFlags,
+ __in_ecount(cchCount1) LPCWSTR lpString1,
+ __in int cchCount1,
+ __in_ecount(cchCount2) LPCWSTR lpString2,
+ __in int cchCount2,
+ __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
+ return SortDllCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
+
+ }
+
+ // SortDllCompareString handles any modification to flags
+ // necessary before the actual call to the dll
+ __success(return != 0) int WINAPI SortDllCompareString(
+ __in PSORTHANDLE pSort,
+ __in DWORD dwCmpFlags,
+ __in_ecount(cchCount1) LPCWSTR lpString1,
+ __in int cchCount1,
+ __in_ecount(cchCount2) LPCWSTR lpString2,
+ __in int cchCount2,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ if (pSort == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ // Note that the dll will have the opposite behavior of CompareStringEx for the
+ // linguistic casing flag (eg: use flag for bad behavior, linguistic
+ // by default) because we want new public APIs to have the "right"
+ // behavior by default
+ dwCmpFlags ^= NORM_LINGUISTIC_CASING;
+
+ return pSort->pSortCompareString(pSort, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2, lpReserved, lParam);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SortFindString
+ //
+ // Finds lpStringValue within lpStringSource based on the rules given
+ // in dwFindNLSStringFlags.
+ //
+ // Supposed to call the dll for the appropriate version. If the default
+ // version isn't available call the ordinal behavior (for minwin)
+ //
+ // Just get the sort hash node and call the worker function
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ __success(return != 0) int WINAPI SortFindString(
+ __in LPCWSTR lpLocaleName,
+ __in DWORD dwFindNLSStringFlags,
+ __in_ecount(cchSource) LPCWSTR lpStringSource,
+ __in int cchSource,
+ __in_ecount(cchValue) LPCWSTR lpStringValue,
+ __in int cchValue,
+ __out_opt LPINT pcchFound,
+ __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ PSORTHANDLE pSort = GetSortNode(lpLocaleName, VersionValue(lpVersionInformation));
+ return SortDllFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, pcchFound, lpReserved, lParam);
+ }
+
+ // SortDllFindString handles any modification to flags
+ // necessary before the actual call to the dll
+ __success(return != 0) int WINAPI SortDllFindString(
+ __in PSORTHANDLE pSort,
+ __in DWORD dwFindNLSStringFlags,
+ __in_ecount(cchSource) LPCWSTR lpStringSource,
+ __in int cchSource,
+ __in_ecount(cchValue) LPCWSTR lpStringValue,
+ __in int cchValue,
+ __out_opt LPINT pcchFound,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam)
+ {
+ if (pSort == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ // Note that the dll will have the opposite behavior of FindNlsString for the
+ // linguistic casing flag (eg: use flag for bad behavior, linguistic
+ // by default) because we want new public APIs to have the "right"
+ // behavior by default
+ dwFindNLSStringFlags ^= NORM_LINGUISTIC_CASING;
+
+ int cchFound; // we need to get the length even if the caller doesn't care about it (see below)
+ int result = pSort->pSortFindString(pSort, dwFindNLSStringFlags, lpStringSource, cchSource, lpStringValue, cchValue, &cchFound, lpReserved, lParam);
+ // When searching from end with an empty pattern (either empty string or all ignored characters)
+ // a match is found (result != -1)
+ // Currently we get a result == 0 but we are hoping this will change
+ // with Win7 to be the length of the source string (thus pointing past-the-end)
+ // and the length of the match (cchFound) will be 0
+ // For compatibility, we need to return the index of the last character (or 0 if the source is empty)
+ if((dwFindNLSStringFlags & FIND_FROMEND) &&
+ result != -1 &&
+ cchFound == 0 &&
+ cchSource != 0)
+ {
+ result = cchSource - 1;
+ }
+
+ // if the caller cares about the length, give it to them
+ if(pcchFound != NULL)
+ {
+ *pcchFound = cchFound;
+ }
+
+ return result;
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // SortIsDefinedString
+ //
+ // This routine looks for code points inside a string to see if they are
+ // defined within the NSL context. If lpVersionInformation is NULL, the
+ // version is the current version. Same thing the dwDefinedVersion is equal
+ // to zero.
+ //
+ // Supposed to call the dll for the appropriate version. If the default
+ // version isn't available call the ordinal behavior (for minwin)
+ //
+ // Just get the sort hash node and call the worker function
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ BOOL WINAPI SortIsDefinedString(
+ __in NLS_FUNCTION Function,
+ __in DWORD dwFlags,
+ __in CONST NLSVERSIONINFOEX * lpVersionInformation,
+ __in_ecount(cchStr) LPCWSTR lpString,
+ __in INT cchStr)
+ {
+ // Get an invariant sort node
+ PSORTHANDLE pSort = GetSortNode(W(""), VersionValue((CONST NLSVERSIONINFO *)lpVersionInformation));
+ return SortDllIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
+ }
+
+ // SortDllIsDefinedString handles any modification to flags
+ // necessary before the actual call to the dll
+ BOOL WINAPI SortDllIsDefinedString(
+ __in PSORTHANDLE pSort,
+ __in NLS_FUNCTION Function,
+ __in DWORD dwFlags,
+ __in_ecount(cchStr) LPCWSTR lpString,
+ __in INT cchStr)
+ {
+ // Fail if we couldn't find one
+ if (pSort == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ return pSort->pSortIsDefinedString(pSort, Function, dwFlags, lpString, cchStr);
+ }
+
+ BOOL SortGetNLSVersion(__in PSORTHANDLE pSort,
+ __in NLS_FUNCTION Function,
+ __inout NLSVERSIONINFO * lpVersionInformation )
+ {
+ lpVersionInformation->dwNLSVersion = pSort->dwNLSVersion;
+ lpVersionInformation->dwDefinedVersion = pSort->dwDefinedVersion;
+
+ return TRUE;
+ }
+
+ // Wrapper for SortGetSortKey and SortChangeCase, which are both
+ // smushed into LCMapStringEx
+ __success(return != 0) int
+ LCMapStringEx (__in LPCWSTR lpLocaleName,
+ __in DWORD dwMapFlags,
+ __in_ecount(cchSrc) LPCWSTR lpSrcStr,
+ __in int cchSrc,
+ __out_ecount_opt(cchDest) LPWSTR lpDestStr, // really this should be __out_awcount_opt(dwMapFlags & LCMAP_SORTKEY, cchDest)
+ __in int cchDest,
+ __in_opt CONST NLSVERSIONINFO * lpVersionInformation,
+ __in_opt LPVOID lpReserved,
+ __in_opt LPARAM lParam )
+ {
+ // Should be either sort key...
+ if (dwMapFlags & LCMAP_SORTKEY)
+ {
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable: 26036) // Prefast - Possible precondition violation due to failure to null terminate string lpDestStr-interpreted differently depending on flag
+#endif // _PREFAST_
+ return SortGetSortKey(lpLocaleName,
+ dwMapFlags & ~(LCMAP_SORTKEY), // Don't need sort key flag
+ lpSrcStr,
+ cchSrc,
+ (LPBYTE)lpDestStr, // Sort keys are bytes not WCHARs
+ cchDest, // Sort keys are bytes not WCHARs
+ lpVersionInformation,
+ lpReserved,
+ lParam);
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ }
+
+ //
+ // Check for changing case conditions. This may be combined with Chinese or Japanese
+ // transliteration, but not with sort key nor ignore space/symbols
+ //
+ _ASSERT(dwMapFlags & (LCMAP_TITLECASE | LCMAP_UPPERCASE | LCMAP_LOWERCASE));
+
+ //
+ // Call casing wrapper, which'll either call the correct version dll
+ // or call ordinal behavior in the minwin case
+ //
+ return SortChangeCase(lpLocaleName,
+ dwMapFlags & ~(LCMAP_BYTEREV),
+ lpSrcStr,
+ cchSrc,
+ lpDestStr,
+ cchDest,
+ lpVersionInformation,
+ lpReserved,
+ lParam);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // IsAvailableVersion()
+ //
+ // Get the SortGetHandle() function for the proper dll version.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ BOOL IsAvailableVersion(__in_opt CONST NLSVERSIONINFO * pVersion)
+ {
+ return GetSortGetHandle(VersionValue(pVersion)) != NULL;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // GetSortHandle()
+ //
+ // Get the SortHandle for the given locale and version
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ PSORTHANDLE GetSortHandle(__in LPCWSTR lpLocaleName, __in_opt CONST NLSVERSIONINFO * pVersion)
+ {
+ DWORD version = VersionValue(pVersion);
+ if (GetSortGetHandle(version) == NULL)
+ {
+ return NULL;
+ }
+ return GetSortNode(lpLocaleName, version);
+ }
+
+}
diff --git a/src/utilcode/splitpath.cpp b/src/utilcode/splitpath.cpp
new file mode 100644
index 0000000000..1be90a828d
--- /dev/null
+++ b/src/utilcode/splitpath.cpp
@@ -0,0 +1,281 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+/***
+*splitpath.c - break down path name into components
+*
+
+*
+*Purpose:
+* To provide support for accessing the individual components of an
+* arbitrary path name
+*
+*******************************************************************************/
+#include "stdafx.h"
+#include "winwrap.h"
+#include "utilcode.h"
+#include "sstring.h"
+
+
+/***
+*_splitpath() - split a path name into its individual components
+*
+*Purpose:
+* to split a path name into its individual components
+*
+*Entry:
+* path - pointer to path name to be parsed
+* drive - pointer to buffer for drive component, if any
+* dir - pointer to buffer for subdirectory component, if any
+* fname - pointer to buffer for file base name component, if any
+* ext - pointer to buffer for file name extension component, if any
+*
+*Exit:
+* drive - pointer to drive string. Includes ':' if a drive was given.
+* dir - pointer to subdirectory string. Includes leading and trailing
+* '/' or '\', if any.
+* fname - pointer to file base name
+* ext - pointer to file extension, if any. Includes leading '.'.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+void SplitPath(
+ register const WCHAR *path,
+ __inout_z __inout_ecount_opt(driveSizeInWords) WCHAR *drive, int driveSizeInWords,
+ __inout_z __inout_ecount_opt(dirSizeInWords) WCHAR *dir, int dirSizeInWords,
+ __inout_z __inout_ecount_opt(fnameSizeInWords) WCHAR *fname, size_t fnameSizeInWords,
+ __inout_z __inout_ecount_opt(extSizeInWords) WCHAR *ext, size_t extSizeInWords)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LPCWSTR _wszDrive, _wszDir, _wszFileName, _wszExt;
+ size_t _cchDrive, _cchDir, _cchFileName, _cchExt;
+
+ SplitPathInterior(path,
+ &_wszDrive, &_cchDrive,
+ &_wszDir, &_cchDir,
+ &_wszFileName, &_cchFileName,
+ &_wszExt, &_cchExt);
+
+ if (drive && _wszDrive)
+ wcsncpy_s(drive, driveSizeInWords, _wszDrive, min(_cchDrive, _MAX_DRIVE));
+
+ if (dir && _wszDir)
+ wcsncpy_s(dir, dirSizeInWords, _wszDir, min(_cchDir, _MAX_DIR));
+
+ if (fname && _wszFileName)
+ wcsncpy_s(fname, fnameSizeInWords, _wszFileName, min(_cchFileName, _MAX_FNAME));
+
+ if (ext && _wszExt)
+ wcsncpy_s(ext, extSizeInWords, _wszExt, min(_cchExt, _MAX_EXT));
+}
+
+//*******************************************************************************
+// A much more sensible version that just points to each section of the string.
+//*******************************************************************************
+void SplitPathInterior(
+ __in LPCWSTR wszPath,
+ __out_opt LPCWSTR *pwszDrive, __out_opt size_t *pcchDrive,
+ __out_opt LPCWSTR *pwszDir, __out_opt size_t *pcchDir,
+ __out_opt LPCWSTR *pwszFileName, __out_opt size_t *pcchFileName,
+ __out_opt LPCWSTR *pwszExt, __out_opt size_t *pcchExt)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Arguments must come in valid pairs
+ _ASSERTE(!!pwszDrive == !!pcchDrive);
+ _ASSERTE(!!pwszDir == !!pcchDir);
+ _ASSERTE(!!pwszFileName == !!pcchFileName);
+ _ASSERTE(!!pwszExt == !!pcchExt);
+
+ register WCHAR *p;
+ LPCWSTR last_slash = NULL, dot = NULL;
+
+ /* we assume that the path argument has the following form, where any
+ * or all of the components may be missing.
+ *
+ * <drive><dir><fname><ext>
+ *
+ * and each of the components has the following expected form(s)
+ *
+ * drive:
+ * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
+ * ':'
+ * dir:
+ * 0 to _MAX_DIR-1 characters in the form of an absolute path
+ * (leading '/' or '\') or relative path, the last of which, if
+ * any, must be a '/' or '\'. E.g -
+ * absolute path:
+ * \top\next\last\ ; or
+ * /top/next/last/
+ * relative path:
+ * top\next\last\ ; or
+ * top/next/last/
+ * Mixed use of '/' and '\' within a path is also tolerated
+ * fname:
+ * 0 to _MAX_FNAME-1 characters not including the '.' character
+ * ext:
+ * 0 to _MAX_EXT-1 characters where, if any, the first must be a
+ * '.'
+ *
+ */
+
+ /* extract drive letter and :, if any */
+
+ if ((wcslen(wszPath) >= (_MAX_DRIVE - 2)) && (*(wszPath + _MAX_DRIVE - 2) == _T(':'))) {
+ if (pwszDrive && pcchDrive) {
+ *pwszDrive = wszPath;
+ *pcchDrive = _MAX_DRIVE - 1;
+ }
+ wszPath += _MAX_DRIVE - 1;
+ }
+ else if (pwszDrive && pcchDrive) {
+ *pwszDrive = NULL;
+ *pcchDrive = 0;
+ }
+
+ /* extract path string, if any. Path now points to the first character
+ * of the path, if any, or the filename or extension, if no path was
+ * specified. Scan ahead for the last occurence, if any, of a '/' or
+ * '\' path separator character. If none is found, there is no path.
+ * We will also note the last '.' character found, if any, to aid in
+ * handling the extension.
+ */
+
+ for (last_slash = NULL, p = (WCHAR *)wszPath; *p; p++) {
+#ifdef _MBCS
+ if (_ISLEADBYTE (*p))
+ p++;
+ else {
+#endif /* _MBCS */
+ if (*p == _T('/') || *p == _T('\\'))
+ /* point to one beyond for later copy */
+ last_slash = p + 1;
+ else if (*p == _T('.'))
+ dot = p;
+#ifdef _MBCS
+ }
+#endif /* _MBCS */
+ }
+
+ if (last_slash) {
+ /* found a path - copy up through last_slash or max. characters
+ * allowed, whichever is smaller
+ */
+
+ if (pwszDir && pcchDir) {
+ *pwszDir = wszPath;
+ *pcchDir = last_slash - wszPath;
+ }
+ wszPath = last_slash;
+ }
+ else if (pwszDir && pcchDir) {
+ *pwszDir = NULL;
+ *pcchDir = 0;
+ }
+
+ /* extract file name and extension, if any. Path now points to the
+ * first character of the file name, if any, or the extension if no
+ * file name was given. Dot points to the '.' beginning the extension,
+ * if any.
+ */
+
+ if (dot && (dot >= wszPath)) {
+ /* found the marker for an extension - copy the file name up to
+ * the '.'.
+ */
+ if (pwszFileName && pcchFileName) {
+ *pwszFileName = wszPath;
+ *pcchFileName = dot - wszPath;
+ }
+ /* now we can get the extension - remember that p still points
+ * to the terminating nul character of path.
+ */
+ if (pwszExt && pcchExt) {
+ *pwszExt = dot;
+ *pcchExt = p - dot;
+ }
+ }
+ else {
+ /* found no extension, give empty extension and copy rest of
+ * string into fname.
+ */
+ if (pwszFileName && pcchFileName) {
+ *pwszFileName = wszPath;
+ *pcchFileName = p - wszPath;
+ }
+ if (pwszExt && pcchExt) {
+ *pwszExt = NULL;
+ *pcchExt = 0;
+ }
+ }
+}
+
+/***
+*_splitpath() - split a path name into its individual components
+*
+*Purpose:
+* to split a path name into its individual components
+*
+*Entry:
+* path - SString representing the path name to be parsed
+* drive - Out SString for drive component
+* dir - Out SString for subdirectory component
+* fname - Out SString for file base name component
+* ext - Out SString for file name extension component
+*
+*Exit:
+* drive - Drive string. Includes ':' if a drive was given.
+* dir - Subdirectory string. Includes leading and trailing
+* '/' or '\', if any.
+* fname - File base name
+* ext - File extension, if any. Includes leading '.'.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+void SplitPath(__in SString const &path,
+ __inout_opt SString *drive,
+ __inout_opt SString *dir,
+ __inout_opt SString *fname,
+ __inout_opt SString *ext)
+{
+ LPWSTR wzDrive = NULL;
+ if (drive != NULL)
+ wzDrive = drive->OpenUnicodeBuffer(_MAX_DRIVE);
+
+ LPWSTR wzDir = NULL;
+ if (dir != NULL)
+ wzDir = dir->OpenUnicodeBuffer(_MAX_DIR);
+
+ LPWSTR wzFname = NULL;
+ if (fname != NULL)
+ wzFname = fname->OpenUnicodeBuffer(_MAX_FNAME);
+
+ LPWSTR wzExt = NULL;
+ if (ext != NULL)
+ wzExt = ext->OpenUnicodeBuffer(_MAX_EXT);
+
+ SplitPath(path,
+ wzDrive, _MAX_DRIVE,
+ wzDir, _MAX_DIR,
+ wzFname, _MAX_FNAME,
+ wzExt, _MAX_EXT);
+
+ if (drive != NULL)
+ drive->CloseBuffer(static_cast<COUNT_T>(wcslen(wzDrive)));
+
+ if (dir != NULL)
+ dir->CloseBuffer(static_cast<COUNT_T>(wcslen(wzDir)));
+
+ if (fname != NULL)
+ fname->CloseBuffer(static_cast<COUNT_T>(wcslen(wzFname)));
+
+ if (ext != NULL)
+ ext->CloseBuffer(static_cast<COUNT_T>(wcslen(wzExt)));
+}
+
diff --git a/src/utilcode/sstring.cpp b/src/utilcode/sstring.cpp
new file mode 100644
index 0000000000..69321e72ef
--- /dev/null
+++ b/src/utilcode/sstring.cpp
@@ -0,0 +1,2871 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ---------------------------------------------------------------------------
+// SString.cpp
+//
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sstring.h"
+#include "ex.h"
+#include "holder.h"
+#include "newapis.h"
+
+
+#if defined(_MSC_VER)
+#pragma inline_depth (25)
+#endif
+
+//-----------------------------------------------------------------------------
+// Static variables
+//-----------------------------------------------------------------------------
+
+// Have one internal, well-known, literal for the empty string.
+const BYTE SString::s_EmptyBuffer[2] = { 0 };
+
+// @todo: these need to be initialized by calling GetACP() or GetConsoleOutputCP()
+// followed by a GetCPInfo to see if the max character size is 1.
+
+UINT SString::s_ACP = 0;
+SVAL_IMPL_INIT(BOOL, SString, s_IsANSIMultibyte, TRUE);
+
+#ifndef DACCESS_COMPILE
+static BYTE s_EmptySpace[sizeof(SString)] = { 0 };
+#endif // DACCESS_COMPILE
+
+#if FEATURE_USE_LCID
+const LocaleID SString::s_defaultLCID = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+#else
+const LocaleID SString::s_defaultLCID = NULL;
+#endif
+
+SPTR_IMPL(SString,SString,s_Empty);
+
+void SString::Startup()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (s_ACP == 0)
+ {
+ CPINFO info;
+
+ UINT ACP = GetACP();
+ if (GetCPInfo(ACP, &info) && info.MaxCharSize == 1)
+ s_IsANSIMultibyte = FALSE;
+
+#ifndef DACCESS_COMPILE
+ s_Empty = PTR_SString(new (s_EmptySpace) SString());
+ s_Empty->SetNormalized();
+#endif // DACCESS_COMPILE
+
+ MemoryBarrier();
+ s_ACP = ACP;
+ }
+}
+
+CHECK SString::CheckStartup()
+{
+ WRAPPER_NO_CONTRACT;
+
+ CHECK(s_Empty != NULL);
+ CHECK_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Case insensitive helpers.
+//-----------------------------------------------------------------------------
+
+static WCHAR MapChar(WCHAR wc, DWORD dwFlags, LocaleID lcid)
+{
+ WRAPPER_NO_CONTRACT;
+
+ WCHAR wTmp;
+
+#ifndef FEATURE_PAL
+
+#ifdef FEATURE_USE_LCID
+ int iRet = WszLCMapString(lcid, dwFlags, &wc, 1, &wTmp, 1);
+#else
+ // TODO: Uncertain if this is the best behavior. Caller should specify locale name
+ if (lcid == NULL || lcid[0]==W('!')) lcid = W("");
+ int iRet = NewApis::LCMapStringEx(lcid, dwFlags, &wc, 1, &wTmp, 1, NULL, NULL, 0);
+#endif
+ if (!iRet) {
+ // This can fail in non-exceptional cases becauseof unknown unicode characters.
+ wTmp = wc;
+ }
+
+#else // !FEATURE_PAL
+ // For PAL, no locale specific processing is done
+
+ if (dwFlags == LCMAP_UPPERCASE)
+ {
+ wTmp = toupper(wc);
+ }
+ else
+ {
+ _ASSERTE(dwFlags == LCMAP_LOWERCASE);
+ wTmp = tolower(wc);
+ }
+#endif // !FEATURE_PAL
+
+ return wTmp;
+}
+
+#define IS_UPPER_A_TO_Z(x) (((x) >= W('A')) && ((x) <= W('Z')))
+#define IS_LOWER_A_TO_Z(x) (((x) >= W('a')) && ((x) <= W('z')))
+#define CAN_SIMPLE_UPCASE(x) (((x)&~0x7f) == 0)
+#define CAN_SIMPLE_DOWNCASE(x) (((x)&~0x7f) == 0)
+#define SIMPLE_UPCASE(x) (IS_LOWER_A_TO_Z(x) ? ((x) - W('a') + W('A')) : (x))
+#define SIMPLE_DOWNCASE(x) (IS_UPPER_A_TO_Z(x) ? ((x) - W('A') + W('a')) : (x))
+
+/* static */
+int SString::CaseCompareHelper(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count, LocaleID lcid, BOOL stopOnNull, BOOL stopOnCount)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(stopOnNull || stopOnCount);
+
+ const WCHAR *buffer1End = buffer1 + count;
+ int diff = 0;
+
+ while (!stopOnCount || (buffer1 < buffer1End))
+ {
+ WCHAR ch1 = *buffer1++;
+ WCHAR ch2 = *buffer2++;
+ diff = ch1 - ch2;
+ if ((ch1 == 0) || (ch2 == 0))
+ {
+ if (diff != 0 || stopOnNull)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (diff != 0)
+ {
+ diff = ((CAN_SIMPLE_UPCASE(ch1) ? SIMPLE_UPCASE(ch1) : MapChar(ch1, LCMAP_UPPERCASE, lcid))
+ - (CAN_SIMPLE_UPCASE(ch2) ? SIMPLE_UPCASE(ch2) : MapChar(ch2, LCMAP_UPPERCASE, lcid)));
+ }
+ if (diff != 0)
+ {
+ break;
+ }
+ }
+ }
+
+ return diff;
+}
+
+#define IS_LOWER_A_TO_Z_ANSI(x) (((x) >= 'a') && ((x) <= 'z'))
+#define CAN_SIMPLE_UPCASE_ANSI(x) (((x) >= 0x20) && ((x) <= 0x7f))
+#define SIMPLE_UPCASE_ANSI(x) (IS_LOWER_A_TO_Z(x) ? ((x) - 'a' + 'A') : (x))
+
+// TODO: Need to get rid of LocaleID and use a LPCWSTR locale name instead.
+int GetCaseInsensitiveValueA(LocaleID lcid, const CHAR *buffer, int length) {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(buffer != NULL);
+ _ASSERTE(length == 1 || ((length == 2) && IsDBCSLeadByte(*buffer)));
+
+ WCHAR wideCh;
+ int sortValue;
+ int conversionReturn = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, buffer, length, &wideCh, 1);
+ if (conversionReturn == 0)
+ {
+ // An invalid sequence should only compare equal to itself, so use a negative mapping.
+ if (length == 1)
+ {
+ sortValue = -((int)((unsigned char)(*buffer)));
+ }
+ else
+ {
+ sortValue = -(((((int)((unsigned char)(*buffer))) << 8) | ((int)((unsigned char)(*(buffer + 1))))));
+ }
+ }
+ else
+ {
+ _ASSERTE(conversionReturn == 1);
+ sortValue = MapChar(wideCh, LCMAP_UPPERCASE, lcid);
+ }
+ return sortValue;
+}
+
+/* static */
+int SString::CaseCompareHelperA(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count, LocaleID lcid, BOOL stopOnNull, BOOL stopOnCount)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(stopOnNull || stopOnCount);
+
+ const CHAR *buffer1End = buffer1 + count;
+ int diff = 0;
+
+ while (!stopOnCount || (buffer1 < buffer1End))
+ {
+ CHAR ch1 = *buffer1;
+ CHAR ch2 = *buffer2;
+ if ((ch1 == 0) || (ch2 == 0))
+ {
+ diff = ch1 - ch2;
+ if (diff != 0 || stopOnNull)
+ {
+ break;
+ }
+ buffer1++;
+ buffer2++;
+ }
+ else if (CAN_SIMPLE_UPCASE_ANSI(ch1) && CAN_SIMPLE_UPCASE_ANSI(ch2))
+ {
+ diff = ch1 - ch2;
+ if (diff != 0)
+ {
+ diff = (SIMPLE_UPCASE_ANSI(ch1) - SIMPLE_UPCASE_ANSI(ch2));
+ if (diff != 0)
+ {
+ break;
+ }
+ }
+ buffer1++;
+ buffer2++;
+ }
+ else
+ {
+ int length = 1;
+ if (s_IsANSIMultibyte
+ && IsDBCSLeadByte(ch1)
+ && IsDBCSLeadByte(ch2)
+ && (!stopOnCount || ((buffer1 + 1) < buffer1End)))
+ {
+ length = 2;
+ }
+ int sortValue1 = GetCaseInsensitiveValueA(lcid, buffer1, length);
+ int sortValue2 = GetCaseInsensitiveValueA(lcid, buffer2, length);
+ diff = sortValue1 - sortValue2;
+ if (diff != 0)
+ {
+ break;
+ }
+ buffer1 += length;
+ buffer2 += length;
+ }
+ }
+ return diff;
+}
+
+
+int CaseHashHelper(const WCHAR *buffer, COUNT_T count, LocaleID lcid)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ const WCHAR *bufferEnd = buffer + count;
+ ULONG hash = 5381;
+
+ while (buffer < bufferEnd)
+ {
+ WCHAR ch = *buffer++;
+ ch = CAN_SIMPLE_UPCASE(ch) ? SIMPLE_UPCASE(ch) : MapChar(ch, LCMAP_UPPERCASE, lcid);
+
+ hash = (((hash << 5) + hash) ^ ch);
+ }
+
+ return hash;
+}
+
+static int CaseHashHelperA(const CHAR *buffer, COUNT_T count)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ const CHAR *bufferEnd = buffer + count;
+ ULONG hash = 5381;
+
+ while (buffer < bufferEnd)
+ {
+ CHAR ch = *buffer++;
+ ch = SIMPLE_UPCASE_ANSI(ch);
+
+ hash = (((hash << 5) + hash) ^ ch);
+ }
+
+ return hash;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the unicode string
+//-----------------------------------------------------------------------------
+void SString::Set(const WCHAR *string)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) wcslen(string), REPRESENTATION_UNICODE);
+ wcscpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// unicode string.
+//-----------------------------------------------------------------------------
+void SString::Set(const WCHAR *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_UNICODE);
+ wcsncpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawUnicode()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a point to the first count characters of the given
+// preallocated unicode string (shallow copy).
+//-----------------------------------------------------------------------------
+void SString::SetPreallocated(const WCHAR *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ SS_POSTCONDITION(IsEmpty());
+ GC_NOTRIGGER;
+ NOTHROW;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ SetImmutable();
+ SetImmutable((BYTE*) string, count*2);
+ ClearAllocated();
+ SetRepresentation(REPRESENTATION_UNICODE);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the given ansi string
+//-----------------------------------------------------------------------------
+void SString::SetASCII(const ASCII *string)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckASCIIString(string));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) strlen(string), REPRESENTATION_ASCII);
+ strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// ascii string
+//-----------------------------------------------------------------------------
+void SString::SetASCII(const ASCII *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckASCIIString(string, count));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_ASCII);
+ strncpy_s(GetRawASCII(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawASCII()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the given UTF8 string
+//-----------------------------------------------------------------------------
+void SString::SetUTF8(const UTF8 *string)
+{
+ SS_CONTRACT_VOID
+ {
+ // !!! Check for illegal UTF8 encoding?
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) strlen(string), REPRESENTATION_UTF8);
+ strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// UTF8 string.
+//-----------------------------------------------------------------------------
+void SString::SetUTF8(const UTF8 *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ // !!! Check for illegal UTF8 encoding?
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_UTF8);
+ strncpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawUTF8()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the given ANSI string
+//-----------------------------------------------------------------------------
+void SString::SetANSI(const ANSI *string)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) strlen(string), REPRESENTATION_ANSI);
+ strcpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// ANSI string.
+//-----------------------------------------------------------------------------
+void SString::SetANSI(const ANSI *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_ANSI);
+ strncpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawANSI()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the given unicode character
+//-----------------------------------------------------------------------------
+void SString::Set(WCHAR character)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ if (character == 0)
+ Clear();
+ else
+ {
+ Resize(1, REPRESENTATION_UNICODE);
+ GetRawUnicode()[0] = character;
+ GetRawUnicode()[1] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the given UTF8 character
+//-----------------------------------------------------------------------------
+void SString::SetUTF8(CHAR character)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (character == 0)
+ Clear();
+ else
+ {
+ Resize(1, REPRESENTATION_UTF8);
+ GetRawUTF8()[0] = character;
+ GetRawUTF8()[1] = 0;
+ }
+
+ SS_RETURN;
+}
+
+
+//-----------------------------------------------------------------------------
+// Set this string to the given ansi literal.
+// This will share the memory and not make a copy.
+//-----------------------------------------------------------------------------
+void SString::SetLiteral(const ASCII *literal)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ PRECONDITION(CheckASCIIString(literal));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SString s(Literal, literal);
+ Set(s);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the given unicode literal.
+// This will share the memory and not make a copy.
+//-----------------------------------------------------------------------------
+void SString::SetLiteral(const WCHAR *literal)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SString s(Literal, literal);
+ Set(s);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Hash the string contents
+//-----------------------------------------------------------------------------
+ULONG SString::Hash() const
+{
+ SS_CONTRACT(ULONG)
+ {
+ INSTANCE_CHECK;
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ SS_RETURN HashString(GetRawUnicode());
+}
+
+//-----------------------------------------------------------------------------
+// Hash the string contents
+//-----------------------------------------------------------------------------
+ULONG SString::HashCaseInsensitive(LocaleID lcid) const
+{
+ SS_CONTRACT(ULONG)
+ {
+ INSTANCE_CHECK;
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToIteratable();
+
+ ULONG result;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_EMPTY:
+ result = CaseHashHelper(GetRawUnicode(), GetRawCount(), lcid);
+ break;
+
+ case REPRESENTATION_ASCII:
+ result = CaseHashHelperA(GetRawASCII(), GetRawCount());
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ SS_RETURN result;
+}
+
+//-----------------------------------------------------------------------------
+// Truncate this string to count characters.
+//-----------------------------------------------------------------------------
+void SString::Truncate(const Iterator &i)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ SS_POSTCONDITION(GetRawCount() == i - Begin());
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ CONSISTENCY_CHECK(IsFixedSize());
+
+ COUNT_T size = i - Begin();
+
+ Resize(size, GetRepresentation(), PRESERVE);
+
+ i.Resync(this, (BYTE *) (GetRawUnicode() + size));
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the ASCII representation for this String to Unicode. We can do this
+// quickly and in-place (if this == &dest), which is why it is optimized.
+//-----------------------------------------------------------------------------
+void SString::ConvertASCIIToUnicode(SString &dest) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(IsRepresentation(REPRESENTATION_ASCII));
+ POSTCONDITION(dest.IsRepresentation(REPRESENTATION_UNICODE));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ // Handle the empty case.
+ if (IsEmpty())
+ {
+ dest.Clear();
+ RETURN;
+ }
+
+ CONSISTENCY_CHECK(CheckPointer(GetRawASCII()));
+ CONSISTENCY_CHECK(GetRawCount() > 0);
+
+ // If dest is the same as this, then we need to preserve on resize.
+ dest.Resize(GetRawCount(), REPRESENTATION_UNICODE,
+ this == &dest ? PRESERVE : DONT_PRESERVE);
+
+ // Make sure the buffer is big enough.
+ CONSISTENCY_CHECK(dest.GetAllocation() > (GetRawCount() * sizeof(WCHAR)));
+
+ // This is a poor man's widen. Since we know that the representation is ASCII,
+ // we can just pad the string with a bunch of zero-value bytes. Of course,
+ // we move from the end of the string to the start so that we can convert in
+ // place (in the case that &dest == this).
+ WCHAR *outBuf = dest.GetRawUnicode() + dest.GetRawCount();
+ ASCII *inBuf = GetRawASCII() + GetRawCount();
+
+ while (GetRawASCII() <= inBuf)
+ {
+ CONSISTENCY_CHECK(dest.GetRawUnicode() <= outBuf);
+ // The casting zero-extends the value, thus giving us the zero-valued byte.
+ *outBuf = (WCHAR) *inBuf;
+ outBuf--;
+ inBuf--;
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the internal representation for this String to Unicode.
+//-----------------------------------------------------------------------------
+void SString::ConvertToUnicode() const
+{
+ CONTRACT_VOID
+ {
+ POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (!IsRepresentation(REPRESENTATION_UNICODE))
+ {
+ if (IsRepresentation(REPRESENTATION_ASCII))
+ {
+ ConvertASCIIToUnicode(*(const_cast<SString *>(this)));
+ }
+ else
+ {
+ StackSString s;
+ ConvertToUnicode(s);
+ PREFIX_ASSUME(!s.IsImmutable());
+ (const_cast<SString*>(this))->Set(s);
+ }
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the internal representation for this String to Unicode, while
+// preserving the iterator if the conversion is done.
+//-----------------------------------------------------------------------------
+void SString::ConvertToUnicode(const CIterator &i) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(i.Check());
+ POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (!IsRepresentation(REPRESENTATION_UNICODE))
+ {
+ CONSISTENCY_CHECK(IsFixedSize());
+
+ COUNT_T index = 0;
+ // Get the current index of the iterator
+ if (i.m_ptr != NULL)
+ {
+ CONSISTENCY_CHECK(GetCharacterSizeShift() == 0);
+ index = (COUNT_T) (i.m_ptr - m_buffer);
+ }
+
+ if (IsRepresentation(REPRESENTATION_ASCII))
+ {
+ ConvertASCIIToUnicode(*(const_cast<SString *>(this)));
+ }
+ else
+ {
+ StackSString s;
+ ConvertToUnicode(s);
+ (const_cast<SString*>(this))->Set(s);
+ }
+
+ // Move the iterator to the new location.
+ if (i.m_ptr != NULL)
+ {
+ i.Resync(this, (BYTE *) (GetRawUnicode() + index));
+ }
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set s to be a copy of this string's contents, but in the unicode format.
+//-----------------------------------------------------------------------------
+void SString::ConvertToUnicode(SString &s) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(s.Check());
+ POSTCONDITION(s.IsRepresentation(REPRESENTATION_UNICODE));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ int page = 0;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ s.Clear();
+ RETURN;
+
+ case REPRESENTATION_UNICODE:
+ s.Set(*this);
+ RETURN;
+
+ case REPRESENTATION_UTF8:
+ page = CP_UTF8;
+ break;
+
+ case REPRESENTATION_ASCII:
+ ConvertASCIIToUnicode(s);
+ RETURN;
+
+ case REPRESENTATION_ANSI:
+ page = CP_ACP;
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ COUNT_T length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, 0, 0);
+ if (length == 0)
+ ThrowLastError();
+
+ s.Resize(length-1, REPRESENTATION_UNICODE);
+
+ length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, s.GetRawUnicode(), length);
+ if (length == 0)
+ ThrowLastError();
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set s to be a copy of this string's contents, but in the ANSI format.
+//-----------------------------------------------------------------------------
+void SString::ConvertToANSI(SString &s) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(s.Check());
+ POSTCONDITION(s.IsRepresentation(REPRESENTATION_ANSI));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ s.Clear();
+ RETURN;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ s.Set(*this);
+ RETURN;
+
+ case REPRESENTATION_UTF8:
+ // No direct conversion to ANSI
+ ConvertToUnicode();
+ // fall through
+
+ case REPRESENTATION_UNICODE:
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // @todo: use WC_NO_BEST_FIT_CHARS
+ COUNT_T length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1,
+ NULL, 0, NULL, NULL);
+
+ s.Resize(length-1, REPRESENTATION_ANSI);
+
+ // @todo: use WC_NO_BEST_FIT_CHARS
+ length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1,
+ s.GetRawANSI(), length, NULL, NULL);
+ if (length == 0)
+ ThrowLastError();
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set s to be a copy of this string's contents, but in the utf8 format.
+//-----------------------------------------------------------------------------
+COUNT_T SString::ConvertToUTF8(SString &s) const
+{
+ CONTRACT(COUNT_T)
+ {
+ PRECONDITION(s.Check());
+ POSTCONDITION(s.IsRepresentation(REPRESENTATION_UTF8));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ s.Clear();
+ RETURN 1;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_UTF8:
+ s.Set(*this);
+ RETURN s.GetRawCount()+1;
+
+ case REPRESENTATION_ANSI:
+ // No direct conversion from ANSI to UTF8
+ ConvertToUnicode();
+ // fall through
+
+ case REPRESENTATION_UNICODE:
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // <TODO> @todo: use WC_NO_BEST_FIT_CHARS </TODO>
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Unicode_Utf8_Length(GetRawUnicode(), & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ s.Resize(length, REPRESENTATION_UTF8);
+
+ //FString::Unicode_Utf8 expects an array all the time
+ //we optimize the empty string by replacing it with null for SString above in Resize
+ if (length > 0)
+ {
+ hr = FString::Unicode_Utf8(GetRawUnicode(), allAscii, (LPSTR) s.GetRawUTF8(), length);
+ }
+ }
+
+ IfFailThrow(hr);
+
+ RETURN length + 1;
+}
+
+//-----------------------------------------------------------------------------
+// Replace a single character with another character.
+//-----------------------------------------------------------------------------
+void SString::Replace(const Iterator &i, WCHAR c)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, 1));
+ POSTCONDITION(Match(i, c));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (IsRepresentation(REPRESENTATION_ASCII) && ((c&~0x7f) == 0))
+ {
+ *(BYTE*)i.m_ptr = (BYTE) c;
+ }
+ else
+ {
+ ConvertToUnicode(i);
+
+ *(USHORT*)i.m_ptr = c;
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Replace the substring specified by position, length with the given string s.
+//-----------------------------------------------------------------------------
+void SString::Replace(const Iterator &i, COUNT_T length, const SString &s)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, length));
+ PRECONDITION(s.Check());
+ POSTCONDITION(Match(i, s));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ Representation representation = GetRepresentation();
+ if (representation == REPRESENTATION_EMPTY)
+ {
+ // This special case contains some optimizations (like literal sharing).
+ Set(s);
+ ConvertToIteratable();
+ i.Resync(this, m_buffer);
+ }
+ else
+ {
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ COUNT_T deleteSize = length<<GetCharacterSizeShift();
+ COUNT_T insertSize = source.GetRawCount()<<source.GetCharacterSizeShift();
+
+ SBuffer::Replace(i, deleteSize, insertSize);
+ SBuffer::Copy(i, source.m_buffer, insertSize);
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string starting at i. Return TRUE & update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::Find(CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ POSTCONDITION(RETVAL == Match(i, s));
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string from s
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ COUNT_T count = source.GetRawCount();
+ const WCHAR *start = i.GetUnicode();
+ const WCHAR *end = GetUnicode() + GetRawCount() - count;
+ while (start <= end)
+ {
+ if (wcsncmp(start, source.GetRawUnicode(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ COUNT_T count = source.GetRawCount();
+ const CHAR *start = i.GetASCII();
+ const CHAR *end = GetRawASCII() + GetRawCount() - count;
+ while (start <= end)
+ {
+ if (strncmp(start, source.GetRawASCII(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ {
+ if (source.GetRawCount() == 0)
+ RETURN TRUE;
+ }
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string starting at i. Return TRUE & update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::Find(CIterator &i, WCHAR c) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ POSTCONDITION(RETVAL == Match(i, c));
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string
+ if (c & ~0x7f)
+ ConvertToUnicode(i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ const WCHAR *start = i.GetUnicode();
+ const WCHAR *end = GetUnicode() + GetRawCount() - 1;
+ while (start <= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ const CHAR *start = i.GetASCII();
+ const CHAR *end = GetRawASCII() + GetRawCount() - 1;
+ while (start <= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string, working backwards staring at i.
+// Return TRUE and update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::FindBack(CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ POSTCONDITION(RETVAL == Match(i, s));
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string from s
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ COUNT_T count = source.GetRawCount();
+ const WCHAR *start = GetRawUnicode() + GetRawCount() - count;
+ if (start > i.GetUnicode())
+ start = i.GetUnicode();
+ const WCHAR *end = GetRawUnicode();
+
+ while (start >= end)
+ {
+ if (wcsncmp(start, source.GetRawUnicode(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ COUNT_T count = source.GetRawCount();
+ const CHAR *start = GetRawASCII() + GetRawCount() - count;
+ if (start > i.GetASCII())
+ start = i.GetASCII();
+ const CHAR *end = GetRawASCII();
+
+ while (start >= end)
+ {
+ if (strncmp(start, source.GetRawASCII(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ {
+ if (source.GetRawCount() == 0)
+ RETURN TRUE;
+ }
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string, working backwards staring at i.
+// Return TRUE and update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::FindBack(CIterator &i, WCHAR c) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ POSTCONDITION(RETVAL == Match(i, c));
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string from s
+ if (c & ~0x7f)
+ ConvertToUnicode(i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ const WCHAR *start = GetRawUnicode() + GetRawCount() - 1;
+ if (start > i.GetUnicode())
+ start = i.GetUnicode();
+ const WCHAR *end = GetRawUnicode();
+
+ while (start >= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ const CHAR *start = GetRawASCII() + GetRawCount() - 1;
+ if (start > i.GetASCII())
+ start = i.GetASCII();
+ const CHAR *end = GetRawASCII();
+
+ while (start >= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string begins with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::BeginsWith(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return Match(Begin(), s);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string begins with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::BeginsWithCaseInsensitive(const SString &s, LocaleID locale) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return MatchCaseInsensitive(Begin(), s, locale);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string begins with the contents of s. Invariant locale.
+//-----------------------------------------------------------------------------
+BOOL SString::BeginsWithCaseInsensitive(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return BeginsWithCaseInsensitive(s, s_defaultLCID);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string ends with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::EndsWith(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Need this check due to iterator arithmetic below.
+ if (GetCount() < s.GetCount())
+ {
+ return FALSE;
+ }
+
+ return Match(End() - s.GetCount(), s);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string ends with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::EndsWithCaseInsensitive(const SString &s, LocaleID locale) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Need this check due to iterator arithmetic below.
+ if (GetCount() < s.GetCount())
+ {
+ return FALSE;
+ }
+
+ return MatchCaseInsensitive(End() - s.GetCount(), s, locale);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string ends with the contents of s. Invariant locale.
+//-----------------------------------------------------------------------------
+BOOL SString::EndsWithCaseInsensitive(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return EndsWithCaseInsensitive(s, s_defaultLCID);
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents to s's contents.
+// The comparison does not take into account localization issues like case folding.
+// Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp).
+//-----------------------------------------------------------------------------
+int SString::Compare(const SString &s) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T smaller;
+ int equals = 0;
+ int result = 0;
+
+ if (GetRawCount() < source.GetRawCount())
+ {
+ smaller = GetRawCount();
+ equals = -1;
+ }
+ else if (GetRawCount() > source.GetRawCount())
+ {
+ smaller = source.GetRawCount();
+ equals = 1;
+ }
+ else
+ {
+ smaller = GetRawCount();
+ equals = 0;
+ }
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ result = wcsncmp(GetRawUnicode(), source.GetRawUnicode(), smaller);
+ break;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ result = strncmp(GetRawASCII(), source.GetRawASCII(), smaller);
+ break;
+
+ case REPRESENTATION_EMPTY:
+ result = 0;
+ break;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ if (result == 0)
+ RETURN equals;
+ else
+ RETURN result;
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents to s's contents.
+// Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp).
+//-----------------------------------------------------------------------------
+
+int SString::CompareCaseInsensitive(const SString &s, LocaleID lcid) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T smaller;
+ int equals = 0;
+ int result = 0;
+
+ if (GetRawCount() < source.GetRawCount())
+ {
+ smaller = GetRawCount();
+ equals = -1;
+ }
+ else if (GetRawCount() > source.GetRawCount())
+ {
+ smaller = source.GetRawCount();
+ equals = 1;
+ }
+ else
+ {
+ smaller = GetRawCount();
+ equals = 0;
+ }
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ result = CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), smaller, lcid, FALSE, TRUE);
+ break;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ result = CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), smaller, lcid, FALSE, TRUE);
+ break;
+
+ case REPRESENTATION_EMPTY:
+ result = 0;
+ break;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ if (result == 0)
+ RETURN equals;
+ else
+ RETURN result;
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents to s's contents.
+// The comparison does not take into account localization issues like case folding.
+// Return 1 if equal, 0 if not.
+//-----------------------------------------------------------------------------
+BOOL SString::Equals(const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory());
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T count = GetRawCount();
+
+ if (count != source.GetRawCount())
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ RETURN (wcsncmp(GetRawUnicode(), source.GetRawUnicode(), count) == 0);
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ RETURN (strncmp(GetRawASCII(), source.GetRawASCII(), count) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents case insensitively to s's contents.
+// Return 1 if equal, 0 if not.
+//-----------------------------------------------------------------------------
+BOOL SString::EqualsCaseInsensitive(const SString &s, LocaleID lcid) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory());
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T count = GetRawCount();
+
+ if (count != source.GetRawCount())
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ RETURN (CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), count, lcid, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ RETURN (CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), count, lcid, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare s's contents to the substring starting at position
+// The comparison does not take into account localization issues like case folding.
+// Return TRUE if equal, FALSE if not
+//-----------------------------------------------------------------------------
+BOOL SString::Match(const CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ COUNT_T remaining = End() - i;
+ COUNT_T count = source.GetRawCount();
+
+ if (remaining < count)
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ RETURN (wcsncmp(i.GetUnicode(), source.GetRawUnicode(), count) == 0);
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ RETURN (strncmp(i.GetASCII(), source.GetRawASCII(), count) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare s's contents case insensitively to the substring starting at position
+// Return TRUE if equal, FALSE if not
+//-----------------------------------------------------------------------------
+BOOL SString::MatchCaseInsensitive(const CIterator &i, const SString &s, LocaleID lcid) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ COUNT_T remaining = End() - i;
+ COUNT_T count = source.GetRawCount();
+
+ if (remaining < count)
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_ANSI:
+ RETURN (CaseCompareHelper(i.GetUnicode(), source.GetRawUnicode(), count, lcid, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_ASCII:
+ RETURN (CaseCompareHelperA(i.GetASCII(), source.GetRawASCII(), count, lcid, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare c case insensitively to the character at position
+// Return TRUE if equal, FALSE if not
+//-----------------------------------------------------------------------------
+BOOL SString::MatchCaseInsensitive(const CIterator &i, WCHAR c, LocaleID lcid) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ NOTHROW;
+ }
+ SS_CONTRACT_END;
+
+ // End() will not throw here
+ CONTRACT_VIOLATION(ThrowsViolation);
+ if (i >= End())
+ SS_RETURN FALSE;
+
+ WCHAR test = i[0];
+
+ SS_RETURN (test == c
+ || ((CAN_SIMPLE_UPCASE(test) ? SIMPLE_UPCASE(test) : MapChar(test, LCMAP_UPPERCASE, lcid))
+ == (CAN_SIMPLE_UPCASE(c) ? SIMPLE_UPCASE(c) : MapChar(c, LCMAP_UPPERCASE, lcid))));
+}
+
+//-----------------------------------------------------------------------------
+// Convert string to unicode lowercase using the invariant culture
+// Note: Please don't use it in PATH as multiple character can map to the same
+// lower case symbol
+//-----------------------------------------------------------------------------
+void SString::LowerCase()
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch)
+ {
+ *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE, s_defaultLCID));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Convert null-terminated string to lowercase using the invariant culture
+//-----------------------------------------------------------------------------
+//static
+void SString::LowerCase(__inout_z LPWSTR wszString)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ if (wszString == NULL)
+ {
+ return;
+ }
+
+ for (WCHAR * pwch = wszString; *pwch != '\0'; ++pwch)
+ {
+ *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE, s_defaultLCID));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Convert string to unicode uppercase using the invariant culture
+// Note: Please don't use it in PATH as multiple character can map to the same
+// upper case symbol
+//-----------------------------------------------------------------------------
+void SString::UpperCase()
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch)
+ {
+ *pwch = (CAN_SIMPLE_UPCASE(*pwch) ? SIMPLE_UPCASE(*pwch) : MapChar(*pwch, LCMAP_UPPERCASE, s_defaultLCID));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Get a const pointer to the internal buffer as an ANSI string.
+//-----------------------------------------------------------------------------
+const CHAR *SString::GetANSI(AbstractScratchBuffer &scratch) const
+{
+ SS_CONTRACT(const CHAR *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (this == NULL)
+ SS_RETURN NULL;
+
+ if (IsRepresentation(REPRESENTATION_ANSI))
+ SS_RETURN GetRawANSI();
+
+ ConvertToANSI((SString&)scratch);
+ SS_RETURN ((SString&)scratch).GetRawANSI();
+}
+
+//-----------------------------------------------------------------------------
+// Get a const pointer to the internal buffer as a UTF8 string.
+//-----------------------------------------------------------------------------
+const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch) const
+{
+ CONTRACT(const UTF8 *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (this == NULL)
+ RETURN NULL;
+
+ if (IsRepresentation(REPRESENTATION_UTF8))
+ RETURN GetRawUTF8();
+
+ ConvertToUTF8((SString&)scratch);
+ RETURN ((SString&)scratch).GetRawUTF8();
+}
+
+const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch, COUNT_T *pcbUtf8) const
+{
+ CONTRACT(const UTF8 *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (this == NULL)
+ RETURN NULL;
+
+ if (IsRepresentation(REPRESENTATION_UTF8))
+ {
+ *pcbUtf8 = GetRawCount() + 1;
+ RETURN GetRawUTF8();
+ }
+
+ *pcbUtf8 = ConvertToUTF8((SString&)scratch);
+ RETURN ((SString&)scratch).GetRawUTF8();
+}
+
+//-----------------------------------------------------------------------------
+// Get a const pointer to the internal buffer which must already be a UTF8 string.
+// This avoids the need to create a scratch buffer we know will never be used.
+//-----------------------------------------------------------------------------
+const UTF8 *SString::GetUTF8NoConvert() const
+{
+ CONTRACT(const UTF8 *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (this == NULL)
+ RETURN NULL;
+
+ if (IsRepresentation(REPRESENTATION_UTF8))
+ RETURN GetRawUTF8();
+
+ ThrowHR(E_INVALIDARG);
+}
+
+//-----------------------------------------------------------------------------
+// Safe version of sprintf.
+// Prints formatted ansi text w/ var args to this buffer.
+//-----------------------------------------------------------------------------
+void SString::Printf(const CHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ VPrintf(format, args);
+ va_end(args);
+}
+
+#ifdef _DEBUG
+//
+// Check the Printf use for potential globalization bugs. %S formatting
+// specifier does Unicode->Ansi or Ansi->Unicode conversion using current
+// C-locale. This almost always means globalization bug in the CLR codebase.
+//
+// Ideally, we would elimitate %S from all format strings. Unfortunately,
+// %S is too widespread in non-shipping code that such cleanup is not feasible.
+//
+static void CheckForFormatStringGlobalizationIssues(const SString &format, const SString &result)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ DEBUG_ONLY;
+ }
+ CONTRACTL_END;
+
+ BOOL fDangerousFormat = FALSE;
+
+ // Check whether the format string contains the %S formatting specifier
+ SString::CIterator itrFormat = format.Begin();
+ while (*itrFormat)
+ {
+ if (*itrFormat++ == '%')
+ {
+ // <TODO>Handle the complex format strings like %blahS</TODO>
+ if (*itrFormat++ == 'S')
+ {
+ fDangerousFormat = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (fDangerousFormat)
+ {
+ BOOL fNonAsciiUsed = FALSE;
+
+ // Now check whether there are any non-ASCII characters in the output.
+
+ // Check whether the result contains non-Ascii characters
+ SString::CIterator itrResult = format.Begin();
+ while (*itrResult)
+ {
+ if (*itrResult++ > 127)
+ {
+ fNonAsciiUsed = TRUE;
+ break;
+ }
+ }
+
+ CONSISTENCY_CHECK_MSGF(!fNonAsciiUsed,
+ ("Non-ASCII string was produced by %%S format specifier. This is likely globalization bug."
+ "To fix this, change the format string to %%s and do the correct encoding at the Printf callsite"));
+ }
+}
+#endif
+
+#ifndef EBADF
+#define EBADF 9
+#endif
+
+#ifndef ENOMEM
+#define ENOMEM 12
+#endif
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+void SString::VPrintf(const CHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // sprintf gives us no means to know how many characters are written
+ // other than guessing and trying
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use the existing buffer
+ int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, args);
+
+ if (result >=0)
+ {
+ // Succeeded in writing. Now resize -
+ Resize(result, REPRESENTATION_ANSI, PRESERVE);
+ SString sss(Ansi, format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+ }
+
+ // Make a guess how long the result will be (note this will be doubled)
+
+ COUNT_T guess = (COUNT_T) strlen(format)+1;
+ if (guess < GetRawCount())
+ guess = GetRawCount();
+ if (guess < MINIMUM_GUESS)
+ guess = MINIMUM_GUESS;
+
+ while (TRUE)
+ {
+ // Double the previous guess - eventually we will get enough space
+ guess *= 2;
+ Resize(guess, REPRESENTATION_ANSI);
+
+ // Clear errno to avoid false alarms
+ errno = 0;
+
+ int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, args);
+
+ if (result >= 0)
+ {
+ // Succeed in writing. Shrink the buffer to fit exactly.
+ Resize(result, REPRESENTATION_ANSI, PRESERVE);
+ SString sss(Ansi, format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+
+ if (errno==ENOMEM)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ if (errno!=0 && errno!=EBADF && errno!=ERANGE)
+ {
+ CONSISTENCY_CHECK_MSG(FALSE, "_vsnprintf failed. Potential globalization bug.");
+ ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
+ }
+ }
+ RETURN;
+}
+
+void SString::Printf(const WCHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ VPrintf(format, args);
+ va_end(args);
+}
+
+void SString::PPrintf(const WCHAR *format, ...)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ va_list argItr;
+ va_start(argItr, format);
+ PVPrintf(format, argItr);
+ va_end(argItr);
+
+ RETURN;
+}
+
+void SString::VPrintf(const WCHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // sprintf gives us no means to know how many characters are written
+ // other than guessing and trying
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use the existing buffer
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args);
+
+ if (result >= 0)
+ {
+ // succeeded
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+ }
+
+ // Make a guess how long the result will be (note this will be doubled)
+
+ COUNT_T guess = (COUNT_T) wcslen(format)+1;
+ if (guess < GetRawCount())
+ guess = GetRawCount();
+ if (guess < MINIMUM_GUESS)
+ guess = MINIMUM_GUESS;
+
+ while (TRUE)
+ {
+ // Double the previous guess - eventually we will get enough space
+ guess *= 2;
+ Resize(guess, REPRESENTATION_UNICODE);
+
+ // Clear errno to avoid false alarms
+ errno = 0;
+
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args);
+
+ if (result >= 0)
+ {
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+
+ if (errno==ENOMEM)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ if (errno!=0 && errno!=EBADF && errno!=ERANGE)
+ {
+ CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf failed. Potential globalization bug.");
+ ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
+ }
+ }
+ RETURN;
+}
+
+void SString::PVPrintf(const WCHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // sprintf gives us no means to know how many characters are written
+ // other than guessing and trying
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use the existing buffer
+#if defined(FEATURE_CORESYSTEM)
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args);
+#else
+ int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, args);
+#endif
+ if (result >= 0)
+ {
+ // succeeded
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+ }
+
+ // Make a guess how long the result will be (note this will be doubled)
+
+ COUNT_T guess = (COUNT_T) wcslen(format)+1;
+ if (guess < GetRawCount())
+ guess = GetRawCount();
+ if (guess < MINIMUM_GUESS)
+ guess = MINIMUM_GUESS;
+
+ while (TRUE)
+ {
+ // Double the previous guess - eventually we will get enough space
+ guess *= 2;
+ Resize(guess, REPRESENTATION_UNICODE, DONT_PRESERVE);
+
+ // Clear errno to avoid false alarms
+ errno = 0;
+
+#if defined(FEATURE_CORESYSTEM)
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, args);
+#else
+ int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, args);
+#endif
+
+ if (result >= 0)
+ {
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+
+ if (errno==ENOMEM)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ if (errno!=0 && errno!=EBADF && errno!=ERANGE)
+ {
+ CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf failed. Potential globalization bug.");
+ ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
+ }
+ }
+ RETURN;
+}
+
+void SString::AppendPrintf(const CHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ AppendVPrintf(format, args);
+ va_end(args);
+}
+
+void SString::AppendVPrintf(const CHAR *format, va_list args)
+{
+ WRAPPER_NO_CONTRACT;
+
+ StackSString s;
+ s.VPrintf(format, args);
+ Append(s);
+}
+
+void SString::AppendPrintf(const WCHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ AppendVPrintf(format, args);
+ va_end(args);
+}
+
+void SString::AppendVPrintf(const WCHAR *format, va_list args)
+{
+ WRAPPER_NO_CONTRACT;
+
+ StackSString s;
+ s.VPrintf(format, args);
+ Append(s);
+}
+
+//----------------------------------------------------------------------------
+// LoadResource - moved to sstring_com.cpp
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Format the message and put the contents in this string
+//----------------------------------------------------------------------------
+
+BOOL SString::FormatMessage(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId,
+ const SString &arg1, const SString &arg2,
+ const SString &arg3, const SString &arg4,
+ const SString &arg5, const SString &arg6,
+ const SString &arg7, const SString &arg8,
+ const SString &arg9, const SString &arg10)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ const WCHAR *args[] = {arg1.GetUnicode(), arg2.GetUnicode(), arg3.GetUnicode(), arg4.GetUnicode(),
+ arg5.GetUnicode(), arg6.GetUnicode(), arg7.GetUnicode(), arg8.GetUnicode(),
+ arg9.GetUnicode(), arg10.GetUnicode()};
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use our existing buffer to hold the result.
+ Resize(GetRawCount(), REPRESENTATION_UNICODE);
+
+ DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ lpSource, dwMessageId, dwLanguageId,
+ GetRawUnicode(), GetRawCount()+1, (va_list*)args);
+
+ // Although we cannot directly detect truncation, we can tell if we
+ // used up all the space (in which case we will assume truncation.)
+
+ if (result != 0 && result < GetRawCount())
+ {
+ if (GetRawUnicode()[result-1] == W(' '))
+ {
+ GetRawUnicode()[result-1] = W('\0');
+ result -= 1;
+ }
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ RETURN TRUE;
+ }
+ }
+
+ // We don't have enough space in our buffer, do dynamic allocation.
+ LocalAllocHolder<WCHAR> string;
+
+ DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ lpSource, dwMessageId, dwLanguageId,
+ (LPWSTR)(LPWSTR*)&string, 0, (va_list*)args);
+
+ if (result == 0)
+ RETURN FALSE;
+ else
+ {
+ if (string[result-1] == W(' '))
+ string[result-1] = W('\0');
+
+ Set(string);
+ RETURN TRUE;
+ }
+}
+
+#if 1
+//----------------------------------------------------------------------------
+// Helper
+//----------------------------------------------------------------------------
+
+// @todo -this should be removed and placed outside of SString
+void SString::MakeFullNamespacePath(const SString &nameSpace, const SString &name)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (nameSpace.GetRepresentation() == REPRESENTATION_UTF8
+ && name.GetRepresentation() == REPRESENTATION_UTF8)
+ {
+ const UTF8 *ns = nameSpace.GetRawUTF8();
+ const UTF8 *n = name.GetRawUTF8();
+ COUNT_T count = ns::GetFullLength(ns, n)-1;
+ Resize(count, REPRESENTATION_UTF8);
+ if (count > 0)
+ ns::MakePath(GetRawUTF8(), count+1, ns, n);
+ }
+ else
+ {
+ const WCHAR *ns = nameSpace;
+ const WCHAR *n = name;
+ COUNT_T count = ns::GetFullLength(ns, n)-1;
+ Resize(count, REPRESENTATION_UNICODE);
+ if (count > 0)
+ ns::MakePath(GetRawUnicode(), count+1, ns, n);
+ }
+
+ RETURN;
+}
+#endif
+
+
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Check to see if the string fits the suggested representation
+//----------------------------------------------------------------------------
+BOOL SString::IsRepresentation(Representation representation) const
+{
+ CONTRACT(BOOL)
+ {
+ PRECONDITION(CheckRepresentation(representation));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ Representation currentRepresentation = GetRepresentation();
+
+ // If representations are the same, cool.
+ if (currentRepresentation == representation)
+ RETURN TRUE;
+
+ // If we have an empty representation, we match everything
+ if (currentRepresentation == REPRESENTATION_EMPTY)
+ RETURN TRUE;
+
+ // If we're a 1 byte charset, there are some more chances to match
+ if (currentRepresentation != REPRESENTATION_UNICODE
+ && representation != REPRESENTATION_UNICODE)
+ {
+ // If we're ASCII, we can be any 1 byte rep
+ if (currentRepresentation == REPRESENTATION_ASCII)
+ RETURN TRUE;
+
+ // We really want to be ASCII - scan to see if we qualify
+ if (ScanASCII())
+ RETURN TRUE;
+ }
+
+ // Sorry, must convert.
+ RETURN FALSE;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Get the contents of the given string in a form which is compatible with our
+// string (and is in a fixed character set.) Updates the given iterator
+// if necessary to keep it in sync.
+//----------------------------------------------------------------------------
+const SString &SString::GetCompatibleString(const SString &s, SString &scratch, const CIterator &i) const
+{
+ CONTRACTL
+ {
+ PRECONDITION(s.Check());
+ PRECONDITION(scratch.Check());
+ PRECONDITION(scratch.CheckEmpty());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ // Since we have an iterator, we should be fixed size already
+ CONSISTENCY_CHECK(IsFixedSize());
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ return s;
+
+ case REPRESENTATION_ASCII:
+ if (s.IsRepresentation(REPRESENTATION_ASCII))
+ return s;
+
+ // We can't in general convert to ASCII, so try unicode.
+ ConvertToUnicode(i);
+ // fall through
+
+ case REPRESENTATION_UNICODE:
+ if (s.IsRepresentation(REPRESENTATION_UNICODE))
+ return s;
+
+ // @todo: we could convert s to unicode - is that a good policy????
+ s.ConvertToUnicode(scratch);
+ return scratch;
+
+ case REPRESENTATION_UTF8:
+ case REPRESENTATION_ANSI:
+ // These should all be impossible since we have an CIterator on us.
+ default:
+ UNREACHABLE_MSG("Unexpected string representation");
+ }
+
+ return s;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Get the contents of the given string in a form which is compatible with our
+// string (and is in a fixed character set.)
+// May convert our string to unicode.
+//----------------------------------------------------------------------------
+const SString &SString::GetCompatibleString(const SString &s, SString &scratch) const
+{
+ CONTRACTL
+ {
+ PRECONDITION(s.Check());
+ PRECONDITION(scratch.Check());
+ PRECONDITION(scratch.CheckEmpty());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // First, make sure we have a fixed size.
+ ConvertToFixed();
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ return s;
+
+ case REPRESENTATION_ANSI:
+ if (s.IsRepresentation(REPRESENTATION_ANSI))
+ return s;
+
+ s.ConvertToANSI(scratch);
+ return scratch;
+
+ case REPRESENTATION_ASCII:
+ if (s.IsRepresentation(REPRESENTATION_ASCII))
+ return s;
+
+ // We can't in general convert to ASCII, so try unicode.
+ ConvertToUnicode();
+ // fall through
+
+ case REPRESENTATION_UNICODE:
+ if (s.IsRepresentation(REPRESENTATION_UNICODE))
+ return s;
+
+ // @todo: we could convert s to unicode in place - is that a good policy????
+ s.ConvertToUnicode(scratch);
+ return scratch;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ return s;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// If we have a 1 byte representation, scan the buffer to see if we can gain
+// some conversion flexibility by labelling it ASCII
+//----------------------------------------------------------------------------
+BOOL SString::ScanASCII() const
+{
+ CONTRACT(BOOL)
+ {
+ POSTCONDITION(IsRepresentation(REPRESENTATION_ASCII) || IsASCIIScanned());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (!IsASCIIScanned())
+ {
+ const CHAR *c = GetRawANSI();
+ const CHAR *cEnd = c + GetRawCount();
+ while (c < cEnd)
+ {
+ if (*c & 0x80)
+ break;
+ c++;
+ }
+ if (c == cEnd)
+ {
+ const_cast<SString *>(this)->SetRepresentation(REPRESENTATION_ASCII);
+ RETURN TRUE;
+ }
+ else
+ const_cast<SString *>(this)->SetASCIIScanned();
+ }
+ RETURN FALSE;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Resize updates the geometry of the string and ensures that
+// the space can be written to.
+// count - number of characters (not including null) to hold
+// preserve - if we realloc, do we copy data from old to new?
+//----------------------------------------------------------------------------
+
+void SString::Resize(COUNT_T count, SString::Representation representation, Preserve preserve)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CountToSize(count) >= count);
+ POSTCONDITION(IsRepresentation(representation));
+ POSTCONDITION(GetRawCount() == count);
+ if (count == 0) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ // If we are resizing to zero, Clear is more efficient
+ if (count == 0)
+ {
+ Clear();
+ }
+ else
+ {
+ SetRepresentation(representation);
+
+ COUNT_T size = CountToSize(count);
+
+ // detect overflow
+ if (size < count)
+ ThrowOutOfMemory();
+
+ ClearNormalized();
+
+ SBuffer::Resize(size, preserve);
+
+ if (IsImmutable())
+ EnsureMutable();
+
+ NullTerminate();
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// This is essentially a specialized version of the above for size 0
+//-----------------------------------------------------------------------------
+void SString::Clear()
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ POSTCONDITION(IsEmpty());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ SetRepresentation(REPRESENTATION_EMPTY);
+
+ if (IsImmutable())
+ {
+ // Use shared empty string rather than allocating a new buffer
+ SBuffer::SetImmutable(s_EmptyBuffer, sizeof(s_EmptyBuffer));
+ }
+ else
+ {
+ // Leave allocated buffer for future growth
+ SBuffer::TweakSize(sizeof(WCHAR));
+ GetRawUnicode()[0] = 0;
+ }
+
+ RETURN;
+}
+
+
+#ifdef DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+// Return a pointer to the raw buffer
+//
+// Returns:
+// A pointer to the raw string buffer.
+//
+void * SString::DacGetRawContent() const
+{
+ if (IsEmpty())
+ {
+ return NULL;
+ }
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ return NULL;
+
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_UTF8:
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ // Note: no need to call DacInstantiateString because we know the exact length already.
+ return SBuffer::DacGetRawContent();
+
+ default:
+ DacNotImpl();
+ return NULL;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Return a pointer to the raw buffer as a pointer to a unicode string. Does not
+// do conversion, and thus requires that the representation already be in unicode.
+//
+// Returns:
+// A pointer to the raw string buffer as a unicode string.
+//
+const WCHAR * SString::DacGetRawUnicode() const
+{
+ if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY))
+ {
+ return W("");
+ }
+
+ if (GetRepresentation() != REPRESENTATION_UNICODE)
+ {
+ DacError(E_UNEXPECTED);
+ }
+
+ HRESULT status = S_OK;
+ WCHAR* wszBuf = NULL;
+ EX_TRY
+ {
+ wszBuf = static_cast<WCHAR*>(SBuffer::DacGetRawContent());
+ }
+ EX_CATCH_HRESULT(status);
+
+ if (SUCCEEDED(status))
+ {
+ return wszBuf;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Copy the string from the target into the provided buffer, converting to unicode if necessary
+//
+// Arguments:
+// cBufChars - size of pBuffer in count of unicode characters.
+// pBuffer - a buffer of cBufChars unicode chars.
+// pcNeedChars - space to store the number of unicode chars in the SString.
+//
+// Returns:
+// true if successful - and buffer is filled with the unicode representation of
+// the string.
+// false if unsuccessful.
+//
+bool SString::DacGetUnicode(COUNT_T cBufChars,
+ __out_z __inout_ecount(cBufChars) WCHAR * pBuffer,
+ COUNT_T * pcNeedChars) const
+{
+ SUPPORTS_DAC;
+
+ PVOID pContent = NULL;
+ int iPage = CP_ACP;
+
+ if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY))
+ {
+ if (pcNeedChars)
+ {
+ *pcNeedChars = 1;
+ }
+ if (pBuffer && cBufChars)
+ {
+ pBuffer[0] = 0;
+ }
+ return true;
+ }
+
+ HRESULT status = S_OK;
+ EX_TRY
+ {
+ pContent = SBuffer::DacGetRawContent();
+ }
+ EX_CATCH_HRESULT(status);
+
+ if (SUCCEEDED(status) && pContent != NULL)
+ {
+ switch (GetRepresentation())
+ {
+
+ case REPRESENTATION_UNICODE:
+
+ if (pcNeedChars)
+ {
+ *pcNeedChars = GetCount() + 1;
+ }
+
+ if (pBuffer && cBufChars)
+ {
+ if (cBufChars > GetCount() + 1)
+ {
+ cBufChars = GetCount() + 1;
+ }
+ memcpy(pBuffer, pContent, cBufChars * sizeof(*pBuffer));
+ pBuffer[cBufChars - 1] = 0;
+ }
+
+ return true;
+
+ case REPRESENTATION_UTF8:
+ iPage = CP_UTF8;
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ // iPage defaults to CP_ACP.
+ if (pcNeedChars)
+ {
+ *pcNeedChars = WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, NULL, 0);
+ }
+ if (pBuffer && cBufChars)
+ {
+ if (!WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, pBuffer, cBufChars))
+ {
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ DacNotImpl();
+ return false;
+ }
+ }
+ return false;
+}
+
+#endif //DACCESS_COMPILE
diff --git a/src/utilcode/sstring_com.cpp b/src/utilcode/sstring_com.cpp
new file mode 100644
index 0000000000..94cdd45c3b
--- /dev/null
+++ b/src/utilcode/sstring_com.cpp
@@ -0,0 +1,103 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ---------------------------------------------------------------------------
+// SString_COM.cpp
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sstring.h"
+#include "ex.h"
+#include "holder.h"
+
+#define DEFAULT_RESOURCE_STRING_SIZE 255
+
+//----------------------------------------------------------------------------
+// Load the string resource into this string.
+//----------------------------------------------------------------------------
+BOOL SString::LoadResource(CCompRC::ResourceCategory eCategory, int resourceID)
+{
+ return SUCCEEDED(LoadResourceAndReturnHR(eCategory, resourceID));
+}
+
+HRESULT SString::LoadResourceAndReturnHR(CCompRC::ResourceCategory eCategory, int resourceID)
+{
+ WRAPPER_NO_CONTRACT;
+ return LoadResourceAndReturnHR(NULL, eCategory,resourceID);
+}
+
+HRESULT SString::LoadResourceAndReturnHR(CCompRC* pResourceDLL, CCompRC::ResourceCategory eCategory, int resourceID)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ }
+ CONTRACT_END;
+
+ HRESULT hr = E_FAIL;
+
+#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
+ if (pResourceDLL == NULL)
+ {
+ pResourceDLL = CCompRC::GetDefaultResourceDll();
+ }
+
+ if (pResourceDLL != NULL)
+ {
+
+ int size = 0;
+
+ EX_TRY
+ {
+ if (GetRawCount() == 0)
+ Resize(DEFAULT_RESOURCE_STRING_SIZE, REPRESENTATION_UNICODE);
+
+ while (TRUE)
+ {
+ // First try and load the string in the amount of space that we have.
+ // In fatal error reporting scenarios, we may not have enough memory to
+ // allocate a larger buffer.
+
+ hr = pResourceDLL->LoadString(eCategory, resourceID, GetRawUnicode(), GetRawCount()+1,&size);
+ if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ if (FAILED(hr))
+ {
+ Clear();
+ break;
+ }
+
+ // Although we cannot generally detect truncation, we can tell if we
+ // used up all the space (in which case we will assume truncation.)
+ if (size < (int)GetRawCount())
+ {
+ break;
+ }
+ }
+
+ // Double the size and try again.
+ Resize(size*2, REPRESENTATION_UNICODE);
+
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ Truncate(Begin() + (COUNT_T) wcslen(GetRawUnicode()));
+ }
+
+ Normalize();
+
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ }
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
+
+ RETURN hr;
+} // SString::LoadResourceAndReturnHR
diff --git a/src/utilcode/stacktrace.cpp b/src/utilcode/stacktrace.cpp
new file mode 100644
index 0000000000..7cea7ffc92
--- /dev/null
+++ b/src/utilcode/stacktrace.cpp
@@ -0,0 +1,978 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "stacktrace.h"
+#include <imagehlp.h>
+#include "corhlpr.h"
+#include "utilcode.h"
+#include "pedecoder.h" // for IMAGE_FILE_MACHINE_NATIVE
+
+//This is a workaround. We need to work with the debugger team to figure
+//out how the module handle of the CLR can be found in a SxS safe way.
+HMODULE GetCLRModuleHack()
+{
+ static HMODULE s_hModCLR = 0;
+ if (!s_hModCLR)
+ {
+ s_hModCLR = GetModuleHandleA(MAIN_CLR_DLL_NAME_A);
+ }
+ return s_hModCLR;
+}
+
+HINSTANCE LoadImageHlp()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+
+ return LoadLibraryExA("imagehlp.dll", NULL, 0);
+}
+
+HINSTANCE LoadDbgHelp()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+
+ return LoadLibraryExA("dbghelp.dll", NULL, 0);
+}
+
+/****************************************************************************
+* SymCallback *
+*---------------------*
+* Description:
+* Callback for imghelp.
+****************************************************************************/
+BOOL __stdcall SymCallback
+(
+HANDLE hProcess,
+ULONG ActionCode,
+PVOID CallbackData,
+PVOID UserContext
+)
+{
+ WRAPPER_NO_CONTRACT;
+
+ switch (ActionCode)
+ {
+ case CBA_DEBUG_INFO:
+ OutputDebugStringA("IMGHLP: ");
+ OutputDebugStringA((LPCSTR) CallbackData);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_START:
+ OutputDebugStringA("IMGHLP: Deferred symbol load start ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
+ OutputDebugStringA("IMGHLP: Deferred symbol load complete ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
+ OutputDebugStringA("IMGHLP: Deferred symbol load failure ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
+ OutputDebugStringA("IMGHLP: Deferred symbol load partial ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+ }
+
+ return FALSE;
+}
+
+// @TODO_IA64: all of this stack trace stuff is pretty much broken on 64-bit
+// right now because this code doesn't use the new SymXxxx64 functions.
+
+#define LOCAL_ASSERT(x)
+//
+//--- Macros ------------------------------------------------------------------
+//
+
+#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
+
+//
+// Types and Constants --------------------------------------------------------
+//
+
+struct SYM_INFO
+{
+ DWORD_PTR dwOffset;
+ char achModule[cchMaxAssertModuleLen];
+ char achSymbol[cchMaxAssertSymbolLen];
+};
+
+//--- Function Pointers to APIs in IMAGEHLP.DLL. Loaded dynamically. ---------
+
+typedef LPAPI_VERSION (__stdcall *pfnImgHlp_ImagehlpApiVersionEx)(
+ LPAPI_VERSION AppVersion
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_StackWalk)(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME StackFrame,
+ LPVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
+ );
+
+#ifdef _WIN64
+typedef DWORD64 (__stdcall *pfnImgHlp_SymGetModuleBase64)(
+ IN HANDLE hProcess,
+ IN DWORD64 dwAddr
+ );
+
+typedef IMAGEHLP_SYMBOL64 PLAT_IMAGEHLP_SYMBOL;
+typedef IMAGEHLP_MODULE64 PLAT_IMAGEHLP_MODULE;
+
+#else
+typedef IMAGEHLP_SYMBOL PLAT_IMAGEHLP_SYMBOL;
+typedef IMAGEHLP_MODULE PLAT_IMAGEHLP_MODULE;
+#endif
+
+#undef IMAGEHLP_SYMBOL
+#undef IMAGEHLP_MODULE
+
+
+typedef BOOL (__stdcall *pfnImgHlp_SymGetModuleInfo)(
+ IN HANDLE hProcess,
+ IN DWORD_PTR dwAddr,
+ OUT PLAT_IMAGEHLP_MODULE* ModuleInfo
+ );
+
+typedef LPVOID (__stdcall *pfnImgHlp_SymFunctionTableAccess)(
+ HANDLE hProcess,
+ DWORD_PTR AddrBase
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymGetSymFromAddr)(
+ IN HANDLE hProcess,
+ IN DWORD_PTR dwAddr,
+ OUT DWORD_PTR* pdwDisplacement,
+ OUT PLAT_IMAGEHLP_SYMBOL* Symbol
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymInitialize)(
+ IN HANDLE hProcess,
+ IN LPSTR UserSearchPath,
+ IN BOOL fInvadeProcess
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymUnDName)(
+ IN PLAT_IMAGEHLP_SYMBOL* sym, // Symbol to undecorate
+ OUT LPSTR UnDecName, // Buffer to store undecorated name in
+ IN DWORD UnDecNameLength // Size of the buffer
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymLoadModule)(
+ IN HANDLE hProcess,
+ IN HANDLE hFile,
+ IN PSTR ImageName,
+ IN PSTR ModuleName,
+ IN DWORD_PTR BaseOfDll,
+ IN DWORD SizeOfDll
+ );
+
+typedef BOOL (_stdcall *pfnImgHlp_SymRegisterCallback)(
+ IN HANDLE hProcess,
+ IN PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
+ IN PVOID UserContext
+ );
+
+typedef DWORD (_stdcall *pfnImgHlp_SymSetOptions)(
+ IN DWORD SymOptions
+ );
+
+typedef DWORD (_stdcall *pfnImgHlp_SymGetOptions)(
+ );
+
+
+struct IMGHLPFN_LOAD
+{
+ LPSTR pszFnName;
+ LPVOID * ppvfn;
+};
+
+
+#if defined(_WIN64)
+typedef void (*pfn_GetRuntimeStackWalkInfo)(
+ IN ULONG64 ControlPc,
+ OUT UINT_PTR* pModuleBase,
+ OUT UINT_PTR* pFuncEntry
+ );
+#endif // _WIN64
+
+
+//
+// Globals --------------------------------------------------------------------
+//
+
+static BOOL g_fLoadedImageHlp = FALSE; // set to true on success
+static BOOL g_fLoadedImageHlpFailed = FALSE; // set to true on failure
+static HINSTANCE g_hinstImageHlp = NULL;
+static HINSTANCE g_hinstDbgHelp = NULL;
+static HANDLE g_hProcess = NULL;
+
+pfnImgHlp_ImagehlpApiVersionEx _ImagehlpApiVersionEx;
+pfnImgHlp_StackWalk _StackWalk;
+pfnImgHlp_SymGetModuleInfo _SymGetModuleInfo;
+pfnImgHlp_SymFunctionTableAccess _SymFunctionTableAccess;
+pfnImgHlp_SymGetSymFromAddr _SymGetSymFromAddr;
+pfnImgHlp_SymInitialize _SymInitialize;
+pfnImgHlp_SymUnDName _SymUnDName;
+pfnImgHlp_SymLoadModule _SymLoadModule;
+pfnImgHlp_SymRegisterCallback _SymRegisterCallback;
+pfnImgHlp_SymSetOptions _SymSetOptions;
+pfnImgHlp_SymGetOptions _SymGetOptions;
+#if defined(_WIN64)
+pfn_GetRuntimeStackWalkInfo _GetRuntimeStackWalkInfo;
+#endif // _WIN64
+
+IMGHLPFN_LOAD ailFuncList[] =
+{
+ { "ImagehlpApiVersionEx", (LPVOID*)&_ImagehlpApiVersionEx },
+ { "StackWalk", (LPVOID*)&_StackWalk },
+ { "SymGetModuleInfo", (LPVOID*)&_SymGetModuleInfo },
+ { "SymFunctionTableAccess", (LPVOID*)&_SymFunctionTableAccess },
+ { "SymGetSymFromAddr", (LPVOID*)&_SymGetSymFromAddr },
+ { "SymInitialize", (LPVOID*)&_SymInitialize },
+ { "SymUnDName", (LPVOID*)&_SymUnDName },
+ { "SymLoadModule", (LPVOID*)&_SymLoadModule },
+ { "SymRegisterCallback", (LPVOID*)&_SymRegisterCallback },
+ { "SymSetOptions", (LPVOID*)&_SymSetOptions },
+ { "SymGetOptions", (LPVOID*)&_SymGetOptions },
+};
+
+
+/****************************************************************************
+* FillSymbolSearchPath *
+*----------------------*
+* Description:
+* Manually pick out all the symbol path information we need for a real
+* stack trace to work. This includes the default NT symbol paths and
+* places on a VBL build machine where they should live.
+****************************************************************************/
+#define MAX_SYM_PATH (1024*8)
+#define DEFAULT_SYM_PATH W("symsrv*symsrv.dll*\\\\symbols\\symbols;")
+#define STR_ENGINE_NAME MAIN_CLR_DLL_NAME_W
+LPSTR FillSymbolSearchPath(CQuickBytes &qb)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+
+#ifndef DACCESS_COMPILE
+ // not allowed to do allocation if current thread suspends EE.
+ if (IsSuspendEEThread ())
+ return NULL;
+#endif
+
+ NewArrayHolder<WCHAR> rcBuff = new (nothrow) WCHAR[MAX_SYM_PATH]; // Working buffer
+ WCHAR rcVerString[64]; // Extension for install directory.
+ int chTotal = 0; // How full is working buffer.
+ int ch;
+
+
+ if (rcBuff == NULL)
+ return NULL; // Unable to allocate working buffer - fail
+
+ ZeroMemory(rcBuff, MAX_SYM_PATH*sizeof(WCHAR));
+
+ // If the NT symbol server path vars are there, then use those.
+ chTotal = WszGetEnvironmentVariable(W("_NT_SYMBOL_PATH"), rcBuff, MAX_SYM_PATH); // Cannot use NumItems(rcBuff) since NumItems does not work with holders
+ if (chTotal + 1 < MAX_SYM_PATH)
+ rcBuff[chTotal++] = W(';');
+
+ // Copy the defacto NT symbol path as well.
+ size_t sympathLength = chTotal + NumItems(DEFAULT_SYM_PATH) + 1;
+ // integer overflow occurred
+ if (sympathLength < (size_t)chTotal || sympathLength < NumItems(DEFAULT_SYM_PATH))
+ {
+ return NULL;
+ }
+
+ if (sympathLength < MAX_SYM_PATH)
+ {
+ wcsncpy_s(&rcBuff[chTotal], MAX_SYM_PATH-chTotal, DEFAULT_SYM_PATH, _TRUNCATE);
+ chTotal = (int) wcslen(rcBuff);
+ }
+
+ // Next, if there is a URTTARGET, add that since that is where ndpsetup places
+ // your symobls on an install.
+ ch = WszGetEnvironmentVariable(W("URTTARGET"), &rcBuff[chTotal], MAX_SYM_PATH - chTotal);
+ if (ch != 0 && (chTotal + ch + 1 < MAX_SYM_PATH))
+ {
+ size_t chNewTotal = chTotal + ch;
+ if (chNewTotal < (size_t)chTotal || chNewTotal < (size_t)ch)
+ { // integer overflow occurred
+ return NULL;
+ }
+ chTotal += ch;
+ rcBuff[chTotal++] = W(';');
+ }
+
+#ifndef SELF_NO_HOST
+ // Fetch the path location of the engine dll and add that path as well, just
+ // in case URTARGET didn't cut it either.
+ // For no-host builds of utilcode, we don't necessarily have an engine DLL in the
+ // process, so skip this part.
+ ch = WszGetModuleFileName(GetCLRModuleHack(),
+ &rcBuff[chTotal], MAX_SYM_PATH - chTotal);
+ size_t pathLocationLength = chTotal + ch + 1;
+ // integer overflow occurred
+ if (pathLocationLength < (size_t)chTotal || pathLocationLength < (size_t)ch)
+ {
+ return NULL;
+ }
+
+ if (ch != 0 && (pathLocationLength < MAX_SYM_PATH))
+ {
+ chTotal = chTotal + ch - NumItems(STR_ENGINE_NAME);
+ rcBuff[chTotal++] = W(';');
+ rcBuff[chTotal] = 0;
+ }
+#endif
+
+ // We want to ensure the resulting string is always NULL-terminated.
+ rcBuff[MAX_SYM_PATH-1] = W('\0');
+
+ // Now we have a working buffer with a bunch of interesting stuff. Time
+ // to convert it back to ansi for the imagehlp api's. Allocate the buffer
+ // 2x bigger to handle worst case for MBCS.
+ ch = ::WszWideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, rcBuff, -1, 0, 0, 0, 0);
+ LPSTR szRtn = (LPSTR) qb.AllocNoThrow(ch + 1);
+ if (!szRtn)
+ return NULL;
+ WszWideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, rcBuff, -1, szRtn, ch+1, 0, 0);
+ return (szRtn);
+}
+
+
+/****************************************************************************
+* MagicInit *
+*-----------*
+* Description:
+* Initializes the symbol loading code. Currently called (if necessary)
+* at the beginning of each method that might need ImageHelp to be
+* loaded.
+****************************************************************************/
+void MagicInit()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ if (g_fLoadedImageHlp || g_fLoadedImageHlpFailed)
+ {
+ return;
+ }
+
+ g_hProcess = GetCurrentProcess();
+
+ if (g_hinstDbgHelp == NULL)
+ {
+ g_hinstDbgHelp = LoadDbgHelp();
+ }
+ if (NULL == g_hinstDbgHelp)
+ {
+ // Imagehlp.dll has dependency on dbghelp.dll through delay load.
+ // If dbghelp.dll is not available, Imagehlp.dll initializes API's like ImageApiVersionEx to
+ // some dummy function. Then we AV when we use data from _ImagehlpApiVersionEx
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+
+ //
+ // Try to load imagehlp.dll
+ //
+ if (g_hinstImageHlp == NULL) {
+ g_hinstImageHlp = LoadImageHlp();
+ }
+ LOCAL_ASSERT(g_hinstImageHlp);
+
+ if (NULL == g_hinstImageHlp)
+ {
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+
+ //
+ // Try to get the API entrypoints in imagehlp.dll
+ //
+ for (int i = 0; i < COUNT_OF(ailFuncList); i++)
+ {
+ *(ailFuncList[i].ppvfn) = GetProcAddress(
+ g_hinstImageHlp,
+ ailFuncList[i].pszFnName);
+ LOCAL_ASSERT(*(ailFuncList[i].ppvfn));
+
+ if (!*(ailFuncList[i].ppvfn))
+ {
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+ }
+
+ API_VERSION AppVersion = { 4, 0, API_VERSION_NUMBER, 0 };
+ LPAPI_VERSION papiver = _ImagehlpApiVersionEx(&AppVersion);
+
+ //
+ // We assume any version 4 or greater is OK.
+ //
+ LOCAL_ASSERT(papiver->Revision >= 4);
+ if (papiver->Revision < 4)
+ {
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+
+ g_fLoadedImageHlp = TRUE;
+
+ //
+ // Initialize imagehlp.dll. A NULL search path is supposed to resolve
+ // symbols but never works. So pull in everything and put some additional
+ // hints that might help out a dev box.
+ //
+
+ _SymSetOptions(_SymGetOptions() | SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
+#ifndef _WIN64
+ _SymRegisterCallback(g_hProcess, SymCallback, 0);
+#endif
+
+ CQuickBytes qbSearchPath;
+ LPSTR szSearchPath = FillSymbolSearchPath(qbSearchPath);
+ _SymInitialize(g_hProcess, szSearchPath, TRUE);
+
+ return;
+}
+
+
+/****************************************************************************
+* FillSymbolInfo *
+*----------------*
+* Description:
+* Fills in a SYM_INFO structure
+****************************************************************************/
+void FillSymbolInfo
+(
+SYM_INFO *psi,
+DWORD_PTR dwAddr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ if (!g_fLoadedImageHlp)
+ {
+ return;
+ }
+
+ LOCAL_ASSERT(psi);
+ memset(psi, 0, sizeof(SYM_INFO));
+
+ PLAT_IMAGEHLP_MODULE mi;
+ mi.SizeOfStruct = sizeof(mi);
+
+ if (!_SymGetModuleInfo(g_hProcess, dwAddr, &mi))
+ {
+ strcpy_s(psi->achModule, _countof(psi->achModule), "<no module>");
+ }
+ else
+ {
+ strcpy_s(psi->achModule, _countof(psi->achModule), mi.ModuleName);
+ _strupr_s(psi->achModule, _countof(psi->achModule));
+ }
+
+ CHAR rgchUndec[256];
+ CHAR * pszSymbol = NULL;
+
+ // Name field of IMAGEHLP_SYMBOL is dynamically sized.
+ // Pad with space for 255 characters.
+ union
+ {
+ CHAR rgchSymbol[sizeof(PLAT_IMAGEHLP_SYMBOL) + 255];
+ PLAT_IMAGEHLP_SYMBOL sym;
+ };
+
+ __try
+ {
+ sym.SizeOfStruct = sizeof(PLAT_IMAGEHLP_SYMBOL);
+ sym.Address = dwAddr;
+ sym.MaxNameLength = 255;
+
+ if (_SymGetSymFromAddr(g_hProcess, dwAddr, &psi->dwOffset, &sym))
+ {
+ pszSymbol = sym.Name;
+
+ if (_SymUnDName(&sym, rgchUndec, COUNT_OF(rgchUndec)-1))
+ {
+ pszSymbol = rgchUndec;
+ }
+ }
+ else
+ {
+ pszSymbol = "<no symbol>";
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ pszSymbol = "<EX: no symbol>";
+ psi->dwOffset = dwAddr - mi.BaseOfImage;
+ }
+
+ strcpy_s(psi->achSymbol, _countof(psi->achSymbol), pszSymbol);
+}
+
+/****************************************************************************
+* FunctionTableAccess *
+*---------------------*
+* Description:
+* Helper for imagehlp's StackWalk API.
+****************************************************************************/
+LPVOID __stdcall FunctionTableAccess
+(
+HANDLE hProcess,
+DWORD_PTR dwPCAddr
+)
+{
+ WRAPPER_NO_CONTRACT;
+
+ HANDLE hFuncEntry = _SymFunctionTableAccess( hProcess, dwPCAddr );
+
+#if defined(_WIN64)
+ if (hFuncEntry == NULL)
+ {
+ if (_GetRuntimeStackWalkInfo == NULL)
+ {
+ _GetRuntimeStackWalkInfo = (pfn_GetRuntimeStackWalkInfo)
+ GetProcAddress(GetCLRModuleHack(), "GetRuntimeStackWalkInfo");
+ if (_GetRuntimeStackWalkInfo == NULL)
+ return NULL;
+ }
+
+ _GetRuntimeStackWalkInfo((ULONG64)dwPCAddr, NULL, (UINT_PTR*)(&hFuncEntry));
+ }
+#endif // _WIN64
+
+ return hFuncEntry;
+}
+
+/****************************************************************************
+* GetModuleBase *
+*---------------*
+* Description:
+* Helper for imagehlp's StackWalk API. Retrieves the base address of
+* the module containing the giving virtual address.
+*
+* NOTE: If the module information for the given module hasnot yet been
+* loaded, then it is loaded on this call.
+*
+* Return:
+* Base virtual address where the module containing ReturnAddress is
+* loaded, or 0 if the address cannot be determined.
+****************************************************************************/
+DWORD_PTR __stdcall GetModuleBase
+(
+HANDLE hProcess,
+DWORD_PTR dwAddr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ PLAT_IMAGEHLP_MODULE ModuleInfo;
+ ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
+
+ if (_SymGetModuleInfo(hProcess, dwAddr, &ModuleInfo))
+ {
+ return ModuleInfo.BaseOfImage;
+ }
+ else
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if (VirtualQueryEx(hProcess, (LPVOID)dwAddr, &mbi, sizeof(mbi)))
+ {
+ if (mbi.Type & MEM_IMAGE)
+ {
+ char achFile[MAX_PATH] = {0};
+ DWORD cch;
+
+ cch = GetModuleFileNameA(
+ (HINSTANCE)mbi.AllocationBase,
+ achFile,
+ MAX_PATH);
+
+ // Ignore the return code since we can't do anything with it.
+ _SymLoadModule(
+ hProcess,
+ NULL,
+ ((cch) ? achFile : NULL),
+ NULL,
+ (DWORD_PTR)mbi.AllocationBase,
+ 0);
+
+ return (DWORD_PTR)mbi.AllocationBase;
+ }
+ }
+ }
+
+#if defined(_WIN64)
+ if (_GetRuntimeStackWalkInfo == NULL)
+ {
+ _GetRuntimeStackWalkInfo = (pfn_GetRuntimeStackWalkInfo)
+ GetProcAddress(GetCLRModuleHack(), "GetRuntimeStackWalkInfo");
+ if (_GetRuntimeStackWalkInfo == NULL)
+ return NULL;
+ }
+
+ DWORD_PTR moduleBase;
+ _GetRuntimeStackWalkInfo((ULONG64)dwAddr, (UINT_PTR*)&moduleBase, NULL);
+ if (moduleBase != NULL)
+ return moduleBase;
+#endif // _WIN64
+
+ return 0;
+}
+
+#if !defined(DACCESS_COMPILE)
+/****************************************************************************
+* GetStackBacktrace *
+*-------------------*
+* Description:
+* Gets a stacktrace of the current stack, including symbols.
+*
+* Return:
+* The number of elements actually retrieved.
+****************************************************************************/
+
+UINT GetStackBacktrace
+(
+UINT ifrStart, // How many stack elements to skip before starting.
+UINT cfrTotal, // How many elements to trace after starting.
+DWORD_PTR* pdwEip, // Array to be filled with stack addresses.
+SYM_INFO* psiSymbols, // This array is filled with symbol information.
+ // It should be big enough to hold cfrTotal elts.
+ // If NULL, no symbol information is stored.
+CONTEXT * pContext // Context to use (or NULL to use current)
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ UINT nElements = 0;
+ DWORD_PTR* pdw = pdwEip;
+ SYM_INFO* psi = psiSymbols;
+
+ MagicInit();
+
+ memset(pdwEip, 0, cfrTotal*sizeof(DWORD_PTR));
+
+ if (psiSymbols)
+ {
+ memset(psiSymbols, 0, cfrTotal * sizeof(SYM_INFO));
+ }
+
+ if (!g_fLoadedImageHlp)
+ {
+ return 0;
+ }
+
+ CONTEXT context;
+ if (pContext == NULL)
+ {
+ ClrCaptureContext(&context);
+ }
+ else
+ {
+ memcpy(&context, pContext, sizeof(CONTEXT));
+ }
+
+#ifdef _WIN64
+ STACKFRAME64 stkfrm;
+ memset(&stkfrm, 0, sizeof(STACKFRAME64));
+#else
+ STACKFRAME stkfrm;
+ memset(&stkfrm, 0, sizeof(STACKFRAME));
+#endif
+
+ stkfrm.AddrPC.Mode = AddrModeFlat;
+ stkfrm.AddrStack.Mode = AddrModeFlat;
+ stkfrm.AddrFrame.Mode = AddrModeFlat;
+#if defined(_M_IX86)
+ stkfrm.AddrPC.Offset = context.Eip;
+ stkfrm.AddrStack.Offset = context.Esp;
+ stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer
+#endif
+
+#ifndef _TARGET_X86_
+ // If we don't have a user-supplied context, then don't skip any frames.
+ // So ignore this function (GetStackBackTrace)
+ // ClrCaptureContext on x86 gives us the ESP/EBP/EIP of its caller's caller
+ // so we don't need to do this.
+ if (pContext == NULL)
+ {
+ ifrStart += 1;
+ }
+#endif // !_TARGET_X86_
+
+ for (UINT i = 0; i < ifrStart + cfrTotal; i++)
+ {
+ if (!_StackWalk(IMAGE_FILE_MACHINE_NATIVE,
+ g_hProcess,
+ GetCurrentThread(),
+ &stkfrm,
+ &context,
+ NULL,
+ (PFUNCTION_TABLE_ACCESS_ROUTINE)FunctionTableAccess,
+ (PGET_MODULE_BASE_ROUTINE)GetModuleBase,
+ NULL))
+ {
+ break;
+ }
+
+ if (i >= ifrStart)
+ {
+ *pdw++ = stkfrm.AddrPC.Offset;
+ nElements++;
+
+ if (psi)
+ {
+ FillSymbolInfo(psi++, stkfrm.AddrPC.Offset);
+ }
+ }
+ }
+
+ LOCAL_ASSERT(nElements == (UINT)(pdw - pdwEip));
+ return nElements;
+}
+#endif // !defined(DACCESS_COMPILE)
+
+/****************************************************************************
+* GetStringFromSymbolInfo *
+*-------------------------*
+* Description:
+* Actually prints the info into the string for the symbol.
+****************************************************************************/
+
+#ifdef _WIN64
+ #define FMT_ADDR_BARE "%08x`%08x"
+#else
+ #define FMT_ADDR_BARE "%08x"
+#endif
+
+void GetStringFromSymbolInfo
+(
+DWORD_PTR dwAddr,
+SYM_INFO *psi, // @parm Pointer to SYMBOL_INFO. Can be NULL.
+__out_ecount (cchMaxAssertStackLevelStringLen) CHAR *pszString // @parm Place to put string.
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ LOCAL_ASSERT(pszString);
+
+ // <module>! <symbol> + 0x<offset> 0x<addr>\n
+
+ if (psi)
+ {
+ sprintf_s(pszString,
+ cchMaxAssertStackLevelStringLen,
+ "%s! %s + 0x%X (0x" FMT_ADDR_BARE ")",
+ (psi->achModule[0]) ? psi->achModule : "<no module>",
+ (psi->achSymbol[0]) ? psi->achSymbol : "<no symbol>",
+ psi->dwOffset,
+ DBG_ADDR(dwAddr));
+ }
+ else
+ {
+
+ sprintf_s(pszString, cchMaxAssertStackLevelStringLen, "<symbols not available> (0x%p)", (void *)dwAddr);
+ }
+
+ LOCAL_ASSERT(strlen(pszString) < cchMaxAssertStackLevelStringLen);
+}
+
+#if !defined(DACCESS_COMPILE)
+
+/****************************************************************************
+* GetStringFromStackLevels *
+*--------------------------*
+* Description:
+* Retrieves a string from the stack frame. If more than one frame, they
+* are separated by newlines
+****************************************************************************/
+void GetStringFromStackLevels
+(
+UINT ifrStart, // @parm How many stack elements to skip before starting.
+UINT cfrTotal, // @parm How many elements to trace after starting.
+ // Can't be more than cfrMaxAssertStackLevels.
+__out_ecount(cchMaxAssertStackLevelStringLen * cfrTotal) CHAR *pszString, // @parm Place to put string.
+ // Max size will be cchMaxAssertStackLevelStringLen * cfrTotal.
+CONTEXT * pContext // @parm Context to start the stack trace at; null for current context.
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ LOCAL_ASSERT(pszString);
+ LOCAL_ASSERT(cfrTotal < cfrMaxAssertStackLevels);
+
+ *pszString = '\0';
+
+ if (cfrTotal == 0)
+ {
+ return;
+ }
+
+ DWORD_PTR rgdwStackAddrs[cfrMaxAssertStackLevels];
+ SYM_INFO rgsi[cfrMaxAssertStackLevels];
+
+ // Ignore this function (GetStringFromStackLevels) if we don't have a user-supplied context.
+ if (pContext == NULL)
+ {
+ ifrStart += 1;
+ }
+
+ UINT uiRetrieved =
+ GetStackBacktrace(ifrStart, cfrTotal, rgdwStackAddrs, rgsi, pContext);
+
+ // First level
+ CHAR aszLevel[cchMaxAssertStackLevelStringLen];
+ GetStringFromSymbolInfo(rgdwStackAddrs[0], &rgsi[0], aszLevel);
+
+ size_t bufSize = cchMaxAssertStackLevelStringLen * cfrTotal;
+
+ strcpy_s(pszString, bufSize, aszLevel);
+
+ // Additional levels
+ for (UINT i = 1; i < uiRetrieved; ++i)
+ {
+ strcat_s(pszString, bufSize, "\n");
+ GetStringFromSymbolInfo(rgdwStackAddrs[i],
+ &rgsi[i], aszLevel);
+ strcat_s(pszString, bufSize, aszLevel);
+ }
+
+ LOCAL_ASSERT(strlen(pszString) <= cchMaxAssertStackLevelStringLen * cfrTotal);
+}
+#endif // !defined(DACCESS_COMPILE)
+
+/****************************************************************************
+* GetStringFromAddr *
+*-------------------*
+* Description:
+* Returns a string from an address.
+****************************************************************************/
+void GetStringFromAddr
+(
+DWORD_PTR dwAddr,
+__out_ecount(cchMaxAssertStackLevelStringLen) LPSTR szString // Place to put string.
+ // Buffer must hold at least cchMaxAssertStackLevelStringLen.
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ LOCAL_ASSERT(szString);
+
+ SYM_INFO si;
+ FillSymbolInfo(&si, dwAddr);
+
+ sprintf_s(szString,
+ cchMaxAssertStackLevelStringLen,
+ "%s! %s + 0x%p (0x%p)",
+ (si.achModule[0]) ? si.achModule : "<no module>",
+ (si.achSymbol[0]) ? si.achSymbol : "<no symbol>",
+ (void*)si.dwOffset,
+ (void*)dwAddr);
+}
+
+/****************************************************************************
+* MagicDeinit *
+*-------------*
+* Description:
+* Cleans up for the symbol loading code. Should be called before exit
+* to free the dynamically loaded imagehlp.dll.
+****************************************************************************/
+void MagicDeinit(void)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (g_hinstImageHlp)
+ {
+ FreeLibrary(g_hinstImageHlp);
+
+ g_hinstImageHlp = NULL;
+ g_fLoadedImageHlp = FALSE;
+ }
+}
+
+#if defined(_TARGET_X86_) && defined(FEATURE_CORECLR)
+/****************************************************************************
+* ClrCaptureContext *
+*-------------------*
+* Description:
+* Exactly the contents of RtlCaptureContext for Win7 - Win2K doesn't
+* support this, so we need it for CoreCLR 4, if we require Win2K support
+****************************************************************************/
+extern "C" __declspec(naked) void __stdcall
+ClrCaptureContext(__out PCONTEXT ctx)
+{
+ __asm {
+ push ebx;
+ mov ebx,dword ptr [esp+8]
+ mov dword ptr [ebx+0B0h],eax
+ mov dword ptr [ebx+0ACh],ecx
+ mov dword ptr [ebx+0A8h],edx
+ mov eax,dword ptr [esp]
+ mov dword ptr [ebx+0A4h],eax
+ mov dword ptr [ebx+0A0h],esi
+ mov dword ptr [ebx+09Ch],edi
+ mov word ptr [ebx+0BCh],cs
+ mov word ptr [ebx+098h],ds
+ mov word ptr [ebx+094h],es
+ mov word ptr [ebx+090h],fs
+ mov word ptr [ebx+08Ch],gs
+ mov word ptr [ebx+0C8h],ss
+ pushfd
+ pop dword ptr [ebx+0C0h]
+ mov eax,dword ptr [ebp+4]
+ mov dword ptr [ebx+0B8h],eax
+ mov eax,dword ptr [ebp]
+ mov dword ptr [ebx+0B4h],eax
+ lea eax,[ebp+8]
+ mov dword ptr [ebx+0C4h],eax
+ mov dword ptr [ebx],10007h
+ pop ebx
+ ret 4
+ }
+}
+#endif // _TARGET_X86_ && FEATURE_CORECLR
diff --git a/src/utilcode/staticnohost/.gitmirror b/src/utilcode/staticnohost/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/utilcode/staticnohost/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/utilcode/staticnohost/CMakeLists.txt b/src/utilcode/staticnohost/CMakeLists.txt
new file mode 100644
index 0000000000..7dc8b9a3af
--- /dev/null
+++ b/src/utilcode/staticnohost/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_definitions(-DSELF_NO_HOST)
+add_definitions(-D_CRTIMP=) # use static version of crt
+
+set(UTILCODE_SOURCES ${UTILCODE_SOURCES} ../hostimpl.cpp)
+
+add_library(utilcodestaticnohost STATIC ${UTILCODE_SOURCES})
diff --git a/src/utilcode/staticnohost/staticnohost.nativeproj b/src/utilcode/staticnohost/staticnohost.nativeproj
new file mode 100644
index 0000000000..4ef47670f3
--- /dev/null
+++ b/src/utilcode/staticnohost/staticnohost.nativeproj
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+ <PropertyGroup Label="Globals">
+ <SccProjectName>SAK</SccProjectName>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>utilcodestaticnohost</OutputName>
+ </PropertyGroup>
+ <Import Project="staticnohost.targets" />
+</Project>
diff --git a/src/utilcode/staticnohost/staticnohost.targets b/src/utilcode/staticnohost/staticnohost.targets
new file mode 100644
index 0000000000..163090adeb
--- /dev/null
+++ b/src/utilcode/staticnohost/staticnohost.targets
@@ -0,0 +1,15 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="dogfood">
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\utilcode.settings.targets" />
+
+ <PropertyGroup>
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DSELF_NO_HOST -D_CRTIMP=</ClAdditionalOptions>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <CppCompile Include="..\hostimpl.cpp" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.targets" />
+
+</Project>
diff --git a/src/utilcode/stdafx.cpp b/src/utilcode/stdafx.cpp
new file mode 100644
index 0000000000..d82c602102
--- /dev/null
+++ b/src/utilcode/stdafx.cpp
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// stdafx.cpp
+//
+
+//
+// Host for precompiled header.
+//
+//*****************************************************************************
+#include "stdafx.h" // Precompiled header key.
diff --git a/src/utilcode/stdafx.h b/src/utilcode/stdafx.h
new file mode 100644
index 0000000000..c572c3b302
--- /dev/null
+++ b/src/utilcode/stdafx.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// stdafx.h
+//
+
+//
+// Common include file for utility code.
+//*****************************************************************************
+#pragma once
+
+#include <switches.h>
+#include <crtwrap.h>
+
+#define IN_WINFIX_CPP
+
+#include <winwrap.h>
+
+#include "volatile.h"
+#include "static_assert.h"
+
diff --git a/src/utilcode/stgpool.cpp b/src/utilcode/stgpool.cpp
new file mode 100644
index 0000000000..a4dda6ba82
--- /dev/null
+++ b/src/utilcode/stgpool.cpp
@@ -0,0 +1,2451 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// StgPool.cpp
+//
+
+//
+// Pools are used to reduce the amount of data actually required in the database.
+// This allows for duplicate string and binary values to be folded into one
+// copy shared by the rest of the database. Strings are tracked in a hash
+// table when insert/changing data to find duplicates quickly. The strings
+// are then persisted consecutively in a stream in the database format.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard include.
+#include <stgpool.h> // Our interface definitions.
+#include <posterror.h> // Error handling.
+#include <safemath.h> // CLRSafeInt integer overflow checking
+#include "../md/inc/streamutil.h"
+
+#include "ex.h"
+
+#ifdef FEATURE_PREJIT
+#include <corcompile.h>
+#endif
+
+using namespace StreamUtil;
+
+#define MAX_CHAIN_LENGTH 20 // Max chain length before rehashing.
+
+//
+//
+// StgPool
+//
+//
+
+
+//*****************************************************************************
+// Free any memory we allocated.
+//*****************************************************************************
+StgPool::~StgPool()
+{
+ WRAPPER_NO_CONTRACT;
+
+ Uninit();
+} // StgPool::~StgPool()
+
+
+//*****************************************************************************
+// Init the pool for use. This is called for both the create empty case.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems) // Estimated item count.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ // Make sure we aren't stomping anything and are properly initialized.
+ _ASSERTE(m_pSegData == m_zeros);
+ _ASSERTE(m_pNextSeg == 0);
+ _ASSERTE(m_pCurSeg == this);
+ _ASSERTE(m_cbCurSegOffset == 0);
+ _ASSERTE(m_cbSegSize == 0);
+ _ASSERTE(m_cbSegNext == 0);
+
+ m_bReadOnly = false;
+ m_bFree = false;
+
+ return S_OK;
+} // StgPool::InitNew
+
+//*****************************************************************************
+// Init the pool from existing data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::InitOnMem(
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // Make sure we aren't stomping anything and are properly initialized.
+ _ASSERTE(m_pSegData == m_zeros);
+ _ASSERTE(m_pNextSeg == 0);
+ _ASSERTE(m_pCurSeg == this);
+ _ASSERTE(m_cbCurSegOffset == 0);
+
+ // Create case requires no further action.
+ if (!pData)
+ return (E_INVALIDARG);
+
+ // Might we be extending this heap?
+ m_bReadOnly = bReadOnly;
+
+
+ m_pSegData = reinterpret_cast<BYTE*>(pData);
+ m_cbSegSize = iSize;
+ m_cbSegNext = iSize;
+
+ m_bFree = false;
+
+ return (S_OK);
+} // StgPool::InitOnMem
+
+//*****************************************************************************
+// Called when the pool must stop accessing memory passed to InitOnMem().
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::TakeOwnershipOfInitMem()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // If the pool doesn't have a pointer to non-owned memory, done.
+ if (m_bFree)
+ return (S_OK);
+
+ // If the pool doesn't have a pointer to memory at all, done.
+ if (m_pSegData == m_zeros)
+ {
+ _ASSERTE(m_cbSegSize == 0);
+ return (S_OK);
+ }
+
+ // Get some memory to keep.
+ BYTE *pData = new (nothrow) BYTE[m_cbSegSize+4];
+ if (pData == 0)
+ return (PostError(OutOfMemory()));
+
+ // Copy the old data to the new memory.
+ memcpy(pData, m_pSegData, m_cbSegSize);
+ m_pSegData = pData;
+ m_bFree = true;
+
+ return (S_OK);
+} // StgPool::TakeOwnershipOfInitMem
+
+//*****************************************************************************
+// Clear out this pool. Cannot use until you call InitNew.
+//*****************************************************************************
+void StgPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Free base segment, if appropriate.
+ if (m_bFree && (m_pSegData != m_zeros))
+ {
+ delete [] m_pSegData;
+ m_bFree = false;
+ }
+
+ // Free chain, if any.
+ StgPoolSeg *pSeg = m_pNextSeg;
+ while (pSeg)
+ {
+ StgPoolSeg *pNext = pSeg->m_pNextSeg;
+ delete [] (BYTE*)pSeg;
+ pSeg = pNext;
+ }
+
+ // Clear vars.
+ m_pSegData = (BYTE*)m_zeros;
+ m_cbSegSize = m_cbSegNext = 0;
+ m_pNextSeg = 0;
+ m_pCurSeg = this;
+ m_cbCurSegOffset = 0;
+} // StgPool::Uninit
+
+//*****************************************************************************
+// Called to copy the pool to writable memory, reset the r/o bit.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::ConvertToRW()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr; // A result.
+ IfFailRet(TakeOwnershipOfInitMem());
+
+ IfFailRet(SetHash(true));
+
+ m_bReadOnly = false;
+
+ return S_OK;
+} // StgPool::ConvertToRW
+
+//*****************************************************************************
+// Turn hashing off or on. Real implementation as required in subclass.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ return S_OK;
+} // StgPool::SetHash
+
+//*****************************************************************************
+// Trim any empty final segment.
+//*****************************************************************************
+void StgPool::Trim()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // If no chained segments, nothing to do.
+ if (m_pNextSeg == 0)
+ return;
+
+ // Handle special case for a segment that was completely unused.
+ if (m_pCurSeg->m_cbSegNext == 0)
+ {
+ // Find the segment which points to the empty segment.
+ StgPoolSeg *pPrev;
+ for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg);
+ _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg);
+
+ // Free the empty segment.
+ delete [] (BYTE*) m_pCurSeg;
+
+ // Fix the pCurSeg pointer.
+ pPrev->m_pNextSeg = 0;
+ m_pCurSeg = pPrev;
+
+ // Adjust the base offset, because the PREVIOUS seg is now current.
+ _ASSERTE(m_pCurSeg->m_cbSegNext <= m_cbCurSegOffset);
+ m_cbCurSegOffset = m_cbCurSegOffset - m_pCurSeg->m_cbSegNext;
+ }
+} // StgPool::Trim
+
+//*****************************************************************************
+// Allocate memory if we don't have any, or grow what we have. If successful,
+// then at least iRequired bytes will be allocated.
+//*****************************************************************************
+bool StgPool::Grow( // true if successful.
+ ULONG iRequired) // Min required bytes to allocate.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END
+
+ ULONG iNewSize; // New size we want.
+ StgPoolSeg *pNew; // Temp pointer for malloc.
+
+ _ASSERTE(!m_bReadOnly);
+
+ // Would this put the pool over 2GB?
+ if ((m_cbCurSegOffset + iRequired) > INT_MAX)
+ return (false);
+
+ // Adjust grow size as a ratio to avoid too many reallocs.
+ if ((m_pCurSeg->m_cbSegNext + m_cbCurSegOffset) / m_ulGrowInc >= 3)
+ m_ulGrowInc *= 2;
+
+ // NOTE: MD\DataSource\RemoteMDInternalRWSource has taken a dependency that there
+ // won't be more than 1000 segments. Given the current exponential growth algorithm
+ // we'll never get anywhere close to that, but if the algorithm changes to allow for
+ // many segments, please update that source as well.
+
+ // If first time, handle specially.
+ if (m_pSegData == m_zeros)
+ {
+ // Allocate the buffer.
+ iNewSize = max(m_ulGrowInc, iRequired);
+ BYTE *pSegData = new (nothrow) BYTE[iNewSize + 4];
+ if (pSegData == NULL)
+ return false;
+ m_pSegData = pSegData;
+
+ // Will need to delete it.
+ m_bFree = true;
+
+ // How big is this initial segment?
+ m_cbSegSize = iNewSize;
+
+ // Do some validation of var fields.
+ _ASSERTE(m_cbSegNext == 0);
+ _ASSERTE(m_pCurSeg == this);
+ _ASSERTE(m_pNextSeg == NULL);
+
+ return true;
+ }
+
+ // Allocate the new space enough for header + data.
+ iNewSize = (ULONG)(max(m_ulGrowInc, iRequired) + sizeof(StgPoolSeg));
+ pNew = (StgPoolSeg *)new (nothrow) BYTE[iNewSize+4];
+ if (pNew == NULL)
+ return false;
+
+ // Set the fields in the new segment.
+ pNew->m_pSegData = reinterpret_cast<BYTE*>(pNew) + sizeof(StgPoolSeg);
+ _ASSERTE(ALIGN4BYTE(reinterpret_cast<ULONG_PTR>(pNew->m_pSegData)) == reinterpret_cast<ULONG_PTR>(pNew->m_pSegData));
+ pNew->m_pNextSeg = 0;
+ pNew->m_cbSegSize = iNewSize - sizeof(StgPoolSeg);
+ pNew->m_cbSegNext = 0;
+
+ // Calculate the base offset of the new segment.
+ m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext;
+
+ // Handle special case for a segment that was completely unused.
+ //<TODO>@todo: Trim();</TODO>
+ if (m_pCurSeg->m_cbSegNext == 0)
+ {
+ // Find the segment which points to the empty segment.
+ StgPoolSeg *pPrev;
+ for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg);
+ _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg);
+
+ // Free the empty segment.
+ delete [] (BYTE *) m_pCurSeg;
+
+ // Link in the new segment.
+ pPrev->m_pNextSeg = pNew;
+ m_pCurSeg = pNew;
+
+ return true;
+ }
+
+#ifndef NO_CRT
+ // Give back any memory that we won't use.
+ if (m_pNextSeg == 0)
+ { // First segment allocated as [header]->[data].
+ // Be sure that we are contracting the allocation.
+ if (m_pCurSeg->m_cbSegNext < (_msize(m_pCurSeg->m_pSegData) - 4))
+ {
+ // Contract the allocation.
+ void *pRealloc = _expand(m_pCurSeg->m_pSegData, m_pCurSeg->m_cbSegNext+4);
+ // Shouldn't have moved.
+ _ASSERTE(pRealloc == m_pCurSeg->m_pSegData);
+ }
+ }
+ else
+ { // Chained segments are allocated together, [header][data].
+ // Be sure that we are contracting the allocation.
+ if (m_pCurSeg->m_cbSegNext+sizeof(StgPoolSeg) < (_msize(m_pCurSeg) - 4))
+ {
+ // Contract the allocation.
+ void *pRealloc = _expand(m_pCurSeg, m_pCurSeg->m_cbSegNext + sizeof(StgPoolSeg) + 4);
+ // Shouldn't have moved.
+ _ASSERTE(pRealloc == m_pCurSeg);
+ }
+ }
+#endif
+
+ // Fix the size of the old segment.
+ m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext;
+
+ // Link the new segment into the chain.
+ m_pCurSeg->m_pNextSeg = pNew;
+ m_pCurSeg = pNew;
+
+ return true;
+} // StgPool::Grow
+
+//*****************************************************************************
+// Add a segment to the chain of segments.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::AddSegment(
+ const void *pData, // The data.
+ ULONG cbData, // Size of the data.
+ bool bCopy) // If true, make a copy of the data.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ StgPoolSeg *pNew; // Temp pointer for malloc.
+
+
+ // If we need to copy the data, just grow the heap by enough to take the
+ // the new data, and copy it in.
+ if (bCopy)
+ {
+ void *pDataToAdd = new (nothrow) BYTE[cbData];
+ IfNullRet(pDataToAdd);
+ memcpy(pDataToAdd, pData, cbData);
+ pData = pDataToAdd;
+ }
+
+ // If first time, handle specially.
+ if (m_pSegData == m_zeros)
+ { // Data was passed in.
+ m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData));
+ m_cbSegSize = cbData;
+ m_cbSegNext = cbData;
+ _ASSERTE(m_pNextSeg == NULL);
+
+ // Will not delete it.
+ m_bFree = false;
+
+ return S_OK;
+ }
+
+ // Not first time. Handle a completely empty tail segment.
+ Trim();
+
+ // Abandon any space past the end of the current live data.
+ _ASSERTE(m_pCurSeg->m_cbSegSize >= m_pCurSeg->m_cbSegNext);
+ m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext;
+
+ // Allocate a new segment header.
+ pNew = (StgPoolSeg *) new (nothrow) BYTE[sizeof(StgPoolSeg)];
+ IfNullRet(pNew);
+
+ // Set the fields in the new segment.
+ pNew->m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData));
+ pNew->m_pNextSeg = NULL;
+ pNew->m_cbSegSize = cbData;
+ pNew->m_cbSegNext = cbData;
+
+ // Calculate the base offset of the new segment.
+ m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext;
+
+ // Link the segment into the chain.
+ _ASSERTE(m_pCurSeg->m_pNextSeg == NULL);
+ m_pCurSeg->m_pNextSeg = pNew;
+ m_pCurSeg = pNew;
+
+ return S_OK;
+} // StgPool::AddSegment
+
+#ifndef DACCESS_COMPILE
+//*****************************************************************************
+// The entire string pool is written to the given stream. The stream is aligned
+// to a 4 byte boundary.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::PersistToStream(
+ IStream *pIStream) // The stream to write to.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ ULONG cbTotal; // Total bytes written.
+ StgPoolSeg *pSeg; // A segment being written.
+
+ _ASSERTE(m_pSegData != m_zeros);
+
+ // Start with the base segment.
+ pSeg = this;
+ cbTotal = 0;
+
+ EX_TRY
+ {
+ // As long as there is data, write it.
+ while (pSeg != NULL)
+ {
+ // If there is data in the segment . . .
+ if (pSeg->m_cbSegNext)
+ { // . . . write and count the data.
+ if (FAILED(hr = pIStream->Write(pSeg->m_pSegData, pSeg->m_cbSegNext, 0)))
+ break;
+ cbTotal += pSeg->m_cbSegNext;
+ }
+
+ // Get the next segment.
+ pSeg = pSeg->m_pNextSeg;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Align to variable (0-4 byte) boundary.
+ UINT32 cbTotalAligned;
+ if (FAILED(Align(cbTotal, &cbTotalAligned)))
+ {
+ hr = COR_E_BADIMAGEFORMAT;
+ }
+ else
+ {
+ if (cbTotalAligned > cbTotal)
+ {
+ _ASSERTE(sizeof(hr) >= 3);
+ hr = 0;
+ hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0);
+ }
+ }
+ }
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return hr;
+} // StgPool::PersistToStream
+#endif //!DACCESS_COMPILE
+
+//*****************************************************************************
+// The entire string pool is written to the given stream. The stream is aligned
+// to a 4 byte boundary.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::PersistPartialToStream(
+ IStream *pIStream, // The stream to write to.
+ ULONG iOffset) // Starting offset.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK; // A result.
+ ULONG cbTotal; // Total bytes written.
+ StgPoolSeg *pSeg; // A segment being written.
+
+ _ASSERTE(m_pSegData != m_zeros);
+
+ // Start with the base segment.
+ pSeg = this;
+ cbTotal = 0;
+
+ // As long as there is data, write it.
+ while (pSeg != NULL)
+ {
+ // If there is data in the segment . . .
+ if (pSeg->m_cbSegNext)
+ { // If this data should be skipped...
+ if (iOffset >= pSeg->m_cbSegNext)
+ { // Skip it
+ iOffset -= pSeg->m_cbSegNext;
+ }
+ else
+ { // At least some data should be written, so write and count the data.
+ IfFailRet(pIStream->Write(pSeg->m_pSegData+iOffset, pSeg->m_cbSegNext-iOffset, 0));
+ cbTotal += pSeg->m_cbSegNext-iOffset;
+ iOffset = 0;
+ }
+ }
+
+ // Get the next segment.
+ pSeg = pSeg->m_pNextSeg;
+ }
+
+ // Align to variable (0-4 byte) boundary.
+ UINT32 cbTotalAligned;
+ if (FAILED(Align(cbTotal, &cbTotalAligned)))
+ {
+ return COR_E_BADIMAGEFORMAT;
+ }
+ if (cbTotalAligned > cbTotal)
+ {
+ _ASSERTE(sizeof(hr) >= 3);
+ hr = 0;
+ hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0);
+ }
+
+ return hr;
+} // StgPool::PersistPartialToStream
+
+// Copies data from pSourcePool starting at index nStartSourceIndex.
+__checkReturn
+HRESULT
+StgPool::CopyPool(
+ UINT32 nStartSourceIndex,
+ const StgPool *pSourcePool)
+{
+ HRESULT hr;
+ UINT32 cbDataSize;
+ BYTE *pbData = NULL;
+
+ if (nStartSourceIndex == pSourcePool->GetRawSize())
+ { // There's nothing to copy
+ return S_OK;
+ }
+ if (nStartSourceIndex > pSourcePool->GetRawSize())
+ { // Invalid input
+ Debug_ReportInternalError("The caller should not pass invalid start index in the pool.");
+ IfFailGo(METADATA_E_INDEX_NOTFOUND);
+ }
+
+ // Allocate new segment
+ cbDataSize = pSourcePool->GetRawSize() - nStartSourceIndex;
+ pbData = new (nothrow) BYTE[cbDataSize];
+ IfNullGo(pbData);
+
+ // Copy data to the new segment
+ UINT32 cbCopiedDataSize;
+ IfFailGo(pSourcePool->CopyData(
+ nStartSourceIndex,
+ pbData,
+ cbDataSize,
+ &cbCopiedDataSize));
+ // Check that we copied everything
+ if (cbDataSize != cbCopiedDataSize)
+ {
+ Debug_ReportInternalError("It is expected to copy everything from the source pool.");
+ IfFailGo(E_FAIL);
+ }
+
+ // Add the newly allocated segment to the pool
+ IfFailGo(AddSegment(
+ pbData,
+ cbDataSize,
+ false)); // fCopyData
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pbData != NULL)
+ {
+ delete [] pbData;
+ }
+ }
+ return hr;
+} // StgPool::CopyPool
+
+// Copies data from the pool into a buffer. It will correctly walk all segments for the copy.
+__checkReturn
+HRESULT
+StgPool::CopyData(
+ UINT32 nOffset,
+ BYTE *pBuffer,
+ UINT32 cbBuffer,
+ UINT32 *pcbWritten) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ PRECONDITION(CheckPointer(pBuffer));
+ PRECONDITION(CheckPointer(pcbWritten));
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ const StgPoolSeg *pSeg; // A segment being written.
+
+ _ASSERTE(m_pSegData != m_zeros);
+
+ // Start with the base segment.
+ pSeg = this;
+ *pcbWritten = 0;
+
+ // As long as there is data, write it.
+ while (pSeg != NULL)
+ {
+ // If there is data in the segment . . .
+ if (pSeg->m_cbSegNext)
+ { // If this data should be skipped...
+ if (nOffset >= pSeg->m_cbSegNext)
+ { // Skip it
+ nOffset -= pSeg->m_cbSegNext;
+ }
+ else
+ {
+ ULONG nNumBytesToCopy = pSeg->m_cbSegNext - nOffset;
+ if (nNumBytesToCopy > (cbBuffer - *pcbWritten))
+ {
+ _ASSERTE(!"Buffer isn't big enough to copy everything!");
+ nNumBytesToCopy = cbBuffer - *pcbWritten;
+ }
+
+ memcpy(pBuffer + *pcbWritten, pSeg->m_pSegData+nOffset, nNumBytesToCopy);
+
+ *pcbWritten += nNumBytesToCopy;
+ nOffset = 0;
+ }
+ }
+
+ // Get the next segment.
+ pSeg = pSeg->m_pNextSeg;
+ }
+
+ return hr;
+} // StgPool::CopyData
+
+//*****************************************************************************
+// Get a pointer to the data at some offset. May require traversing the
+// chain of extensions. It is the caller's responsibility not to attempt
+// to access data beyond the end of a segment.
+// This is an internal accessor, and should only be called when the data
+// is not in the base segment.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::GetData_i(
+ UINT32 nOffset,
+ MetaData::DataBlob *pData)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Shouldn't be called on base segment.
+ _ASSERTE(nOffset >= m_cbSegNext);
+ StgPoolSeg *pSeg = this;
+
+ while ((nOffset > 0) && (nOffset >= pSeg->m_cbSegNext))
+ {
+ // On to next segment.
+ nOffset -= pSeg->m_cbSegNext;
+ pSeg = pSeg->m_pNextSeg;
+
+ // Is there a next?
+ if (pSeg == NULL)
+ {
+ Debug_ReportError("Invalid offset passed - reached end of pool.");
+ pData->Clear();
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+ }
+
+ // For the case where we want to read the first item and the pool is empty.
+ if (nOffset == pSeg->m_cbSegNext)
+ { // Can only be if both == 0
+ Debug_ReportError("Invalid offset passed - it is at the end of pool.");
+ pData->Clear();
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+
+ pData->Init(pSeg->m_pSegData + nOffset, pSeg->m_cbSegNext - nOffset);
+
+ return S_OK;
+} // StgPool::GetData_i
+
+//
+//
+// StgStringPool
+//
+//
+
+
+//*****************************************************************************
+// Create a new, empty string pool.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems) // Estimated item count.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+ UINT32 nEmptyStringOffset;
+
+ // Let base class intialize.
+ IfFailRet(StgPool::InitNew());
+
+ // Set initial table sizes, if specified.
+ if (cbSize > 0)
+ {
+ if (!Grow(cbSize))
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ if (cItems > 0)
+ {
+ m_Hash.SetBuckets(cItems);
+ }
+
+ // Init with empty string.
+ IfFailRet(AddString("", &nEmptyStringOffset));
+ // Empty string had better be at offset 0.
+ _ASSERTE(nEmptyStringOffset == 0);
+
+ return hr;
+} // StgStringPool::InitNew
+
+//*****************************************************************************
+// Load a string heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new strings.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::InitOnMem(
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // There may be up to three extra '\0' characters appended for padding. Trim them.
+ char *pchData = reinterpret_cast<char*>(pData);
+ while (iSize > 1 && pchData[iSize-1] == 0 && pchData[iSize-2] == 0)
+ --iSize;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly));
+
+ //<TODO>@todo: defer this until we hand out a pointer.</TODO>
+ if (!bReadOnly)
+ {
+ IfFailRet(TakeOwnershipOfInitMem());
+ IfFailRet(RehashStrings());
+ }
+
+ return hr;
+} // StgStringPool::InitOnMem
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+void StgStringPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Clear the hash table.
+ m_Hash.Clear();
+
+ // Let base class clean up.
+ StgPool::Uninit();
+} // StgStringPool::Uninit
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // If turning on hash again, need to rehash all strings.
+ if (bHash)
+ hr = RehashStrings();
+
+ m_bHash = bHash;
+ return (hr);
+} // StgStringPool::SetHash
+
+//*****************************************************************************
+// The string will be added to the pool. The offset of the string in the pool
+// is returned in *piOffset. If the string is already in the pool, then the
+// offset will be to the existing copy of the string.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::AddString(
+ LPCSTR szString, // The string to add to pool.
+ UINT32 *pnOffset) // Return offset of string here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ STRINGHASH *pHash; // Hash item for add.
+ ULONG iLen; // To handle non-null strings.
+ LPSTR pData; // Pointer to location for new string.
+ HRESULT hr;
+
+ _ASSERTE(!m_bReadOnly);
+
+ // Null pointer is an error.
+ if (szString == 0)
+ return (PostError(E_INVALIDARG));
+
+ // Find the real length we need in buffer.
+ iLen = (ULONG)(strlen(szString) + 1);
+
+ // Where to put the new string?
+ if (iLen > GetCbSegAvailable())
+ {
+ if (!Grow(iLen))
+ return (PostError(OutOfMemory()));
+ }
+ pData = reinterpret_cast<LPSTR>(GetNextLocation());
+
+ // Copy the data into the buffer.
+ strcpy_s(pData, iLen, szString);
+
+ // If the hash table is to be kept built (default).
+ if (m_bHash)
+ {
+ // Find or add the entry.
+ pHash = m_Hash.Find(pData, true);
+ if (!pHash)
+ return (PostError(OutOfMemory()));
+
+ // If the entry was new, keep the new string.
+ if (pHash->iOffset == 0xffffffff)
+ {
+ *pnOffset = pHash->iOffset = GetNextOffset();
+ SegAllocate(iLen);
+
+ // Check for hash chains that are too long.
+ if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH)
+ {
+ IfFailRet(RehashStrings());
+ }
+ }
+ // Else use the old one.
+ else
+ {
+ *pnOffset = pHash->iOffset;
+ }
+ }
+ // Probably an import which defers the hash table for speed.
+ else
+ {
+ *pnOffset = GetNextOffset();
+ SegAllocate(iLen);
+ }
+ return S_OK;
+} // StgStringPool::AddString
+
+//*****************************************************************************
+// Add a string to the pool with Unicode to UTF8 conversion.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::AddStringW(
+ LPCWSTR szString, // The string to add to pool.
+ UINT32 *pnOffset) // Return offset of string here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ STRINGHASH *pHash; // Hash item for add.
+ ULONG iLen; // Correct length after conversion.
+ LPSTR pData; // Pointer to location for new string.
+
+ _ASSERTE(!m_bReadOnly);
+
+ // Null pointer is an error.
+ if (szString == 0)
+ return (PostError(E_INVALIDARG));
+
+ // Special case empty string.
+ if (*szString == '\0')
+ {
+ *pnOffset = 0;
+ return (S_OK);
+ }
+
+ // How many bytes will be required in the heap?
+ iLen = ::WszWideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ szString,
+ -1, // null-terminated string
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ // WCTMB includes trailing 0 if (when passing parameter #4 (length) -1.
+
+ // Check for room.
+ if (iLen > GetCbSegAvailable())
+ {
+ if (!Grow(iLen))
+ return (PostError(OutOfMemory()));
+ }
+ pData = reinterpret_cast<LPSTR>(GetNextLocation());
+
+ // Convert the data in place to the correct location.
+ iLen = ::WszWideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ szString,
+ -1,
+ pData,
+ GetCbSegAvailable(),
+ NULL,
+ NULL);
+ if (iLen == 0)
+ return (BadError(HRESULT_FROM_NT(GetLastError())));
+
+ // If the hash table is to be kept built (default).
+ if (m_bHash)
+ {
+ // Find or add the entry.
+ pHash = m_Hash.Find(pData, true);
+ if (!pHash)
+ return (PostError(OutOfMemory()));
+
+ // If the entry was new, keep the new string.
+ if (pHash->iOffset == 0xffffffff)
+ {
+ *pnOffset = pHash->iOffset = GetNextOffset();
+ SegAllocate(iLen);
+ }
+ // Else use the old one.
+ else
+ {
+ *pnOffset = pHash->iOffset;
+ }
+ }
+ // Probably an import which defers the hash table for speed.
+ else
+ {
+ *pnOffset = GetNextOffset();
+ SegAllocate(iLen);
+ }
+ return (S_OK);
+} // StgStringPool::AddStringW
+
+
+//*****************************************************************************
+// Clears out the existing hash table used to eliminate duplicates. Then
+// rebuilds the hash table from scratch based on the current data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::RehashStrings()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ ULONG iOffset; // Loop control.
+ ULONG iMax; // End of loop.
+ ULONG iSeg; // Location within segment.
+ StgPoolSeg *pSeg = this; // To loop over segments.
+ STRINGHASH *pHash; // Hash item for add.
+ LPCSTR pString; // A string;
+ ULONG iLen; // The string's length.
+ int iBuckets; // Buckets in the hash.
+ int iCount; // Items in the hash.
+ int iNewBuckets; // New count of buckets in the hash.
+
+ // Determine the new bucket size.
+ iBuckets = m_Hash.Buckets();
+ iCount = m_Hash.Count();
+ iNewBuckets = max(iCount, iBuckets+iBuckets/2+1);
+
+ // Remove any stale data.
+ m_Hash.Clear();
+ m_Hash.SetBuckets(iNewBuckets);
+
+ // How far should the loop go.
+ iMax = GetNextOffset();
+
+ // Go through each string, skipping initial empty string.
+ for (iSeg=iOffset=1; iOffset < iMax; )
+ {
+ // Get the string from the pool.
+ pString = reinterpret_cast<LPCSTR>(pSeg->m_pSegData + iSeg);
+ // Add the string to the hash table.
+ if ((pHash = m_Hash.Add(pString)) == 0)
+ return (PostError(OutOfMemory()));
+ pHash->iOffset = iOffset;
+
+ // Move to next string.
+ iLen = (ULONG)(strlen(pString) + 1);
+ iOffset += iLen;
+ iSeg += iLen;
+ if (iSeg >= pSeg->m_cbSegNext)
+ {
+ pSeg = pSeg->m_pNextSeg;
+ iSeg = 0;
+ }
+ }
+ return (S_OK);
+} // StgStringPool::RehashStrings
+
+//
+//
+// StgGuidPool
+//
+//
+
+__checkReturn
+HRESULT
+StgGuidPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems) // Estimated item count.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr; // A result.
+
+ if (FAILED(hr = StgPool::InitNew()))
+ return (hr);
+
+ // Set initial table sizes, if specified.
+ if (cbSize)
+ if (!Grow(cbSize))
+ return E_OUTOFMEMORY;
+ if (cItems)
+ m_Hash.SetBuckets(cItems);
+
+ return (S_OK);
+} // StgGuidPool::InitNew
+
+//*****************************************************************************
+// Load a Guid heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new Guids.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::InitOnMem(
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly));
+
+ // For init on existing mem case.
+ if (pData && iSize)
+ {
+ // If we cannot update, then we don't need a hash table.
+ if (bReadOnly)
+ return S_OK;
+
+ //<TODO>@todo: defer this until we hand out a pointer.</TODO>
+ IfFailRet(TakeOwnershipOfInitMem());
+
+ // Build the hash table on the data.
+ if (FAILED(hr = RehashGuids()))
+ {
+ Uninit();
+ return hr;
+ }
+ }
+
+ return S_OK;
+} // StgGuidPool::InitOnMem
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+void StgGuidPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Clear the hash table.
+ m_Hash.Clear();
+
+ // Let base class clean up.
+ StgPool::Uninit();
+} // StgGuidPool::Uninit
+
+//*****************************************************************************
+// Add a segment to the chain of segments.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::AddSegment(
+ const void *pData, // The data.
+ ULONG cbData, // Size of the data.
+ bool bCopy) // If true, make a copy of the data.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // Want an integeral number of GUIDs.
+ _ASSERTE((cbData % sizeof(GUID)) == 0);
+
+ return StgPool::AddSegment(pData, cbData, bCopy);
+
+} // StgGuidPool::AddSegment
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // If turning on hash again, need to rehash all guids.
+ if (bHash)
+ hr = RehashGuids();
+
+ m_bHash = bHash;
+ return (hr);
+} // StgGuidPool::SetHash
+
+//*****************************************************************************
+// The Guid will be added to the pool. The index of the Guid in the pool
+// is returned in *piIndex. If the Guid is already in the pool, then the
+// index will be to the existing copy of the Guid.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::AddGuid(
+ const GUID *pGuid, // The Guid to add to pool.
+ UINT32 *pnIndex) // Return 1-based index of Guid here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ GUIDHASH *pHash = NULL; // Hash item for add.
+
+ GUID guid = *pGuid;
+ SwapGuid(&guid);
+
+ // Special case for GUID_NULL
+ if (guid == GUID_NULL)
+ {
+ *pnIndex = 0;
+ return S_OK;
+ }
+
+ // If the hash table is to be kept built (default).
+ if (m_bHash)
+ {
+ // Find or add the entry.
+ pHash = m_Hash.Find(&guid, true);
+ if (!pHash)
+ return (PostError(OutOfMemory()));
+
+ // If the guid was found, just use it.
+ if (pHash->iIndex != 0xffffffff)
+ { // Return 1-based index.
+ *pnIndex = pHash->iIndex;
+ return S_OK;
+ }
+ }
+
+ // Space on heap for new guid?
+ if (sizeof(GUID) > GetCbSegAvailable())
+ {
+ if (!Grow(sizeof(GUID)))
+ return (PostError(OutOfMemory()));
+ }
+
+ // Copy the guid to the heap.
+ *reinterpret_cast<GUID*>(GetNextLocation()) = guid;
+
+ // Give the 1-based index back to caller.
+ *pnIndex = (GetNextOffset() / sizeof(GUID)) + 1;
+
+ // If hashing, save the 1-based index in the hash.
+ if (m_bHash)
+ pHash->iIndex = *pnIndex;
+
+ // Update heap counters.
+ SegAllocate(sizeof(GUID));
+
+ return S_OK;
+} // StgGuidPool::AddGuid
+
+//*****************************************************************************
+// Recompute the hashes for the pool.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::RehashGuids()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ ULONG iOffset; // Loop control.
+ ULONG iMax; // End of loop.
+ ULONG iSeg; // Location within segment.
+ StgPoolSeg *pSeg = this; // To loop over segments.
+ GUIDHASH *pHash; // Hash item for add.
+ GUID *pGuid; // A guid;
+
+ // Remove any stale data.
+ m_Hash.Clear();
+
+ // How far should the loop go.
+ iMax = GetNextOffset();
+
+ // Go through each guid.
+ for (iSeg=iOffset=0; iOffset < iMax; )
+ {
+ // Get a pointer to the guid.
+ pGuid = reinterpret_cast<GUID*>(pSeg->m_pSegData + iSeg);
+ // Add the guid to the hash table.
+ if ((pHash = m_Hash.Add(pGuid)) == 0)
+ return (PostError(OutOfMemory()));
+ pHash->iIndex = iOffset / sizeof(GUID);
+
+ // Move to next Guid.
+ iOffset += sizeof(GUID);
+ iSeg += sizeof(GUID);
+ if (iSeg > pSeg->m_cbSegNext)
+ {
+ pSeg = pSeg->m_pNextSeg;
+ iSeg = 0;
+ }
+ }
+ return (S_OK);
+} // StgGuidPool::RehashGuids
+
+//
+//
+// StgBlobPool
+//
+//
+
+
+
+//*****************************************************************************
+// Create a new, empty blob pool.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems, // Estimated item count.
+ BOOL fAddEmptryItem) // Should we add an empty item at offset 0
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+
+ // Let base class intialize.
+ IfFailRet(StgPool::InitNew());
+
+ // Set initial table sizes, if specified.
+ if (cbSize > 0)
+ {
+ if (!Grow(cbSize))
+ return E_OUTOFMEMORY;
+ }
+ if (cItems > 0)
+ m_Hash.SetBuckets(cItems);
+
+ // Init with empty blob.
+
+ // Normally must do this, regardless if we currently have anything in the pool.
+ // If we don't do this, the first blob that gets added to the pool will
+ // have an offset of 0. This will cause this blob to have a token of
+ // 0x70000000, which is considered a nil string token.
+ //
+ // By inserting a zero length blob into the pool the being with, we're
+ // assured that the first blob added to the pool will have an offset
+ // of 1 and a token of 0x70000001, which is a valid token.
+ //
+ // The only time we wouldn't want to do this is if we're reading in a delta metadata.
+ // Then, we don't care if the first string is at offset 0... when the delta gets applied,
+ // the string will get moved to the appropriate offset.
+ if (fAddEmptryItem)
+ {
+ MetaData::DataBlob emptyBlob(NULL, 0);
+ UINT32 nIndex_Ignore;
+ IfFailRet(AddBlob(&emptyBlob, &nIndex_Ignore));
+ // Empty blob better be at offset 0.
+ _ASSERTE(nIndex_Ignore == 0);
+ }
+ return hr;
+} // StgBlobPool::InitNew
+
+//*****************************************************************************
+// Init the blob pool for use. This is called for both create and read case.
+// If there is existing data and bCopyData is true, then the data is rehashed
+// to eliminate dupes in future adds.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::InitOnMem(
+ void *pBuf, // Predefined data.
+ ULONG iBufSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pBuf, iBufSize, bReadOnly));
+
+ // Init hash table from existing data.
+ // If we cannot update, we don't need a hash table.
+ if (bReadOnly)
+ {
+ return S_OK;
+ }
+
+ //<TODO>@todo: defer this until we hand out a pointer.</TODO>
+ IfFailRet(TakeOwnershipOfInitMem());
+
+ UINT32 nMaxOffset = GetNextOffset();
+ for (UINT32 nOffset = 0; nOffset < nMaxOffset; )
+ {
+ MetaData::DataBlob blob;
+ BLOBHASH *pHash;
+
+ IfFailRet(GetBlobWithSizePrefix(nOffset, &blob));
+
+ // Add the blob to the hash table.
+ if ((pHash = m_Hash.Add(blob.GetDataPointer())) == NULL)
+ {
+ Uninit();
+ return E_OUTOFMEMORY;
+ }
+ pHash->iOffset = nOffset;
+
+ nOffset += blob.GetSize();
+ }
+ return S_OK;
+} // StgBlobPool::InitOnMem
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+void StgBlobPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Clear the hash table.
+ m_Hash.Clear();
+
+ // Let base class clean up.
+ StgPool::Uninit();
+} // StgBlobPool::Uninit
+
+
+//*****************************************************************************
+// The blob will be added to the pool. The offset of the blob in the pool
+// is returned in *piOffset. If the blob is already in the pool, then the
+// offset will be to the existing copy of the blob.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::AddBlob(
+ const MetaData::DataBlob *pData,
+ UINT32 *pnOffset) // Return offset of blob here.
+{
+ BLOBHASH *pHash; // Hash item for add.
+ void *pBytes; // Working pointer.
+ BYTE *pStartLoc; // Location to write real blob
+ ULONG iRequired; // How much buffer for this blob?
+ ULONG iFillerLen; // space to fill to make byte-aligned
+ HRESULT hr;
+
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // Can we handle this blob?
+ if (pData->GetSize() > CPackedLen::MAX_LEN)
+ return (PostError(CLDB_E_TOO_BIG));
+
+ // worst case is we need three more bytes to ensure byte-aligned, hence the 3
+ iRequired = pData->GetSize() + CPackedLen::Size(pData->GetSize()) + 3;
+ if (iRequired > GetCbSegAvailable())
+ {
+ if (!Grow(iRequired))
+ return (PostError(OutOfMemory()));
+ }
+
+ // unless changed due to alignment, the location of the blob is just
+ // the value returned by GetNextLocation(), which is also a iFillerLen of
+ // 0
+
+ pStartLoc = (BYTE *)GetNextLocation();
+ iFillerLen = 0;
+
+ // technichally, only the data portion must be DWORD-aligned. So, if the
+ // data length is zero, we don't need to worry about alignment.
+
+ // Pack in the length at pStartLoc (the start location)
+ pBytes = CPackedLen::PutLength(pStartLoc, pData->GetSize());
+
+ // Put the bytes themselves.
+ memcpy(pBytes, pData->GetDataPointer(), pData->GetSize());
+
+ // Find or add the entry.
+ if ((pHash = m_Hash.Find(GetNextLocation() + iFillerLen, true)) == NULL)
+ return (PostError(OutOfMemory()));
+
+ // If the entry was new, keep the new blob.
+ if (pHash->iOffset == 0xffffffff)
+ {
+ // this blob's offset is increased by iFillerLen bytes
+ pHash->iOffset = *pnOffset = GetNextOffset() + iFillerLen;
+ // only SegAllocate what we actually used, rather than what we requested
+ SegAllocate(pData->GetSize() + CPackedLen::Size(pData->GetSize()) + iFillerLen);
+
+ // Check for hash chains that are too long.
+ if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH)
+ {
+ IfFailRet(RehashBlobs());
+ }
+ }
+ // Else use the old one.
+ else
+ {
+ *pnOffset = pHash->iOffset;
+ }
+
+ return S_OK;
+} // StgBlobPool::AddBlob
+
+//*****************************************************************************
+// Return a pointer to a blob, and the size of the blob.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+
+ if (nOffset == 0)
+ {
+ // TODO: It would be nice to remove it, but people read behind the end of buffer,
+ // e.g. VBC reads 2 zeros even though the size is 0 when it's storing string in the blob.
+ // Nice to have: Move this to the public API only as a compat layer.
+ pData->Init((BYTE *)m_zeros, 0);
+ return S_OK;
+ }
+
+ IfFailGo(StgPool::GetData(nOffset, pData));
+
+ UINT32 cbBlobContentSize;
+ if (!pData->GetCompressedU(&cbBlobContentSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ if (!pData->TruncateToExactSize(cbBlobContentSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgBlobPool::GetBlob
+
+//*****************************************************************************
+// Return a pointer to a blob, and the size of the blob.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::GetBlobWithSizePrefix(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+
+ if (nOffset == 0)
+ {
+ // TODO: Should be a static empty blob once we get rid of m_zeros
+ pData->Init((BYTE *)m_zeros, 1);
+ return S_OK;
+ }
+
+ IfFailGo(StgPool::GetData(nOffset, pData));
+
+ UINT32 cbBlobContentSize;
+ UINT32 cbBlobSizePrefixSize;
+ if (!pData->PeekCompressedU(&cbBlobContentSize, &cbBlobSizePrefixSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ //_ASSERTE(cbBlobSizePrefixSize <= 4);
+ //_ASSERTE(cbBlobContentSize <= CompressedInteger::const_Max);
+
+ // Cannot overflow, because previous asserts hold (in comments)
+ UINT32 cbBlobSize;
+ cbBlobSize = cbBlobContentSize + cbBlobSizePrefixSize;
+ if (!pData->TruncateToExactSize(cbBlobSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgBlobPool::GetBlob
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // If turning on hash again, need to rehash all Blobs.
+ if (bHash)
+ hr = RehashBlobs();
+
+ //<TODO>@todo: m_bHash = bHash;</TODO>
+ return (hr);
+} // StgBlobPool::SetHash
+
+//*****************************************************************************
+// Clears out the existing hash table used to eliminate duplicates. Then
+// rebuilds the hash table from scratch based on the current data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::RehashBlobs()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ void const *pBlob; // Pointer to a given blob.
+ ULONG cbBlob; // Length of a blob.
+ int iSizeLen = 0; // Size of an encoded length.
+ ULONG iOffset; // Location within iteration.
+ ULONG iMax; // End of loop.
+ ULONG iSeg; // Location within segment.
+ StgPoolSeg *pSeg = this; // To loop over segments.
+ BLOBHASH *pHash; // Hash item for add.
+ int iBuckets; // Buckets in the hash.
+ int iCount; // Items in the hash.
+ int iNewBuckets; // New count of buckets in the hash.
+
+ // Determine the new bucket size.
+ iBuckets = m_Hash.Buckets();
+ iCount = m_Hash.Count();
+ iNewBuckets = max(iCount, iBuckets+iBuckets/2+1);
+
+ // Remove any stale data.
+ m_Hash.Clear();
+ m_Hash.SetBuckets(iNewBuckets);
+
+ // How far should the loop go.
+ iMax = GetNextOffset();
+
+ // Go through each string, skipping initial empty string.
+ for (iSeg=iOffset=0; iOffset < iMax; )
+ {
+ // Get the string from the pool.
+ pBlob = pSeg->m_pSegData + iSeg;
+
+ cbBlob = CPackedLen::GetLength(pBlob, &iSizeLen);
+ if (cbBlob == (ULONG)-1)
+ { // Invalid blob size encoding
+
+ //#GarbageInBlobHeap
+ // Note that this is allowed in ECMA spec (see chapter "#US and #Blob heaps"):
+ // Both these heaps can contain garbage, as long as any part that is reachable from any of
+ // the tables contains a valid 'blob'.
+
+ // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine
+ return S_OK;
+ }
+ //_ASSERTE((iSizeLen >= 1) && (iSizeLen <= 4) && (cbBlob <= 0x1fffffff));
+
+ // Make it blob size incl. its size encoding (cannot integer overflow)
+ cbBlob += iSizeLen;
+ // Check for integer overflow and that the entire blob entry is in this segment
+ if ((iSeg > (iSeg + cbBlob)) || ((iSeg + cbBlob) > pSeg->m_cbSegNext))
+ { // Invalid blob size
+
+ // See code:#GarbageInBlobHeap
+ // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine
+ return S_OK;
+ }
+
+ // Add the blob to the hash table.
+ if ((pHash = m_Hash.Add(pBlob)) == 0)
+ {
+ Uninit();
+ return (E_OUTOFMEMORY);
+ }
+ pHash->iOffset = iOffset;
+
+ // Move to next blob.
+ iOffset += cbBlob;
+ iSeg += cbBlob;
+ if (iSeg >= pSeg->m_cbSegNext)
+ {
+ pSeg = pSeg->m_pNextSeg;
+ iSeg = 0;
+ }
+ }
+ return (S_OK);
+} // StgBlobPool::RehashBlobs
+
+
+//
+// CInMemoryStream
+//
+
+
+ULONG
+STDMETHODCALLTYPE CInMemoryStream::Release()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACTL_END
+
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ {
+ if (m_dataCopy != NULL)
+ delete [] m_dataCopy;
+
+ delete this;
+ }
+ return (cRef);
+} // CInMemoryStream::Release
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::QueryInterface(REFIID riid, PVOID *ppOut)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ if (!ppOut)
+ {
+ return E_POINTER;
+ }
+
+ *ppOut = NULL;
+ if (riid == IID_IStream || riid == IID_ISequentialStream || riid == IID_IUnknown)
+ {
+ *ppOut = this;
+ AddRef();
+ return (S_OK);
+ }
+
+ return E_NOINTERFACE;
+
+} // CInMemoryStream::QueryInterface
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ ULONG cbRead = min(cb, m_cbSize - m_cbCurrent);
+
+ if (cbRead == 0)
+ return (S_FALSE);
+ memcpy(pv, (void *) ((ULONG_PTR) m_pMem + m_cbCurrent), cbRead);
+ if (pcbRead)
+ *pcbRead = cbRead;
+ m_cbCurrent += cbRead;
+ return (S_OK);
+} // CInMemoryStream::Read
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ if (ovadd_gt(m_cbCurrent, cb, m_cbSize))
+ return (OutOfMemory());
+
+ memcpy((BYTE *) m_pMem + m_cbCurrent, pv, cb);
+ m_cbCurrent += cb;
+ if (pcbWritten) *pcbWritten = cb;
+ return (S_OK);
+} // CInMemoryStream::Write
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ _ASSERTE(dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR);
+ _ASSERTE(dlibMove.QuadPart <= static_cast<LONGLONG>(ULONG_MAX));
+
+ if (dwOrigin == STREAM_SEEK_SET)
+ {
+ m_cbCurrent = (ULONG) dlibMove.QuadPart;
+ }
+ else
+ if (dwOrigin == STREAM_SEEK_CUR)
+ {
+ m_cbCurrent+= (ULONG)dlibMove.QuadPart;
+ }
+
+ if (plibNewPosition)
+ {
+ plibNewPosition->QuadPart = m_cbCurrent;
+ }
+
+ return (m_cbCurrent < m_cbSize) ? (S_OK) : E_FAIL;
+} // CInMemoryStream::Seek
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ HRESULT hr;
+ // We don't handle pcbRead or pcbWritten.
+ _ASSERTE(pcbRead == 0);
+ _ASSERTE(pcbWritten == 0);
+
+ _ASSERTE(cb.QuadPart <= ULONG_MAX);
+ ULONG cbTotal = min(static_cast<ULONG>(cb.QuadPart), m_cbSize - m_cbCurrent);
+ ULONG cbRead=min(1024, cbTotal);
+ CQuickBytes rBuf;
+ void *pBuf = rBuf.AllocNoThrow(cbRead);
+ if (pBuf == 0)
+ return (PostError(OutOfMemory()));
+
+ while (cbTotal)
+ {
+ if (cbRead > cbTotal)
+ cbRead = cbTotal;
+ if (FAILED(hr=Read(pBuf, cbRead, 0)))
+ return (hr);
+ if (FAILED(hr=pstm->Write(pBuf, cbRead, 0)))
+ return (hr);
+ cbTotal -= cbRead;
+ }
+
+ // Adjust seek pointer to the end.
+ m_cbCurrent = m_cbSize;
+
+ return (S_OK);
+} // CInMemoryStream::CopyTo
+
+HRESULT
+CInMemoryStream::CreateStreamOnMemory(
+ void *pMem, // Memory to create stream on.
+ ULONG cbSize, // Size of data.
+ IStream **ppIStream, // Return stream object here.
+ BOOL fDeleteMemoryOnRelease)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CInMemoryStream *pIStream; // New stream object.
+ if ((pIStream = new (nothrow) CInMemoryStream) == 0)
+ return (PostError(OutOfMemory()));
+ pIStream->InitNew(pMem, cbSize);
+ if (fDeleteMemoryOnRelease)
+ {
+ // make sure this memory is allocated using new
+ pIStream->m_dataCopy = (BYTE *)pMem;
+ }
+ *ppIStream = pIStream;
+ return (S_OK);
+} // CInMemoryStream::CreateStreamOnMemory
+
+HRESULT
+CInMemoryStream::CreateStreamOnMemoryCopy(
+ void *pMem,
+ ULONG cbSize,
+ IStream **ppIStream)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CInMemoryStream *pIStream; // New stream object.
+ if ((pIStream = new (nothrow) CInMemoryStream) == 0)
+ return (PostError(OutOfMemory()));
+
+ // Init the stream.
+ pIStream->m_cbCurrent = 0;
+ pIStream->m_cbSize = cbSize;
+
+ // Copy the data.
+ pIStream->m_dataCopy = new (nothrow) BYTE[cbSize];
+
+ if (pIStream->m_dataCopy == NULL)
+ {
+ delete pIStream;
+ return (PostError(OutOfMemory()));
+ }
+
+ pIStream->m_pMem = pIStream->m_dataCopy;
+ memcpy(pIStream->m_dataCopy, pMem, cbSize);
+
+ *ppIStream = pIStream;
+ return (S_OK);
+} // CInMemoryStream::CreateStreamOnMemoryCopy
+
+//---------------------------------------------------------------------------
+// CGrowableStream is a simple IStream implementation that grows as
+// its written to. All the memory is contigious, so read access is
+// fast. A grow does a realloc, so be aware of that if you're going to
+// use this.
+//---------------------------------------------------------------------------
+
+//Constructs a new GrowableStream
+// multiplicativeGrowthRate - when the stream grows it will be at least this
+// multiple of its old size. Values greater than 1 ensure O(N) amortized
+// performance growing the stream to size N, 1 ensures O(N^2) amortized perf
+// but gives the tightest memory usage. Valid range is [1.0, 2.0].
+// additiveGrowthRate - when the stream grows it will increase in size by at least
+// this number of bytes. Larger numbers cause fewer re-allocations at the cost of
+// increased memory usage.
+CGrowableStream::CGrowableStream(float multiplicativeGrowthRate, DWORD additiveGrowthRate)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ m_swBuffer = NULL;
+ m_dwBufferSize = 0;
+ m_dwBufferIndex = 0;
+ m_dwStreamLength = 0;
+ m_cRef = 1;
+
+ // Lets make sure these values stay somewhat sane... if you adjust the limits
+ // make sure you also write correct overflow checking code in EnsureCapcity
+ _ASSERTE(multiplicativeGrowthRate >= 1.0F && multiplicativeGrowthRate <= 2.0F);
+ m_multiplicativeGrowthRate = min(max(1.0F, multiplicativeGrowthRate), 2.0F);
+
+ _ASSERTE(additiveGrowthRate >= 1);
+ m_additiveGrowthRate = max(1, additiveGrowthRate);
+} // CGrowableStream::CGrowableStream
+
+#ifndef DACCESS_COMPILE
+
+CGrowableStream::~CGrowableStream()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Destroy the buffer.
+ if (m_swBuffer != NULL)
+ delete [] m_swBuffer;
+
+ m_swBuffer = NULL;
+ m_dwBufferSize = 0;
+} // CGrowableStream::~CGrowableStream
+
+// Grows the stream and optionally the internal buffer to ensure it is at least
+// newLogicalSize
+HRESULT CGrowableStream::EnsureCapacity(DWORD newLogicalSize)
+{
+ _ASSERTE(m_dwBufferSize >= m_dwStreamLength);
+
+ // If there is no enough space left in the buffer, grow it
+ if (newLogicalSize > m_dwBufferSize)
+ {
+ // Grow to max of newLogicalSize, m_dwBufferSize*multiplicativeGrowthRate, and
+ // m_dwBufferSize+m_additiveGrowthRate
+ S_UINT32 addSize = S_UINT32(m_dwBufferSize) + S_UINT32(m_additiveGrowthRate);
+ if (addSize.IsOverflow())
+ {
+ addSize = S_UINT32(UINT_MAX);
+ }
+
+ // this should have been enforced in the constructor too
+ _ASSERTE(m_multiplicativeGrowthRate <= 2.0 && m_multiplicativeGrowthRate >= 1.0);
+
+ // 2*UINT_MAX doesn't overflow a float so this certain to be safe
+ float multSizeF = (float)m_dwBufferSize * m_multiplicativeGrowthRate;
+ DWORD multSize;
+ if(multSizeF > (float)UINT_MAX)
+ {
+ multSize = UINT_MAX;
+ }
+ else
+ {
+ multSize = (DWORD)multSizeF;
+ }
+
+ DWORD newBufferSize = max(max(newLogicalSize, multSize), addSize.Value());
+
+ char *tmp = new (nothrow) char[newBufferSize];
+ if(tmp == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (m_swBuffer) {
+ memcpy (tmp, m_swBuffer, m_dwBufferSize);
+ delete [] m_swBuffer;
+ }
+ m_swBuffer = (BYTE *)tmp;
+ m_dwBufferSize = newBufferSize;
+ }
+
+ _ASSERTE(m_dwBufferSize >= newLogicalSize);
+ // the internal buffer is big enough, might have to increase logical size
+ // though
+ if(newLogicalSize > m_dwStreamLength)
+ {
+ m_dwStreamLength = newLogicalSize;
+ }
+
+ _ASSERTE(m_dwBufferSize >= m_dwStreamLength);
+ return S_OK;
+}
+
+ULONG
+STDMETHODCALLTYPE
+CGrowableStream::Release()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+
+ if (cRef == 0)
+ delete this;
+
+ return cRef;
+} // CGrowableStream::Release
+
+HRESULT
+STDMETHODCALLTYPE
+CGrowableStream::QueryInterface(
+ REFIID riid,
+ PVOID *ppOut)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ if (riid != IID_IUnknown && riid!=IID_ISequentialStream && riid!=IID_IStream)
+ return E_NOINTERFACE;
+
+ *ppOut = this;
+ AddRef();
+ return (S_OK);
+} // CGrowableStream::QueryInterface
+
+HRESULT
+CGrowableStream::Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ HRESULT hr = S_OK;
+ DWORD dwCanReadBytes = 0;
+
+ if (NULL == pv)
+ return E_POINTER;
+
+ // short-circuit a zero-length read or see if we are at the end
+ if (cb == 0 || m_dwBufferIndex >= m_dwStreamLength)
+ {
+ if (pcbRead != NULL)
+ *pcbRead = 0;
+
+ return S_OK;
+ }
+
+ // Figure out if we have enough room in the stream (excluding any
+ // unused space at the end of the buffer)
+ dwCanReadBytes = cb;
+
+ S_UINT32 dwNewIndex = S_UINT32(dwCanReadBytes) + S_UINT32(m_dwBufferIndex);
+ if (dwNewIndex.IsOverflow() || (dwNewIndex.Value() > m_dwStreamLength))
+ {
+ // Only read whatever is left in the buffer (if any)
+ dwCanReadBytes = (m_dwStreamLength - m_dwBufferIndex);
+ }
+
+ // copy from our buffer to caller's buffer
+ memcpy(pv, &m_swBuffer[m_dwBufferIndex], dwCanReadBytes);
+
+ // adjust our current position
+ m_dwBufferIndex += dwCanReadBytes;
+
+ // if they want the info, tell them how many byte we read for them
+ if (pcbRead != NULL)
+ *pcbRead = dwCanReadBytes;
+
+ return hr;
+} // CGrowableStream::Read
+
+HRESULT
+CGrowableStream::Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ HRESULT hr = S_OK;
+ DWORD dwActualWrite = 0;
+
+ // avoid NULL write
+ if (cb == 0)
+ {
+ hr = S_OK;
+ goto Error;
+ }
+
+ // Check if our buffer is large enough
+ _ASSERTE(m_dwBufferIndex <= m_dwStreamLength);
+ _ASSERTE(m_dwStreamLength <= m_dwBufferSize);
+
+ // If there is no enough space left in the buffer, grow it
+ if (cb > (m_dwStreamLength - m_dwBufferIndex))
+ {
+ // Determine the new size needed
+ S_UINT32 size = S_UINT32(m_dwBufferSize) + S_UINT32(cb);
+ if (size.IsOverflow())
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ goto Error;
+ }
+
+ hr = EnsureCapacity(size.Value());
+ if(FAILED(hr))
+ {
+ goto Error;
+ }
+ }
+
+ if ((pv != NULL) && (cb > 0))
+ {
+ // write to current position in the buffer
+ memcpy(&m_swBuffer[m_dwBufferIndex], pv, cb);
+
+ // now update our current index
+ m_dwBufferIndex += cb;
+
+ // in case they want to know the number of bytes written
+ dwActualWrite = cb;
+ }
+
+Error:
+ if (pcbWritten)
+ *pcbWritten = dwActualWrite;
+
+ return hr;
+} // CGrowableStream::Write
+
+STDMETHODIMP
+CGrowableStream::Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ // a Seek() call on STREAM_SEEK_CUR and a dlibMove == 0 is a
+ // request to get the current seek position.
+ if ((dwOrigin == STREAM_SEEK_CUR && dlibMove.u.LowPart == 0) &&
+ (dlibMove.u.HighPart == 0) &&
+ (NULL != plibNewPosition))
+ {
+ goto Error;
+ }
+
+ // we only support STREAM_SEEK_SET (beginning of buffer)
+ if (dwOrigin != STREAM_SEEK_SET)
+ return E_NOTIMPL;
+
+ // did they ask to seek past end of stream? If so we're supposed to
+ // extend with zeros. But we've never supported that.
+ if (dlibMove.u.LowPart > m_dwStreamLength)
+ return E_UNEXPECTED;
+
+ // we ignore the high part of the large integer
+ SIMPLIFYING_ASSUMPTION(dlibMove.u.HighPart == 0);
+ m_dwBufferIndex = dlibMove.u.LowPart;
+
+Error:
+ if (NULL != plibNewPosition)
+ {
+ plibNewPosition->u.HighPart = 0;
+ plibNewPosition->u.LowPart = m_dwBufferIndex;
+ }
+
+ return S_OK;
+} // CGrowableStream::Seek
+
+STDMETHODIMP
+CGrowableStream::SetSize(
+ ULARGE_INTEGER libNewSize)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ DWORD dwNewSize = libNewSize.u.LowPart;
+
+ _ASSERTE(libNewSize.u.HighPart == 0);
+
+ // we don't support large allocations
+ if (libNewSize.u.HighPart > 0)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = EnsureCapacity(dwNewSize);
+ if(FAILED(hr))
+ {
+ return hr;
+ }
+
+ // EnsureCapacity doesn't shrink the logicalSize if dwNewSize is smaller
+ // and SetSize is allowed to shrink the stream too. Note that we won't
+ // release physical memory here, we just appear to get smaller
+ m_dwStreamLength = dwNewSize;
+
+ return S_OK;
+} // CGrowableStream::SetSize
+
+STDMETHODIMP
+CGrowableStream::Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ if (NULL == pstatstg)
+ return E_POINTER;
+
+ // this is the only useful information we hand out - the length of the stream
+ pstatstg->cbSize.u.HighPart = 0;
+ pstatstg->cbSize.u.LowPart = m_dwStreamLength;
+ pstatstg->type = STGTY_STREAM;
+
+ // we ignore the grfStatFlag - we always assume STATFLAG_NONAME
+ pstatstg->pwcsName = NULL;
+
+ pstatstg->grfMode = 0;
+ pstatstg->grfLocksSupported = 0;
+ pstatstg->clsid = CLSID_NULL;
+ pstatstg->grfStateBits = 0;
+
+ return S_OK;
+} // CGrowableStream::Stat
+
+//
+// Clone - Make a deep copy of the stream into a new cGrowableStream instance
+//
+// Arguments:
+// ppStream - required output parameter for the new stream instance
+//
+// Returns:
+// S_OK on succeess, or an error code on failure.
+//
+HRESULT
+CGrowableStream::Clone(
+ IStream **ppStream)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ if (NULL == ppStream)
+ return E_POINTER;
+
+ // Copy our entire buffer into the new stream
+ CGrowableStream * newStream = new (nothrow) CGrowableStream();
+ if (newStream == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ HRESULT hr = newStream->Write(m_swBuffer, m_dwStreamLength, NULL);
+ if (FAILED(hr))
+ {
+ delete newStream;
+ return hr;
+ }
+
+ *ppStream = newStream;
+ return S_OK;
+} // CGrowableStream::Clone
+
+#endif // !DACCESS_COMPILE
diff --git a/src/utilcode/stgpooli.cpp b/src/utilcode/stgpooli.cpp
new file mode 100644
index 0000000000..653289b1a1
--- /dev/null
+++ b/src/utilcode/stgpooli.cpp
@@ -0,0 +1,349 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// StgPool.cpp
+//
+
+//
+// Pools are used to reduce the amount of data actually required in the database.
+// This allows for duplicate string and binary values to be folded into one
+// copy shared by the rest of the database. Strings are tracked in a hash
+// table when insert/changing data to find duplicates quickly. The strings
+// are then persisted consecutively in a stream in the database format.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard include.
+#include <stgpool.h> // Our interface definitions.
+
+int CStringPoolHash::Cmp(
+ const void *pData, // A string.
+ void *pItem) // A hash item which refers to a string.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPCSTR p1 = reinterpret_cast<LPCSTR>(pData);
+ LPCSTR p2;
+ if (FAILED(m_Pool->GetString(reinterpret_cast<STRINGHASH*>(pItem)->iOffset, &p2)))
+ {
+ return -1;
+ }
+ return (strcmp(p1, p2));
+} // int CStringPoolHash::Cmp()
+
+
+int CBlobPoolHash::Cmp(
+ const void *pData, // A blob.
+ void *pItem) // A hash item which refers to a blob.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ ULONG ul1;
+ ULONG ul2;
+ MetaData::DataBlob data2;
+
+ // Get size of first item.
+ ul1 = CPackedLen::GetLength(pData);
+ // Adjust size to include the length of size field.
+ ul1 += CPackedLen::Size(ul1);
+
+ // Get the second item.
+ if (FAILED(m_Pool->GetData(reinterpret_cast<BLOBHASH*>(pItem)->iOffset, &data2)))
+ {
+ return -1;
+ }
+
+ // Get and adjust size of second item.
+ ul2 = CPackedLen::GetLength(data2.GetDataPointer());
+ ul2 += CPackedLen::Size(ul2);
+
+ if (ul1 < ul2)
+ return (-1);
+ else if (ul1 > ul2)
+ return (1);
+ return (memcmp(pData, data2.GetDataPointer(), ul1));
+} // int CBlobPoolHash::Cmp()
+
+int CGuidPoolHash::Cmp(const void *pData, void *pItem)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ GUID *p2;
+ if (FAILED(m_Pool->GetGuid(reinterpret_cast<GUIDHASH*>(pItem)->iIndex, &p2)))
+ {
+ return -1;
+ }
+ return (memcmp(pData, p2, sizeof(GUID)));
+} // int CGuidPoolHash::Cmp()
+
+//
+//
+// CPackedLen
+//
+//
+
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+void const *CPackedLen::GetData( // Pointer to data, or 0 on error.
+ void const *pData, // First byte of length.
+ ULONG *pLength) // Put length here, or -1 on error.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData);
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ *pLength = (*pBytes & 0x7f);
+ return pBytes + 1;
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ *pLength = ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ return pBytes + 2;
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ *pLength = ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ return pBytes + 4;
+ }
+
+ *pLength = (ULONG) -1;
+ return 0;
+} // void const *CPackedLen::GetData()
+
+#ifndef MAX_PTR
+#define MAX_PTR ((BYTE*)(~(SSIZE_T)0))
+#endif
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+HRESULT CPackedLen::SafeGetLength( // S_OK, or error
+ void const *pDataSource, // First byte of length.
+ void const *pDataSourceEnd, // End of valid source data memory
+ ULONG *pLength, // Length of data, if return S_OK
+ void const **ppDataNext) // Pointer immediately following encoded length
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (pDataSource == NULL ||
+ pDataSourceEnd == NULL ||
+ pDataSourceEnd < pDataSource ||
+ ppDataNext == NULL ||
+ pLength == NULL ||
+ pDataSource > (MAX_PTR - 4))
+ {
+ return E_INVALIDARG;
+ }
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pDataSource);
+ BYTE const *pBytesEnd = reinterpret_cast<BYTE const*>(pDataSourceEnd);
+
+ size_t cbAvail = pBytesEnd - pBytes;
+
+ if (cbAvail < 1)
+ { // Fail if no source data available
+ return COR_E_OVERFLOW;
+ }
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ *pLength = (*pBytes & 0x7f);
+ *ppDataNext = pBytes + 1;
+ return S_OK;
+ }
+
+ if (cbAvail < 2)
+ { // Fail if not enough source data available
+ return COR_E_OVERFLOW;
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ *pLength = ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ *ppDataNext = pBytes + 2;
+ return S_OK;
+ }
+
+ if (cbAvail < 4)
+ { // Fail if not enough source data available
+ return COR_E_OVERFLOW;
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ *pLength = ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ *ppDataNext = pBytes + 4;
+ return S_OK;;
+ }
+
+ return COR_E_OVERFLOW;
+} // CPackedLen::GetLength
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+HRESULT CPackedLen::SafeGetData( // S_OK, or error
+ void const *pDataSource, // First byte of length.
+ void const *pDataSourceEnd, // End of valid source data memory
+ ULONG *pcbData, // Length of data
+ void const **ppData) // Start of data
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr = S_OK;
+
+ IfFailRet(SafeGetLength(pDataSource, pDataSourceEnd, pcbData, ppData));
+
+ if (*pcbData == 0)
+ { // Zero length value means zero data, so no range checking required.
+ return S_OK;
+ }
+
+ BYTE const *pbData = reinterpret_cast<BYTE const*>(*ppData);
+
+ if (pbData + *pcbData < pbData)
+ { // First check for integer overflow
+ return COR_E_OVERFLOW;
+ }
+
+ if (pDataSourceEnd < pbData + *pcbData)
+ { // Now check for data buffer overflow
+ return COR_E_OVERFLOW;
+ }
+
+ return S_OK;
+} // CPackedLen::GetLength
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+HRESULT CPackedLen::SafeGetData( // S_OK, or error
+ void const *pDataSource, // First byte of data
+ ULONG cbDataSource, // Count of valid bytes in data source
+ ULONG *pcbData, // Length of data
+ void const **ppData) // Start of data
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return SafeGetData(pDataSource, (void const *)((BYTE const *)pDataSource + cbDataSource), pcbData, ppData);
+} // CPackedLen::GetLength
+
+//*****************************************************************************
+// Parse a length, return the length, pointer to actual bytes.
+//*****************************************************************************
+ULONG CPackedLen::GetLength( // Length or -1 on error.
+ void const *pData, // First byte of length.
+ void const **ppCode) // Put pointer to bytes here, if not 0.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData);
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ if (ppCode) *ppCode = pBytes + 1;
+ return (*pBytes & 0x7f);
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ if (ppCode) *ppCode = pBytes + 2;
+ return ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ if (ppCode) *ppCode = pBytes + 4;
+ return ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ }
+
+ return (ULONG) -1;
+} // ULONG CPackedLen::GetLength()
+
+//*****************************************************************************
+// Parse a length, return the length, size of the length.
+//*****************************************************************************
+ULONG CPackedLen::GetLength( // Length or -1 on error.
+ void const *pData, // First byte of length.
+ int *pSizeLen) // Put size of length here, if not 0.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData);
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ if (pSizeLen) *pSizeLen = 1;
+ return (*pBytes & 0x7f);
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ if (pSizeLen) *pSizeLen = 2;
+ return ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ if (pSizeLen) *pSizeLen = 4;
+ return ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ }
+
+ return (ULONG) -1;
+} // ULONG CPackedLen::GetLength()
+
+//*****************************************************************************
+// Encode a length.
+//*****************************************************************************
+#ifdef _MSC_VER
+#pragma warning(disable:4244) // conversion from unsigned long to unsigned char
+#endif
+void* CPackedLen::PutLength( // First byte past length.
+ void *pData, // Pack the length here.
+ ULONG iLen) // The length.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE *pBytes = reinterpret_cast<BYTE*>(pData);
+
+ if (iLen <= 0x7F)
+ {
+ *pBytes = iLen;
+ return pBytes + 1;
+ }
+
+ if (iLen <= 0x3FFF)
+ {
+ *pBytes = (iLen >> 8) | 0x80;
+ *(pBytes+1) = iLen & 0xFF;
+ return pBytes + 2;
+ }
+
+ _ASSERTE(iLen <= 0x1FFFFFFF);
+ *pBytes = (iLen >> 24) | 0xC0;
+ *(pBytes+1) = (iLen >> 16) & 0xFF;
+ *(pBytes+2) = (iLen >> 8) & 0xFF;
+ *(pBytes+3) = iLen & 0xFF;
+ return pBytes + 4;
+} // void* CPackedLen::PutLength()
+#ifdef _MSC_VER
+#pragma warning(default:4244) // conversion from unsigned long to unsigned char
+#endif
+
diff --git a/src/utilcode/stgpoolreadonly.cpp b/src/utilcode/stgpoolreadonly.cpp
new file mode 100644
index 0000000000..abd27b884d
--- /dev/null
+++ b/src/utilcode/stgpoolreadonly.cpp
@@ -0,0 +1,212 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// StgPoolReadOnly.cpp
+//
+
+//
+// Read only pools are used to reduce the amount of data actually required in the database.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard include.
+#include <stgpool.h> // Our interface definitions.
+#include "metadatatracker.h"
+//
+//
+// StgPoolReadOnly
+//
+//
+
+#if METADATATRACKER_ENABLED
+MetaDataTracker *MetaDataTracker::m_MDTrackers = NULL;
+BOOL MetaDataTracker::s_bEnabled = FALSE;
+
+void (*MetaDataTracker::s_IBCLogMetaDataAccess)(const void *addr) = NULL;
+void (*MetaDataTracker::s_IBCLogMetaDataSearch)(const void *result) = NULL;
+
+#endif // METADATATRACKER_ENABLED
+
+const BYTE StgPoolSeg::m_zeros[64] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+
+//*****************************************************************************
+// Free any memory we allocated.
+//*****************************************************************************
+StgPoolReadOnly::~StgPoolReadOnly()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+
+//*****************************************************************************
+// Init the pool from existing data.
+//*****************************************************************************
+HRESULT StgPoolReadOnly::InitOnMemReadOnly(// Return code.
+ void *pData, // Predefined data.
+ ULONG iSize) // Size of data.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ // Make sure we aren't stomping anything and are properly initialized.
+ _ASSERTE(m_pSegData == m_zeros);
+
+ // Create case requires no further action.
+ if (pData == NULL)
+ return E_INVALIDARG;
+
+ // Keep m_zeros data pointer if there's no content of the pool
+ if (iSize != 0)
+ {
+ m_pSegData = reinterpret_cast<BYTE*>(pData);
+ }
+ m_cbSegSize = iSize;
+ m_cbSegNext = iSize;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Prepare to shut down or reinitialize.
+//*****************************************************************************
+void StgPoolReadOnly::Uninit()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pSegData = (BYTE*)m_zeros;
+ m_pNextSeg = 0;
+}
+
+
+//*****************************************************************************
+// Convert a string to UNICODE into the caller's buffer.
+//*****************************************************************************
+HRESULT StgPoolReadOnly::GetStringW( // Return code.
+ ULONG iOffset, // Offset of string in pool.
+ __out_ecount(cchBuffer) LPWSTR szOut, // Output buffer for string.
+ int cchBuffer) // Size of output buffer.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr;
+ LPCSTR pString; // The string in UTF8.
+ int iChars;
+
+ IfFailRet(GetString(iOffset, &pString));
+ iChars = ::WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, szOut, cchBuffer);
+ if (iChars == 0)
+ return (BadError(HRESULT_FROM_NT(GetLastError())));
+ return S_OK;
+}
+
+//*****************************************************************************
+// Return a pointer to a null terminated blob given an offset previously
+// handed out by Addblob or Findblob.
+//*****************************************************************************
+HRESULT
+StgPoolReadOnly::GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+ UINT32 cbBlobContentSize;
+
+ // This should not be a necessary special case. The zero byte at the
+ // start of the pool will code for a length of zero. We will return
+ // a pointer to the next length byte, but the caller should notice that
+ // the size is zero, and should not look at any bytes.
+ // [SL] Yes, but we don't need all further computations and checks if iOffset==0
+
+ if (nOffset == 0)
+ {
+ pData->Clear();
+ return S_OK;
+ }
+
+ // Is the offset within this heap?
+ if (!IsValidOffset(nOffset))
+ {
+ Debug_ReportError("Invalid blob offset.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+
+ IfFailGo(GetDataReadOnly(nOffset, pData));
+ if (!pData->GetCompressedU(&cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - size compression.");
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ if (!pData->TruncateToExactSize(cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - reaches behind the end of data block.");
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgPoolReadOnly::GetBlob
+
+//*****************************************************************************
+// code:StgPoolReadOnly::GetBlob specialization with inlined check for valid offsets to avoid redundant code:StgPoolReadOnly::GetDataReadOnly calls.
+// code:StgPoolReadOnly::GetDataReadOnly is not cheap because of it performs binary lookup in hot metadata.
+//*****************************************************************************
+HRESULT
+StgBlobPoolReadOnly::GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+ UINT32 cbBlobContentSize;
+
+ // This should not be a necessary special case. The zero byte at the
+ // start of the pool will code for a length of zero. We will return
+ // a pointer to the next length byte, but the caller should notice that
+ // the size is zero, and should not look at any bytes.
+ // [SL] Yes, but we don't need all further computations and checks if iOffset==0
+
+ if (nOffset == 0)
+ {
+ pData->Clear();
+ return S_OK;
+ }
+
+ if (m_pSegData == m_zeros)
+ {
+ Debug_ReportError("Invalid blob offset.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+
+ IfFailGo(GetDataReadOnly(nOffset, pData));
+ if (!pData->GetCompressedU(&cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - size compression.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+ if (!pData->TruncateToExactSize(cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - reaches behind the end of data block.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgBlobPoolReadOnly::GetBlob
diff --git a/src/utilcode/stresslog.cpp b/src/utilcode/stresslog.cpp
new file mode 100644
index 0000000000..3e1a928c03
--- /dev/null
+++ b/src/utilcode/stresslog.cpp
@@ -0,0 +1,720 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+/*************************************************************************************/
+/* StressLog.cpp */
+/*************************************************************************************/
+
+/*************************************************************************************/
+
+#include "stdafx.h" // precompiled headers
+
+#include "switches.h"
+#include "stresslog.h"
+#include "clrhost.h"
+#define DONOT_DEFINE_ETW_CALLBACK
+#include "eventtracebase.h"
+#include "ex.h"
+
+ #if !defined(STRESS_LOG_READONLY)
+HANDLE StressLogChunk::s_LogChunkHeap = NULL;
+#endif // !STRESS_LOG_READONLY
+
+/*********************************************************************************/
+#if defined(_TARGET_X86_)
+
+/* This is like QueryPerformanceCounter but a lot faster. On machines with
+ variable-speed CPUs (for power management), this is not accurate, but may
+ be good enough.
+*/
+__forceinline __declspec(naked) unsigned __int64 getTimeStamp() {
+ STATIC_CONTRACT_LEAF;
+
+ __asm {
+ RDTSC // read time stamp counter
+ ret
+ };
+}
+
+#else // _TARGET_X86_
+unsigned __int64 getTimeStamp() {
+ STATIC_CONTRACT_LEAF;
+
+ LARGE_INTEGER ret;
+ ZeroMemory(&ret, sizeof(LARGE_INTEGER));
+
+ QueryPerformanceCounter(&ret);
+
+ return ret.QuadPart;
+}
+
+#endif // _TARGET_X86_
+
+#if defined(_TARGET_X86_)
+
+/*********************************************************************************/
+/* Get the the frequency cooresponding to 'getTimeStamp'. For x86, this is the
+ frequency of the RDTSC instruction, which is just the clock rate of the CPU.
+ This can vary due to power management, so this is at best a rough approximation.
+*/
+unsigned __int64 getTickFrequency()
+{
+ //
+ // At startup, the OS calculates the CPU clock frequency and makes it available
+ // at HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0
+ //
+
+ unsigned __int64 hz = 0;
+
+ HKEY hKey;
+ if (ERROR_SUCCESS == RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ W("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
+ 0,
+ KEY_QUERY_VALUE,
+ &hKey))
+ {
+ DWORD mhz;
+ DWORD mhzType;
+ DWORD cbMhz = (DWORD)sizeof(mhz);
+ if (ERROR_SUCCESS == RegQueryValueExW(
+ hKey,
+ W("~MHz"),
+ NULL,
+ &mhzType,
+ (LPBYTE)&mhz,
+ &cbMhz))
+ {
+ _ASSERTE(REG_DWORD == mhzType);
+ _ASSERTE((DWORD)sizeof(mhz) == cbMhz);
+
+ hz = (unsigned __int64)mhz * 1000000;
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ return hz;
+}
+
+#else // _TARGET_X86_
+
+
+/*********************************************************************************/
+/* Get the the frequency cooresponding to 'getTimeStamp'. For non-x86
+ architectures, this is just the performance counter frequency.
+*/
+unsigned __int64 getTickFrequency()
+{
+ LARGE_INTEGER ret;
+ ZeroMemory(&ret, sizeof(LARGE_INTEGER));
+ QueryPerformanceFrequency(&ret);
+ return ret.QuadPart;
+}
+
+#endif // _TARGET_X86_
+
+#ifdef STRESS_LOG
+
+StressLog StressLog::theLog = { 0, 0, 0, 0, 0, 0, TLS_OUT_OF_INDEXES, 0, 0, 0 };
+const static unsigned __int64 RECYCLE_AGE = 0x40000000L; // after a billion cycles, we can discard old threads
+
+/*********************************************************************************/
+void StressLog::Enter(CRITSEC_COOKIE) {
+ STATIC_CONTRACT_LEAF;
+
+ IncCantAllocCount();
+ ClrEnterCriticalSection(theLog.lock);
+ DecCantAllocCount();
+}
+
+void StressLog::Leave(CRITSEC_COOKIE) {
+ STATIC_CONTRACT_LEAF;
+
+ IncCantAllocCount();
+ ClrLeaveCriticalSection(theLog.lock);
+ DecCantAllocCount();
+}
+
+/*********************************************************************************/
+void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread,
+ unsigned maxBytesTotal, HMODULE hMod)
+{
+ STATIC_CONTRACT_LEAF;
+
+ if (theLog.MaxSizePerThread != 0)
+ {
+ // guard ourself against multiple initialization. First init wins.
+ return;
+ }
+
+ _ASSERTE (theLog.TLSslot == (unsigned int)TLS_OUT_OF_INDEXES);
+ theLog.lock = ClrCreateCriticalSection(CrstStressLog,(CrstFlags)(CRST_UNSAFE_ANYMODE|CRST_DEBUGGER_THREAD));
+ // StressLog::Terminate is going to free memory.
+ if (maxBytesPerThread < STRESSLOG_CHUNK_SIZE)
+ {
+ maxBytesPerThread = STRESSLOG_CHUNK_SIZE;
+ }
+ theLog.MaxSizePerThread = maxBytesPerThread;
+
+ if (maxBytesTotal < STRESSLOG_CHUNK_SIZE * 256)
+ {
+ maxBytesTotal = STRESSLOG_CHUNK_SIZE * 256;
+ }
+ theLog.MaxSizeTotal = maxBytesTotal;
+ theLog.totalChunk = 0;
+ theLog.facilitiesToLog = facilities | LF_ALWAYS;
+ theLog.levelToLog = level;
+ theLog.deadCount = 0;
+ theLog.TLSslot = TlsIdx_StressLog;
+
+ theLog.tickFrequency = getTickFrequency();
+
+ GetSystemTimeAsFileTime (&theLog.startTime);
+ theLog.startTimeStamp = getTimeStamp();
+
+#ifndef FEATURE_PAL
+ theLog.moduleOffset = (SIZE_T)hMod; // HMODULES are base addresses.
+
+#ifdef _DEBUG
+ HMODULE hModNtdll = GetModuleHandleA("ntdll.dll");
+ theLog.RtlCaptureStackBackTrace = reinterpret_cast<PFNRtlCaptureStackBackTrace>(
+ GetProcAddress(hModNtdll, "RtlCaptureStackBackTrace"));
+#endif // _DEBUG
+
+#else // !FEATURE_PAL
+ theLog.moduleOffset = (SIZE_T)PAL_GetCoreClrModuleBase();
+#endif // !FEATURE_PAL
+
+#if !defined (STRESS_LOG_READONLY)
+ StressLogChunk::s_LogChunkHeap = ClrHeapCreate (0, STRESSLOG_CHUNK_SIZE * 128, 0);
+ if (StressLogChunk::s_LogChunkHeap == NULL)
+ {
+ StressLogChunk::s_LogChunkHeap = ClrGetProcessHeap ();
+ }
+ _ASSERTE (StressLogChunk::s_LogChunkHeap);
+#endif //!STRESS_LOG_READONLY
+}
+
+/*********************************************************************************/
+void StressLog::Terminate(BOOL fProcessDetach) {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES) {
+ theLog.facilitiesToLog = 0;
+
+ StressLogLockHolder lockh(theLog.lock, FALSE);
+ if (!fProcessDetach) {
+ lockh.Acquire(); lockh.Release(); // The Enter() Leave() forces a memory barrier on weak memory model systems
+ // we want all the other threads to notice that facilitiesToLog is now zero
+
+ // This is not strictly threadsafe, since there is no way of insuring when all the
+ // threads are out of logMsg. In practice, since they can no longer enter logMsg
+ // and there are no blocking operations in logMsg, simply sleeping will insure
+ // that everyone gets out.
+ ClrSleepEx(2, FALSE);
+ lockh.Acquire();
+ }
+
+ // Free the log memory
+ ThreadStressLog* ptr = theLog.logs;
+ theLog.logs = 0;
+ while(ptr != 0) {
+ ThreadStressLog* tmp = ptr;
+ ptr = ptr->next;
+ delete tmp;
+ }
+
+ theLog.TLSslot = TLS_OUT_OF_INDEXES;
+ if (!fProcessDetach) {
+ lockh.Release();
+ }
+ }
+#if !defined (STRESS_LOG_READONLY)
+ if (StressLogChunk::s_LogChunkHeap != NULL && StressLogChunk::s_LogChunkHeap != ClrGetProcessHeap ())
+ {
+ ClrHeapDestroy (StressLogChunk::s_LogChunkHeap);
+ }
+#endif //!STRESS_LOG_READONLY
+}
+
+/*********************************************************************************/
+/* create a new thread stress log buffer associated with Thread local slot TLSslot, for the Stress log */
+
+ThreadStressLog* StressLog::CreateThreadStressLog() {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ static PVOID callerID = NULL;
+
+ ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot);
+ if (msgs != NULL)
+ {
+ return msgs;
+ }
+
+ if (callerID == ClrTeb::GetFiberPtrId())
+ {
+ return NULL;
+ }
+
+ //if we are not allowed to allocate stress log, we should not even try to take the lock
+ if (!StressLogChunk::s_LogChunkHeap || !CanThisThreadCallIntoHost() || IsInCantAllocStressLogRegion ())
+ {
+ return NULL;
+ }
+
+ // if it looks like we won't be allowed to allocate a new chunk, exit early
+ if (theLog.deadCount == 0 && !AllowNewChunk (0))
+ {
+ return NULL;
+ }
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return NULL);
+ StressLogLockHolder lockh(theLog.lock, FALSE);
+
+ class NestedCaller
+ {
+ public:
+ NestedCaller()
+ {
+ }
+ ~NestedCaller()
+ {
+ callerID = NULL;
+ }
+ void Mark()
+ {
+ callerID = ClrTeb::GetFiberPtrId();
+ }
+ };
+
+ NestedCaller nested;
+
+ BOOL noFLSNow = FALSE;
+
+ PAL_CPP_TRY
+ {
+ // Acquiring the lack can throw an OOM exception the first time its called on a thread. We go
+ // ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if
+ // we fail.
+ lockh.Acquire();
+ nested.Mark();
+
+ // ClrFlsSetValue can throw an OOM exception the first time its called on a thread for a given slot. We go
+ // ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if
+ // we fail.
+ ClrFlsSetValue(theLog.TLSslot, NULL);
+ }
+ PAL_CPP_CATCH_DERIVED(OutOfMemoryException, obj)
+ {
+ // Just leave on any exception. Note: can't goto or return from within EX_CATCH...
+ noFLSNow = TRUE;
+ }
+ PAL_CPP_ENDTRY;
+
+ if (noFLSNow == FALSE && theLog.facilitiesToLog != 0)
+ msgs = CreateThreadStressLogHelper();
+
+ END_SO_INTOLERANT_CODE;
+
+ return msgs;
+}
+
+ThreadStressLog* StressLog::CreateThreadStressLogHelper() {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SO_INTOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES); // because facilitiesToLog is != 0
+
+
+ BOOL skipInsert = FALSE;
+ ThreadStressLog* msgs = NULL;
+
+ // See if we can recycle a dead thread
+ if (theLog.deadCount > 0)
+ {
+ unsigned __int64 recycleStamp = getTimeStamp() - RECYCLE_AGE;
+ msgs = theLog.logs;
+ //find out oldest dead ThreadStressLog in case we can't find one within
+ //recycle age but can't create a new chunk
+ ThreadStressLog * oldestDeadMsg = NULL;
+
+ while(msgs != 0)
+ {
+ if (msgs->isDead)
+ {
+ BOOL hasTimeStamp = msgs->curPtr != (StressMsg *)msgs->chunkListTail->EndPtr();
+ if (hasTimeStamp && msgs->curPtr->timeStamp < recycleStamp)
+ {
+ skipInsert = TRUE;
+ InterlockedDecrement(&theLog.deadCount);
+ break;
+ }
+
+ if (!oldestDeadMsg)
+ {
+ oldestDeadMsg = msgs;
+ }
+ else if (hasTimeStamp && oldestDeadMsg->curPtr->timeStamp > msgs->curPtr->timeStamp)
+ {
+ oldestDeadMsg = msgs;
+ }
+ }
+
+ msgs = msgs->next;
+ }
+
+ //if the total stress log size limit is already passed and we can't add new chunk,
+ //always reuse the oldest dead msg
+ if (!AllowNewChunk (0) && !msgs)
+ {
+ msgs = oldestDeadMsg;
+ skipInsert = TRUE;
+ InterlockedDecrement(&theLog.deadCount);
+ }
+ }
+
+ if (msgs == 0) {
+ FAULT_NOT_FATAL(); // We don't mind if we can't allocate here, we'll try again later.
+ if (IsInCantAllocStressLogRegion ())
+ {
+ goto LEAVE;
+ }
+
+ msgs = new (nothrow) ThreadStressLog;
+
+ if (msgs == 0 ||!msgs->IsValid ())
+ {
+ delete msgs;
+ msgs = 0;
+ goto LEAVE;
+ }
+ }
+
+ msgs->Activate ();
+
+ // We know this isn't going to throw an exception now because the call to ClrFlsSetValue above succeeded for
+ // this thread.
+ {
+ CONTRACT_VIOLATION(ThrowsViolation);
+ ClrFlsSetValue(theLog.TLSslot, msgs);
+ }
+
+ if (!skipInsert) {
+#ifdef _DEBUG
+ ThreadStressLog* walk = theLog.logs;
+ while (walk)
+ {
+ _ASSERTE (walk != msgs);
+ walk = walk->next;
+ }
+#endif
+ // Put it into the stress log
+ msgs->next = theLog.logs;
+ theLog.logs = msgs;
+ }
+
+LEAVE:
+ ;
+ return msgs;
+}
+
+/*********************************************************************************/
+/* static */
+void StressLog::ThreadDetach(ThreadStressLog *msgs) {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+#ifndef DACCESS_COMPILE
+ if (msgs == 0)
+ {
+ return;
+ }
+
+ _ASSERTE(theLog.TLSslot != (unsigned int)TLS_OUT_OF_INDEXES); // because facilitiesToLog is != 0
+ // We are deleting a fiber. The thread is running a different fiber now.
+ // We should write this message to the StressLog for deleted fiber.
+ msgs->LogMsg (LF_STARTUP, 0, "******* DllMain THREAD_DETACH called Thread dying *******\n");
+#endif
+
+ msgs->isDead = TRUE;
+ InterlockedIncrement(&theLog.deadCount);
+}
+
+BOOL StressLog::AllowNewChunk (LONG numChunksInCurThread)
+{
+ _ASSERTE (numChunksInCurThread <= theLog.totalChunk);
+ DWORD perThreadLimit = theLog.MaxSizePerThread;
+
+#ifndef DACCESS_COMPILE
+ if (numChunksInCurThread == 0 && IsSuspendEEThread())
+ return TRUE;
+
+ if (IsGCSpecialThread())
+ {
+ perThreadLimit *= GC_STRESSLOG_MULTIPLY;
+ }
+#endif
+
+ if ((DWORD)numChunksInCurThread * STRESSLOG_CHUNK_SIZE >= perThreadLimit)
+ {
+ return FALSE;
+ }
+
+ return (DWORD)theLog.totalChunk * STRESSLOG_CHUNK_SIZE < theLog.MaxSizeTotal;
+}
+
+BOOL StressLog::ReserveStressLogChunks (unsigned chunksToReserve)
+{
+ ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot);
+
+ if (msgs == 0)
+ {
+ msgs = CreateThreadStressLog();
+
+ if (msgs == 0)
+ return FALSE;
+ }
+
+ if (chunksToReserve == 0)
+ {
+ chunksToReserve = (theLog.MaxSizePerThread + STRESSLOG_CHUNK_SIZE - 1) / STRESSLOG_CHUNK_SIZE;
+ }
+
+ LONG numTries = (LONG)chunksToReserve - msgs->chunkListLength;
+ for (LONG i = 0; i < numTries; i++)
+ {
+ msgs->GrowChunkList ();
+ }
+
+ return msgs->chunkListLength >= (LONG)chunksToReserve;
+}
+
+void (*FSwitchToSOTolerant)();
+void (*FSwitchToSOIntolerant)();
+void TrackSO(BOOL tolerance)
+{
+ if (tolerance)
+ {
+ if (FSwitchToSOTolerant)
+ {
+ FSwitchToSOTolerant();
+ }
+ }
+ else
+ {
+ if (FSwitchToSOIntolerant)
+ {
+ FSwitchToSOIntolerant();
+ }
+ }
+}
+
+/*********************************************************************************/
+/* fetch a buffer that can be used to write a stress message, it is thread safe */
+void ThreadStressLog::LogMsg ( DWORD_PTR facility, int cArgs, const char* format, va_list Args)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ // Asserts in this function cause infinite loops in the asserting mechanism.
+ // Just use debug breaks instead.
+
+#ifndef DACCESS_COMPILE
+#ifdef _DEBUG
+ // _ASSERTE ( cArgs >= 0 && cArgs <= 7 );
+ if (cArgs < 0 || cArgs > 7) DebugBreak();
+#endif //
+
+ size_t offs = ((size_t)format - StressLog::theLog.moduleOffset);
+
+ // _ASSERTE ( offs < StressMsg::maxOffset );
+ if (offs >= StressMsg::maxOffset)
+ {
+#ifdef _DEBUG
+ DebugBreak(); // in lieu of the above _ASSERTE
+#endif // _DEBUG
+
+ // Set it to this string instead.
+ offs =
+#ifdef _DEBUG
+ (size_t)"<BUG: StressLog format string beyond maxOffset>";
+#else // _DEBUG
+ 0; // a 0 offset is ignored by StressLog::Dump
+#endif // _DEBUG else
+ }
+
+ // Get next available slot
+ StressMsg* msg = AdvanceWrite(cArgs);
+
+ msg->timeStamp = getTimeStamp();
+ msg->facility = facility;
+ msg->formatOffset = offs;
+ msg->numberOfArgs = cArgs;
+
+ for ( int i = 0; i < cArgs; ++i )
+ {
+ void* data = va_arg(Args, void*);
+ msg->args[i] = data;
+ }
+
+#ifdef _DEBUG
+ if (!IsValid () || threadId != GetCurrentThreadId ())
+ DebugBreak();
+#endif // _DEBUG
+#endif //DACCESS_COMPILE
+}
+
+FORCEINLINE BOOL StressLog::InlinedStressLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#if defined(DACCESS_COMPILE)
+ return FALSE;
+#else
+ return ((theLog.facilitiesToLog & facility) && (level <= theLog.levelToLog));
+#endif
+}
+
+BOOL StressLog::StressLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return InlinedStressLogOn(facility, level);
+}
+
+FORCEINLINE BOOL StressLog::InlinedETWLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_CORECLR) && !defined(DACCESS_COMPILE)
+ return ((Microsoft_Windows_DotNETRuntimeStressHandle != 0) &&
+ (ETW_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context, (UCHAR)level, facility) != 0));
+#else
+ return FALSE;
+#endif
+}
+
+BOOL StressLog::ETWLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return InlinedETWLogOn(facility, level);
+}
+
+#if !defined(DACCESS_COMPILE)
+BOOL StressLog::LogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return InlinedStressLogOn(facility, level) || InlinedETWLogOn(facility, level);
+}
+#endif
+
+/* static */
+void StressLog::LogMsg (unsigned level, unsigned facility, int cArgs, const char* format, ... )
+{
+ STATIC_CONTRACT_SUPPORTS_DAC;
+#ifndef DACCESS_COMPILE
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // Any stresslog LogMsg could theoretically create a new stress log and thus
+ // enter a critical section. But we don't want these to cause violations in
+ // CANNOT_TAKE_LOCK callers, since the callers would otherwise be fine in runs that don't
+ // set the stress log config parameter.
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ _ASSERTE ( cArgs >= 0 && cArgs <= 7 );
+
+ va_list Args;
+ va_start(Args, format);
+
+ if(InlinedStressLogOn(facility, level))
+ {
+ ThreadStressLog* msgs = (ThreadStressLog*) ClrFlsGetValue(theLog.TLSslot);
+
+ if (msgs == 0) {
+ msgs = CreateThreadStressLog();
+
+ if (msgs == 0)
+ return;
+ }
+ msgs->LogMsg (facility, cArgs, format, Args);
+ }
+
+// Stress Log ETW feature available only on the desktop versions of the runtime
+#if !defined(FEATURE_CORECLR)
+ if(InlinedETWLogOn(facility, level))
+ {
+#define MAX_STRESSLOG_DATA_ETW_LENGTH 256
+ CHAR logMessage[MAX_STRESSLOG_DATA_ETW_LENGTH];
+
+ ULONG messageLength = (USHORT)_vsnprintf_s(logMessage, COUNTOF(logMessage), MAX_STRESSLOG_DATA_ETW_LENGTH-1, format, Args);
+
+ if(messageLength >= 0 &&
+ messageLength < MAX_STRESSLOG_DATA_ETW_LENGTH) // this condition has been added to make prefast happy
+ {
+ logMessage[messageLength] = 0;
+ }
+ messageLength++;
+ logMessage[MAX_STRESSLOG_DATA_ETW_LENGTH-1] = 0;
+ FireEtwStressLogEvent_V1((UINT32)facility, (UCHAR)level, logMessage, GetClrInstanceId());
+#undef MAX_STRESSLOG_DATA_ETW_LENGTH
+ }
+#endif // !FEATURE_CORECLR
+
+ va_end(Args);
+#endif //!DACCESS_COMPILE
+}
+
+#ifdef _DEBUG
+/* static */
+void StressLog::LogCallStack(const char *const callTag){
+ if (theLog.RtlCaptureStackBackTrace)
+ {
+ size_t CallStackTrace[MAX_CALL_STACK_TRACE];
+ ULONG hash;
+ USHORT stackTraceCount = theLog.RtlCaptureStackBackTrace (2, MAX_CALL_STACK_TRACE, (PVOID *)CallStackTrace, &hash);
+ if (stackTraceCount > MAX_CALL_STACK_TRACE)
+ stackTraceCount = MAX_CALL_STACK_TRACE;
+ LogMsgOL("Start of %s stack \n", callTag);
+ USHORT i = 0;
+ for (;i < stackTraceCount; i++)
+ {
+ LogMsgOL("(%s stack)%pK\n", callTag, CallStackTrace[i]);
+ }
+ LogMsgOL("End of %s stack\n", callTag);
+ }
+}
+#endif //_DEBUG
+
+#endif // STRESS_LOG
+
diff --git a/src/utilcode/sxshelpers.cpp b/src/utilcode/sxshelpers.cpp
new file mode 100644
index 0000000000..4d2734d7e9
--- /dev/null
+++ b/src/utilcode/sxshelpers.cpp
@@ -0,0 +1,1509 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+//
+// sxshelpers.cpp
+//
+// Some helping classes and methods for SxS in mscoree and mscorwks
+//
+
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "sxshelpers.h"
+
+#define SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE (0x00000001)
+#define SXS_GUID_INFORMATION_CLR_FLAG_IS_CLASS (0x00000002)
+
+typedef struct _SXS_GUID_INFORMATION_CLR
+{
+ DWORD cbSize;
+ DWORD dwFlags;
+ PCWSTR pcwszRuntimeVersion;
+ PCWSTR pcwszTypeName;
+ PCWSTR pcwszAssemblyIdentity;
+} SXS_GUID_INFORMATION_CLR, *PSXS_GUID_INFORMATION_CLR;
+typedef const SXS_GUID_INFORMATION_CLR *PCSXS_GUID_INFORMATION_CLR;
+
+#define SXS_LOOKUP_CLR_GUID_USE_ACTCTX (0x00000001)
+#define SXS_LOOKUP_CLR_GUID_FIND_SURROGATE (0x00010000)
+#define SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS (0x00020000)
+#define SXS_LOOKUP_CLR_GUID_FIND_ANY (SXS_LOOKUP_CLR_GUID_FIND_CLR_CLASS | SXS_LOOKUP_CLR_GUID_FIND_SURROGATE)
+
+#define SXS_DLL_NAME_W (W("sxs.dll"))
+#define SXS_LOOKUP_CLR_GUID ("SxsLookupClrGuid")
+
+typedef BOOL (WINAPI* PFN_SXS_LOOKUP_CLR_GUID)(
+ IN DWORD dwFlags,
+ IN LPGUID pClsid,
+ IN HANDLE hActCtx,
+ IN OUT PVOID pvOutputBuffer,
+ IN SIZE_T cbOutputBuffer,
+ OUT PSIZE_T pcbOutputBuffer
+ );
+
+// forward declaration
+BOOL TranslateWin32AssemblyIdentityToFusionDisplayName(__deref_out_z LPWSTR *ppwzFusionDisplayName, PCWSTR lpWin32AssemblyIdentity);
+
+// The initial size of the buffer passed to SxsLookupClrGuid.
+#define INIT_GUID_LOOKUP_BUFFER_SIZE 512
+
+// Function pointer to the function to lookup a CLR type by GUID in the unmanaged
+// fusion activation context.
+PFN_SXS_LOOKUP_CLR_GUID g_pfnLookupGuid = NULL;
+Volatile<BOOL> g_fSxSInfoInitialized = FALSE;
+
+HMODULE g_hmSxsDll = NULL;
+
+// And Here are the functions for getting shim info from
+// Win32 activation context
+
+// FindShimInfoFromWin32
+//
+// This method is used in ComInterop. If a COM client calls
+// CoCreateInstance on a managed COM server, we will use this method
+// trying to find required info of the managed COM server from Win32 subsystem.
+// If this fails, we will fall back to query the registry.
+//
+// Parameters:
+// rclsid: [in] The CLSID of the managed COM server
+// bLoadRecord: [in] Set to TRUE if we are looking for a record
+// *ppwzRuntimeVersion: [out] Runtime version
+// *ppwzClassName: [out] Class name
+// *ppwzAssemblyString: [out] Assembly display name
+// *pfRegFreePIA: [out] TRUE if the entry is <clrSurrogate>
+// Return:
+// FAILED(hr) if cannot find shim info from Win32
+// SUCCEEDED(HR) if shim info is found from Win32
+
+HRESULT
+FindShimInfoFromWin32(
+ REFCLSID rClsid,
+ BOOL bLoadRecord,
+ __deref_out_z __deref_opt_out_opt LPWSTR *ppwszRuntimeVersion,
+ __deref_out_z __deref_opt_out_opt LPWSTR *ppwszSupportedRuntimeVersions,
+ __deref_out_z __deref_opt_out_opt LPWSTR *ppwszClassName,
+ __deref_out_z __deref_opt_out_opt LPWSTR *ppwszAssemblyString,
+ BOOL *pfRegFreePIA
+ )
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ CQuickBytes rDataBuffer;
+ SIZE_T cbWritten;
+ HRESULT hr = S_OK;
+ PCSXS_GUID_INFORMATION_CLR pFoundInfo = NULL;
+ SIZE_T cch;
+ GUID MyGuid = rClsid;
+ DWORD dwFlags = bLoadRecord ? SXS_LOOKUP_CLR_GUID_FIND_SURROGATE : SXS_LOOKUP_CLR_GUID_FIND_ANY;
+
+ if (!ppwszRuntimeVersion && !ppwszClassName && !ppwszAssemblyString)
+ IfFailGo(E_INVALIDARG);
+
+ if (ppwszRuntimeVersion)
+ *ppwszRuntimeVersion = NULL;
+
+ if (ppwszSupportedRuntimeVersions)
+ *ppwszSupportedRuntimeVersions = NULL;
+
+ if (ppwszClassName)
+ *ppwszClassName = NULL;
+
+ if (ppwszAssemblyString)
+ *ppwszAssemblyString = NULL;
+
+ if (pfRegFreePIA)
+ *pfRegFreePIA = FALSE;
+
+ // If we haven't initialized the SxS info yet, then do so now.
+ if (!g_fSxSInfoInitialized)
+ {
+ if (g_hmSxsDll == NULL)
+ g_hmSxsDll = WszLoadLibrary(SXS_DLL_NAME_W);
+
+ if (g_hmSxsDll != NULL)
+ {
+ // Lookup the SxsLookupClrGuid function in the SxS DLL.
+ g_pfnLookupGuid = (PFN_SXS_LOOKUP_CLR_GUID)GetProcAddress(g_hmSxsDll, SXS_LOOKUP_CLR_GUID);
+ }
+
+ // The SxS info has been initialized.
+ g_fSxSInfoInitialized = TRUE;
+ }
+
+ // If we don't have the proc address of SxsLookupClrGuid, then return a failure.
+ if (g_pfnLookupGuid == NULL)
+ IfFailGo(E_FAIL);
+
+ // Resize the CQuickBytes to the initial buffer size.
+ IfFailGo(rDataBuffer.ReSizeNoThrow(INIT_GUID_LOOKUP_BUFFER_SIZE));
+
+ if (!g_pfnLookupGuid(dwFlags, &MyGuid, INVALID_HANDLE_VALUE, rDataBuffer.Ptr(), rDataBuffer.Size(), &cbWritten))
+ {
+ const DWORD dwLastError = ::GetLastError();
+
+ // Failed b/c we need more space? Expand and try again.
+ if (dwLastError == ERROR_INSUFFICIENT_BUFFER)
+ {
+ IfFailGo(rDataBuffer.ReSizeNoThrow(cbWritten));
+
+ // Still failed even with enough space? Bummer.
+ if (!g_pfnLookupGuid(dwFlags, &MyGuid, INVALID_HANDLE_VALUE, rDataBuffer.Ptr(), rDataBuffer.Size(), &cbWritten))
+ IfFailGo(E_FAIL);
+ }
+ // All other failures are real failures - probably the section isn't present
+ // or some other problem.
+ else
+ {
+ IfFailGo(E_FAIL);
+ }
+ }
+
+ pFoundInfo = (PCSXS_GUID_INFORMATION_CLR)rDataBuffer.Ptr();
+
+ if (pFoundInfo->dwFlags == SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE && ppwszRuntimeVersion)
+ {
+ // Surrogate does not have runtime version information !!!
+ IfFailGo(E_FAIL);
+ }
+
+ //
+ // This is special - translate the win32 assembly name into a managed
+ // assembly identity.
+ //
+ if (ppwszAssemblyString && pFoundInfo->pcwszAssemblyIdentity)
+ {
+ if (!TranslateWin32AssemblyIdentityToFusionDisplayName(ppwszAssemblyString, pFoundInfo->pcwszAssemblyIdentity))
+ IfFailGo(E_FAIL);
+ }
+
+ //
+ // For each field, allocate the outbound pointer and call through.
+ //
+ if (ppwszClassName && pFoundInfo->pcwszTypeName)
+ {
+ cch = wcslen(pFoundInfo->pcwszTypeName);
+
+ if (cch > 0)
+ {
+ IfNullGo(*ppwszClassName = new (nothrow) WCHAR[cch + 1]);
+ wcscpy_s(*ppwszClassName, cch+1, pFoundInfo->pcwszTypeName);
+ }
+ else
+ IfFailGo(E_FAIL);
+ }
+
+ if (ppwszRuntimeVersion)
+ {
+ if (pFoundInfo->pcwszRuntimeVersion && (cch = wcslen(pFoundInfo->pcwszRuntimeVersion)) > 0)
+ {
+ IfNullGo(*ppwszRuntimeVersion = new (nothrow) WCHAR[cch + 1]);
+ wcscpy_s(*ppwszRuntimeVersion, cch+1, pFoundInfo->pcwszRuntimeVersion);
+ }
+ else
+ {
+ // Sxs.dll returns empty string even when the runtimeVersion attribute is missing so
+ // we cannot tell whether it's not there or is empty. We'll return 1.0 in both cases.
+ //
+ // The goal is to emulate pre-4.0 behavior where this function wasn't called with
+ // non-NULL ppwszRuntimeVersion on the COM activation path at all so shim loaded the
+ // latest runtime on the machine.
+ IfNullGo(*ppwszRuntimeVersion = DuplicateString(V1_VERSION_NUM));
+ }
+ }
+
+ if (pfRegFreePIA)
+ {
+ *pfRegFreePIA = (pFoundInfo->dwFlags == SXS_GUID_INFORMATION_CLR_FLAG_IS_SURROGATE);
+ }
+
+ErrExit:
+ //
+ // Deallocate in case of failure
+ //
+ if (FAILED(hr))
+ {
+ if (ppwszRuntimeVersion && *ppwszRuntimeVersion)
+ {
+ delete [] *ppwszRuntimeVersion;
+ *ppwszRuntimeVersion = NULL;
+ }
+ if (ppwszAssemblyString && *ppwszAssemblyString)
+ {
+ delete [] *ppwszAssemblyString;
+ *ppwszAssemblyString = NULL;
+ }
+ if (ppwszClassName && *ppwszClassName)
+ {
+ delete [] *ppwszClassName;
+ *ppwszClassName = NULL;
+ }
+ }
+
+ return hr;
+}
+
+// TranslateWin32AssemblyIdentityToFusionDisplayName
+//
+// Culture info is missing in the assemblyIdentity returned from win32,
+// So Need to do a little more work here to get the correct fusion display name
+//
+// replace "language=" in assemblyIdentity to "culture=" if any.
+// If "language=" is not present in assemblyIdentity, add "culture=neutral"
+// to it.
+//
+// Also check other attributes as well.
+//
+// Parameters:
+// ppwzFusionDisplayName: the corrected output of assembly displayname
+// lpWin32AssemblyIdentity: input assemblyIdentity returned from win32
+//
+// returns:
+// TRUE if the conversion is done.
+// FALSE otherwise
+
+BOOL TranslateWin32AssemblyIdentityToFusionDisplayName(__deref_out_z LPWSTR *ppwzFusionDisplayName, PCWSTR lpWin32AssemblyIdentity)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ size_t size = 0;
+ LPWSTR lpAssemblyIdentityCopy = NULL;
+ LPWSTR lpVersionKey = W("version=");
+ LPWSTR lpPublicKeyTokenKey = W("publickeytoken=");
+ LPWSTR lpCultureKey = W("culture=");
+ LPWSTR lpNeutral = W("neutral");
+ LPWSTR lpLanguageKey = W("language=");
+ LPWSTR lpMatch = NULL;
+ LPWSTR lpwzFusionDisplayName = NULL;
+
+ if (ppwzFusionDisplayName == NULL) return FALSE;
+ *ppwzFusionDisplayName = NULL;
+
+ if (lpWin32AssemblyIdentity == NULL) return FALSE;
+
+ size = wcslen(lpWin32AssemblyIdentity);
+ if (size == 0) return FALSE;
+
+ // make a local copy
+ lpAssemblyIdentityCopy = new (nothrow) WCHAR[size+1];
+ if (!lpAssemblyIdentityCopy)
+ return FALSE;
+
+ wcscpy_s(lpAssemblyIdentityCopy, size+1, lpWin32AssemblyIdentity);
+
+ // convert to lower case
+ _wcslwr_s(lpAssemblyIdentityCopy, size+1);
+
+ // check if "version" key is presented
+ if (!wcsstr(lpAssemblyIdentityCopy, lpVersionKey))
+ {
+ // version is not presented, append it
+ size += wcslen(lpVersionKey)+8; // length of ","+"0.0.0.0"
+ lpwzFusionDisplayName = new (nothrow) WCHAR[size+1];
+ if (!lpwzFusionDisplayName)
+ {
+ // clean up
+ delete[] lpAssemblyIdentityCopy;
+ return FALSE;
+ }
+
+ //copy old one
+ wcscpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy);
+ wcscat_s(lpwzFusionDisplayName, size+1, W(","));
+ wcscat_s(lpwzFusionDisplayName, size+1, lpVersionKey);
+ wcscat_s(lpwzFusionDisplayName, size+1, W("0.0.0.0"));
+
+ // delete the old copy
+ delete[] lpAssemblyIdentityCopy;
+
+ // lpAssemblyIdentityCopy has the new copy
+ lpAssemblyIdentityCopy = lpwzFusionDisplayName;
+ lpwzFusionDisplayName = NULL;
+ }
+
+ // check if "publickeytoken" key is presented
+ if (!wcsstr(lpAssemblyIdentityCopy, lpPublicKeyTokenKey))
+ {
+ // publickeytoken is not presented, append it
+ size += wcslen(lpPublicKeyTokenKey)+5; //length of ","+"null"
+ lpwzFusionDisplayName = new (nothrow) WCHAR[size+1];
+ if (!lpwzFusionDisplayName)
+ {
+ // clean up
+ delete[] lpAssemblyIdentityCopy;
+ return FALSE;
+ }
+
+ // copy the old one
+ wcscpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy);
+ wcscat_s(lpwzFusionDisplayName, size+1, W(","));
+ wcscat_s(lpwzFusionDisplayName, size+1, lpPublicKeyTokenKey);
+ wcscat_s(lpwzFusionDisplayName, size+1, W("null"));
+
+ // delete the old copy
+ delete[] lpAssemblyIdentityCopy;
+
+ // lpAssemblyIdentityCopy has the new copy
+ lpAssemblyIdentityCopy = lpwzFusionDisplayName;
+ lpwzFusionDisplayName = NULL;
+ }
+
+ if (wcsstr(lpAssemblyIdentityCopy, lpCultureKey))
+ {
+ // culture info is already included in the assemblyIdentity
+ // nothing need to be done
+ lpwzFusionDisplayName = lpAssemblyIdentityCopy;
+ *ppwzFusionDisplayName = lpwzFusionDisplayName;
+ return TRUE;
+ }
+
+ if ((lpMatch = wcsstr(lpAssemblyIdentityCopy, lpLanguageKey)) !=NULL )
+ {
+ // language info is included in the assembly identity
+ // need to replace it with culture
+
+ // final size
+ size += wcslen(lpCultureKey)-wcslen(lpLanguageKey);
+ lpwzFusionDisplayName = new (nothrow) WCHAR[size + 1];
+ if (!lpwzFusionDisplayName)
+ {
+ // clean up
+ delete[] lpAssemblyIdentityCopy;
+ return FALSE;
+ }
+ wcsncpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy, lpMatch-lpAssemblyIdentityCopy);
+ lpwzFusionDisplayName[lpMatch-lpAssemblyIdentityCopy] = W('\0');
+ wcscat_s(lpwzFusionDisplayName, size+1, lpCultureKey);
+ wcscat_s(lpwzFusionDisplayName, size+1, lpMatch+wcslen(lpLanguageKey));
+ *ppwzFusionDisplayName = lpwzFusionDisplayName;
+
+ // clean up
+ delete[] lpAssemblyIdentityCopy;
+ return TRUE;
+ }
+ else
+ {
+ // neither culture or language key is presented
+ // let us attach culture info key to the identity
+ size += wcslen(lpCultureKey)+wcslen(lpNeutral)+1;
+ lpwzFusionDisplayName = new (nothrow) WCHAR[size + 1];
+ if (!lpwzFusionDisplayName)
+ {
+ // clean up
+ delete[] lpAssemblyIdentityCopy;
+ return FALSE;
+ }
+
+ wcscpy_s(lpwzFusionDisplayName, size+1, lpAssemblyIdentityCopy);
+ wcscat_s(lpwzFusionDisplayName, size+1, W(","));
+ wcscat_s(lpwzFusionDisplayName, size+1, lpCultureKey);
+ wcscat_s(lpwzFusionDisplayName, size+1, lpNeutral);
+ *ppwzFusionDisplayName = lpwzFusionDisplayName;
+
+ // clean up
+ delete[] lpAssemblyIdentityCopy;
+ return TRUE;
+ }
+}
+
+//****************************************************************************
+// AssemblyVersion
+//
+// class to handle assembly version
+// Since only functions in this file will use it,
+// we declare it in the cpp file so other people won't use it.
+//
+//****************************************************************************
+
+// Extract version info from pwzVersion, expecting "a.b.c.d",
+// where a,b,c and d are all digits.
+HRESULT AssemblyVersion::Init(__in_z LPCWSTR pcwzVersion, BOOL bStartsWithV)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr = S_OK;
+ LPWSTR pwzVersionCopy = NULL;
+ LPWSTR pwzTokens = NULL;
+ LPWSTR pwzToken = NULL;
+ size_t size = 0;
+ int iVersion = 0;
+
+ if ((pcwzVersion == NULL) || (*pcwzVersion == W('\0')))
+ IfFailGo(E_INVALIDARG);
+
+ // If fStartsWithV is true, then the version string should start with a v.
+ // Verify this and if it is the case, start tokenizing at the character after the v.
+ if (bStartsWithV)
+ {
+ if (*pcwzVersion == W('v') || *pcwzVersion == W('V'))
+ pcwzVersion++;
+ else
+ IfFailGo(E_INVALIDARG);
+ }
+
+ IfFailGo(ValidateVersion(pcwzVersion));
+
+ size = wcslen(pcwzVersion);
+
+ IfNullGo(pwzVersionCopy = new (nothrow) WCHAR[size + 1]);
+
+ wcscpy_s(pwzVersionCopy, size+1, pcwzVersion);
+ pwzTokens = pwzVersionCopy;
+
+ // parse major version
+ pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens);
+ if (pwzToken != NULL)
+ {
+ iVersion = _wtoi(pwzToken);
+ if (iVersion > 0xffff)
+ IfFailGo(E_INVALIDARG);
+ _major = (WORD)iVersion;
+ }
+
+ // parse minor version
+ pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens);
+ if (pwzToken != NULL)
+ {
+ iVersion = _wtoi(pwzToken);
+ if (iVersion > 0xffff)
+ IfFailGo(E_INVALIDARG);
+ _minor = (WORD)iVersion;
+ }
+
+ // parse build version
+ pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens);
+ if (pwzToken != NULL)
+ {
+ iVersion = _wtoi(pwzToken);
+ if (iVersion > 0xffff)
+ IfFailGo(E_INVALIDARG);
+ _build = (WORD)iVersion;
+ }
+
+ // parse revision version
+ pwzToken = wcstok_s(pwzTokens, W("."),&pwzTokens);
+ if (pwzToken != NULL)
+ {
+ iVersion = _wtoi(pwzToken);
+ if (iVersion > 0xffff)
+ IfFailGo(E_INVALIDARG);
+ _revision = (WORD)iVersion;
+ }
+
+ErrExit:
+ if (pwzVersionCopy)
+ delete[] pwzVersionCopy;
+ return hr;
+}
+
+// pcwzVersion must be in format of a.b.c.d, where a, b, c, d are numbers
+HRESULT AssemblyVersion::ValidateVersion(LPCWSTR pcwzVersion)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ LPCWSTR pwCh = pcwzVersion;
+ INT dots = 0; // number of dots
+ BOOL bIsDot = FALSE; // is previous char a dot?
+
+ // first char cannot be .
+ if (*pwCh == W('.'))
+ return E_INVALIDARG;
+
+ for(;*pwCh != W('\0');pwCh++)
+ {
+ if (*pwCh == W('.'))
+ {
+ if (bIsDot) // ..
+ return E_INVALIDARG;
+ else
+ {
+ dots++;
+ bIsDot = TRUE;
+ }
+ }
+ /*
+ // We can't do this sort of validation, because then our v1.2.x86chk version numbers will be invalid
+ else if (!iswdigit(*pwCh))
+ return E_INVALIDARG;
+ */
+ else
+ bIsDot = FALSE;
+ }
+
+ if (dots > 3)
+ return E_INVALIDARG;
+
+ return S_OK;
+}
+
+BOOL operator==(const AssemblyVersion& version1,
+ const AssemblyVersion& version2)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return ((version1._major == version2._major)
+ && (version1._minor == version2._minor)
+ && (version1._build == version2._build)
+ && (version1._revision == version2._revision));
+}
+
+BOOL operator>=(const AssemblyVersion& version1,
+ const AssemblyVersion& version2)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ ULONGLONG ulVersion1;
+ ULONGLONG ulVersion2;
+
+ ulVersion1 = version1._major;
+ ulVersion1 = (ulVersion1<<16)|version1._minor;
+ ulVersion1 = (ulVersion1<<16)|version1._build;
+ ulVersion1 = (ulVersion1<<16)|version1._revision;
+
+ ulVersion2 = version2._major;
+ ulVersion2 = (ulVersion2<<16)|version2._minor;
+ ulVersion2 = (ulVersion2<<16)|version2._build;
+ ulVersion2 = (ulVersion2<<16)|version2._revision;
+
+ return (ulVersion1 >= ulVersion2);
+}
+
+enum RegistryBasePath
+{
+ RegistryBasePath_Record,
+ RegistryBasePath_CLSID_InprocServer32,
+ RegistryBasePath_CLSID_LocalServer32_32Key,
+ RegistryBasePath_CLSID_LocalServer32_64Key,
+};
+
+// Find which subkey has the highest verion
+// If return S_OK, *ppwzHighestVersion has the highest version string.
+// *pbIsTopKey indicates if top key is the one with highest version.
+// If return S_FALSE, cannot find any version. *ppwzHighestVersion is set
+// to NULL, and *pbIsTopKey is TRUE.
+// If failed, *ppwzHighestVersion will be set to NULL, and *pbIsTopKey is
+// undefined.
+// Note: If succeeded, this function will allocate memory for *ppwzVersion.
+// Caller is responsible to release them
+HRESULT FindHighestVersion(REFCLSID rclsid, RegistryBasePath basePath, AssemblyVersion *prvHighestAllowed,
+ __deref_out_z LPWSTR *ppwzHighestVersion, BOOL *pbIsTopKey, BOOL *pbIsUnmanagedObject)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr = S_OK;
+ WCHAR szID[64];
+ WCHAR clsidKeyname[128];
+ WCHAR wzSubKeyName[32];
+ DWORD cwSubKeySize;
+ DWORD dwIndex; // subkey index
+ HKEY hKeyCLSID = NULL;
+ HKEY hSubKey = NULL;
+ DWORD type;
+ DWORD size;
+ BOOL bIsTopKey = FALSE; // Does top key have the highest version?
+ BOOL bGotVersion = FALSE; // Do we get anything out of registry?
+ LONG lResult;
+ LPWSTR wzAssemblyString = NULL;
+ DWORD numSubKeys = 0;
+ AssemblyVersion avHighest; WCHAR wzHighest[32];
+ AssemblyVersion avCurrent; WCHAR wzCurrent[32];
+
+ _ASSERTE(pbIsUnmanagedObject != NULL);
+ *pbIsUnmanagedObject = FALSE;
+
+ if ((ppwzHighestVersion == NULL) || (pbIsTopKey == NULL))
+ IfFailGo(E_INVALIDARG);
+
+ *ppwzHighestVersion = NULL;
+ *pbIsTopKey = FALSE;
+
+ if (!GuidToLPWSTR(rclsid, szID, NumItems(szID)))
+ IfFailGo(E_INVALIDARG);
+
+ if (basePath == RegistryBasePath_Record)
+ {
+ wcscpy_s(clsidKeyname, 128, W("Record\\"));
+ wcscat_s(clsidKeyname, 128, szID);
+ }
+ else
+ {
+ wcscpy_s(clsidKeyname, 128, W("CLSID\\"));
+ wcscat_s(clsidKeyname, 128, szID);
+ if (basePath == RegistryBasePath_CLSID_InprocServer32)
+ {
+ wcscat_s(clsidKeyname, 128, W("\\InprocServer32"));
+ }
+ else
+ {
+ _ASSERTE(basePath == RegistryBasePath_CLSID_LocalServer32_32Key || basePath == RegistryBasePath_CLSID_LocalServer32_64Key);
+ wcscat_s(clsidKeyname, 128, W("\\LocalServer32"));
+ }
+ }
+
+ // Open HKCR\CLSID\<clsid> , or HKCR\Record\<RecordId>
+ REGSAM accessFlags = KEY_ENUMERATE_SUB_KEYS | KEY_READ;
+ if (basePath == RegistryBasePath_CLSID_LocalServer32_32Key)
+ {
+ // open the WoW key
+ accessFlags |= KEY_WOW64_32KEY;
+ }
+ else if (basePath == RegistryBasePath_CLSID_LocalServer32_64Key)
+ {
+ // open the 64-bit key
+ accessFlags |= KEY_WOW64_64KEY;
+ }
+
+ IfFailWin32Go(WszRegOpenKeyEx(
+ HKEY_CLASSES_ROOT,
+ clsidKeyname,
+ 0,
+ accessFlags,
+ &hKeyCLSID));
+
+
+ //
+ // Start by looking for a version subkey.
+ //
+
+ IfFailWin32Go(WszRegQueryInfoKey(hKeyCLSID, NULL, NULL, NULL,
+ &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
+
+ for ( dwIndex = 0; dwIndex < numSubKeys; dwIndex++)
+ {
+ cwSubKeySize = NumItems(wzSubKeyName);
+
+ IfFailWin32Go(WszRegEnumKeyEx(hKeyCLSID, //HKCR\CLSID\<clsid>\InprocServer32
+ dwIndex, // which subkey
+ wzSubKeyName, // subkey name
+ &cwSubKeySize, // size of subkey name
+ NULL, // lpReserved
+ NULL, // lpClass
+ NULL, // lpcbClass
+ NULL)); // lpftLastWriteTime
+
+ hr = avCurrent.Init(wzSubKeyName, FALSE);
+ if (FAILED(hr))
+ {
+ // not valid version subkey, ignore
+ continue;
+ }
+ wcscpy_s(wzCurrent, COUNTOF(wzCurrent), wzSubKeyName);
+
+ IfFailWin32Go(WszRegOpenKeyEx(
+ hKeyCLSID,
+ wzSubKeyName,
+ 0,
+ accessFlags,
+ &hSubKey));
+
+ // Check if this is a non-interop scenario
+ lResult = WszRegQueryValueEx(
+ hSubKey,
+ SBSVERSIONVALUE,
+ NULL,
+ &type,
+ NULL,
+ &size);
+ if (lResult == ERROR_SUCCESS)
+ {
+ *pbIsUnmanagedObject = TRUE;
+ }
+ // This is an interop assembly
+ else
+ {
+ lResult = WszRegQueryValueEx(
+ hSubKey,
+ W("Assembly"),
+ NULL,
+ &type,
+ NULL,
+ &size);
+ if (!((lResult == ERROR_SUCCESS)&&(type == REG_SZ)&&(size > 0)))
+ {
+ // do not have value "Assembly"
+ RegCloseKey(hSubKey);
+ hSubKey = NULL;
+ continue;
+ }
+
+ lResult = WszRegQueryValueEx(
+ hSubKey,
+ W("Class"),
+ NULL,
+ &type,
+ NULL,
+ &size);
+ if (!((lResult == ERROR_SUCCESS)&&(type == REG_SZ)&&(size > 0)))
+ {
+ // do not have value "Class"
+ RegCloseKey(hSubKey);
+ hSubKey = NULL;
+ continue;
+ }
+
+ lResult = WszRegQueryValueEx(
+ hSubKey,
+ W("RuntimeVersion"),
+ NULL,
+ &type,
+ NULL,
+ &size);
+ if (!((lResult == ERROR_SUCCESS)&&(type == REG_SZ)&&(size > 0)))
+ {
+ // We didn't find a RuntimeVersion value. This is fine for Records since
+ // in V1.1, Records didn't have a RuntimeVersion value. However if we aren't
+ // dealing with a Record, then this version subkey is invalid.
+ if (basePath != RegistryBasePath_Record)
+ {
+ // do not have value "RuntimeVersion"
+ RegCloseKey(hSubKey);
+ hSubKey = NULL;
+ continue;
+ }
+ }
+ else
+ {
+ // If a highest allowed runtime version was specified, make sure that the component
+ // was built with a runtime version lower or equal to the highest.
+ if (prvHighestAllowed)
+ {
+ NewArrayHolder<WCHAR> wzRuntimeVersionString =
+ new (nothrow) WCHAR[(size/sizeof(WCHAR)) + 1];
+ IfNullGo(wzRuntimeVersionString);
+ // RegQueryValueEx() does not guarantee NULL-terminated strings
+ wzRuntimeVersionString[size/sizeof(WCHAR)] = W('\0');
+ IfFailWin32Go(WszRegQueryValueEx(
+ hSubKey,
+ W("RuntimeVersion"),
+ NULL,
+ &type,
+ (LPBYTE)(WCHAR*)wzRuntimeVersionString,
+ &size));
+
+ AssemblyVersion rvCurrent;
+ rvCurrent.Init(wzRuntimeVersionString, TRUE);
+
+ // We only care about major and minor version number.
+ rvCurrent.SetBuild(0);
+ rvCurrent.SetRevision(0);
+
+ if ((*prvHighestAllowed) < rvCurrent)
+ {
+ // This version of the component was built with a runtime version higher
+ // than maximum allowed one so we don't want to consider it.
+ RegCloseKey(hSubKey);
+ hSubKey = NULL;
+ continue;
+ }
+ }
+ }
+ }
+
+ // ok. Now I believe this is a valid subkey
+ RegCloseKey(hSubKey);
+ hSubKey = NULL;
+
+ if (bGotVersion)
+ {
+ if (avCurrent >= avHighest)
+ {
+ avHighest = avCurrent;
+ wcscpy_s(wzHighest, COUNTOF(wzHighest), wzCurrent);
+ }
+ }
+ else
+ {
+ avHighest = avCurrent;
+ wcscpy_s(wzHighest, COUNTOF(wzHighest), wzCurrent);
+ }
+
+ bGotVersion = TRUE;
+ }
+
+
+ //
+ // If there are no subkeys, then look at the top level key.
+ //
+
+ if (!bGotVersion)
+ {
+ // make sure value Class exists
+ // If not dealing with record, also make sure RuntimeVersion exists.
+ if ((WszRegQueryValueEx(hKeyCLSID, W("Class"), NULL, &type, NULL, &size) == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0))
+ {
+ // If there is no RuntimeVersion value, we will assume the component was built against
+ // the V1.0 CLR.
+ BOOL bSupportedVersion = TRUE;
+
+ lResult = WszRegQueryValueEx(
+ hKeyCLSID,
+ W("RuntimeVersion"),
+ NULL,
+ &type,
+ NULL,
+ &size);
+ if ((lResult == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0))
+ {
+ // If a highest allowed runtime version was specified, make sure that the component
+ // was built with a runtime version lower or equal to the highest.
+ if (prvHighestAllowed)
+ {
+ NewArrayHolder<WCHAR> wzRuntimeVersionString =
+ new (nothrow) WCHAR[(size/sizeof(WCHAR)) + 1];
+ IfNullGo(wzRuntimeVersionString);
+ // RegQueryValueEx() does not guarantee NULL-terminated strings
+ wzRuntimeVersionString[size/sizeof(WCHAR)] = W('\0');
+ IfFailWin32Go(WszRegQueryValueEx(
+ hKeyCLSID,
+ W("RuntimeVersion"),
+ NULL,
+ &type,
+ (LPBYTE)(WCHAR*)wzRuntimeVersionString,
+ &size));
+
+ AssemblyVersion rvCurrent;
+ rvCurrent.Init(wzRuntimeVersionString, TRUE);
+
+ // We only care about major and minor version number.
+ rvCurrent.SetBuild(0);
+ rvCurrent.SetRevision(0);
+
+ if ((*prvHighestAllowed) < rvCurrent)
+ {
+ // This version of the component was built with a runtime version higher
+ // than maximum allowed one so we don't want to consider it.
+ bSupportedVersion = FALSE;
+ }
+ }
+ }
+
+ if (bSupportedVersion)
+ {
+ // Get the size of assembly display name
+ lResult = WszRegQueryValueEx(
+ hKeyCLSID,
+ W("Assembly"),
+ NULL,
+ &type,
+ NULL,
+ &size);
+
+ if ((lResult == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0))
+ {
+ IfNullGo(wzAssemblyString = new (nothrow) WCHAR[size + 1]);
+ IfFailWin32Go(WszRegQueryValueEx(
+ hKeyCLSID,
+ W("Assembly"),
+ NULL,
+ &type,
+ (LPBYTE)wzAssemblyString,
+ &size));
+
+ // Now we have the assembly display name.
+ // Extract the version out.
+
+ // first lowercase display name
+ _wcslwr_s(wzAssemblyString,size+1);
+
+ // locate "version="
+ LPWSTR pwzVersion = wcsstr(wzAssemblyString, W("version="));
+ if (pwzVersion) {
+ // point to the character after "version="
+ pwzVersion += 8; // length of W("version=")
+
+ // Now find the next W(',')
+ LPWSTR pwzEnd = pwzVersion;
+
+ while((*pwzEnd != W(',')) && (*pwzEnd != W('\0')))
+ pwzEnd++;
+
+ // terminate version string
+ *pwzEnd = W('\0');
+
+ // trim version string
+ while(iswspace(*pwzVersion))
+ pwzVersion++;
+
+ pwzEnd--;
+ while(iswspace(*pwzEnd)&&(pwzEnd > pwzVersion))
+ {
+ *pwzEnd = W('\0');
+ pwzEnd--;
+ }
+
+ // Make sure the version is valid.
+ if(SUCCEEDED(avHighest.Init(pwzVersion, FALSE)))
+ {
+ // This is the first version found, so it is the highest version
+ wcscpy_s(wzHighest, COUNTOF(wzHighest), pwzVersion);
+ bIsTopKey = TRUE;
+ bGotVersion = TRUE;
+ }
+ }
+ }
+ }
+ } // end of handling of key HKCR\CLSID\<clsid>\InprocServer32
+ }
+
+ if (bGotVersion)
+ {
+ // Now we have the highest version. Copy it out
+ size_t cchHighest = wcslen(wzHighest) + 1;
+ *ppwzHighestVersion = new (nothrow) WCHAR[cchHighest];
+ wcscpy_s(*ppwzHighestVersion, cchHighest, wzHighest);
+
+ *pbIsTopKey = bIsTopKey;
+
+ // return S_OK to indicate we successfully found the highest version.
+ hr = S_OK;
+ }
+ else
+ {
+ // return E_CLASSNOTREG to indicate that we didn't find anything
+ hr = REGDB_E_CLASSNOTREG;
+ }
+
+ErrExit:
+ if (hKeyCLSID)
+ RegCloseKey(hKeyCLSID);
+ if (hSubKey)
+ RegCloseKey(hSubKey);
+ if (wzAssemblyString)
+ delete[] wzAssemblyString;
+
+ return hr;
+}
+
+// If the value exists and is retrieved successfully, returns S_OK
+// If the value does not exist, returns S_FALSE
+// If some other error occurs, returns error-specific HRESULT
+static
+HRESULT ReadRegistryStringValue(
+ HKEYHolder &hKey,
+ LPCWSTR wszName,
+ __deref_out_z __deref_out_opt LPWSTR *pwszValue)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pwszValue != NULL);
+ *pwszValue = NULL;
+
+ // extract the string value.
+ DWORD dwSize;
+ DWORD dwType;
+ NewArrayHolder<WCHAR> wszValue(NULL);
+ hr = HRESULT_FROM_WIN32(WszRegQueryValueEx(hKey, wszName, NULL, &dwType, NULL, &dwSize));
+
+ // If the function succeeds, the return value is ERROR_SUCCESS.
+ // If the wszName registry value does not exist, the function returns ERROR_FILE_NOT_FOUND.
+ if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS))
+ {
+ // The value is not a string
+ if (dwType != REG_SZ)
+ return E_INVALIDARG;
+
+ if(!ClrSafeInt<DWORD>::addition(dwSize, 1, dwSize))
+ IfFailWin32Ret(ERROR_ARITHMETIC_OVERFLOW);
+
+ IfNullRet(wszValue = new (nothrow) WCHAR[dwSize]);
+ IfFailWin32Ret(WszRegQueryValueEx(hKey, wszName, NULL, NULL, (LPBYTE)static_cast<LPCWSTR>(wszValue), &dwSize));
+ hr = S_OK;
+ }
+ else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
+ {
+ return S_FALSE;
+ }
+
+ _ASSERTE(hr == S_OK);
+ wszValue.SuppressRelease();
+ *pwszValue = wszValue;
+ return hr;
+}
+
+// FindRuntimeVersionFromRegistry
+//
+// Find the runtimeVersion corresponding to the highest version
+HRESULT FindRuntimeVersionFromRegistry(
+ REFCLSID rclsid,
+ __deref_out_z __deref_out_opt LPWSTR *ppwzRuntimeVersion,
+ __deref_out_z __deref_out_opt LPWSTR *ppwzSupportedVersions)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr = S_OK;
+ HKEYHolder userKey;
+ WCHAR szID[64];
+ WCHAR keyname[256];
+ DWORD size;
+ DWORD type;
+ NewArrayHolder<WCHAR> pwzVersion = NULL;
+ BOOL bIsTopKey;
+ BOOL bIsUnmanagedObject = FALSE;
+ NewArrayHolder<WCHAR> pwzRuntimeVersion = NULL;
+ NewArrayHolder<WCHAR> pwzSupportedRuntimeVersions = NULL;
+
+ if (ppwzRuntimeVersion == NULL)
+ IfFailRet(E_INVALIDARG);
+
+ // Initialize the string passed in to NULL.
+ *ppwzRuntimeVersion = NULL;
+
+ if (ppwzSupportedVersions != NULL)
+ *ppwzSupportedVersions = NULL;
+
+ // Convert the GUID to its string representation.
+ if (GuidToLPWSTR(rclsid, szID, NumItems(szID)) == 0)
+ IfFailRet(E_INVALIDARG);
+
+ // retrieve the highest version.
+ IfFailRet(FindHighestVersion(rclsid, RegistryBasePath_CLSID_InprocServer32, NULL, &pwzVersion, &bIsTopKey, &bIsUnmanagedObject));
+
+ if (!bIsUnmanagedObject)
+ {
+ // if highest version is in top key,
+ // we will look at HKCR\CLSID\<clsid>\InprocServer32 or HKCR\Record\<RecordId>
+ // Otherwise we will look at HKCR\CLSID\<clsid>\InprocServer32\<version> or HKCR\Record\<RecordId>\<Version>
+ wcscpy_s(keyname, 256, W("CLSID\\"));
+ wcscat_s(keyname, 256, szID);
+ wcscat_s(keyname, 256, W("\\InprocServer32"));
+ if (!bIsTopKey)
+ {
+ wcscat_s(keyname, 256, W("\\"));
+ wcscat_s(keyname, 256, pwzVersion);
+ }
+
+ // open the registry key
+ IfFailWin32Ret(WszRegOpenKeyEx(HKEY_CLASSES_ROOT, keyname, 0, KEY_READ, &userKey));
+
+ // extract the runtime version.
+ IfFailRet(ReadRegistryStringValue(userKey, W("RuntimeVersion"), &pwzRuntimeVersion));
+ if (hr == S_FALSE)
+ {
+ IfNullRet(pwzRuntimeVersion = DuplicateString(V1_VERSION_NUM));
+ }
+
+ // extract the supported runtime versions
+ if (ppwzSupportedVersions != NULL)
+ IfFailRet(ReadRegistryStringValue(userKey, W("SupportedRuntimeVersions"), &pwzSupportedRuntimeVersions));
+ }
+
+ else
+ {
+ // We need to prepend the 'v' to the version string
+ IfNullRet(pwzRuntimeVersion = new (nothrow) WCHAR[wcslen(pwzVersion)+1+1]); // +1 for the v, +1 for the null
+ *pwzRuntimeVersion = W('v');
+ wcscpy_s(pwzRuntimeVersion+1, wcslen(pwzVersion)+1, pwzVersion);
+ }
+
+ _ASSERTE(SUCCEEDED(hr));
+
+ // now we have the data, copy it out
+ pwzRuntimeVersion.SuppressRelease();
+ *ppwzRuntimeVersion = pwzRuntimeVersion;
+
+ if (ppwzSupportedVersions != NULL)
+ {
+ pwzSupportedRuntimeVersions.SuppressRelease();
+ *ppwzSupportedVersions = pwzSupportedRuntimeVersions;
+ }
+
+ return hr;
+}
+
+// FindShimInfoFromRegistry
+//
+HRESULT FindShimInfoFromRegistryWorker(
+ REFCLSID rclsid,
+ RegistryBasePath basePath,
+ WORD wHighestRuntimeMajorVersion,
+ WORD wHighestRuntimeMinorVersion,
+ __deref_out_z LPWSTR *ppwzClassName,
+ __deref_out_z LPWSTR *ppwzAssemblyString,
+ __deref_out_z LPWSTR *ppwzCodeBase)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr = S_OK;
+ HKEY userKey = NULL;
+ WCHAR szID[64];
+ WCHAR keyname[256];
+ DWORD size;
+ DWORD type;
+ LPWSTR pwzVersion = NULL;
+ BOOL bIsTopKey;
+ NewArrayHolder<WCHAR> wzClassName = NULL;
+ NewArrayHolder<WCHAR> wzAssemblyString = NULL;
+ NewArrayHolder<WCHAR> wzCodeBase = NULL;
+ LONG lResult;
+ AssemblyVersion highestRuntimeVersion;
+
+ // at least one should be specified.
+ // codebase is optional
+ if ((ppwzClassName == NULL) && (ppwzAssemblyString == NULL))
+ IfFailGo(E_INVALIDARG);
+
+ // Initialize the strings passed in to NULL.
+ if (ppwzClassName)
+ *ppwzClassName = NULL;
+ if (ppwzAssemblyString)
+ *ppwzAssemblyString = NULL;
+ if (ppwzCodeBase)
+ *ppwzCodeBase = NULL;
+
+ // Convert the GUID to its string representation.
+ if (GuidToLPWSTR(rclsid, szID, NumItems(szID)) == 0)
+ IfFailGo(E_INVALIDARG);
+
+ // retrieve the highest version.
+ BOOL bIsUnmanaged = FALSE;
+
+ // Initialize the highest runtime version based on the passed in major and minor version numbers.
+ highestRuntimeVersion.Init(wHighestRuntimeMajorVersion, wHighestRuntimeMinorVersion, 0, 0);
+
+ IfFailGo(FindHighestVersion(rclsid, basePath, &highestRuntimeVersion, &pwzVersion, &bIsTopKey, &bIsUnmanaged));
+
+ // if highest version is in top key,
+ // we will look at HKCR\CLSID\<clsid>\{Inproc,Local}Server32 or HKCR\Record\<RecordId>
+ // Otherwise we will look at HKCR\CLSID\<clsid>\{Inproc,Local}Server32\<version> or HKCR\Record\<RecordId>\<Version>
+ if (basePath == RegistryBasePath_Record)
+ {
+ wcscpy_s(keyname, 256, W("Record\\"));
+ wcscat_s(keyname, 256, szID);
+ }
+ else
+ {
+ wcscpy_s(keyname, 256, W("CLSID\\"));
+ wcscat_s(keyname, 256, szID);
+ if (basePath == RegistryBasePath_CLSID_InprocServer32)
+ {
+ wcscat_s(keyname, 256, W("\\InprocServer32"));
+ }
+ else
+ {
+ _ASSERTE(basePath == RegistryBasePath_CLSID_LocalServer32_32Key || basePath == RegistryBasePath_CLSID_LocalServer32_64Key);
+ wcscat_s(keyname, 256, W("\\LocalServer32"));
+ }
+ }
+ if (!bIsTopKey)
+ {
+ wcscat_s(keyname, 256, W("\\"));
+ wcscat_s(keyname, 256, pwzVersion);
+ }
+
+ // open the registry
+ REGSAM accessFlags = KEY_READ;
+ if (basePath == RegistryBasePath_CLSID_LocalServer32_32Key)
+ {
+ // open the WoW key
+ accessFlags |= KEY_WOW64_32KEY;
+ }
+ else if (basePath == RegistryBasePath_CLSID_LocalServer32_64Key)
+ {
+ // open the 64-bit key
+ accessFlags |= KEY_WOW64_64KEY;
+ }
+
+ IfFailWin32Go(WszRegOpenKeyEx(HKEY_CLASSES_ROOT, keyname, 0, accessFlags, &userKey));
+
+ // get the class name
+ IfFailWin32Go(WszRegQueryValueEx(userKey, W("Class"), NULL, &type, NULL, &size));
+ IfNullGo(wzClassName = new (nothrow) WCHAR[size + 1]);
+ IfFailWin32Go(WszRegQueryValueEx(userKey, W("Class"), NULL, NULL, (LPBYTE)wzClassName.GetValue(), &size));
+
+ // get the assembly string
+ IfFailWin32Go(WszRegQueryValueEx(userKey, W("Assembly"), NULL, &type, NULL, &size));
+ IfNullGo(wzAssemblyString = new (nothrow) WCHAR[size + 1]);
+ IfFailWin32Go(WszRegQueryValueEx(userKey, W("Assembly"), NULL, NULL, (LPBYTE)wzAssemblyString.GetValue(), &size));
+
+ // get the code base if requested
+ if (ppwzCodeBase)
+ {
+ // get the codebase, however not finding it does not constitute
+ // a fatal error.
+ lResult = WszRegQueryValueEx(userKey, W("CodeBase"), NULL, &type, NULL, &size);
+ if ((lResult == ERROR_SUCCESS) && (type == REG_SZ) && (size > 0))
+ {
+ IfNullGo(wzCodeBase = new (nothrow) WCHAR[size + 1]);
+ IfFailWin32Go(WszRegQueryValueEx(userKey, W("CodeBase"), NULL, NULL, (LPBYTE)wzCodeBase.GetValue(), &size));
+ }
+ }
+
+ // now we got everything. Copy them out
+ if (ppwzClassName)
+ *ppwzClassName = wzClassName.Extract();
+
+ if (ppwzAssemblyString)
+ *ppwzAssemblyString = wzAssemblyString.Extract();
+
+ if (ppwzCodeBase)
+ *ppwzCodeBase = wzCodeBase.Extract();
+
+ hr = S_OK;
+
+ErrExit:
+ if (userKey)
+ RegCloseKey(userKey);
+
+ if (pwzVersion)
+ delete[] pwzVersion;
+
+ return hr;
+}
+
+// Find shim info corresponding to the highest version
+HRESULT FindShimInfoFromRegistry(
+ REFCLSID rclsid,
+ BOOL bLoadRecord,
+ WORD wHighestRuntimeMajorVersion,
+ WORD wHighestRuntimeMinorVersion,
+ __deref_out_z LPWSTR *ppwzClassName,
+ __deref_out_z LPWSTR *ppwzAssemblyString,
+ __deref_out_z LPWSTR *ppwzCodeBase)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ if (bLoadRecord)
+ {
+ return FindShimInfoFromRegistryWorker(rclsid,
+ RegistryBasePath_Record,
+ wHighestRuntimeMajorVersion,
+ wHighestRuntimeMinorVersion,
+ ppwzClassName,
+ ppwzAssemblyString,
+ ppwzCodeBase);
+ }
+
+ // try InprocServer32 first
+ HRESULT hr = FindShimInfoFromRegistryWorker(rclsid,
+ RegistryBasePath_CLSID_InprocServer32,
+ wHighestRuntimeMajorVersion,
+ wHighestRuntimeMinorVersion,
+ ppwzClassName,
+ ppwzAssemblyString,
+ ppwzCodeBase);
+
+ // if it fails try both 64-bit and WoW LocalServer32
+ if (FAILED(hr))
+ {
+ // prefer the bitness of the process, use the other bitness as a fallback
+ hr = FindShimInfoFromRegistryWorker(rclsid,
+#ifdef _WIN64
+ RegistryBasePath_CLSID_LocalServer32_64Key,
+#else // _WIN64
+ RegistryBasePath_CLSID_LocalServer32_32Key,
+#endif // _WIN64
+ wHighestRuntimeMajorVersion,
+ wHighestRuntimeMinorVersion,
+ ppwzClassName,
+ ppwzAssemblyString,
+ ppwzCodeBase);
+
+ if (FAILED(hr)
+#ifndef _WIN64
+ && RunningInWow64()
+#endif // !_WIN64
+ )
+ {
+ hr = FindShimInfoFromRegistryWorker(rclsid,
+#ifdef _WIN64
+ RegistryBasePath_CLSID_LocalServer32_32Key,
+#else // _WIN64
+ RegistryBasePath_CLSID_LocalServer32_64Key,
+#endif // _WIN64
+ wHighestRuntimeMajorVersion,
+ wHighestRuntimeMinorVersion,
+ ppwzClassName,
+ ppwzAssemblyString,
+ ppwzCodeBase);
+ }
+ }
+ return hr;
+}
+
+
+// ----------------------------------------------------------------------------------------------------------
+//
+// Gets config file name from Win32 manifest file. Strips the last extension (.manifest) from the manifest
+// file name. Doesn't strip the last extension if it is '.exe'.
+//
+// Note: Config file name could be already set in activation context by COM+ (out-of-process COM) - they
+// pick up CLR version information from registry.
+//
+// Arguments:
+// wszBuffer - [in, out] The buffer to fill the configuration file name (can be NULL)
+// cBuffer - [in] Size of the buffer in wide characters
+// pcNameSize - [out] Size of the name filled in the buffer (in wide characters including terminating null).
+// Can be NULL.
+//
+// Return value:
+// S_OK - Success. Buffer (wszBuffer) and config file name size (pcNameSize) are filled.
+// HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) - Buffer is too small for filling config file name.
+// Sets pcNameSize (if not NULL) to the size of config file name including null terminator.
+//
+HRESULT GetConfigFileFromWin32Manifest(__out_ecount(cBuffer) WCHAR *wszBuffer,
+ SIZE_T cBuffer,
+ SIZE_T *pcNameSize)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr = S_OK;
+
+ HANDLE hActCtx = NULL;
+ SIZE_T cbInfo = 0;
+ SIZE_T cConfigFileName = 0;
+
+ // Get detailed information about activation activation context
+ if (!WszQueryActCtxW(0, // Flags
+ hActCtx, // Activation context being queried
+ NULL, // Specific to the information class
+ ActivationContextDetailedInformation,
+ // Information class - detailed information
+ NULL, // [out] Buffer
+ 0, // [in] Buffer size
+ &cbInfo)) // [out] Written/required sized in buffer
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ CQuickBytes qbInfo;
+ ACTIVATION_CONTEXT_DETAILED_INFORMATION *pInfo = NULL;
+
+ pInfo = (ACTIVATION_CONTEXT_DETAILED_INFORMATION *)qbInfo.AllocNoThrow(cbInfo);
+ IfNullGo(pInfo);
+
+ if (WszQueryActCtxW(0, // Flags
+ hActCtx, // Activation context being queried
+ NULL, // Specific to the information class
+ ActivationContextDetailedInformation,
+ // Information class - detailed information
+ pInfo, // [out] Buffer
+ cbInfo, // [in] Buffer size
+ &cbInfo) && // [out] Written size in the buffer
+ pInfo->ulAppDirPathType == ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE)
+ { // Application manifest was loaded from Win32 file (not an URL, AssemblyRef etc.)
+ WCHAR *wszConfigFileName = NULL;
+ CQuickWSTR qwszConfigFileName;
+
+ if (pInfo->lpRootConfigurationPath != NULL)
+ { // Configuration file name is provided, use it
+ wszConfigFileName = (WCHAR *)pInfo->lpRootConfigurationPath;
+ }
+ else if (pInfo->lpRootManifestPath != NULL)
+ { // Manifest file name is provided, use it
+ SIZE_T cManifestFileName = wcslen(pInfo->lpRootManifestPath);
+ if (cManifestFileName != 0)
+ { // Manifest file does exist
+ WCHAR wszConfigFileExtension[] = W(".config");
+ SIZE_T cConfigFileExtension = sizeof(wszConfigFileExtension) / sizeof(WCHAR);
+ // Allocate space for manifest file name + .config + terminating null
+ // (included in .config extension)
+ SIZE_T cConfigFileNameAllocated = cManifestFileName + cConfigFileExtension;
+ wszConfigFileName = qwszConfigFileName.AllocNoThrow(cConfigFileNameAllocated);
+ IfNullGo(wszConfigFileName);
+ // Use manifest file name as the template for config file name
+ wcscpy_s(wszConfigFileName, cConfigFileNameAllocated, pInfo->lpRootManifestPath);
+
+ // Find the last extension in the manifest file name (or NULL if not found)
+ LPWSTR wszLastExtension = wcsrchr(wszConfigFileName, W('.'));
+ // Is the manifest file in an external separate file?
+ if ((wszLastExtension != NULL) && (_wcsicmp(wszLastExtension, W(".exe")) != 0))
+ { // It is an external manifest file (with .manifest or similar extension)
+ // Excluded are files without an extension and with .exe extension (embeded
+ // manifest in executable resources)
+
+ // Strip the last extension in the manifest file name
+ *wszLastExtension = 0;
+ }
+ // Manifest file name has stripped last extension (.manifest)
+
+ // Append the .config extension behind the manifest file name
+ wcscat_s(wszConfigFileName,
+ cConfigFileNameAllocated,
+ wszConfigFileExtension);
+ }
+ }
+
+ // Do we have a configuration file name?
+ if (wszConfigFileName != NULL)
+ { // We have a configuration file name
+ // Get the real configuration file name length (including terminating null)
+ cConfigFileName = wcslen(wszConfigFileName) + 1;
+ // Check the output buffer - is its size sufficient?
+ if ((wszBuffer == NULL) || (cConfigFileName > cBuffer))
+ { // Insufficient output buffer (too small or not passed)
+ IfFailGo(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
+ }
+ else
+ { // Fill output buffer
+ wcscpy_s(wszBuffer, cConfigFileName, wszConfigFileName);
+ }
+ }
+ }
+ }
+ }
+
+ErrExit:
+ // Should we return config file name size?
+ if (pcNameSize != NULL)
+ { // We should return name size
+ // Return either copied size or potentially copied size
+ *pcNameSize = cConfigFileName;
+ }
+ return hr;
+}
+
+HRESULT GetApplicationPathFromWin32Manifest(__out_ecount(dwBuffer) WCHAR* buffer, SIZE_T dwBuffer, SIZE_T* pSize)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr = S_OK;
+
+ // Get the basic activation context first.
+ ACTIVATION_CONTEXT_DETAILED_INFORMATION* pInfo = NULL;
+ SIZE_T length = 0;
+
+ HANDLE hActCtx = NULL;
+ SIZE_T nCount = 0;
+
+ if (!WszQueryActCtxW(0, hActCtx, NULL, ActivationContextDetailedInformation,
+ NULL, nCount, &nCount))
+ {
+
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+
+ pInfo = (ACTIVATION_CONTEXT_DETAILED_INFORMATION*) alloca(nCount);
+
+ if (WszQueryActCtxW(0, hActCtx, NULL, ActivationContextDetailedInformation,
+ pInfo, nCount, &nCount) &&
+ pInfo->ulAppDirPathType == ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE)
+ {
+
+ if(pInfo->lpAppDirPath) {
+ length = wcslen(pInfo->lpAppDirPath) + 1;
+ if(length > dwBuffer || buffer == NULL) {
+ hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else {
+ wcscpy_s(buffer, dwBuffer, pInfo->lpAppDirPath);
+ }
+ }
+
+ }
+ }
+ }
+ if(pSize) *pSize = length;
+ return hr;
+}
diff --git a/src/utilcode/tlbutils.cpp b/src/utilcode/tlbutils.cpp
new file mode 100644
index 0000000000..88906bac64
--- /dev/null
+++ b/src/utilcode/tlbutils.cpp
@@ -0,0 +1,222 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+// Utilities used to help manipulating typelibs.
+
+#include "stdafx.h" // Precompiled header key.
+
+#include "tlbutils.h"
+#include "dispex.h"
+#include "posterror.h"
+#include "ndpversion.h"
+
+#define CUSTOM_MARSHALER_ASM ", CustomMarshalers, Version=" VER_ASSEMBLYVERSION_STR ", Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
+
+static const LPCWSTR DLL_EXTENSION = {W(".dll")};
+static const int DLL_EXTENSION_LEN = 4;
+static const LPCWSTR EXE_EXTENSION = {W(".exe")};
+static const int EXE_EXTENSION_LEN = 4;
+
+const StdConvertibleItfInfo aStdConvertibleInterfaces[] =
+{
+ { "System.Runtime.InteropServices.Expando.IExpando", (GUID*)&IID_IDispatchEx,
+ "System.Runtime.InteropServices.CustomMarshalers.ExpandoToDispatchExMarshaler" CUSTOM_MARSHALER_ASM, "IExpando" },
+
+ { "System.Reflection.IReflect", (GUID*)&IID_IDispatchEx,
+ "System.Runtime.InteropServices.CustomMarshalers.ExpandoToDispatchExMarshaler" CUSTOM_MARSHALER_ASM, "IReflect" },
+
+ { "System.Collections.IEnumerator", (GUID*)&IID_IEnumVARIANT,
+ "System.Runtime.InteropServices.CustomMarshalers.EnumeratorToEnumVariantMarshaler" CUSTOM_MARSHALER_ASM, "" },
+
+ { "System.Type", (GUID*)&IID_ITypeInfo,
+ "System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler" CUSTOM_MARSHALER_ASM, "" },
+};
+
+// This method returns the custom marshaler info to convert the native interface
+// to its managed equivalent. Or null if the interface is not a standard convertible interface.
+const StdConvertibleItfInfo *GetConvertionInfoFromNativeIID(REFGUID rGuidNativeItf)
+{
+ CONTRACT (const StdConvertibleItfInfo*)
+ {
+ NOTHROW;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ // Look in the table of interfaces that have standard convertions to see if the
+ // specified interface is there.
+ for (int i = 0; i < sizeof(aStdConvertibleInterfaces) / sizeof(StdConvertibleItfInfo); i++)
+ {
+ if (IsEqualGUID(rGuidNativeItf, *(aStdConvertibleInterfaces[i].m_pNativeTypeIID)))
+ RETURN &aStdConvertibleInterfaces[i];
+ }
+
+ // The interface is not in the table.
+ RETURN NULL;
+}
+
+//*****************************************************************************
+// Given a typelib, determine the managed namespace name.
+//*****************************************************************************
+HRESULT GetNamespaceNameForTypeLib( // S_OK or error.
+ ITypeLib *pITLB, // [IN] The TypeLib.
+ BSTR *pwzNamespace) // [OUT] Put the namespace name here.
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW); // PostError goes down a throwing path right now. Revisit this when fixed.
+ PRECONDITION(CheckPointer(pITLB));
+ PRECONDITION(CheckPointer(pwzNamespace));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ ITypeLib2 *pITLB2=0; //For getting custom value.
+ TLIBATTR *pAttr=0; // Typelib attributes.
+ BSTR szPath=0; // Typelib path.
+
+ // If custom attribute for namespace exists, use it.
+ if (pITLB->QueryInterface(IID_ITypeLib2, (void **)&pITLB2) == S_OK)
+ {
+ VARIANT vt;
+ VariantInit(&vt);
+ if (pITLB2->GetCustData(GUID_ManagedName, &vt) == S_OK)
+ {
+ if (V_VT(&vt) == VT_BSTR)
+ {
+ // If the namespace ends with .dll then remove the extension.
+ LPWSTR pDest = wcsstr(vt.bstrVal, DLL_EXTENSION);
+ if (pDest && (pDest[DLL_EXTENSION_LEN] == 0 || pDest[DLL_EXTENSION_LEN] == ' '))
+ *pDest = 0;
+
+ if (!pDest)
+ {
+ // If the namespace ends with .exe then remove the extension.
+ pDest = wcsstr(vt.bstrVal, EXE_EXTENSION);
+ if (pDest && (pDest[EXE_EXTENSION_LEN] == 0 || pDest[EXE_EXTENSION_LEN] == ' '))
+ *pDest = 0;
+ }
+
+ if (pDest)
+ {
+ // We removed the extension so re-allocate a string of the new length.
+ *pwzNamespace = SysAllocString(vt.bstrVal);
+ SysFreeString(vt.bstrVal);
+ }
+ else
+ {
+ // There was no extension to remove so we can use the string returned
+ // by GetCustData().
+ *pwzNamespace = vt.bstrVal;
+ }
+
+ goto ErrExit;
+ }
+ else
+ {
+ VariantClear(&vt);
+ }
+ }
+ }
+
+ // No custom attribute, use library name.
+ IfFailGo(pITLB->GetDocumentation(MEMBERID_NIL, pwzNamespace, 0, 0, 0));
+
+ErrExit:
+ if (szPath)
+ ::SysFreeString(szPath);
+ if (pAttr)
+ pITLB->ReleaseTLibAttr(pAttr);
+ if (pITLB2)
+ pITLB2->Release();
+
+ return hr;
+} // HRESULT GetNamespaceNameForTypeLib()
+
+//*****************************************************************************
+// Given an ITypeInfo, determine the managed name. Optionally supply a default
+// namespace, otherwise derive namespace from containing typelib.
+//*****************************************************************************
+HRESULT GetManagedNameForTypeInfo( // S_OK or error.
+ ITypeInfo *pITI, // [IN] The TypeInfo.
+ LPCWSTR wzNamespace, // [IN, OPTIONAL] Default namespace name.
+ LPCWSTR wzAsmName, // [IN, OPTIONAL] Assembly name.
+ BSTR *pwzName) // [OUT] Put the name here.
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ PRECONDITION(CheckPointer(pITI));
+ PRECONDITION(CheckPointer(wzNamespace, NULL_OK));
+ PRECONDITION(CheckPointer(wzAsmName, NULL_OK));
+ PRECONDITION(CheckPointer(pwzName));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ ITypeInfo2 *pITI2=0; // For getting custom value.
+ ITypeLib *pITLB=0; // Containing typelib.
+
+ BSTR bstrName=0; // Typeinfo's name.
+ BSTR bstrNamespace=0; // Typelib's namespace.
+ int cchFullyQualifiedName; // Size of namespace + name buffer.
+ int cchAsmName=0; // The size of the assembly name.
+ int cchAsmQualifiedName=0; // The size of the assembly qualified name buffer.
+ CQuickArray<WCHAR> qbFullyQualifiedName; // The fully qualified type name.
+
+ // Check for a custom value with name.
+ if (pITI->QueryInterface(IID_ITypeInfo2, (void **)&pITI2) == S_OK)
+ {
+ VARIANT vt; // For getting custom value.
+ ::VariantInit(&vt);
+ if (pITI2->GetCustData(GUID_ManagedName, &vt) == S_OK && vt.vt == VT_BSTR)
+ { // There is a custom value with the name. Just believe it.
+ *pwzName = vt.bstrVal;
+ vt.bstrVal = 0;
+ vt.vt = VT_EMPTY;
+ goto ErrExit;
+ }
+ }
+
+ // Still need name, get the namespace.
+ if (wzNamespace == 0)
+ {
+ IfFailGo(pITI->GetContainingTypeLib(&pITLB, 0));
+ IfFailGo(GetNamespaceNameForTypeLib(pITLB, &bstrNamespace));
+ wzNamespace = bstrNamespace;
+ }
+
+ // Get the name, and combine with namespace.
+ IfFailGo(pITI->GetDocumentation(MEMBERID_NIL, &bstrName, 0,0,0));
+ cchFullyQualifiedName = (int)(wcslen(bstrName) + wcslen(wzNamespace) + 1);
+ IfFailGo(qbFullyQualifiedName.ReSizeNoThrow(cchFullyQualifiedName + 1));
+ ns::MakePath(qbFullyQualifiedName.Ptr(), cchFullyQualifiedName + 1, wzNamespace, bstrName);
+
+ // If the assembly name is specified, then add it to the type name.
+ if (wzAsmName)
+ {
+ cchAsmName = (int)wcslen(wzAsmName);
+ cchAsmQualifiedName = cchFullyQualifiedName + cchAsmName + 3;
+ IfNullGo(*pwzName = ::SysAllocStringLen(0, cchAsmQualifiedName));
+ ns::MakeAssemblyQualifiedName(*pwzName, cchAsmQualifiedName, qbFullyQualifiedName.Ptr(), cchFullyQualifiedName, wzAsmName, cchAsmName);
+ }
+ else
+ {
+ IfNullGo(*pwzName = ::SysAllocStringLen(qbFullyQualifiedName.Ptr(), cchFullyQualifiedName));
+ }
+
+ErrExit:
+ if (bstrName)
+ ::SysFreeString(bstrName);
+ if (bstrNamespace)
+ ::SysFreeString(bstrNamespace);
+ if (pITLB)
+ pITLB->Release();
+ if (pITI2)
+ pITI2->Release();
+
+ return (hr);
+} // HRESULT GetManagedNameForTypeInfo()
diff --git a/src/utilcode/tls.cpp b/src/utilcode/tls.cpp
new file mode 100644
index 0000000000..c80d8c3d03
--- /dev/null
+++ b/src/utilcode/tls.cpp
@@ -0,0 +1,272 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+/* TLS.CPP:
+ *
+
+ *
+ * Encapsulates TLS access for maximum performance.
+ *
+ */
+
+#include "stdafx.h"
+
+#include "unsafe.h"
+#include "tls.h"
+#include "contract.h"
+#include "corerror.h"
+#include "ex.h"
+#include "clrhost.h"
+
+#ifndef SELF_NO_HOST
+#include "clrconfig.h"
+#endif
+
+#include "clrnt.h"
+
+#ifndef SELF_NO_HOST
+
+//---------------------------------------------------------------------------
+// Win95 and WinNT store the TLS in different places relative to the
+// fs:[0]. This api reveals which. Can also return TLSACCESS_GENERIC if
+// no info is available about the Thread location (you have to use the TlsGetValue
+// api.) This is intended for use by stub generators that want to inline TLS
+// access.
+//---------------------------------------------------------------------------
+TLSACCESSMODE GetTLSAccessMode(DWORD tlsIndex)
+{
+ // Static contracts because this is used by contract infrastructure
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ TLSACCESSMODE tlsAccessMode = TLSACCESS_GENERIC;
+
+#ifdef _DEBUG
+ // Debug builds allow user to throw a switch to force use of the generic
+ // (non-optimized) Thread/AppDomain getters. Even if the user doesn't throw
+ // the switch, force tests to go down the generic getter code path about 1% of the
+ // time so it's exercised a couple dozen times during each devbvt run.
+ if ((CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_UseGenericTlsGetters) != 0) || DbgRandomOnExe(.01))
+ return TLSACCESS_GENERIC;
+#endif
+
+ if (tlsIndex < TLS_MINIMUM_AVAILABLE)
+ {
+ tlsAccessMode = TLSACCESS_WNT;
+ }
+ else
+ if (tlsIndex < (TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS))
+ {
+ // Expansion slots are lazily created at the first call to
+ // TlsGetValue on a thread, and the code we generate
+ // assumes that the expansion slots will exist.
+ //
+ // <TODO> On newer flavors of NT we could use the vectored
+ // exception handler to take the AV, call TlsGetValue, and
+ // resume execution at the start of the getter. </TODO>
+ tlsAccessMode = TLSACCESS_GENERIC;//TLSACCESS_WNT_HIGH;
+ }
+ else
+ {
+ //
+ // If the app verifier is enabled, TLS indices
+ // are faked to help detect invalid handle use.
+ //
+ }
+
+ return tlsAccessMode;
+}
+
+//---------------------------------------------------------------------------
+// Creates a platform-optimized version of TlsGetValue compiled
+// for a particular index. Can return NULL.
+//---------------------------------------------------------------------------
+// A target for the optimized getter can be passed in, this is
+// useful so that code can avoid an indirect call for the GetThread
+// and GetAppDomain calls for instance. If NULL is passed then
+// we will allocate from the executeable heap.
+POPTIMIZEDTLSGETTER MakeOptimizedTlsGetter(DWORD tlsIndex, LPVOID pBuffer, SIZE_T cbBuffer, POPTIMIZEDTLSGETTER pGenericImpl, BOOL fForceGeneric)
+{
+ // Static contracts because this is used by contract infrastructure
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ ARM_ONLY(pBuffer = ThumbCodeToDataPointer<BYTE*>(pBuffer));
+
+ // Buffer that should be big enough to encode the TLS getter on any reasonable platform
+ TADDR patch[4 INDEBUG(+4 /* last error trashing */)];
+
+ PBYTE pPatch = (PBYTE)&patch;
+
+ TLSACCESSMODE mode = fForceGeneric ? TLSACCESS_GENERIC : GetTLSAccessMode(tlsIndex);
+
+#if defined(_DEBUG)
+ if (mode != TLSACCESS_GENERIC)
+ {
+ //
+ // Trash last error in debug builds
+ //
+
+#ifdef _TARGET_X86_
+ *((DWORD*) (pPatch + 0)) = 0x05c764; // mov dword ptr fs:[offsetof(TEB, LastErrorValue)], LAST_ERROR_TRASH_VALUE
+ *((DWORD*) (pPatch + 3)) = offsetof(TEB, LastErrorValue);
+ *((DWORD*) (pPatch + 7)) = LAST_ERROR_TRASH_VALUE;
+ pPatch += 11;
+#endif // _TARGET_X86_
+
+#ifdef _TARGET_AMD64_
+ // iDNA doesn't like writing directly to gs:[nn]
+ *((UINT64*)(pPatch + 0)) = 0x25048b4865; // mov rax, gs:[offsetof(TEB, NtTib.Self)]
+ *((DWORD*) (pPatch + 5)) = offsetof(TEB, NtTib.Self);
+ *((WORD*) (pPatch + 9)) = 0x80c7; // mov dword ptr [rax + offsetof(TEB, LastErrorValue)], LAST_ERROR_TRASH_VALUE
+ *((DWORD*) (pPatch + 11)) = offsetof(TEB, LastErrorValue);
+ *((DWORD*) (pPatch + 15)) = LAST_ERROR_TRASH_VALUE;
+ pPatch += 19;
+#endif
+ }
+#endif // _DEBUG
+
+ switch (mode)
+ {
+#ifdef _TARGET_X86_
+ case TLSACCESS_WNT:
+ *((WORD*) (pPatch + 0)) = 0xa164; // mov eax, fs:[IMM32]
+ *((DWORD*) (pPatch + 2)) = offsetof(TEB, TlsSlots) + tlsIndex * sizeof(void*);
+ *((BYTE*) (pPatch + 6)) = 0xc3; // retn
+ pPatch += 7;
+ break;
+
+ case TLSACCESS_GENERIC:
+ if (pGenericImpl == NULL)
+ return NULL;
+
+ _ASSERTE(pBuffer != NULL);
+ *((BYTE*) (pPatch + 0)) = 0xE9; // jmp pGenericImpl
+ TADDR rel32 = ((TADDR)pGenericImpl - ((TADDR)pBuffer + 1 + sizeof(INT32)));
+ *((INT32*) (pPatch + 1)) = (INT32)rel32;
+ pPatch += 5;
+ break;
+#endif // _TARGET_X86_
+
+#ifdef _TARGET_AMD64_
+ case TLSACCESS_WNT:
+ *((UINT64*)(pPatch + 0)) = 0x25048b4865; // mov rax, gs:[IMM32]
+ *((DWORD*) (pPatch + 5)) = offsetof(TEB, TlsSlots) + (tlsIndex * sizeof(void*));
+ *((BYTE*) (pPatch + 9)) = 0xc3; // return
+ pPatch += 10;
+ break;
+
+ case TLSACCESS_GENERIC:
+ if (pGenericImpl == NULL)
+ return NULL;
+
+ _ASSERTE(pBuffer != NULL);
+ *((BYTE*) (pPatch + 0)) = 0xE9; // jmp pGenericImpl
+ TADDR rel32 = ((TADDR)pGenericImpl - ((TADDR)pBuffer + 1 + sizeof(INT32)));
+ _ASSERTE((INT64)(INT32)rel32 == (INT64)rel32);
+ *((INT32*) (pPatch + 1)) = (INT32)rel32;
+ pPatch += 5;
+
+ *pPatch++ = 0xCC; // Make sure there is full 8 bytes worth of data
+ *pPatch++ = 0xCC;
+ *pPatch++ = 0xCC;
+ break;
+
+#endif // _TARGET_AMD64_
+
+#ifdef _TARGET_ARM_
+ case TLSACCESS_WNT:
+ {
+ WORD slotOffset = (WORD)(offsetof(TEB, TlsSlots) + tlsIndex * sizeof(void*));
+ _ASSERTE(slotOffset < 4096);
+
+ WORD *pInstr = (WORD*)pPatch;
+
+ *pInstr++ = 0xee1d; // mrc p15, 0, r0, c13, c0, 2
+ *pInstr++ = 0x0f50;
+ *pInstr++ = 0xf8d0; // ldr r0, [r0, #slotOffset]
+ *pInstr++ = slotOffset;
+ *pInstr++ = 0x4770; // bx lr
+
+ pPatch = (PBYTE)pInstr;
+ }
+ break;
+
+ case TLSACCESS_GENERIC:
+ {
+ if (pGenericImpl == NULL)
+ return NULL;
+
+ _ASSERTE(pBuffer != NULL);
+
+ *(DWORD *)pPatch = 0x9000F000; // b pGenericImpl
+ PutThumb2BlRel24((WORD*)pPatch, (TADDR)pGenericImpl - ((TADDR)pBuffer + 4 + THUMB_CODE));
+
+ pPatch += 4;
+ }
+ break;
+#endif // _TARGET_ARM_
+ }
+
+ SIZE_T cbCode = (TADDR)pPatch - (TADDR)&patch;
+ _ASSERTE(cbCode <= sizeof(patch));
+
+ if (pBuffer != NULL)
+ {
+ _ASSERTE_ALL_BUILDS("clr/src/utilcode/tls.cpp", cbCode <= cbBuffer);
+
+ // We assume that the first instruction of the buffer is a short jump to dummy helper
+ // that can be atomically overwritten to avoid races with other threads executing the code.
+ // It is the same basic technique as hot patching.
+
+ // Assert on all builds to make sure that retail optimizations are not affecting the alignment.
+ _ASSERTE_ALL_BUILDS("clr/src/utilcode/tls.cpp", IS_ALIGNED((void*)pBuffer, sizeof(TADDR)));
+
+ // Size of short jump that gets patched last.
+ if (cbCode > sizeof(TADDR))
+ {
+ memcpy((BYTE *)pBuffer + sizeof(TADDR), &patch[1], cbCode - sizeof(TADDR));
+ FlushInstructionCache(GetCurrentProcess(), (BYTE *)pBuffer + sizeof(TADDR), cbCode - sizeof(TADDR));
+ }
+
+ // Make sure that the the dummy implementation still works.
+ _ASSERTE(((POPTIMIZEDTLSGETTER)ARM_ONLY(DataPointerToThumbCode<BYTE*>)(pBuffer))() == NULL);
+
+ // It is important for this write to happen atomically
+ VolatileStore<TADDR>((TADDR *)pBuffer, patch[0]);
+
+ FlushInstructionCache(GetCurrentProcess(), (BYTE *)pBuffer, sizeof(TADDR));
+ }
+ else
+ {
+ pBuffer = (BYTE*) new (executable, nothrow) BYTE[cbCode];
+ if (pBuffer == NULL)
+ return NULL;
+
+ memcpy(pBuffer, &patch, cbCode);
+
+ FlushInstructionCache(GetCurrentProcess(), pBuffer, cbCode);
+ }
+
+ return (POPTIMIZEDTLSGETTER)ARM_ONLY(DataPointerToThumbCode<BYTE*>)(pBuffer);
+}
+
+
+//---------------------------------------------------------------------------
+// Frees a function created by MakeOptimizedTlsGetter().
+//---------------------------------------------------------------------------
+VOID FreeOptimizedTlsGetter(POPTIMIZEDTLSGETTER pOptimizedTlsGetter)
+{
+ // Static contracts because this is used by contract infrastructure
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ BYTE* pGetter = (BYTE*)pOptimizedTlsGetter;
+#ifdef _TARGET_ARM_
+ pGetter = ThumbCodeToDataPointer<BYTE*>(pGetter);
+#endif
+ DeleteExecutable(pGetter);
+}
+
+#endif // !SELF_NO_HOST
diff --git a/src/utilcode/util.cpp b/src/utilcode/util.cpp
new file mode 100644
index 0000000000..865009d38e
--- /dev/null
+++ b/src/utilcode/util.cpp
@@ -0,0 +1,4091 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// util.cpp
+//
+
+//
+// This contains a bunch of C++ utility classes.
+//
+//*****************************************************************************
+#include "stdafx.h" // Precompiled header key.
+#include "utilcode.h"
+#include "metadata.h"
+#include "ex.h"
+#include "pedecoder.h"
+#include "loaderheap.h"
+#include "sigparser.h"
+#include "cor.h"
+
+#ifndef FEATURE_CORECLR
+#include "metahost.h"
+#endif // !FEATURE_CORECLR
+
+const char g_RTMVersion[]= "v1.0.3705";
+
+#ifndef DACCESS_COMPILE
+UINT32 g_nClrInstanceId = 0;
+#endif //!DACCESS_COMPILE
+
+//********** Code. ************************************************************
+
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
+extern WinRTStatusEnum gWinRTStatus = WINRT_STATUS_UNINITED;
+#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
+
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
+//------------------------------------------------------------------------------
+//
+// Attempt to detect the presense of Windows Runtime support on the current OS.
+// Our algorithm to do this is to ensure that:
+// 1. combase.dll exists
+// 2. combase.dll contains a RoInitialize export
+//
+
+void InitWinRTStatus()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ WinRTStatusEnum winRTStatus = WINRT_STATUS_UNSUPPORTED;
+
+ const WCHAR wszComBaseDll[] = W("\\combase.dll");
+ const SIZE_T cchComBaseDll = _countof(wszComBaseDll);
+
+ WCHAR wszComBasePath[MAX_PATH + 1];
+ const SIZE_T cchComBasePath = _countof(wszComBasePath);
+
+ ZeroMemory(wszComBasePath, cchComBasePath * sizeof(wszComBasePath[0]));
+
+ UINT cchSystemDirectory = WszGetSystemDirectory(wszComBasePath, MAX_PATH);
+
+ // Make sure that we're only probing in the system directory. If we can't find the system directory, or
+ // we find it but combase.dll doesn't fit into it, we'll fall back to a safe default of saying that WinRT
+ // is simply not present.
+ if (cchSystemDirectory > 0 && cchComBasePath - cchSystemDirectory >= cchComBaseDll)
+ {
+ if (wcscat_s(wszComBasePath, wszComBaseDll) == 0)
+ {
+ HModuleHolder hComBase(WszLoadLibrary(wszComBasePath));
+ if (hComBase != NULL)
+ {
+ FARPROC activateInstace = GetProcAddress(hComBase, "RoInitialize");
+ if (activateInstace != NULL)
+ {
+ winRTStatus = WINRT_STATUS_SUPPORTED;
+ }
+ }
+ }
+ }
+
+ gWinRTStatus = winRTStatus;
+}
+#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
+//*****************************************************************************
+// Convert a string of hex digits into a hex value of the specified # of bytes.
+//*****************************************************************************
+HRESULT GetHex( // Return status.
+ LPCSTR szStr, // String to convert.
+ int size, // # of bytes in pResult.
+ void *pResult) // Buffer for result.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int count = size * 2; // # of bytes to take from string.
+ unsigned int Result = 0; // Result value.
+ char ch;
+
+ _ASSERTE(size == 1 || size == 2 || size == 4);
+
+ while (count-- && (ch = *szStr++) != '\0')
+ {
+ switch (ch)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ Result = 16 * Result + (ch - '0');
+ break;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ Result = 16 * Result + 10 + (ch - 'A');
+ break;
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ Result = 16 * Result + 10 + (ch - 'a');
+ break;
+
+ default:
+ return (E_FAIL);
+ }
+ }
+
+ // Set the output.
+ switch (size)
+ {
+ case 1:
+ *((BYTE *) pResult) = (BYTE) Result;
+ break;
+
+ case 2:
+ *((WORD *) pResult) = (WORD) Result;
+ break;
+
+ case 4:
+ *((DWORD *) pResult) = Result;
+ break;
+
+ default:
+ _ASSERTE(0);
+ break;
+ }
+ return (S_OK);
+}
+
+//*****************************************************************************
+// Convert a pointer to a string into a GUID.
+//*****************************************************************************
+HRESULT LPCSTRToGuid( // Return status.
+ LPCSTR szGuid, // String to convert.
+ GUID *psGuid) // Buffer for converted GUID.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+
+ // Verify the surrounding syntax.
+ if (strlen(szGuid) != 38 || szGuid[0] != '{' || szGuid[9] != '-' ||
+ szGuid[14] != '-' || szGuid[19] != '-' || szGuid[24] != '-' || szGuid[37] != '}')
+ {
+ return (E_FAIL);
+ }
+
+ // Parse the first 3 fields.
+ if (FAILED(GetHex(szGuid + 1, 4, &psGuid->Data1)))
+ return E_FAIL;
+ if (FAILED(GetHex(szGuid + 10, 2, &psGuid->Data2)))
+ return E_FAIL;
+ if (FAILED(GetHex(szGuid + 15, 2, &psGuid->Data3)))
+ return E_FAIL;
+
+ // Get the last two fields (which are byte arrays).
+ for (i = 0; i < 2; ++i)
+ {
+ if (FAILED(GetHex(szGuid + 20 + (i * 2), 1, &psGuid->Data4[i])))
+ {
+ return E_FAIL;
+ }
+ }
+ for (i=0; i < 6; ++i)
+ {
+ if (FAILED(GetHex(szGuid + 25 + (i * 2), 1, &psGuid->Data4[i+2])))
+ {
+ return E_FAIL;
+ }
+ }
+ return S_OK;
+}
+
+//
+//
+// Global utility functions.
+//
+//
+
+
+
+typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid,
+ REFIID riid,
+ void **ppv);
+
+EXTERN_C const IID _IID_IClassFactory =
+ {0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
+
+// ----------------------------------------------------------------------------
+// FakeCoCreateInstanceEx
+//
+// Description:
+// A private function to do the equivalent of a CoCreateInstance in cases where we
+// can't make the real call. Use this when, for instance, you need to create a symbol
+// reader in the Runtime but we're not CoInitialized. Obviously, this is only good
+// for COM objects for which CoCreateInstance is just a glorified find-and-load-me
+// operation.
+//
+// Arguments:
+// * rclsid - [in] CLSID of object to instantiate
+// * wszDllPath [in] - Path to profiler DLL. If wszDllPath is NULL, FakeCoCreateInstanceEx
+// will look up the registry to find the path of the COM dll associated with rclsid.
+// If the path ends in a backslash, FakeCoCreateInstanceEx will treat this as a prefix
+// if the InprocServer32 found in the registry is a simple filename (not a full path).
+// This allows the caller to specify the directory in which the InprocServer32 should
+// be found. Also, if this path is provided and the InprocServer32 is MSCOREE.DLL, then
+// the Server value is used instead, if it exists.
+// * riid - [in] IID of interface on object to return in ppv
+// * ppv - [out] Pointer to implementation of requested interface
+// * phmodDll - [out] HMODULE of DLL that was loaded to instantiate the COM object.
+// The caller may eventually call FreeLibrary() on this if it can be determined
+// that we no longer reference the generated COM object or dependencies. Else, the
+// caller may ignore this and the DLL will stay loaded forever. If caller
+// specifies phmodDll==NULL, then this parameter is ignored and the HMODULE is not
+// returned.
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+// Notes:
+// * (*phmodDll) on [out] may always be trusted, even if this function returns an
+// error. Therefore, even if creation of the COM object failed, if (*phmodDll !=
+// NULL), then the DLL was actually loaded. The caller may wish to call
+// FreeLibrary on (*phmodDll) in such a case.
+HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
+ LPCWSTR wszDllPath,
+ REFIID riid,
+ void ** ppv,
+ HMODULE * phmodDll)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ // Call the function to get a class factory for the rclsid passed in.
+ ReleaseHolder<IClassFactory> classFactory;
+ HModuleHolder hDll;
+ IfFailRet(FakeCoCallDllGetClassObject(rclsid, wszDllPath, _IID_IClassFactory, (void**)&classFactory, &hDll));
+
+ // Ask the class factory to create an instance of the
+ // necessary object.
+ IfFailRet(classFactory->CreateInstance(NULL, riid, ppv));
+
+ hDll.SuppressRelease();
+
+ if (phmodDll != NULL)
+ {
+ *phmodDll = hDll.GetValue();
+ }
+
+ return hr;
+}
+
+HRESULT FakeCoCallDllGetClassObject(REFCLSID rclsid,
+ LPCWSTR wszDllPath,
+ REFIID riid,
+ void ** ppv,
+ HMODULE * phmodDll)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(ppv != NULL);
+
+ HRESULT hr = S_OK;
+
+ if (phmodDll != NULL)
+ { // Initialize [out] HMODULE (if it was requested)
+ *phmodDll = NULL;
+ }
+
+ bool fIsDllPathPrefix = (wszDllPath != NULL) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\'));
+
+ // - An empty string will be treated as NULL.
+ // - A string ending will a backslash will be treated as a prefix for where to look for the DLL
+ // if the InProcServer32 value is just a DLL name and not a full path.
+ StackSString ssDllName;
+ if ((wszDllPath == NULL) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
+ {
+#ifndef FEATURE_PAL
+ IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
+
+ EX_TRY
+ {
+ if (fIsDllPathPrefix)
+ {
+ if (Clr::Util::Com::IsMscoreeInprocServer32(ssDllName))
+ { // If the InprocServer32 is mscoree.dll, then we skip the shim and look for
+ // the corresponding server DLL (if it exists) in the directory provided.
+ hr = Clr::Util::Com::FindServerUsingCLSID(rclsid, ssDllName);
+
+ if (FAILED(hr))
+ { // We don't fail if there is no server object, because in this case we assume that
+ // the clsid is implemented in the runtime itself (clr.dll) and we do not place
+ // entries in the registry for this case.
+ ssDllName.Set(MAIN_CLR_MODULE_NAME_W);
+ }
+ }
+
+ SString::Iterator i = ssDllName.Begin();
+ if (!ssDllName.Find(i, W('\\')))
+ { // If the InprocServer32 is just a DLL name (not a fully qualified path), then
+ // prefix wszFilePath with wszDllPath.
+ ssDllName.Insert(i, wszDllPath);
+ }
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ wszDllPath = ssDllName.GetUnicode();
+#else // !FEATURE_PAL
+ return E_FAIL;
+#endif // !FEATURE_PAL
+ }
+ _ASSERTE(wszDllPath != NULL);
+
+ // We've got the name of the DLL to load, so load it.
+ HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, NULL, GetLoadWithAlteredSearchPathFlag());
+ if (hDll == NULL)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // We've loaded the DLL, so find the DllGetClassObject function.
+ DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject");
+ if (dllGetClassObject == NULL)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // Call the function to get a class object for the rclsid and riid passed in.
+ IfFailRet(dllGetClassObject(rclsid, riid, ppv));
+
+ hDll.SuppressRelease();
+
+ if (phmodDll != NULL)
+ {
+ *phmodDll = hDll.GetValue();
+ }
+
+ return hr;
+}
+
+#if USE_UPPER_ADDRESS
+static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in.
+static BYTE * s_CodeMaxAddr;
+static BYTE * s_CodeAllocStart;
+static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region.
+#endif
+
+//
+// Use this function to initialize the s_CodeAllocHint
+// during startup. base is runtime .dll base address,
+// size is runtime .dll virtual size.
+//
+void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset)
+{
+#if USE_UPPER_ADDRESS
+
+#ifdef _DEBUG
+ // If GetForceRelocs is enabled we don't constrain the pMinAddr
+ if (PEDecoder::GetForceRelocs())
+ return;
+#endif
+
+//
+ // If we are using the UPPER_ADDRESS space (on Win64)
+ // then for any code heap that doesn't specify an address
+ // range using [pMinAddr..pMaxAddr] we place it in the
+ // upper address space
+ // This enables us to avoid having to use long JumpStubs
+ // to reach the code for our ngen-ed images.
+ // Which are also placed in the UPPER_ADDRESS space.
+ //
+ SIZE_T reach = 0x7FFF0000u;
+
+ // We will choose the preferred code region based on the address of clr.dll. The JIT helpers
+ // in clr.dll are the most heavily called functions.
+ s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0;
+ s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1;
+
+ BYTE * pStart;
+
+ if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS &&
+ (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr)
+ {
+ // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista)
+ // Use the code head start address that does not cause collisions with NGen images.
+ // This logic is coupled with scripts that we use to assign base addresses.
+ pStart = (BYTE *)CODEHEAP_START_ADDRESS;
+ }
+ else
+ if (base > UINT32_MAX)
+ {
+ // clr.dll got address assigned by ASLR?
+ // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned
+ // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images
+ // that can be placed at higher addresses than clr.dll.
+ pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8;
+ }
+ else
+ {
+ // clr.dll missed the base address?
+ // Try to occupy the space right after it.
+ pStart = (BYTE *)(base + size);
+ }
+
+ // Randomize the adddress space
+ pStart += PAGE_SIZE * randomPageOffset;
+
+ s_CodeAllocStart = pStart;
+ s_CodeAllocHint = pStart;
+#endif
+}
+
+//
+// Use this function to reset the s_CodeAllocHint
+// after unloading an AppDomain
+//
+void ResetCodeAllocHint()
+{
+ LIMITED_METHOD_CONTRACT;
+#if USE_UPPER_ADDRESS
+ s_CodeAllocHint = s_CodeAllocStart;
+#endif
+}
+
+//
+// Returns TRUE if p is located in near clr.dll that allows us
+// to use rel32 IP-relative addressing modes.
+//
+BOOL IsPreferredExecutableRange(void * p)
+{
+ LIMITED_METHOD_CONTRACT;
+#if USE_UPPER_ADDRESS
+ if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr)
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+//
+// Allocate free memory that will be used for executable code
+// Handles the special requirements that we have on 64-bit platforms
+// where we want the executable memory to be located near clr.dll
+//
+BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize,
+ DWORD flAllocationType,
+ DWORD flProtect)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+#if USE_UPPER_ADDRESS
+ //
+ // If we are using the UPPER_ADDRESS space (on Win64)
+ // then for any heap that will contain executable code
+ // we will place it in the upper address space
+ //
+ // This enables us to avoid having to use JumpStubs
+ // to reach the code for our ngen-ed images on x64,
+ // since they are also placed in the UPPER_ADDRESS space.
+ //
+ BYTE * pHint = s_CodeAllocHint;
+
+ if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL)
+ {
+ // Try to allocate in the preferred region after the hint
+ BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect);
+
+ if (pResult != NULL)
+ {
+ s_CodeAllocHint = pResult + dwSize;
+ return pResult;
+ }
+
+ // Try to allocate in the preferred region before the hint
+ pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect);
+
+ if (pResult != NULL)
+ {
+ s_CodeAllocHint = pResult + dwSize;
+ return pResult;
+ }
+
+ s_CodeAllocHint = NULL;
+ }
+
+ // Fall through to
+#endif // USE_UPPER_ADDRESS
+
+ return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect);
+
+}
+
+//
+// Allocate free memory with specific alignment.
+//
+LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment)
+{
+ // Verify that the alignment is a power of 2
+ _ASSERTE(alignment != 0);
+ _ASSERTE((alignment & (alignment - 1)) == 0);
+
+#ifndef FEATURE_PAL
+
+ // The VirtualAlloc on Windows ensures 64kB alignment
+ _ASSERTE(alignment <= 0x10000);
+ return ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+
+#else // !FEATURE_PAL
+
+ // UNIXTODO: Add a specialized function to PAL so that we don't have to waste memory
+ dwSize += alignment;
+ SIZE_T addr = (SIZE_T)ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+ return (LPVOID)((addr + (alignment - 1)) & ~(alignment - 1));
+
+#endif // !FEATURE_PAL
+}
+
+// Reserves free memory within the range [pMinAddr..pMaxAddr] using
+// ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it.
+//
+// This method only supports the flAllocationType of MEM_RESERVE
+// Callers also should set dwSize to a multiple of sysInfo.dwAllocationGranularity (64k).
+// That way they can reserve a large region and commit smaller sized pages
+// from that region until it fills up.
+//
+// This functions returns the reserved memory block upon success
+//
+// It returns NULL when it fails to find any memory that satisfies
+// the range.
+//
+
+#ifdef _DEBUG
+static DWORD ShouldInjectFaultInRange()
+{
+ static DWORD fInjectFaultInRange = 99;
+
+ if (fInjectFaultInRange == 99)
+ fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40);
+ return fInjectFaultInRange;
+}
+#endif
+
+BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
+ const BYTE *pMaxAddr,
+ SIZE_T dwSize,
+ DWORD flAllocationType,
+ DWORD flProtect)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ PRECONDITION(dwSize != 0);
+ PRECONDITION(flAllocationType == MEM_RESERVE);
+ }
+ CONTRACTL_END;
+
+ BYTE *pResult = NULL;
+ //
+ // First lets normalize the pMinAddr and pMaxAddr values
+ //
+ // If pMinAddr is NULL then set it to BOT_MEMORY
+ if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY))
+ {
+ pMinAddr = (BYTE *) BOT_MEMORY;
+ }
+
+ // If pMaxAddr is NULL then set it to TOP_MEMORY
+ if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY))
+ {
+ pMaxAddr = (BYTE *) TOP_MEMORY;
+ }
+
+ // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY
+ // then we can call ClrVirtualAlloc instead
+ if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY))
+ {
+ return (BYTE*) ClrVirtualAlloc(NULL, dwSize, flAllocationType, flProtect);
+ }
+
+ // If pMaxAddr is not greater than pMinAddr we can not make an allocation
+ if (dwSize == 0 || pMaxAddr <= pMinAddr)
+ {
+ return NULL;
+ }
+
+ // We will do one scan: [pMinAddr .. pMaxAddr]
+ // Align to 64k. See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
+ BYTE *tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+
+ // Now scan memory and try to find a free block of the size requested.
+ while ((tryAddr + dwSize) <= (BYTE *) pMaxAddr)
+ {
+ MEMORY_BASIC_INFORMATION mbInfo;
+
+ // Use VirtualQuery to find out if this address is MEM_FREE
+ //
+ if (!ClrVirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo)))
+ break;
+
+ // Is there enough memory free from this start location?
+ if ((mbInfo.State == MEM_FREE) && (mbInfo.RegionSize >= (SIZE_T) dwSize))
+ {
+ // Try reserving the memory using VirtualAlloc now
+ pResult = (BYTE*) ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect);
+
+ if (pResult != NULL)
+ {
+ return pResult;
+ }
+#ifdef _DEBUG
+ // pResult == NULL
+ else if (ShouldInjectFaultInRange())
+ {
+ return NULL;
+ }
+#endif // _DEBUG
+
+ // We could fail in a race. Just move on to next region and continue trying
+ tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY;
+ }
+ else
+ {
+ // Try another section of memory
+ tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY,
+ (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
+ }
+ }
+
+ // Our tryAddr reached pMaxAddr
+ return NULL;
+}
+
+//******************************************************************************
+// NumaNodeInfo
+//******************************************************************************
+#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
+#if !defined(FEATURE_CORESYSTEM)
+/*static*/ NumaNodeInfo::PGNPN NumaNodeInfo::m_pGetNumaProcessorNode = NULL;
+#endif
+/*static*/ NumaNodeInfo::PGNHNN NumaNodeInfo::m_pGetNumaHighestNodeNumber = NULL;
+/*static*/ NumaNodeInfo::PVAExN NumaNodeInfo::m_pVirtualAllocExNuma = NULL;
+
+#if !defined(FEATURE_CORESYSTEM)
+/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNode(UCHAR proc_no, PUCHAR node_no)
+{
+ return (*m_pGetNumaProcessorNode)(proc_no, node_no);
+}
+#endif
+
+/*static*/ LPVOID NumaNodeInfo::VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T dwSize,
+ DWORD allocType, DWORD prot, DWORD node)
+{
+ return (*m_pVirtualAllocExNuma)(hProc, lpAddr, dwSize, allocType, prot, node);
+}
+#if !defined(FEATURE_CORECLR) || defined(FEATURE_CORESYSTEM)
+/*static*/ NumaNodeInfo::PGNPNEx NumaNodeInfo::m_pGetNumaProcessorNodeEx = NULL;
+
+/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no)
+{
+ return (*m_pGetNumaProcessorNodeEx)(proc_no, node_no);
+}
+#endif
+#endif
+
+/*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE;
+/*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI()
+{
+#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
+ //check for numa support if multiple heaps are used
+ ULONG highest = 0;
+
+ if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0)
+ return FALSE;
+
+ // check if required APIs are supported
+ HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W);
+ if (hMod == NULL)
+ return FALSE;
+
+ m_pGetNumaHighestNodeNumber = (PGNHNN) GetProcAddress(hMod, "GetNumaHighestNodeNumber");
+ if (m_pGetNumaHighestNodeNumber == NULL)
+ return FALSE;
+
+ // fail to get the highest numa node number
+ if (!m_pGetNumaHighestNodeNumber(&highest) || (highest == 0))
+ return FALSE;
+
+#if !defined(FEATURE_CORESYSTEM)
+ m_pGetNumaProcessorNode = (PGNPN) GetProcAddress(hMod, "GetNumaProcessorNode");
+ if (m_pGetNumaProcessorNode == NULL)
+ return FALSE;
+#endif
+
+#if !defined(FEATURE_CORECLR) || defined(FEATURE_CORESYSTEM)
+ m_pGetNumaProcessorNodeEx = (PGNPNEx) GetProcAddress(hMod, "GetNumaProcessorNodeEx");
+ if (m_pGetNumaProcessorNodeEx == NULL)
+ return FALSE;
+#endif
+
+ m_pVirtualAllocExNuma = (PVAExN) GetProcAddress(hMod, "VirtualAllocExNuma");
+ if (m_pVirtualAllocExNuma == NULL)
+ return FALSE;
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware()
+{
+ return m_enableGCNumaAware;
+}
+
+/*static*/ void NumaNodeInfo::InitNumaNodeInfo()
+{
+ m_enableGCNumaAware = InitNumaNodeInfoAPI();
+}
+
+//******************************************************************************
+// NumaNodeInfo
+//******************************************************************************
+#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
+/*static*/ CPUGroupInfo::PGLPIEx CPUGroupInfo::m_pGetLogicalProcessorInformationEx = NULL;
+/*static*/ CPUGroupInfo::PSTGA CPUGroupInfo::m_pSetThreadGroupAffinity = NULL;
+/*static*/ CPUGroupInfo::PGTGA CPUGroupInfo::m_pGetThreadGroupAffinity = NULL;
+#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR)
+/*static*/ CPUGroupInfo::PGCPNEx CPUGroupInfo::m_pGetCurrentProcessorNumberEx = NULL;
+/*static*/ CPUGroupInfo::PGST CPUGroupInfo::m_pGetSystemTimes = NULL;
+#endif
+/*static*/ //CPUGroupInfo::PNTQSIEx CPUGroupInfo::m_pNtQuerySystemInformationEx = NULL;
+
+/*static*/ BOOL CPUGroupInfo::GetLogicalProcessorInformationEx(DWORD relationship,
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (*m_pGetLogicalProcessorInformationEx)(relationship, slpiex, count);
+}
+
+/*static*/ BOOL CPUGroupInfo::SetThreadGroupAffinity(HANDLE h,
+ GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (*m_pSetThreadGroupAffinity)(h, groupAffinity, previousGroupAffinity);
+}
+
+/*static*/ BOOL CPUGroupInfo::GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (*m_pGetThreadGroupAffinity)(h, groupAffinity);
+}
+
+#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR)
+/*static*/ BOOL CPUGroupInfo::GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime)
+{
+ LIMITED_METHOD_CONTRACT;
+ return (*m_pGetSystemTimes)(idleTime, kernelTime, userTime);
+}
+#endif
+#endif
+
+/*static*/ BOOL CPUGroupInfo::m_enableGCCPUGroups = FALSE;
+/*static*/ BOOL CPUGroupInfo::m_threadUseAllCpuGroups = FALSE;
+/*static*/ WORD CPUGroupInfo::m_nGroups = 0;
+/*static*/ WORD CPUGroupInfo::m_nProcessors = 0;
+/*static*/ WORD CPUGroupInfo::m_initialGroup = 0;
+/*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL;
+/*static*/ LONG CPUGroupInfo::m_initialization = 0;
+
+// Check and setup function pointers for >64 LP Support
+/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoAPI()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL)
+ HMODULE hMod = GetModuleHandleW(WINDOWS_KERNEL32_DLLNAME_W);
+ if (hMod == NULL)
+ return FALSE;
+
+ m_pGetLogicalProcessorInformationEx = (PGLPIEx)GetProcAddress(hMod, "GetLogicalProcessorInformationEx");
+ if (m_pGetLogicalProcessorInformationEx == NULL)
+ return FALSE;
+
+ m_pSetThreadGroupAffinity = (PSTGA)GetProcAddress(hMod, "SetThreadGroupAffinity");
+ if (m_pSetThreadGroupAffinity == NULL)
+ return FALSE;
+
+ m_pGetThreadGroupAffinity = (PGTGA)GetProcAddress(hMod, "GetThreadGroupAffinity");
+ if (m_pGetThreadGroupAffinity == NULL)
+ return FALSE;
+
+#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR)
+ m_pGetCurrentProcessorNumberEx = (PGCPNEx)GetProcAddress(hMod, "GetCurrentProcessorNumberEx");
+ if (m_pGetCurrentProcessorNumberEx == NULL)
+ return FALSE;
+
+ m_pGetSystemTimes = (PGST)GetProcAddress(hMod, "GetSystemTimes");
+ if (m_pGetSystemTimes == NULL)
+ return FALSE;
+#endif
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoArray()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL)
+ BYTE *bBuffer = NULL;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL;
+ DWORD cbSLPIEx = 0;
+ DWORD byteOffset = 0;
+ DWORD dwNumElements = 0;
+ DWORD dwWeight = 1;
+
+ if (CPUGroupInfo::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return FALSE;
+
+ _ASSERTE(cbSLPIEx);
+
+ // Fail to allocate buffer
+ bBuffer = new (nothrow) BYTE[ cbSLPIEx ];
+ if (bBuffer == NULL)
+ return FALSE;
+
+ pSLPIEx = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)bBuffer;
+ if (!m_pGetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx))
+ {
+ delete[] bBuffer;
+ return FALSE;
+ }
+
+ pRecord = pSLPIEx;
+ while (byteOffset < cbSLPIEx)
+ {
+ if (pRecord->Relationship == RelationGroup)
+ {
+ m_nGroups = pRecord->Group.ActiveGroupCount;
+ break;
+ }
+ byteOffset += pRecord->Size;
+ pRecord = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(bBuffer + byteOffset);
+ }
+
+ m_CPUGroupInfoArray = new (nothrow) CPU_Group_Info[m_nGroups];
+ if (m_CPUGroupInfoArray == NULL)
+ {
+ delete[] bBuffer;
+ return FALSE;
+ }
+
+ for (DWORD i = 0; i < m_nGroups; i++)
+ {
+ m_CPUGroupInfoArray[i].nr_active = (WORD)pRecord->Group.GroupInfo[i].ActiveProcessorCount;
+ m_CPUGroupInfoArray[i].active_mask = pRecord->Group.GroupInfo[i].ActiveProcessorMask;
+ m_nProcessors += m_CPUGroupInfoArray[i].nr_active;
+ dwWeight *= (DWORD)m_CPUGroupInfoArray[i].nr_active;
+ }
+
+ //NOTE: the weight setting should work fine with 4 CPU groups upto 64 LPs each. the minimum number of threads
+ // per group before the weight overflow is 2^32/(2^6x2^6x2^6) = 2^14 (i.e. 16K threads)
+ for (DWORD i = 0; i < m_nGroups; i++)
+ {
+ m_CPUGroupInfoArray[i].groupWeight = dwWeight / (DWORD)m_CPUGroupInfoArray[i].nr_active;
+ m_CPUGroupInfoArray[i].activeThreadWeight = 0;
+ }
+
+ delete[] bBuffer; // done with it; free it
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoRange()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL)
+ WORD begin = 0;
+ WORD nr_proc = 0;
+
+ for (WORD i = 0; i < m_nGroups; i++)
+ {
+ nr_proc += m_CPUGroupInfoArray[i].nr_active;
+ m_CPUGroupInfoArray[i].begin = begin;
+ m_CPUGroupInfoArray[i].end = nr_proc - 1;
+ begin = nr_proc;
+ }
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ void CPUGroupInfo::InitCPUGroupInfo()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL)
+ BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
+ BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;
+
+ if (!enableGCCPUGroups)
+ return;
+
+ if (!InitCPUGroupInfoAPI())
+ return;
+
+ if (!InitCPUGroupInfoArray())
+ return;
+
+ if (!InitCPUGroupInfoRange())
+ return;
+
+ // initalGroup is whatever the CPU group that the main thread is running on
+ GROUP_AFFINITY groupAffinity;
+ CPUGroupInfo::GetThreadGroupAffinity(GetCurrentThread(), &groupAffinity);
+ m_initialGroup = groupAffinity.Group;
+
+ // only enable CPU groups if more than one group exists
+ BOOL hasMultipleGroups = m_nGroups > 1;
+ m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups;
+ m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups;
+#endif // _TARGET_AMD64_
+}
+
+/*static*/ BOOL CPUGroupInfo::IsInitialized()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (m_initialization == -1);
+}
+
+/*static*/ void CPUGroupInfo::EnsureInitialized()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // CPUGroupInfo needs to be initialized only once. This could happen in three cases
+ // 1. CLR initialization at begining of EEStartup, or
+ // 2. Sometimes, when hosted by ASP.NET, the hosting process may initialize ThreadPool
+ // before initializing CLR, thus require CPUGroupInfo to be initialized to determine
+ // if CPU group support should/could be enabled.
+ // 3. Call into Threadpool functions before Threadpool _and_ CLR is initialized.
+ // Vast majority of time, CPUGroupInfo is initialized in case 1. or 2.
+ // The chance of contention will be extremely small, so the following code should be fine
+ //
+retry:
+ if (IsInitialized())
+ return;
+
+ if (InterlockedCompareExchange(&m_initialization, 1, 0) == 0)
+ {
+ InitCPUGroupInfo();
+ m_initialization = -1;
+ }
+ else //some other thread started initialization, just wait until complete;
+ {
+ while (m_initialization != -1)
+ {
+ SwitchToThread();
+ }
+ goto retry;
+ }
+}
+
+/*static*/ WORD CPUGroupInfo::GetNumActiveProcessors()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (WORD)m_nProcessors;
+}
+
+/*static*/ void CPUGroupInfo::GetGroupForProcessor(WORD processor_number,
+ WORD* group_number, WORD* group_processor_number)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(FEATURE_REDHAWK) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL)
+ WORD bTemp = 0;
+ WORD bDiff = processor_number - bTemp;
+
+ for (WORD i=0; i < m_nGroups; i++)
+ {
+ bTemp += m_CPUGroupInfoArray[i].nr_active;
+ if (bTemp > processor_number)
+ {
+ *group_number = i;
+ *group_processor_number = bDiff;
+ break;
+ }
+ bDiff = processor_number - bTemp;
+ }
+#else
+ *group_number = 0;
+ *group_processor_number = 0;
+#endif
+}
+
+/*static*/ DWORD CPUGroupInfo::CalculateCurrentProcessorNumber()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) && defined(_TARGET_AMD64_) && !defined(FEATURE_PAL)
+ // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
+ _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
+
+ PROCESSOR_NUMBER proc_no;
+ proc_no.Group=0;
+ proc_no.Number=0;
+ proc_no.Reserved=0;
+ (*m_pGetCurrentProcessorNumberEx)(&proc_no);
+
+ DWORD fullNumber = 0;
+ for (WORD i = 0; i < proc_no.Group; i++)
+ fullNumber += (DWORD)m_CPUGroupInfoArray[i].nr_active;
+ fullNumber += (DWORD)(proc_no.Number);
+
+ return fullNumber;
+#else
+ return 0;
+#endif
+}
+
+#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR) && !defined(FEATURE_PAL)
+//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
+/*static*/ void CPUGroupInfo::ChooseCPUGroupAffinity(GROUP_AFFINITY *gf)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if defined(_TARGET_AMD64_)
+ WORD i, minGroup = 0;
+ DWORD minWeight = 0;
+
+ // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
+ _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
+
+ for (i = 0; i < m_nGroups; i++)
+ {
+ minGroup = (m_initialGroup + i) % m_nGroups;
+
+ // the group is not filled up, use it
+ if (m_CPUGroupInfoArray[minGroup].activeThreadWeight / m_CPUGroupInfoArray[minGroup].groupWeight
+ < (DWORD)m_CPUGroupInfoArray[minGroup].nr_active)
+ goto found;
+ }
+
+ // all groups filled up, distribute proportionally
+ minGroup = m_initialGroup;
+ minWeight = m_CPUGroupInfoArray[m_initialGroup].activeThreadWeight;
+ for (i = 0; i < m_nGroups; i++)
+ {
+ if (m_CPUGroupInfoArray[i].activeThreadWeight < minWeight)
+ {
+ minGroup = i;
+ minWeight = m_CPUGroupInfoArray[i].activeThreadWeight;
+ }
+ }
+
+found:
+ gf->Group = minGroup;
+ gf->Mask = m_CPUGroupInfoArray[minGroup].active_mask;
+ gf->Reserved[0] = 0;
+ gf->Reserved[1] = 0;
+ gf->Reserved[2] = 0;
+ m_CPUGroupInfoArray[minGroup].activeThreadWeight += m_CPUGroupInfoArray[minGroup].groupWeight;
+#endif
+}
+
+//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
+/*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf)
+{
+ LIMITED_METHOD_CONTRACT;
+#if defined(_TARGET_AMD64_)
+ // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
+ _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
+
+ WORD group = gf->Group;
+ m_CPUGroupInfoArray[group].activeThreadWeight -= m_CPUGroupInfoArray[group].groupWeight;
+#endif
+}
+#endif
+
+/*static*/ BOOL CPUGroupInfo::CanEnableGCCPUGroups()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_enableGCCPUGroups;
+}
+
+/*static*/ BOOL CPUGroupInfo::CanEnableThreadUseAllCpuGroups()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_threadUseAllCpuGroups;
+}
+
+//******************************************************************************
+// Returns the number of processors that a process has been configured to run on
+//******************************************************************************
+//******************************************************************************
+// Returns the number of processors that a process has been configured to run on
+//******************************************************************************
+int GetCurrentProcessCpuCount()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ static int cCPUs = 0;
+
+ if (cCPUs != 0)
+ return cCPUs;
+
+#if !defined(FEATURE_CORESYSTEM)
+
+ DWORD_PTR pmask, smask;
+
+ if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
+ return 1;
+
+ if (pmask == 1)
+ return 1;
+
+ pmask &= smask;
+
+ int count = 0;
+ while (pmask)
+ {
+ if (pmask & 1)
+ count++;
+
+ pmask >>= 1;
+ }
+
+ // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
+ // than 64 processors, which would leave us with a count of 0. Since the GC
+ // expects there to be at least one processor to run on (and thus at least one
+ // heap), we'll return 64 here if count is 0, since there are likely a ton of
+ // processors available in that case. The GC also cannot (currently) handle
+ // the case where there are more than 64 processors, so we will return a
+ // maximum of 64 here.
+ if (count == 0 || count > 64)
+ count = 64;
+
+ cCPUs = count;
+
+ return count;
+
+#else // !FEATURE_CORESYSTEM
+
+ SYSTEM_INFO sysInfo;
+ ::GetSystemInfo(&sysInfo);
+ cCPUs = sysInfo.dwNumberOfProcessors;
+ return sysInfo.dwNumberOfProcessors;
+
+#endif // !FEATURE_CORESYSTEM
+}
+
+DWORD_PTR GetCurrentProcessCpuMask()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_CORESYSTEM)
+ DWORD_PTR pmask, smask;
+
+ if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
+ return 1;
+
+ pmask &= smask;
+ return pmask;
+#else
+ return 0;
+#endif
+}
+
+/**************************************************************************/
+
+/**************************************************************************/
+void ConfigMethodSet::init(const CLRConfig::ConfigStringInfo & info)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ LPWSTR str = CLRConfig::GetConfigValue(info);
+ if (str)
+ {
+ m_list.Insert(str);
+ delete[] str;
+ }
+ m_inited = 1;
+}
+
+/**************************************************************************/
+bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_inited == 1);
+
+ if (m_list.IsEmpty())
+ return false;
+ return(m_list.IsInList(methodName, className, sig));
+}
+
+/**************************************************************************/
+void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal);
+ m_inited = 1;
+}
+
+/**************************************************************************/
+void ConfigString::init(const CLRConfig::ConfigStringInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ // Note: m_value will be leaking
+ m_value = CLRConfig::GetConfigValue(info);
+ m_inited = 1;
+}
+
+//=============================================================================
+// AssemblyNamesList
+//=============================================================================
+// The string should be of the form
+// MyAssembly
+// MyAssembly;mscorlib;System
+// MyAssembly;mscorlib System
+
+AssemblyNamesList::AssemblyNamesList(__in LPWSTR list)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ WCHAR prevChar = '?'; // dummy
+ LPWSTR nameStart = NULL; // start of the name currently being processed. NULL if no current name
+ AssemblyName ** ppPrevLink = &m_pNames;
+
+ for (LPWSTR listWalk = list; prevChar != '\0'; prevChar = *listWalk, listWalk++)
+ {
+ WCHAR curChar = *listWalk;
+
+ if (iswspace(curChar) || curChar == ';' || curChar == '\0' )
+ {
+ //
+ // Found white-space
+ //
+
+ if (nameStart)
+ {
+ // Found the end of the current name
+
+ AssemblyName * newName = new AssemblyName();
+ size_t nameLen = listWalk - nameStart;
+
+ MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
+ newName->m_assemblyName = new char[nameLen + 1];
+ memcpy(newName->m_assemblyName, temp, nameLen * sizeof(newName->m_assemblyName[0]));
+ newName->m_assemblyName[nameLen] = '\0';
+
+ *ppPrevLink = newName;
+ ppPrevLink = &newName->m_next;
+
+ nameStart = NULL;
+ }
+ }
+ else if (!nameStart)
+ {
+ //
+ // Found the start of a new name
+ //
+
+ nameStart = listWalk;
+ }
+ }
+
+ _ASSERTE(!nameStart); // cannot be in the middle of a name
+ *ppPrevLink = NULL;
+}
+
+AssemblyNamesList::~AssemblyNamesList()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ for (AssemblyName * pName = m_pNames; pName; /**/)
+ {
+ AssemblyName * cur = pName;
+ pName = pName->m_next;
+
+ delete [] cur->m_assemblyName;
+ delete cur;
+ }
+}
+
+bool AssemblyNamesList::IsInList(LPCUTF8 assemblyName)
+{
+ if (IsEmpty())
+ return false;
+
+ for (AssemblyName * pName = m_pNames; pName; pName = pName->m_next)
+ {
+ if (_stricmp(pName->m_assemblyName, assemblyName) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// MethodNamesList
+//=============================================================================
+// str should be of the form :
+// "foo1 MyNamespace.MyClass:foo3 *:foo4 foo5(x,y,z)"
+// "MyClass:foo2 MyClass:*" will match under _DEBUG
+//
+
+void MethodNamesListBase::Insert(__in_z LPWSTR str)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ enum State { NO_NAME, CLS_NAME, FUNC_NAME, ARG_LIST }; // parsing state machine
+
+ const char SEP_CHAR = ' '; // current character use to separate each entry
+// const char SEP_CHAR = ';'; // better character use to separate each entry
+
+ WCHAR lastChar = '?'; // dummy
+ LPWSTR nameStart = NULL; // while walking over the classname or methodname, this points to start
+ MethodName nameBuf; // Buffer used while parsing the current entry
+ MethodName** lastName = &pNames; // last entry inserted into the list
+ bool bQuote = false;
+
+ nameBuf.methodName = NULL;
+ nameBuf.className = NULL;
+ nameBuf.numArgs = -1;
+ nameBuf.next = NULL;
+
+ for(State state = NO_NAME; lastChar != '\0'; str++)
+ {
+ lastChar = *str;
+
+ switch(state)
+ {
+ case NO_NAME:
+ if (*str != SEP_CHAR)
+ {
+ nameStart = str;
+ state = CLS_NAME; // we have found the start of the next entry
+ }
+ break;
+
+ case CLS_NAME:
+ if (*nameStart == '"')
+ {
+ while (*str && *str!='"')
+ {
+ str++;
+ }
+ nameStart++;
+ bQuote=true;
+ }
+
+ if (*str == ':')
+ {
+ if (*nameStart == '*' && !bQuote)
+ {
+ // Is the classname string a wildcard. Then set it to NULL
+ nameBuf.className = NULL;
+ }
+ else
+ {
+ int len = (int)(str - nameStart);
+
+ // Take off the quote
+ if (bQuote) { len--; bQuote=false; }
+
+ nameBuf.className = new char[len + 1];
+ MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
+ memcpy(nameBuf.className, temp, len*sizeof(nameBuf.className[0]));
+ nameBuf.className[len] = '\0';
+ }
+ if (str[1] == ':') // Accept class::name syntax too
+ str++;
+ nameStart = str + 1;
+ state = FUNC_NAME;
+ }
+ else if (*str == '\0' || *str == SEP_CHAR || *str == '(')
+ {
+ /* This was actually a method name without any class */
+ nameBuf.className = NULL;
+ goto DONE_FUNC_NAME;
+ }
+ break;
+
+ case FUNC_NAME:
+ if (*nameStart == '"')
+ {
+ while ( (nameStart==str) || // workaround to handle when className!=NULL
+ (*str && *str!='"'))
+ {
+ str++;
+ }
+
+ nameStart++;
+ bQuote=true;
+ }
+
+ if (*str == '\0' || *str == SEP_CHAR || *str == '(')
+ {
+ DONE_FUNC_NAME:
+ _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == '(');
+
+ if (*nameStart == '*' && !bQuote)
+ {
+ // Is the name string a wildcard. Then set it to NULL
+ nameBuf.methodName = NULL;
+ }
+ else
+ {
+ int len = (int)(str - nameStart);
+
+ // Take off the quote
+ if (bQuote) { len--; bQuote=false; }
+
+ nameBuf.methodName = new char[len + 1];
+ MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
+ memcpy(nameBuf.methodName, temp, len*sizeof(nameBuf.methodName[0]));
+ nameBuf.methodName[len] = '\0';
+ }
+
+ if (*str == '\0' || *str == SEP_CHAR)
+ {
+ nameBuf.numArgs = -1;
+ goto DONE_ARG_LIST;
+ }
+ else
+ {
+ _ASSERTE(*str == '(');
+ nameBuf.numArgs = -1;
+ state = ARG_LIST;
+ }
+ }
+ break;
+
+ case ARG_LIST:
+ if (*str == '\0' || *str == ')')
+ {
+ if (nameBuf.numArgs == -1)
+ nameBuf.numArgs = 0;
+
+ DONE_ARG_LIST:
+ _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == ')');
+
+ // We have parsed an entire method name.
+ // Create a new entry in the list for it
+
+ MethodName * newName = new MethodName();
+ *newName = nameBuf;
+ newName->next = NULL;
+ *lastName = newName;
+ lastName = &newName->next;
+ state = NO_NAME;
+
+ // Skip anything after the argument list until we find the next
+ // separator character, otherwise if we see "func(a,b):foo" we
+ // create entries for "func(a,b)" as well as ":foo".
+ if (*str == ')')
+ {
+ while (*str && *str != SEP_CHAR)
+ {
+ str++;
+ }
+ lastChar = *str;
+ }
+ }
+ else
+ {
+ if (*str != SEP_CHAR && nameBuf.numArgs == -1)
+ nameBuf.numArgs = 1;
+ if (*str == ',')
+ nameBuf.numArgs++;
+ }
+ break;
+
+ default: _ASSERTE(!"Bad state"); break;
+ }
+ }
+}
+
+/**************************************************************/
+
+void MethodNamesListBase::Destroy()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ for(MethodName * pName = pNames; pName; /**/)
+ {
+ if (pName->className)
+ delete [] pName->className;
+ if (pName->methodName)
+ delete [] pName->methodName;
+
+ MethodName * curName = pName;
+ pName = pName->next;
+ delete curName;
+ }
+}
+
+/**************************************************************/
+bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, PCCOR_SIGNATURE sig)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ ULONG numArgs = -1;
+ if (sig != NULL)
+ {
+ sig++; // Skip calling convention
+ numArgs = CorSigUncompressData(sig);
+ }
+
+ // Try to match all the entries in the list
+
+ for(MethodName * pName = pNames; pName; pName = pName->next)
+ {
+ // If numArgs is valid, check for mismatch
+ if (pName->numArgs != -1 && (ULONG)pName->numArgs != numArgs)
+ continue;
+
+ // If methodName is valid, check for mismatch
+ if (pName->methodName) {
+ if (strcmp(pName->methodName, methName) != 0) {
+
+ // C++ embeds the class name into the method name,
+ // deal with that here (workaround)
+ const char* ptr = strchr(methName, ':');
+ if (ptr != 0 && ptr[1] == ':' && strcmp(&ptr[2], pName->methodName) == 0) {
+ unsigned clsLen = (unsigned)(ptr - methName);
+ if (pName->className == 0 || strncmp(pName->className, methName, clsLen) == 0)
+ return true;
+ }
+ continue;
+ }
+ }
+
+ // check for class Name exact match
+ if (clsName == 0 || pName->className == 0 || strcmp(pName->className, clsName) == 0)
+ return true;
+
+ // check for suffix wildcard like System.*
+ unsigned len = (unsigned)strlen(pName->className);
+ if (len > 0 && pName->className[len-1] == '*' && strncmp(pName->className, clsName, len-1) == 0)
+ return true;
+
+#ifdef _DEBUG
+ // Maybe className doesnt include namespace. Try to match that
+ LPCUTF8 onlyClass = ns::FindSep(clsName);
+ if (onlyClass && strcmp(pName->className, onlyClass+1) == 0)
+ return true;
+#endif
+ }
+ return(false);
+}
+
+//=============================================================================
+// Signature Validation Functions (scaled down version from MDValidator
+//=============================================================================
+
+//*****************************************************************************
+// This function validates one argument given an offset into the signature
+// where the argument begins. This function assumes that the signature is well
+// formed as far as the compression scheme is concerned.
+// <TODO>@todo: Validate tokens embedded.</TODO>
+//*****************************************************************************
+HRESULT validateOneArg(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ SigParser *pSig,
+ ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
+ IMDInternalImport* pImport, // [IN] Internal MD Import interface ptr
+ BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg
+
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ BYTE elementType; // Current element type being processed.
+ mdToken token; // Embedded token.
+ ULONG ulArgCnt; // Argument count for function pointer.
+ ULONG ulIndex; // Index for type parameters
+ ULONG ulRank; // Rank of the array.
+ ULONG ulSizes; // Count of sized dimensions of the array.
+ ULONG ulLbnds; // Count of lower bounds of the array.
+ ULONG ulCallConv;
+
+ HRESULT hr = S_OK; // Value returned.
+ BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+ while(bRepeat)
+ {
+ bRepeat = FALSE;
+ // Validate that the argument is not missing.
+
+ // Get the element type.
+ if (FAILED(pSig->GetByte(&elementType)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSARG);
+ }
+
+ // Walk past all the modifier types.
+ while (elementType & ELEMENT_TYPE_MODIFIER)
+ {
+ if (elementType == ELEMENT_TYPE_SENTINEL)
+ {
+ if(pulNSentinels) *pulNSentinels+=1;
+ if(TypeFromToken(tk) != mdtMemberRef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
+ }
+ if (FAILED(pSig->GetByte(&elementType)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSELTYPE);
+ }
+ }
+
+ switch (elementType)
+ {
+ case ELEMENT_TYPE_VOID:
+ if(bNoVoidAllowed) IfFailGo(VLDTR_E_SIG_BADVOID);
+
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_I:
+ break;
+ case ELEMENT_TYPE_PTR:
+ // Validate the referenced type.
+ if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr);
+ break;
+ case ELEMENT_TYPE_BYREF: //fallthru
+ if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD);
+ case ELEMENT_TYPE_PINNED:
+ case ELEMENT_TYPE_SZARRAY:
+ // Validate the referenced type.
+ if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE))) IfFailGo(hr);
+ break;
+ case ELEMENT_TYPE_CMOD_OPT:
+ case ELEMENT_TYPE_CMOD_REQD:
+ bRepeat = TRUE; // go on validating, we're not done with this arg
+ case ELEMENT_TYPE_VALUETYPE: //fallthru
+ case ELEMENT_TYPE_CLASS:
+ // See if the token is missing.
+ if (FAILED(pSig->GetToken(&token)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSTKN);
+ }
+ // Token validation .
+ if(pImport)
+ {
+ ULONG rid = RidFromToken(token);
+ ULONG typ = TypeFromToken(token);
+ ULONG maxrid = pImport->GetCountWithTokenKind(typ);
+ if(typ == mdtTypeDef) maxrid++;
+ if((rid==0)||(rid > maxrid)) IfFailGo(VLDTR_E_SIG_TKNBAD);
+ }
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ // <TODO>@todo: More function pointer validation?</TODO>
+ // Validate that calling convention is present.
+ if (FAILED(pSig->GetCallingConvInfo(&ulCallConv)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTR);
+ }
+ if(((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX)
+ ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) IfFailGo(VLDTR_E_MD_BADCALLINGCONV);
+
+ // Validate that argument count is present.
+ if (FAILED(pSig->GetData(&ulArgCnt)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
+ }
+
+ // FNPTR signature must follow the rules of MethodDef
+ // Validate and consume return type.
+ IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, FALSE));
+
+ // Validate and consume the arguments.
+ while(ulArgCnt--)
+ {
+ IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, TRUE));
+ }
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ // Validate and consume the base type.
+ IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
+
+ // Validate that the rank is present.
+ if (FAILED(pSig->GetData(&ulRank)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSRANK);
+ }
+
+ // Process the sizes.
+ if (ulRank)
+ {
+ // Validate that the count of sized-dimensions is specified.
+ if (FAILED(pSig->GetData(&ulSizes)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSNSIZE);
+ }
+
+ // Loop over the sizes.
+ while(ulSizes--)
+ {
+ // Validate the current size.
+ if (FAILED(pSig->GetData(NULL)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSSIZE);
+ }
+ }
+
+ // Validate that the count of lower bounds is specified.
+ if (FAILED(pSig->GetData(&ulLbnds)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSNLBND);
+ }
+
+ // Loop over the lower bounds.
+ while(ulLbnds--)
+ {
+ // Validate the current lower bound.
+ if (FAILED(pSig->GetData(NULL)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSLBND);
+ }
+ }
+ }
+ break;
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ // Validate that index is present.
+ if (FAILED(pSig->GetData(&ulIndex)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
+ }
+
+ //@todo GENERICS: check that index is in range
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ // Validate the generic type.
+ IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
+
+ // Validate that parameter count is present.
+ if (FAILED(pSig->GetData(&ulArgCnt)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
+ }
+
+ //@todo GENERICS: check that number of parameters matches definition?
+
+ // Validate and consume the parameters.
+ while(ulArgCnt--)
+ {
+ IfFailGo(validateOneArg(tk, pSig, NULL, pImport, TRUE));
+ }
+ break;
+
+ case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch
+ if(TypeFromToken(tk) == mdtMethodDef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
+ break;
+
+ default:
+ IfFailGo(VLDTR_E_SIG_BADELTYPE);
+ break;
+ } // switch (ulElementType)
+ } // end while(bRepeat)
+ErrExit:
+
+ END_SO_INTOLERANT_CODE;
+ return hr;
+} // validateOneArg()
+
+//*****************************************************************************
+// This function validates the given Method/Field/Standalone signature.
+//@todo GENERICS: MethodInstantiation?
+//*****************************************************************************
+HRESULT validateTokenSig(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size in bytes of the signature.
+ DWORD dwFlags, // [IN] Method flags.
+ IMDInternalImport* pImport) // [IN] Internal MD Import interface ptr
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ ULONG ulCallConv; // Calling convention.
+ ULONG ulArgCount = 1; // Count of arguments (1 because of the return type)
+ ULONG ulTyArgCount = 0; // Count of type arguments
+ ULONG ulArgIx = 0; // Starting index of argument (standalone sig: 1)
+ ULONG i; // Looping index.
+ HRESULT hr = S_OK; // Value returned.
+ ULONG ulNSentinels = 0;
+ SigParser sig(pbSig, cbSig);
+
+ _ASSERTE(TypeFromToken(tk) == mdtMethodDef ||
+ TypeFromToken(tk) == mdtMemberRef ||
+ TypeFromToken(tk) == mdtSignature ||
+ TypeFromToken(tk) == mdtFieldDef);
+
+ // Check for NULL signature.
+ if (!pbSig || !cbSig) return VLDTR_E_SIGNULL;
+
+ // Validate the calling convention.
+
+ // Moves behind calling convention
+ IfFailRet(sig.GetCallingConvInfo(&ulCallConv));
+ i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
+ switch(TypeFromToken(tk))
+ {
+ case mdtMethodDef: // MemberRefs have no flags available
+ // If HASTHIS is set on the calling convention, the method should not be static.
+ if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
+ IsMdStatic(dwFlags)) return VLDTR_E_MD_THISSTATIC;
+
+ // If HASTHIS is not set on the calling convention, the method should be static.
+ if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
+ !IsMdStatic(dwFlags)) return VLDTR_E_MD_NOTTHISNOTSTATIC;
+ // fall thru to callconv check;
+
+ case mdtMemberRef:
+ if(i == IMAGE_CEE_CS_CALLCONV_FIELD) return validateOneArg(tk, &sig, NULL, pImport, TRUE);
+
+ // EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli)
+ if((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG)
+ || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) return VLDTR_E_MD_BADCALLINGCONV;
+ break;
+
+ case mdtSignature:
+ if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli
+ {
+ if((i >= IMAGE_CEE_CS_CALLCONV_MAX)
+ ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) return VLDTR_E_MD_BADCALLINGCONV;
+ }
+ else
+ ulArgIx = 1; // Local variable signatures don't have a return type
+ break;
+
+ case mdtFieldDef:
+ if(i != IMAGE_CEE_CS_CALLCONV_FIELD) return VLDTR_E_MD_BADCALLINGCONV;
+ return validateOneArg(tk, &sig, NULL, pImport, TRUE);
+ }
+ // Is there any sig left for arguments?
+
+ // Get the type argument count
+ if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ if (FAILED(sig.GetData(&ulTyArgCount)))
+ {
+ return VLDTR_E_MD_NOARGCNT;
+ }
+ }
+
+ // Get the argument count.
+ if (FAILED(sig.GetData(&ulArgCount)))
+ {
+ return VLDTR_E_MD_NOARGCNT;
+ }
+
+ // Validate the return type and the arguments.
+ // (at this moment ulArgCount = num.args+1, ulArgIx = (standalone sig. ? 1 :0); )
+ for(; ulArgIx < ulArgCount; ulArgIx++)
+ {
+ if(FAILED(hr = validateOneArg(tk, &sig, &ulNSentinels, pImport, (ulArgIx!=0)))) return hr;
+ }
+
+ // <TODO>@todo: we allow junk to be at the end of the signature (we may not consume it all)
+ // do we care?</TODO>
+
+ if((ulNSentinels != 0) && ((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG ))
+ return VLDTR_E_SIG_SENTMUSTVARARG;
+ if(ulNSentinels > 1) return VLDTR_E_SIG_MULTSENTINELS;
+ return S_OK;
+} // validateTokenSig()
+
+const CHAR g_VersionBase[] = "v1.";
+const CHAR g_DevelopmentVersion[] = "x86";
+const CHAR g_RetString[] = "retail";
+const CHAR g_ComplusString[] = "COMPLUS";
+
+const WCHAR g_VersionBaseW[] = W("v1.");
+const WCHAR g_DevelopmentVersionW[] = W("x86");
+const WCHAR g_RetStringW[] = W("retail");
+const WCHAR g_ComplusStringW[] = W("COMPLUS");
+
+//*****************************************************************************
+// Determine the version number of the runtime that was used to build the
+// specified image. The pMetadata pointer passed in is the pointer to the
+// metadata contained in the image.
+//*****************************************************************************
+static BOOL IsReallyRTM(LPCWSTR szVersion)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (szVersion==NULL)
+ return FALSE;
+
+ size_t lgth = sizeof(g_VersionBaseW) / sizeof(WCHAR) - 1;
+ size_t foundLgth = wcslen(szVersion);
+
+ // Have normal version, v1.*
+ if ( (foundLgth >= lgth+2) &&
+ !wcsncmp(szVersion, g_VersionBaseW, lgth) ) {
+
+ // v1.0.* means RTM
+ if (szVersion[lgth+1] == W('.')) {
+ if (szVersion[lgth] == W('0'))
+ return TRUE;
+ }
+
+ // Check for dev version (v1.x86ret, v1.x86fstchk...)
+ else if(!wcsncmp(szVersion+lgth, g_DevelopmentVersionW,
+ (sizeof(g_DevelopmentVersionW) / sizeof(WCHAR) - 1)))
+ return TRUE;
+ }
+ // Some weird version...
+ else if( (!wcscmp(szVersion, g_RetStringW)) ||
+ (!wcscmp(szVersion, g_ComplusStringW)) )
+ return TRUE;
+ return FALSE;
+}
+
+void AdjustImageRuntimeVersion(SString* pVersion)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ if (IsReallyRTM(*pVersion))
+ {
+ pVersion->SetANSI(g_RTMVersion);
+ }
+};
+
+HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pString);
+ STORAGESIGNATURE* pSig = (STORAGESIGNATURE*) pMetaData;
+
+ // Verify the signature.
+
+ // If signature didn't match, you shouldn't be here.
+ if (pSig->GetSignature() != STORAGE_MAGIC_SIG)
+ return CLDB_E_FILE_CORRUPT;
+
+ // The version started in version 1.1
+ if (pSig->GetMajorVer() < 1)
+ return CLDB_E_FILE_OLDVER;
+
+ if (pSig->GetMajorVer() == 1 && pSig->GetMinorVer() < 1)
+ return CLDB_E_FILE_OLDVER;
+
+ // Header data starts after signature.
+ *pString = (LPCSTR) pSig->pVersion;
+ if(*pString) {
+ size_t lgth = sizeof(g_VersionBase) / sizeof(char) - 1;
+ size_t foundLgth = strlen(*pString);
+
+ // Have normal version, v1.*
+ if ( (foundLgth >= lgth+2) &&
+ !strncmp(*pString, g_VersionBase, lgth) ) {
+
+ // v1.0.* means RTM
+ if ((*pString)[lgth+1] == '.') {
+ if ((*pString)[lgth] == '0')
+ *pString = g_RTMVersion;
+ }
+
+ // Check for dev version (v1.x86ret, v1.x86fstchk...)
+ else if(!strncmp(&(*pString)[lgth], g_DevelopmentVersion,
+ (sizeof(g_DevelopmentVersion) / sizeof(char) - 1)))
+ *pString = g_RTMVersion;
+ }
+
+ // Some weird version...
+ else if( (!strcmp(*pString, g_RetString)) ||
+ (!strcmp(*pString, g_ComplusString)) )
+ *pString = g_RTMVersion;
+ }
+
+ return S_OK;
+}
+
+//*****************************************************************************
+// Convert a UTF8 string to Unicode, into a CQuickArray<WCHAR>.
+//*****************************************************************************
+HRESULT Utf2Quick(
+ LPCUTF8 pStr, // The string to convert.
+ CQuickArray<WCHAR> &rStr, // The QuickArray<WCHAR> to convert it into.
+ int iCurLen) // Inital characters in the array to leave (default 0).
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ int iReqLen; // Required additional length.
+ int iActLen;
+ int bAlloc = 0; // If non-zero, allocation was required.
+
+ if (iCurLen < 0 )
+ {
+ _ASSERTE_MSG(false, "Invalid current length");
+ return E_INVALIDARG;
+ }
+
+ // Calculate the space available
+ S_SIZE_T cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
+ if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+
+ // Attempt the conversion.
+ LPWSTR rNewStr = rStr.Ptr()+iCurLen;
+ if(rNewStr < rStr.Ptr())
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+ iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
+
+ // If the buffer was too small, determine what is required.
+ if (iReqLen == 0)
+ bAlloc = iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, 0, 0);
+ // Resize the buffer. If the buffer was large enough, this just sets the internal
+ // counter, but if it was too small, this will attempt a reallocation. Note that
+ // the length includes the terminating W('/0').
+ IfFailGo(rStr.ReSizeNoThrow(iCurLen+iReqLen));
+ // If we had to realloc, then do the conversion again, now that the buffer is
+ // large enough.
+ if (bAlloc) {
+ //recalculating cchAvail since MaxSize could have been changed.
+ cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
+ if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+ //reculculating rNewStr
+ rNewStr = rStr.Ptr()+iCurLen;
+
+ if(rNewStr < rStr.Ptr())
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+ iActLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
+ _ASSERTE(iReqLen == iActLen);
+ }
+ErrExit:
+ return hr;
+} // HRESULT Utf2Quick()
+
+
+//*****************************************************************************
+// Extract the movl 64-bit unsigned immediate from an IA64 bundle
+// (Format X2)
+//*****************************************************************************
+UINT64 GetIA64Imm64(UINT64 * pBundle)
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ return GetIA64Imm64(temp0, temp1);
+}
+
+UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT64 imm64 = 0;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a movl opcode, with template 4 or 5
+ //
+ UINT64 templa = (qword0 >> 0) & 0x1f;
+ UINT64 opcode = (qword1 >> 60) & 0xf;
+
+ _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
+#endif
+
+ imm64 = (qword1 >> 59) << 63; // 1 i
+ imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm41
+ imm64 |= (qword0 >> 46) << 22; // 18 low bits of imm41
+ imm64 |= (qword1 >> 23) & 0x200000; // 1 ic
+ imm64 |= (qword1 >> 29) & 0x1F0000; // 5 imm5c
+ imm64 |= (qword1 >> 43) & 0xFF80; // 9 imm9d
+ imm64 |= (qword1 >> 36) & 0x7F; // 7 imm7b
+
+ return imm64;
+}
+
+//*****************************************************************************
+// Deposit the movl 64-bit unsigned immediate into an IA64 bundle
+// (Format X2)
+//*****************************************************************************
+void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a movl opcode, with template 4 or 5
+ //
+ UINT64 templa = (pBundle[0] >> 0) & 0x1f;
+ UINT64 opcode = (pBundle[1] >> 60) & 0xf ;
+
+ _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
+#endif
+
+ const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xF000080FFF800000);
+
+ /* Clear all bits used as part of the imm64 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0;
+ UINT64 temp1;
+
+ temp1 = (imm64 >> 63) << 59; // 1 i
+ temp1 |= (imm64 & 0xFF80) << 43; // 9 imm9d
+ temp1 |= (imm64 & 0x1F0000) << 29; // 5 imm5c
+ temp1 |= (imm64 & 0x200000) << 23; // 1 ic
+ temp1 |= (imm64 & 0x7F) << 36; // 7 imm7b
+ temp1 |= (imm64 << 1) >> 41; // 23 high bits of imm41
+ temp0 = (imm64 >> 22) << 46; // 18 low bits of imm41
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the addl 22-bit signed immediate from an IA64 bundle
+// (Format A5)
+//*****************************************************************************
+INT32 GetIA64Imm22(UINT64 * pBundle, UINT32 slot)
+{
+ INT32 imm22 = 0;
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ if (slot == 0)
+ {
+ if ((temp0 >> 41) & 1)
+ imm22 = 0xFFE00000; // 1 sign bit
+ imm22 |= (temp0 >> 11) & 0x001F0000; // 5 imm5c
+ imm22 |= (temp0 >> 25) & 0x0000FF80; // 9 imm9d
+ imm22 |= (temp0 >> 18) & 0x0000007F; // 7 imm7b
+ }
+ else if (slot == 1)
+ {
+ if ((temp1 >> 18) & 1)
+ imm22 = 0xFFE00000; // 1 sign bit
+ imm22 |= (temp1 << 9) & 0x001F0000; // 5 imm5c
+ imm22 |= (temp1 >> 2) & 0x0000FF80; // 9 imm9d
+ imm22 |= (temp1 << 5) & 0x00000060; // 2 imm7b (hi2)
+ imm22 |= (temp0 >> 59) & 0x0000001F; // 5 imm7b (lo5)
+ }
+ else if (slot == 2)
+ {
+ if ((temp1 >> 59) & 1)
+ imm22 = 0xFFE00000; // 1 sign bit
+ imm22 |= (temp1 >> 32) & 0x001F0000; // 5 imm5c
+ imm22 |= (temp1 >> 43) & 0x0000FF80; // 9 imm9d
+ imm22 |= (temp1 >> 36) & 0x0000007F; // 7 imm7b
+ }
+
+ return imm22;
+}
+
+//*****************************************************************************
+// Deposit the addl 22-bit signed immediate into an IA64 bundle
+// (Format A5)
+//*****************************************************************************
+void PutIA64Imm22(UINT64 * pBundle, UINT32 slot, INT32 imm22)
+{
+ if (slot == 0)
+ {
+ const UINT64 mask0 = UI64(0xFFFFFC000603FFFF);
+ /* Clear all bits used as part of the imm22 */
+ pBundle[0] &= mask0;
+
+ UINT64 temp0;
+
+ temp0 = (UINT64) (imm22 & 0x200000) << 20; // 1 s
+ temp0 |= (UINT64) (imm22 & 0x1F0000) << 11; // 5 imm5c
+ temp0 |= (UINT64) (imm22 & 0x00FF80) << 25; // 9 imm9d
+ temp0 |= (UINT64) (imm22 & 0x00007F) << 18; // 7 imm7b
+
+ /* Or in the new bits used in the imm22 */
+ pBundle[0] |= temp0;
+
+ }
+ else if (slot == 1)
+ {
+ const UINT64 mask0 = UI64(0x07FFFFFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xFFFFFFFFFFF8000C);
+ /* Clear all bits used as part of the imm22 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0;
+ UINT64 temp1;
+
+ temp1 = (UINT64) (imm22 & 0x200000) >> 4; // 1 s
+ temp1 |= (UINT64) (imm22 & 0x1F0000) >> 9; // 5 imm5c
+ temp1 |= (UINT64) (imm22 & 0x00FF80) << 2; // 9 imm9d
+ temp1 |= (UINT64) (imm22 & 0x000060) >> 5; // 2 imm7b (hi2)
+ temp0 = (UINT64) (imm22 & 0x00001F) << 59; // 5 imm7b (hi2)
+
+ /* Or in the new bits used in the imm22 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ }
+ else if (slot == 0)
+ {
+ const UINT64 mask1 = UI64(0xF000180FFFFFFFFF);
+ /* Clear all bits used as part of the imm22 */
+ pBundle[1] &= mask1;
+
+ UINT64 temp1;
+
+ temp1 = (UINT64) (imm22 & 0x200000) << 37; // 1 s
+ temp1 |= (UINT64) (imm22 & 0x1F0000) << 32; // 5 imm5c
+ temp1 |= (UINT64) (imm22 & 0x00FF80) << 43; // 9 imm9d
+ temp1 |= (UINT64) (imm22 & 0x00007F) << 36; // 7 imm7b
+
+ /* Or in the new bits used in the imm22 */
+ pBundle[1] |= temp1;
+ }
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the IP-Relative signed 25-bit immediate from an IA64 bundle
+// (Formats B1, B2 or B3)
+// Note that due to branch target alignment requirements
+// the lowest four bits in the result will always be zero.
+//*****************************************************************************
+INT32 GetIA64Rel25(UINT64 * pBundle, UINT32 slot)
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ return GetIA64Rel25(temp0, temp1, slot);
+}
+
+INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ INT32 imm25 = 0;
+
+ if (slot == 2)
+ {
+ if ((qword1 >> 59) & 1)
+ imm25 = 0xFF000000;
+ imm25 |= (qword1 >> 32) & 0x00FFFFF0; // 20 imm20b
+ }
+ else if (slot == 1)
+ {
+ if ((qword1 >> 18) & 1)
+ imm25 = 0xFF000000;
+ imm25 |= (qword1 << 9) & 0x00FFFE00; // high 15 of imm20b
+ imm25 |= (qword0 >> 55) & 0x000001F0; // low 5 of imm20b
+ }
+ else if (slot == 0)
+ {
+ if ((qword0 >> 41) & 1)
+ imm25 = 0xFF000000;
+ imm25 |= (qword0 >> 14) & 0x00FFFFF0; // 20 imm20b
+ }
+
+ return imm25;
+}
+
+//*****************************************************************************
+// Deposit the IP-Relative signed 25-bit immediate into an IA64 bundle
+// (Formats B1, B2 or B3)
+// Note that due to branch target alignment requirements
+// the lowest four bits are required to be zero.
+//*****************************************************************************
+void PutIA64Rel25(UINT64 * pBundle, UINT32 slot, INT32 imm25)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE((imm25 & 0xF) == 0);
+
+ if (slot == 2)
+ {
+ const UINT64 mask1 = UI64(0xF700000FFFFFFFFF);
+ /* Clear all bits used as part of the imm25 */
+ pBundle[1] &= mask1;
+
+ UINT64 temp1;
+
+ temp1 = (UINT64) (imm25 & 0x1000000) << 35; // 1 s
+ temp1 |= (UINT64) (imm25 & 0x0FFFFF0) << 32; // 20 imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[1] |= temp1;
+ }
+ else if (slot == 1)
+ {
+ const UINT64 mask0 = UI64(0x0EFFFFFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xFFFFFFFFFFFB8000);
+ /* Clear all bits used as part of the imm25 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0;
+ UINT64 temp1;
+
+ temp1 = (UINT64) (imm25 & 0x1000000) >> 7; // 1 s
+ temp1 |= (UINT64) (imm25 & 0x0FFFE00) >> 9; // high 15 of imm20b
+ temp0 = (UINT64) (imm25 & 0x00001F0) << 55; // low 5 of imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ }
+ else if (slot == 0)
+ {
+ const UINT64 mask0 = UI64(0xFFFFFDC00003FFFF);
+ /* Clear all bits used as part of the imm25 */
+ pBundle[0] &= mask0;
+
+ UINT64 temp0;
+
+ temp0 = (UINT64) (imm25 & 0x1000000) << 16; // 1 s
+ temp0 |= (UINT64) (imm25 & 0x0FFFFF0) << 14; // 20 imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+
+ }
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the IP-Relative signed 64-bit immediate from an IA64 bundle
+// (Formats X3 or X4)
+//*****************************************************************************
+INT64 GetIA64Rel64(UINT64 * pBundle)
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ return GetIA64Rel64(temp0, temp1);
+}
+
+INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ INT64 imm64 = 0;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a brl opcode, with template 4 or 5
+ //
+ UINT64 templa = (qword0 >> 0) & 0x1f;
+ UINT64 opcode = (qword1 >> 60) & 0xf;
+
+ _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
+ ((templa == 0x4) || (templa == 0x5)));
+#endif
+
+ imm64 = (qword1 >> 59) << 63; // 1 i
+ imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm39
+ imm64 |= (qword0 >> 48) << 24; // 16 low bits of imm39
+ imm64 |= (qword1 >> 32) & 0xFFFFF0; // 20 imm20b
+ // 4 bits of zeros
+ return imm64;
+}
+
+//*****************************************************************************
+// Deposit the IP-Relative signed 64-bit immediate into an IA64 bundle
+// (Formats X3 or X4)
+//*****************************************************************************
+void PutIA64Rel64(UINT64 * pBundle, INT64 imm64)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a brl opcode, with template 4 or 5
+ //
+ UINT64 templa = (pBundle[0] >> 0) & 0x1f;
+ UINT64 opcode = (pBundle[1] >> 60) & 0xf;
+
+ _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
+ ((templa == 0x4) || (templa == 0x5)));
+ _ASSERTE((imm64 & 0xF) == 0);
+#endif
+
+ const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xF700000FFF800000);
+
+ /* Clear all bits used as part of the imm64 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0 = (imm64 & UI64(0x000000FFFF000000)) << 24; // 16 low bits of imm39
+ UINT64 temp1 = (imm64 & UI64(0x8000000000000000)) >> 4 // 1 i
+ | (imm64 & UI64(0x7FFFFF0000000000)) >> 40 // 23 high bits of imm39
+ | (imm64 & UI64(0x0000000000FFFFF0)) << 32; // 20 imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N)
+//*****************************************************************************
+static FORCEINLINE UINT16 GetThumb2Imm16(UINT16 * p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return ((p[0] << 12) & 0xf000) |
+ ((p[0] << 1) & 0x0800) |
+ ((p[1] >> 4) & 0x0700) |
+ ((p[1] >> 0) & 0x00ff);
+}
+
+//*****************************************************************************
+// Extract the 32-bit immediate from movw/movt sequence
+//*****************************************************************************
+UINT32 GetThumb2Mov32(UINT16 * p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Make sure we are decoding movw/movt sequence
+ _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
+ _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
+
+ return (UINT32)GetThumb2Imm16(p) + ((UINT32)GetThumb2Imm16(p + 2) << 16);
+}
+
+//*****************************************************************************
+// Deposit the 16-bit immediate into ARM Thumb2 Instruction (format T2_N)
+//*****************************************************************************
+static FORCEINLINE void PutThumb2Imm16(UINT16 * p, UINT16 imm16)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ USHORT Opcode0 = p[0];
+ USHORT Opcode1 = p[1];
+ Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1));
+ Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0));
+ Opcode0 |= (imm16 & 0xf000) >> 12;
+ Opcode0 |= (imm16 & 0x0800) >> 1;
+ Opcode1 |= (imm16 & 0x0700) << 4;
+ Opcode1 |= (imm16 & 0x00ff) << 0;
+ p[0] = Opcode0;
+ p[1] = Opcode1;
+}
+
+//*****************************************************************************
+// Deposit the 32-bit immediate into movw/movt Thumb2 sequence
+//*****************************************************************************
+void PutThumb2Mov32(UINT16 * p, UINT32 imm32)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Make sure we are decoding movw/movt sequence
+ _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
+ _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
+
+ PutThumb2Imm16(p, (UINT16)imm32);
+ PutThumb2Imm16(p + 2, (UINT16)(imm32 >> 16));
+}
+
+//*****************************************************************************
+// Extract the 24-bit rel offset from bl instruction
+//*****************************************************************************
+INT32 GetThumb2BlRel24(UINT16 * p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ USHORT Opcode0 = p[0];
+ USHORT Opcode1 = p[1];
+
+ UINT32 S = Opcode0 >> 10;
+ UINT32 J2 = Opcode1 >> 11;
+ UINT32 J1 = Opcode1 >> 13;
+
+ INT32 ret =
+ ((S << 24) & 0x1000000) |
+ (((J1 ^ S ^ 1) << 23) & 0x0800000) |
+ (((J2 ^ S ^ 1) << 22) & 0x0400000) |
+ ((Opcode0 << 12) & 0x03FF000) |
+ ((Opcode1 << 1) & 0x0000FFE);
+
+ // Sign-extend and return
+ return (ret << 7) >> 7;
+}
+
+//*****************************************************************************
+// Extract the 24-bit rel offset from bl instruction
+//*****************************************************************************
+void PutThumb2BlRel24(UINT16 * p, INT32 imm24)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInThumb2BlRel24(imm24));
+
+#if defined(_TARGET_ARM_)
+ // Ensure that the ThumbBit is not set on the offset
+ // as it cannot be encoded.
+ _ASSERTE(!(imm24 & THUMB_CODE));
+#endif // _TARGET_ARM_
+
+ USHORT Opcode0 = p[0];
+ USHORT Opcode1 = p[1];
+ Opcode0 &= 0xF800;
+ Opcode1 &= 0xD000;
+
+ UINT32 S = (imm24 & 0x1000000) >> 24;
+ UINT32 J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1;
+ UINT32 J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1;
+
+ Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10);
+ Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11);
+
+ p[0] = Opcode0;
+ p[1] = Opcode1;
+
+ _ASSERTE(GetThumb2BlRel24(p) == imm24);
+}
+
+//*****************************************************************************
+// Extract the PC-Relative offset from a b or bl instruction
+//*****************************************************************************
+INT32 GetArm64Rel28(UINT32 * pCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT32 branchInstr = *pCode;
+
+ // first shift 6 bits left to set the sign bit,
+ // then arithmetic shift right by 4 bits
+ INT32 imm28 = (((INT32)(branchInstr & 0x03FFFFFF)) << 6) >> 4;
+
+ return imm28;
+}
+
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm28' into a b or bl instruction
+//*****************************************************************************
+void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInRel28(imm28));
+ _ASSERTE((imm28 & 0x3) == 0); // the low two bits must be zero
+
+ UINT32 branchInstr = *pCode;
+
+ branchInstr &= 0xFC000000; // keep bits 31-26
+
+ // Assemble the pc-relative delta 'imm28' into the branch instruction
+ branchInstr |= ((imm28 >> 2) & 0x03FFFFFF);
+
+ *pCode = branchInstr; // write the assembled instruction
+
+ _ASSERTE(GetArm64Rel28(pCode) == imm28);
+}
+
+//---------------------------------------------------------------------
+// Splits a command line into argc/argv lists, using the VC7 parsing rules.
+//
+// This functions interface mimics the CommandLineToArgvW api.
+//
+// If function fails, returns NULL.
+//
+// If function suceeds, call delete [] on return pointer when done.
+//
+//---------------------------------------------------------------------
+// NOTE: Implementation-wise, once every few years it would be a good idea to
+// compare this code with the C runtime library's parse_cmdline method,
+// which is in vctools\crt\crtw32\startup\stdargv.c. (Note we don't
+// support wild cards, and we use Unicode characters exclusively.)
+// We are up to date as of ~6/2005.
+//---------------------------------------------------------------------
+LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+
+ *pNumArgs = 0;
+
+ int nch = (int)wcslen(lpCmdLine);
+
+ // Calculate the worstcase storage requirement. (One pointer for
+ // each argument, plus storage for the arguments themselves.)
+ int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1);
+ LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)];
+ if (!pAlloc)
+ return NULL;
+
+ LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt
+ LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments
+ LPCWSTR psrc = lpCmdLine;
+ WCHAR c;
+ BOOL inquote;
+ BOOL copychar;
+ int numslash;
+
+ // First, parse the program name (argv[0]). Argv[0] is parsed under
+ // special rules. Anything up to the first whitespace outside a quoted
+ // subtring is accepted. Backslashes are treated as normal characters.
+ argv[ (*pNumArgs)++ ] = pdst;
+ inquote = FALSE;
+ do {
+ if (*psrc == W('"') )
+ {
+ inquote = !inquote;
+ c = *psrc++;
+ continue;
+ }
+ *pdst++ = *psrc;
+
+ c = *psrc++;
+
+ } while ( (c != W('\0') && (inquote || (c != W(' ') && c != W('\t')))) );
+
+ if ( c == W('\0') ) {
+ psrc--;
+ } else {
+ *(pdst-1) = W('\0');
+ }
+
+ inquote = FALSE;
+
+
+
+ /* loop on each argument */
+ for(;;)
+ {
+ if ( *psrc )
+ {
+ while (*psrc == W(' ') || *psrc == W('\t'))
+ {
+ ++psrc;
+ }
+ }
+
+ if (*psrc == W('\0'))
+ break; /* end of args */
+
+ /* scan an argument */
+ argv[ (*pNumArgs)++ ] = pdst;
+
+ /* loop through scanning one argument */
+ for (;;)
+ {
+ copychar = 1;
+ /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
+ 2N+1 backslashes + " ==> N backslashes + literal "
+ N backslashes ==> N backslashes */
+ numslash = 0;
+ while (*psrc == W('\\'))
+ {
+ /* count number of backslashes for use below */
+ ++psrc;
+ ++numslash;
+ }
+ if (*psrc == W('"'))
+ {
+ /* if 2N backslashes before, start/end quote, otherwise
+ copy literally */
+ if (numslash % 2 == 0)
+ {
+ if (inquote && psrc[1] == W('"'))
+ {
+ psrc++; /* Double quote inside quoted string */
+ }
+ else
+ {
+ /* skip first quote char and copy second */
+ copychar = 0; /* don't copy quote */
+ inquote = !inquote;
+ }
+ }
+ numslash /= 2; /* divide numslash by two */
+ }
+
+ /* copy slashes */
+ while (numslash--)
+ {
+ *pdst++ = W('\\');
+ }
+
+ /* if at end of arg, break loop */
+ if (*psrc == W('\0') || (!inquote && (*psrc == W(' ') || *psrc == W('\t'))))
+ break;
+
+ /* copy character into argument */
+ if (copychar)
+ {
+ *pdst++ = *psrc;
+ }
+ ++psrc;
+ }
+
+ /* null-terminate the argument */
+
+ *pdst++ = W('\0'); /* terminate string */
+ }
+
+ /* We put one last argument in -- a null ptr */
+ argv[ (*pNumArgs) ] = NULL;
+
+ // If we hit this assert, we overwrote our destination buffer.
+ // Since we're supposed to allocate for the worst
+ // case, either the parsing rules have changed or our worse case
+ // formula is wrong.
+ _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc);
+ return argv;
+}
+
+Volatile<PVOID> ForbidCallsIntoHostOnThisThread::s_pvOwningFiber = NULL;
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+enum SOViolationType {
+ SO_Violation_Intolerant = 0,
+ SO_Violation_NotMainline = 1,
+ SO_Violation_Backout = 2,
+};
+
+struct HashedSOViolations {
+ ULONG m_hash;
+ HashedSOViolations* m_pNext;
+ HashedSOViolations(ULONG hash, HashedSOViolations *pNext) : m_hash(hash), m_pNext(pNext) {}
+};
+
+static HashedSOViolations *s_pHashedSOViolations = NULL;
+
+void SOViolation(const char *szFunction, const char *szFile, int lineNum, SOViolationType violation);
+
+
+//
+// SOTolerantViolation is used to report an SO-intolerant function that is not running behind a probe.
+//
+void SOTolerantViolation(const char *szFunction, const char *szFile, int lineNum)
+{
+ return SOViolation(szFunction, szFile, lineNum, SO_Violation_Intolerant);
+}
+
+//
+// SONotMainlineViolation is used to report any code with SO_NOT_MAINLINE being run in a test environment
+// with COMPLUS_NO_SO_NOT_MAINLINE enabled
+//
+void SONotMainlineViolation(const char *szFunction, const char *szFile, int lineNum)
+{
+ return SOViolation(szFunction, szFile, lineNum, SO_Violation_NotMainline);
+}
+
+//
+// SONotMainlineViolation is used to report any code with SO_NOT_MAINLINE being run in a test environment
+// with COMPLUS_NO_SO_NOT_MAINLINE enabled
+//
+void SOBackoutViolation(const char *szFunction, const char *szFile, int lineNum)
+{
+ return SOViolation(szFunction, szFile, lineNum, SO_Violation_Backout);
+}
+
+//
+// Code common to SO violations
+//
+// The default is to throw up an ASSERT. But the function can also dump violations to a file and
+// ensure that only unique violations are tracked.
+//
+void SOViolation(const char *szFunction, const char *szFile, int lineNum, SOViolationType violationType)
+{
+ // This function is called from places that don't allow a throw. But this is debug-only
+ // code that should eventually never be called once all the violations are gone.
+ CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation);
+
+ static BOOL fDumpToFileInitialized = FALSE;
+ static BOOL fDumpToFile = FALSE;
+
+#pragma warning(disable:4640) // Suppress warning: construction of local static object is not thread-safe
+ static SString hashFN;
+ static SString fnameFN;
+ static SString detailsFN;
+#pragma warning(default:4640)
+
+ static int dumpLock = -1;
+
+ static CHAR szExprWithStack[10480];
+ static DWORD stackTraceLength = 20;
+
+ if (fDumpToFileInitialized == FALSE)
+ {
+ stackTraceLength = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SODumpViolationsStackTraceLength, stackTraceLength);
+ // Limit the length or we'll overflow our buffer
+ if (stackTraceLength > cfrMaxAssertStackLevels)
+ {
+ stackTraceLength = cfrMaxAssertStackLevels;
+ }
+ NewArrayHolder<WCHAR> dumpDir(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_SODumpViolationsDir));
+ if (dumpDir == NULL)
+ {
+ fDumpToFileInitialized = TRUE;
+ }
+ else
+ {
+ fDumpToFile = TRUE;
+ hashFN.Append(SString(dumpDir.GetValue()));
+ hashFN.Append(W("\\SOViolationHashes.txt"));
+ fnameFN.Append(SString(dumpDir.GetValue()));
+ fnameFN.Append(W("\\SOViolationFunctionNames.txt"));
+ detailsFN.Append(SString(dumpDir.GetValue()));
+ detailsFN.Append(W("\\SOViolationDetails.txt"));
+ }
+ }
+
+ char buff[1024];
+
+ if (violationType == SO_Violation_NotMainline)
+ {
+ sprintf_s(buff,
+ _countof(buff),
+ "CONTRACT VIOLATION by %s at \"%s\" @ %d\n\n"
+ "SO-not-mainline function being called with not-mainline checking enabled.\n"
+ "\nPlease open a bug against the feature owner.\n"
+ "\nNOTE: You can disable this ASSERT by setting COMPLUS_SOEnableDefaultRWValidation=0.\n"
+ " or by turning of not-mainline checking by by setting COMPLUS_NO_SO_NOT_MAINLINE=0.\n"
+ "\nFor details about this feature, see, in a CLR enlistment,\n"
+ "src\\ndp\\clr\\doc\\OtherDevDocs\\untriaged\\clrdev_web\\SO Guide for CLR Developers.doc\n",
+ szFunction, szFile, lineNum);
+ }
+ else if (violationType == SO_Violation_Backout)
+ {
+ sprintf_s(buff,
+ _countof(buff),
+ "SO Backout Marker overrun.\n\n"
+ "A dtor or handler path exceeded the backout code stack consumption limit.\n"
+ "\nPlease open a bug against the feature owner.\n"
+ "\nNOTE: You can disable this ASSERT by setting COMPLUS_SOEnableBackoutStackValidation=0.\n"
+ "\nFor details about this feature, see, in a CLR enlistment,\n"
+ "src\\ndp\\clr\\doc\\OtherDevDocs\\untriaged\\clrdev_web\\SO Guide for CLR Developers.doc\n",
+ szFunction, szFile, lineNum);
+ }
+ else
+ {
+ sprintf_s(buff,
+ _countof(buff),
+ "CONTRACT VIOLATION by %s at \"%s\" @ %d\n\n"
+ "SO-intolerant function called outside an SO probe.\n"
+ "\nPlease open a bug against the feature owner.\n"
+ "\nNOTE: You can disable this ASSERT by setting COMPLUS_SOEnableDefaultRWValidation=0.\n"
+ "\nFor details about this feature, see, in a CLR enlistment,\n"
+ "src\\ndp\\clr\\doc\\OtherDevDocs\\untriaged\\clrdev_web\\SO Guide for CLR Developers.doc\n",
+ szFunction, szFile, lineNum);
+ }
+
+ // At this point, we've checked if we should dump to file or not and so can either
+ // do the assert or fall through and dump to a file.
+ if (! fDumpToFile)
+ {
+ DbgAssertDialog((char *)szFile, lineNum, buff);
+ return;
+ }
+
+ // If we are dumping violations to a file, we want to avoid duplicates so that we can run multiple tests
+ // and find unique violations and not end up with massively long files.
+ // We keep three files:
+ // 1) a list of the hashed strings for each unique filename/function
+ // 2) a list of the actual filename/function for unique violations and
+ // 3) a detailed assert dump for the violation itself
+ //
+ // First thing to do is read in the hashes file if this is our first violation. We read the filenames into a linked
+ // list with their hashes.
+ //
+ // Then we want to search through the list for that violation
+
+ // If it's new, then we insert the violation at the front of our list and append it to the violation files
+ // Otherwise, if we've already seen this violation, we can ignore it.
+
+
+ HANDLE hashesDumpFileHandle = INVALID_HANDLE_VALUE;
+
+ StackScratchBuffer buffer;
+ // First see if we've initialized yet
+ if (fDumpToFileInitialized == FALSE)
+ {
+ LONG lAlreadyOwned = InterlockedExchange((LPLONG)&dumpLock, 1);
+ if (lAlreadyOwned == 1)
+ {
+ // somebody else has gotten here first. So just skip this violation.
+ return;
+ }
+
+ // This is our first time through, so read in the existing file and create a linked list of hashed names from it.
+ hashesDumpFileHandle = CreateFileA(
+ hashFN.GetANSI(buffer),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH,
+ NULL);
+
+ // If we successfully opened the file, pull out each hash number and add it to a linked list of known violations.
+ // Otherwise, if we couldn't open the file, assume that there were no preexisting violations. The worse thing
+ // that will happen in this case is that we might report some dups.
+ if (hashesDumpFileHandle != INVALID_HANDLE_VALUE)
+ {
+ DWORD dwFileSize = GetFileSize( hashesDumpFileHandle, NULL );
+
+ NewArrayHolder<char> pBuffer(new char[dwFileSize]);
+ DWORD cbBuffer = dwFileSize;
+ DWORD cbRead;
+ DWORD result = ReadFile( hashesDumpFileHandle, pBuffer.GetValue(), cbBuffer, &cbRead, NULL );
+
+ CloseHandle( hashesDumpFileHandle );
+ hashesDumpFileHandle = INVALID_HANDLE_VALUE;
+
+ // If we couldn't read the file, assume that there were no preexisting violations. Worse thing
+ // that will happen is we might report some dups.
+ if (result && cbRead == cbBuffer)
+ {
+ char *pBuf = pBuffer.GetValue();
+ COUNT_T count = 0;
+ LOG((LF_EH, LL_INFO100000, "SOTolerantViolation: Reading known violations\n"));
+ while (count < cbRead)
+ {
+ char *pHashStart = pBuf + count;
+ char *pHashEnd = strstr(pHashStart, "\r\n");
+ COUNT_T len = static_cast<COUNT_T>(pHashEnd-pHashStart);
+ SString hashString(SString::Ascii, pHashStart, len);
+ ULONG hashValue = wcstoul(hashString.GetUnicode(), NULL, 16);
+ HashedSOViolations *pHashedSOViolations = new HashedSOViolations(hashValue, s_pHashedSOViolations);
+ s_pHashedSOViolations = pHashedSOViolations;
+ count += (len + 2);
+ LOG((LF_ALWAYS, LL_ALWAYS, " %8.8x\n", pHashedSOViolations->m_hash));
+ }
+ }
+ }
+ fDumpToFileInitialized = TRUE;
+ dumpLock = -1;
+ }
+
+
+ SString violation;
+ violation.Append(SString(SString::Ascii, szFile));
+ violation.Append(W(" "));
+ violation.Append(SString(SString::Ascii, szFunction));
+ HashedSOViolations *cur = s_pHashedSOViolations;
+
+ // look for the violation in the list
+ while (cur != NULL)
+ {
+ if (cur->m_hash == violation.Hash())
+ {
+ return;
+ }
+ cur = cur->m_pNext;
+ }
+
+ LONG lAlreadyOwned = InterlockedExchange((LPLONG)&dumpLock, 1);
+ if (lAlreadyOwned == 1)
+ {
+ // somebody else has gotten here first. So just skip this violation.
+ return;
+ }
+
+ HANDLE functionsDumpFileHandle = INVALID_HANDLE_VALUE;
+ HANDLE detailsDumpFileHandle = INVALID_HANDLE_VALUE;
+
+ // This is a new violation
+ // Append new violations to the output files
+ functionsDumpFileHandle = CreateFileA(
+ fnameFN.GetANSI(buffer), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL);
+
+ if (functionsDumpFileHandle != INVALID_HANDLE_VALUE)
+ {
+ // First write it to the filename dump
+ SetFilePointer(functionsDumpFileHandle, NULL, NULL, FILE_END);
+
+ DWORD written;
+ char *szExpr = &szExprWithStack[0];
+ sprintf_s(szExpr, _countof(szExprWithStack), "%s %8.8x\r\n", violation.GetANSI(buffer), violation.Hash());
+ WriteFile(functionsDumpFileHandle, szExpr, static_cast<DWORD>(strlen(szExpr)), &written, NULL);
+ CloseHandle(functionsDumpFileHandle);
+
+ // Now write it to the hashes dump. Once we've got it in the filename dump, we don't
+ // care if these others fail. We can live w/o detailed info or with dups.
+ hashesDumpFileHandle = CreateFileA(
+ hashFN.GetANSI(buffer), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL);
+
+ if (hashesDumpFileHandle != INVALID_HANDLE_VALUE)
+ {
+ SetFilePointer(hashesDumpFileHandle, NULL, NULL, FILE_END);
+
+ DWORD written;
+ sprintf_s(szExpr, _countof(szExprWithStack), "%8.8x", violation.Hash());
+ strcat_s(szExpr, _countof(szExprWithStack), "\r\n");
+ WriteFile(hashesDumpFileHandle, szExpr, static_cast<DWORD>(strlen(szExpr)), &written, NULL);
+ CloseHandle(hashesDumpFileHandle);
+ hashesDumpFileHandle = INVALID_HANDLE_VALUE;
+ }
+
+ // Now write it to the details dump
+ strcpy_s(szExpr, _countof(szExprWithStack), buff);
+ strcat_s(szExpr, _countof(szExprWithStack), "\n\n");
+#ifndef FEATURE_PAL
+ GetStringFromStackLevels(1, stackTraceLength, szExprWithStack + strlen(szExprWithStack));
+ strcat_s(szExpr, _countof(szExprWithStack), "\n\n");
+#endif // FEATURE_PAL
+ char exeName[300];
+ GetModuleFileNameA(NULL, exeName, sizeof(exeName)/sizeof(WCHAR));
+ strcat_s(szExpr, _countof(szExprWithStack), exeName);
+ strcat_s(szExpr, _countof(szExprWithStack), "\n\n\n");
+
+ detailsDumpFileHandle = CreateFileA(
+ detailsFN.GetANSI(buffer), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL);
+
+ if (detailsDumpFileHandle != INVALID_HANDLE_VALUE)
+ {
+ SetFilePointer(detailsDumpFileHandle, NULL, NULL, FILE_END);
+ WriteFile(detailsDumpFileHandle, szExpr, static_cast<DWORD>(strlen(szExpr)), &written, NULL);
+ CloseHandle(detailsDumpFileHandle);
+ detailsDumpFileHandle = INVALID_HANDLE_VALUE;
+ }
+
+ // add the new violation to our list
+ HashedSOViolations *pHashedSOViolations = new HashedSOViolations(violation.Hash(), s_pHashedSOViolations);
+ s_pHashedSOViolations = pHashedSOViolations;
+ LOG((LF_ALWAYS, LL_ALWAYS, "SOTolerantViolation: Adding new violation %8.8x %s\n", pHashedSOViolations->m_hash, violation.GetANSI(buffer)));
+ dumpLock = -1;
+ }
+}
+
+void SoTolerantViolationHelper(const char *szFunction,
+ const char *szFile,
+ int lineNum)
+{
+ // Keep this function separate to avoid overhead of EH in the normal case where we don't assert
+ // Enter SO-tolerant mode for scope of this call so that we don't get contract asserts
+ // in anything called downstream of CONTRACT_ASSERT. If we unwind out of here, our dtor
+ // will reset our state to what it was on entry.
+ CONTRACT_VIOLATION(SOToleranceViolation);
+
+ SOTolerantViolation(szFunction, szFile, lineNum);
+
+}
+
+void CloseSOTolerantViolationFile()
+{
+ // We used to have a file to close. Now we just cleanup the memory.
+ HashedSOViolations *ptr = s_pHashedSOViolations;
+ while (ptr != NULL)
+ {
+ s_pHashedSOViolations = s_pHashedSOViolations->m_pNext;
+ delete ptr;
+ ptr = s_pHashedSOViolations;
+ }
+}
+#endif //ENABLE_CONTRACTS_IMPL
+
+BOOL FileExists(LPCWSTR filename)
+{
+ WIN32_FIND_DATA data;
+ HANDLE h = WszFindFirstFile(filename, &data);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ return FALSE;
+ }
+
+ ::FindClose(h);
+
+ return TRUE;
+}
+
+#ifndef FEATURE_CORECLR
+// Current users for FileLock are ngen and ngen service
+
+FileLockHolder::FileLockHolder()
+{
+ _hLock = INVALID_HANDLE_VALUE;
+}
+
+FileLockHolder::~FileLockHolder()
+{
+ Release();
+}
+
+// the amount of time we want to wait
+#define FILE_LOCK_RETRY_TIME 100
+
+void FileLockHolder::Acquire(LPCWSTR lockName, HANDLE hInterrupt, BOOL* pInterrupted)
+{
+ DWORD dwErr = 0;
+ DWORD dwAccessDeniedRetry = 0;
+ const DWORD MAX_ACCESS_DENIED_RETRIES = 10;
+
+ if (pInterrupted)
+ {
+ *pInterrupted = FALSE;
+ }
+
+ _ASSERTE(_hLock == INVALID_HANDLE_VALUE);
+
+ for (;;) {
+ _hLock = WszCreateFile(lockName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
+ if (_hLock != INVALID_HANDLE_VALUE) {
+ return;
+ }
+
+ dwErr = GetLastError();
+ // Logically we should only expect ERROR_SHARING_VIOLATION, but Windows can also return
+ // ERROR_ACCESS_DENIED for underlying NtStatus DELETE_PENDING. That happens when another process
+ // (gacutil.exe or indexer) have the file opened. Unfortunately there is no public API that would
+ // allow us to detect this NtStatus and distinguish it from ‘real’ access denied (candidates are
+ // RtlGetLastNtStatus that is not documented on MSDN and NtCreateFile that is internal and can change
+ // at any time), so we retry on access denied, but only for a limited number of times.
+ if (dwErr == ERROR_SHARING_VIOLATION ||
+ (dwErr == ERROR_ACCESS_DENIED && ++dwAccessDeniedRetry <= MAX_ACCESS_DENIED_RETRIES))
+ {
+ // Somebody is holding the lock. Let's sleep, and come back again.
+ if (hInterrupt)
+ {
+ _ASSERTE(pInterrupted &&
+ "If you can be interrupted, you better want to know if you actually were interrupted");
+ if (WaitForSingleObject(hInterrupt, FILE_LOCK_RETRY_TIME) == WAIT_OBJECT_0)
+ {
+ if (pInterrupted)
+ {
+ *pInterrupted = TRUE;
+ }
+
+ // We've been interrupted, so return without acquiring
+ return;
+ }
+ }
+ else
+ {
+ ClrSleepEx(FILE_LOCK_RETRY_TIME, FALSE);
+ }
+ }
+ else {
+ ThrowHR(HRESULT_FROM_WIN32(dwErr));
+ }
+ }
+}
+
+
+HRESULT FileLockHolder::AcquireNoThrow(LPCWSTR lockName, HANDLE hInterrupt, BOOL* pInterrupted)
+{
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ Acquire(lockName, hInterrupt, pInterrupted);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
+
+BOOL FileLockHolder::IsTaken(LPCWSTR lockName)
+{
+
+ // We don't want to do an acquire the lock to know if its taken, so we want to see if the file
+ // exists. However, in situations like unplugging a machine, a DELETE_ON_CLOSE still leaves the file
+ // around. We try to delete it here. If the lock is acquired, DeleteFile will fail, as the file is
+ // not opened with SHARE_DELETE.
+ WszDeleteFile(lockName);
+
+ return FileExists(lockName);
+}
+
+void FileLockHolder::Release()
+{
+ if (_hLock != INVALID_HANDLE_VALUE) {
+ CloseHandle(_hLock);
+ _hLock = INVALID_HANDLE_VALUE;
+ }
+}
+#endif // FEATURE_CORECLR
+
+//======================================================================
+// This function returns true, if it can determine that the instruction pointer
+// refers to a code address that belongs in the range of the given image.
+// <TODO>@TODO: Merge with IsIPInModule from vm\util.hpp</TODO>
+
+BOOL IsIPInModule(HMODULE_TGT hModule, PCODE ip)
+{
+ STATIC_CONTRACT_LEAF;
+ SUPPORTS_DAC;
+
+ struct Param
+ {
+ HMODULE_TGT hModule;
+ PCODE ip;
+ BOOL fRet;
+ } param;
+ param.hModule = hModule;
+ param.ip = ip;
+ param.fRet = FALSE;
+
+// UNIXTODO: implement a proper version for PAL
+#ifndef FEATURE_PAL
+ PAL_TRY(Param *, pParam, &param)
+ {
+ PTR_BYTE pBase = dac_cast<PTR_BYTE>(pParam->hModule);
+
+ PTR_IMAGE_DOS_HEADER pDOS = NULL;
+ PTR_IMAGE_NT_HEADERS pNT = NULL;
+ USHORT cbOptHdr;
+ PCODE baseAddr;
+
+ //
+ // First, must validate the format of the PE headers to make sure that
+ // the fields we're interested in using exist in the image.
+ //
+
+ // Validate the DOS header.
+ pDOS = PTR_IMAGE_DOS_HEADER(pBase);
+ if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) ||
+ pDOS->e_lfanew == 0)
+ {
+ goto lDone;
+ }
+
+ // Validate the NT header
+ pNT = PTR_IMAGE_NT_HEADERS(pBase + VAL32(pDOS->e_lfanew));
+
+ if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
+ {
+ goto lDone;
+ }
+
+ // Validate that the optional header is large enough to contain the fields
+ // we're interested, namely IMAGE_OPTIONAL_HEADER::SizeOfImage. The reason
+ // we don't just check that SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL_HEADER
+ // is due to VSW443590, which states that the extensibility of this structure
+ // is such that it is possible to include only a portion of the optional header.
+ cbOptHdr = pNT->FileHeader.SizeOfOptionalHeader;
+
+ // Check that the magic field is contained by the optional header and set to the correct value.
+ if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, Magic) + sizeofmember(IMAGE_OPTIONAL_HEADER, Magic)) ||
+ pNT->OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC))
+ {
+ goto lDone;
+ }
+
+ // Check that the SizeOfImage is contained by the optional header.
+ if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage) + sizeofmember(IMAGE_OPTIONAL_HEADER, SizeOfImage)))
+ {
+ goto lDone;
+ }
+
+ //
+ // The real check
+ //
+
+ baseAddr = dac_cast<PCODE>(pBase);
+ if ((pParam->ip < baseAddr) || (pParam->ip >= (baseAddr + VAL32(pNT->OptionalHeader.SizeOfImage))))
+ {
+ goto lDone;
+ }
+
+ pParam->fRet = TRUE;
+
+lDone: ;
+ }
+ PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY
+#endif // !FEATURE_PAL
+
+ return param.fRet;
+}
+
+#ifdef FEATURE_CORRUPTING_EXCEPTIONS
+
+// To include definition of EXCEPTION_SOFTSO
+#include "corexcep.h"
+
+// These functions provide limited support for corrupting exceptions
+// outside the VM folder. Its limited since we dont have access to the
+// throwable.
+//
+// These functions are also wrapped by the corresponding CEHelper
+// methods in excep.cpp.
+
+// Given an exception code, this method returns a BOOL to indicate if the
+// code belongs to a corrupting exception or not.
+BOOL IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*=TRUE*/)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // By default, assume its not corrupting
+ BOOL fIsCorruptedStateException = FALSE;
+
+ if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_legacyCorruptedStateExceptionsPolicy) == 1)
+ {
+ return fIsCorruptedStateException;
+ }
+
+ // If we have been asked not to include SO in the CSE check
+ // and the code represent SO, then exit now.
+ if ((fCheckForSO == FALSE) && (dwExceptionCode == STATUS_STACK_OVERFLOW))
+ {
+ return fIsCorruptedStateException;
+ }
+
+ switch(dwExceptionCode)
+ {
+ case STATUS_ACCESS_VIOLATION:
+ case STATUS_STACK_OVERFLOW:
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_IN_PAGE_ERROR:
+ case EXCEPTION_INVALID_DISPOSITION:
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ case STATUS_UNWIND_CONSOLIDATE:
+ fIsCorruptedStateException = TRUE;
+ break;
+ }
+
+ return fIsCorruptedStateException;
+}
+
+#endif // FEATURE_CORRUPTING_EXCEPTIONS
+
+void EnableTerminationOnHeapCorruption()
+{
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+}
+
+RUNTIMEVERSIONINFO RUNTIMEVERSIONINFO::notDefined;
+
+BOOL IsV2RuntimeLoaded(void)
+{
+#ifndef FEATURE_CORECLR
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ ReleaseHolder<ICLRMetaHost> pMetaHost(NULL);
+ ReleaseHolder<IEnumUnknown> pEnum(NULL);
+ ReleaseHolder<IUnknown> pUnk(NULL);
+ ReleaseHolder<ICLRRuntimeInfo> pRuntime(NULL);
+ HRESULT hr;
+
+ HModuleHolder hModule = WszLoadLibrary(MSCOREE_SHIM_W);
+ if (hModule == NULL)
+ return FALSE;
+
+ CLRCreateInstanceFnPtr pfnCLRCreateInstance = (CLRCreateInstanceFnPtr)::GetProcAddress(hModule, "CLRCreateInstance");
+ if (pfnCLRCreateInstance == NULL)
+ return FALSE;
+
+ hr = (*pfnCLRCreateInstance)(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *)&pMetaHost);
+ if (FAILED(hr))
+ return FALSE;
+
+ hr = pMetaHost->EnumerateLoadedRuntimes(GetCurrentProcess(), &pEnum);
+ if (FAILED(hr))
+ return FALSE;
+
+ while (pEnum->Next(1, &pUnk, NULL) == S_OK)
+ {
+ hr = pUnk->QueryInterface(IID_ICLRRuntimeInfo, (void **)&pRuntime);
+ if (FAILED(hr))
+ continue;
+
+ WCHAR wszVersion[30];
+ DWORD cchVersion = _countof(wszVersion);
+ hr = pRuntime->GetVersionString(wszVersion, &cchVersion);
+ if (FAILED(hr))
+ continue;
+
+ // Is it a V2 runtime?
+ if ((cchVersion < 3) ||
+ ((wszVersion[0] != W('v')) && (wszVersion[0] != W('V'))) ||
+ (wszVersion[1] != W('2')) ||
+ (wszVersion[2] != W('.')))
+ continue;
+
+ return TRUE;
+ }
+#endif // FEATURE_CORECLR
+
+ return FALSE;
+}
+
+#ifdef FEATURE_COMINTEROP
+BOOL IsClrHostedLegacyComObject(REFCLSID rclsid)
+{
+ // let's simply check for all CLSIDs that are known to be runtime implemented and capped to 2.0
+ return (
+ rclsid == CLSID_ComCallUnmarshal ||
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ rclsid == CLSID_CorRuntimeHost ||
+ rclsid == CLSID_CLRRuntimeHost ||
+ rclsid == CLSID_CLRProfiling ||
+#endif
+ rclsid == CLSID_CorMetaDataDispenser ||
+ rclsid == CLSID_CorMetaDataDispenserRuntime ||
+ rclsid == CLSID_TypeNameFactory);
+}
+#endif // FEATURE_COMINTEROP
+
+// Returns the directory for HMODULE. So, if HMODULE was for "C:\Dir1\Dir2\Filename.DLL",
+// then this would return "C:\Dir1\Dir2\" (note the trailing backslash).
+HRESULT GetHModuleDirectory(
+ __in HMODULE hMod,
+ __out_z __out_ecount(cchPath) LPWSTR wszPath,
+ size_t cchPath)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ DWORD dwRet = WszGetModuleFileName(hMod, wszPath, static_cast<DWORD>(cchPath));
+
+ if (dwRet == cchPath)
+ { // If there are cchPath characters in the string, it means that the string
+ // itself is longer than cchPath and GetModuleFileName had to truncate at cchPath.
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ else if (dwRet == 0)
+ { // Some other error.
+ return HRESULT_FROM_GetLastError();
+ }
+
+ LPWSTR wszEnd = wcsrchr(wszPath, W('\\'));
+ if (wszEnd == NULL)
+ { // There was no backslash? Not sure what's going on.
+ return E_UNEXPECTED;
+ }
+
+ // Include the backslash in the resulting string.
+ *(++wszEnd) = W('\0');
+
+ return S_OK;
+}
+
+SString & GetHModuleDirectory(HMODULE hMod, SString &ssDir)
+{
+ LPWSTR wzDir = ssDir.OpenUnicodeBuffer(_MAX_PATH);
+ HRESULT hr = GetHModuleDirectory(hMod, wzDir, _MAX_PATH);
+ ssDir.CloseBuffer(FAILED(hr) ? 0 : static_cast<COUNT_T>(wcslen(wzDir)));
+ IfFailThrow(hr);
+ return ssDir;
+}
+
+#if !defined(FEATURE_CORECLR) && !defined(SELF_NO_HOST) && !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+
+namespace UtilCode
+{
+
+#pragma warning(push)
+#pragma warning(disable:4996) // For use of deprecated LoadLibraryShim
+
+ // When a NULL version is passed to LoadLibraryShim, this told the shim to bind the already-loaded
+ // runtime or to the latest runtime. In hosted environments, we already know a runtime (or two) is
+ // loaded, and since we are no longer guaranteed that a call to mscoree!LoadLibraryShim with a NULL
+ // version will return the correct runtime, this code uses the ClrCallbacks infrastructure
+ // available to get the ICLRRuntimeInfo for the runtime in which this code is hosted, and then
+ // calls ICLRRuntimeInfo::LoadLibrary to make sure that the load occurs within the context of the
+ // correct runtime.
+ HRESULT LoadLibraryShim(LPCWSTR szDllName, LPCWSTR szVersion, LPVOID pvReserved, HMODULE *phModDll)
+ {
+ HRESULT hr = S_OK;
+
+ if (szVersion != NULL)
+ { // If a version is provided, then we just fall back to the legacy function to allow
+ // it to construct the explicit path and load from that location.
+ //@TODO: Can we verify that all callers of LoadLibraryShim in hosted environments always pass null and eliminate this code?
+ return ::LoadLibraryShim(szDllName, szVersion, pvReserved, phModDll);
+ }
+
+ //
+ // szVersion is NULL, which means we should load the DLL from the hosted environment's directory.
+ //
+
+ typedef ICLRRuntimeInfo *GetCLRRuntime_t();
+ GetCLRRuntime_t *pfnGetCLRRuntime =
+ reinterpret_cast<GetCLRRuntime_t *>((*GetClrCallbacks().m_pfnGetCLRFunction)("GetCLRRuntime"));
+ if (pfnGetCLRRuntime == NULL)
+ return E_UNEXPECTED;
+
+ ICLRRuntimeInfo* pRI = (*pfnGetCLRRuntime)();
+ if (pRI == NULL)
+ return E_UNEXPECTED;
+
+ return pRI->LoadLibrary(szDllName, phModDll);
+ }
+
+#pragma warning(pop)
+
+}
+
+#endif //!FEATURE_CORECLR && !SELF_NO_HOST && !FEATURE_UTILCODE_NO_DEPENDENCIES
+
+namespace Clr
+{
+namespace Util
+{
+ static BOOL g_fLocalAppDataDirectoryInitted = FALSE;
+ static WCHAR *g_wszLocalAppDataDirectory = NULL;
+
+// This api returns a pointer to a null-terminated string that contains the local appdata directory
+// or it returns NULL in the case that the directory could not be found. The return value from this function
+// is not actually checked for existence.
+ HRESULT GetLocalAppDataDirectory(LPCWSTR *ppwzLocalAppDataDirectory)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ *ppwzLocalAppDataDirectory = NULL;
+
+ EX_TRY
+ {
+ if (!g_fLocalAppDataDirectoryInitted)
+ {
+ WCHAR *wszLocalAppData = NULL;
+
+ DWORD cCharsNeeded;
+ cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), NULL, 0);
+
+ if ((cCharsNeeded != 0) && (cCharsNeeded < MAX_PATH))
+ {
+ wszLocalAppData = new WCHAR[cCharsNeeded];
+ cCharsNeeded = GetEnvironmentVariableW(W("LOCALAPPDATA"), wszLocalAppData, cCharsNeeded);
+ if (cCharsNeeded != 0)
+ {
+ // We've collected the appropriate app data directory into a local. Now publish it.
+ if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) == NULL)
+ {
+ // This variable doesn't need to be freed, as it has been stored in the global
+ wszLocalAppData = NULL;
+ }
+ }
+ }
+
+ g_fLocalAppDataDirectoryInitted = TRUE;
+ delete[] wszLocalAppData;
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (SUCCEEDED(hr))
+ *ppwzLocalAppDataDirectory = g_wszLocalAppDataDirectory;
+
+ return hr;
+ }
+
+ HRESULT SetLocalAppDataDirectory(LPCWSTR pwzLocalAppDataDirectory)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ if (pwzLocalAppDataDirectory == NULL || *pwzLocalAppDataDirectory == W('\0'))
+ return E_INVALIDARG;
+
+ if (g_fLocalAppDataDirectoryInitted)
+ return E_UNEXPECTED;
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ size_t size = wcslen(pwzLocalAppDataDirectory) + 1;
+ WCHAR *wszLocalAppData = new WCHAR[size];
+ wcscpy_s(wszLocalAppData, size, pwzLocalAppDataDirectory);
+
+ // We've collected the appropriate app data directory into a local. Now publish it.
+ if (InterlockedCompareExchangeT(&g_wszLocalAppDataDirectory, wszLocalAppData, NULL) != NULL)
+ {
+ // Someone else already set LocalAppData. Free our copy and return an error.
+ delete[] wszLocalAppData;
+ hr = E_UNEXPECTED;
+ }
+
+ g_fLocalAppDataDirectoryInitted = TRUE;
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+ }
+
+#ifndef FEATURE_PAL
+namespace Reg
+{
+ HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue)
+ {
+ STANDARD_VM_CONTRACT;
+
+ if (hKey == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ RegKeyHolder hTargetKey;
+ if (wszSubKeyName == NULL || *wszSubKeyName == W('\0'))
+ { // No subkey was requested, use hKey as the resolved key.
+ hTargetKey = hKey;
+ hTargetKey.SuppressRelease();
+ }
+ else
+ { // Try to open the specified subkey.
+ if (WszRegOpenKeyEx(hKey, wszSubKeyName, 0, KEY_READ, &hTargetKey) != ERROR_SUCCESS)
+ return REGDB_E_CLASSNOTREG;
+ }
+
+ DWORD type;
+ DWORD size;
+ if ((WszRegQueryValueEx(hTargetKey, wszValueName, 0, &type, 0, &size) == ERROR_SUCCESS) &&
+ type == REG_SZ && size > 0)
+ {
+ LPWSTR wszValueBuf = ssValue.OpenUnicodeBuffer(static_cast<COUNT_T>((size / sizeof(WCHAR)) - 1));
+ LONG lResult = WszRegQueryValueEx(
+ hTargetKey,
+ wszValueName,
+ 0,
+ 0,
+ reinterpret_cast<LPBYTE>(wszValueBuf),
+ &size);
+
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ if (lResult == ERROR_SUCCESS)
+ {
+ // Can't count on the returned size being accurate - I've seen at least
+ // one string with an extra NULL at the end that will cause the resulting
+ // SString to count the extra NULL as part of the string. An extra
+ // terminating NULL is not a legitimate scenario for REG_SZ - this must
+ // be done using REG_MULTI_SZ - however this was tolerated in the
+ // past and so it would be a breaking change to stop doing so.
+ _ASSERTE(wcslen(wszValueBuf) <= (size / sizeof(WCHAR)) - 1);
+ ssValue.CloseBuffer((COUNT_T)wcsnlen(wszValueBuf, (size_t)size));
+ }
+ else
+ {
+ ssValue.CloseBuffer(0);
+ return HRESULT_FROM_WIN32(lResult);
+ }
+
+ return S_OK;
+ }
+ else
+ {
+ return REGDB_E_KEYMISSING;
+ }
+ }
+
+ HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKey, LPCWSTR wszName, __deref_out __deref_out_z LPWSTR* pwszValue)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ StackSString ssValue;
+ if (SUCCEEDED(hr = ReadStringValue(hKey, wszSubKey, wszName, ssValue)))
+ {
+ *pwszValue = new WCHAR[ssValue.GetCount() + 1];
+ wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+ }
+} // namespace Reg
+
+namespace Com
+{
+ namespace __imp
+ {
+ static
+ HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, SString & ssValue)
+ {
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ WCHAR wszClsid[39];
+ if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0)
+ return E_UNEXPECTED;
+
+ StackSString ssKeyName;
+ ssKeyName.Append(SL(W("CLSID\\")));
+ ssKeyName.Append(wszClsid);
+ ssKeyName.Append(SL(W("\\")));
+ ssKeyName.Append(wszSubKeyName);
+
+ return Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), NULL, ssValue);
+ }
+
+ static
+ HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, __deref_out __deref_out_z LPWSTR* pwszValue)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ StackSString ssValue;
+ if (SUCCEEDED(hr = FindSubKeyDefaultValueForCLSID(rclsid, wszSubKeyName, ssValue)))
+ {
+ *pwszValue = new WCHAR[ssValue.GetCount() + 1];
+ wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+ }
+ }
+
+ HRESULT FindServerUsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszServerName)
+ {
+ WRAPPER_NO_CONTRACT;
+ return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), pwszServerName);
+ }
+
+ HRESULT FindServerUsingCLSID(REFCLSID rclsid, SString & ssServerName)
+ {
+ WRAPPER_NO_CONTRACT;
+ return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("Server"), ssServerName);
+ }
+
+ HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, __deref_out __deref_out_z LPWSTR* pwszInprocServer32Name)
+ {
+ WRAPPER_NO_CONTRACT;
+ return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), pwszInprocServer32Name);
+ }
+
+ HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name)
+ {
+ WRAPPER_NO_CONTRACT;
+ return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name);
+ }
+
+ BOOL IsMscoreeInprocServer32(const SString & ssInprocServer32Name)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ return (ssInprocServer32Name.EqualsCaseInsensitive(SL(MSCOREE_SHIM_W)) ||
+ ssInprocServer32Name.EndsWithCaseInsensitive(SL(W("\\") MSCOREE_SHIM_W)));
+ }
+
+ BOOL CLSIDHasMscoreeAsInprocServer32(REFCLSID rclsid)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ StackSString ssInprocServer32;
+ FindInprocServer32UsingCLSID(rclsid, ssInprocServer32);
+ return IsMscoreeInprocServer32(ssInprocServer32);
+ }
+
+} // namespace Com
+#endif // FEATURE_PAL
+
+namespace Win32
+{
+ void GetModuleFileName(
+ HMODULE hModule,
+ SString & ssFileName,
+ bool fAllowLongFileNames)
+ {
+ STANDARD_VM_CONTRACT;
+
+ // Try to use what the SString already has allocated. If it does not have anything allocated
+ // or it has < 20 characters allocated, then bump the size requested to _MAX_PATH.
+ DWORD dwSize = (DWORD)(ssFileName.GetUnicodeAllocation()) + 1;
+ dwSize = (dwSize < 20) ? (_MAX_PATH) : (dwSize);
+ DWORD dwResult = WszGetModuleFileName(hModule, ssFileName.OpenUnicodeBuffer(dwSize - 1), dwSize);
+
+ // if there was a failure, dwResult == 0;
+ // if there was insufficient buffer, dwResult == dwSize;
+ // if there was sufficient buffer and a successful write, dwResult < dwSize
+ ssFileName.CloseBuffer(dwResult < dwSize ? dwResult : 0);
+
+ if (dwResult == 0)
+ ThrowHR(HRESULT_FROM_GetLastError());
+
+ // Ok, we didn't have enough buffer. Let's loop, doubling the buffer each time, until we succeed.
+ while (dwResult == dwSize)
+ {
+ dwSize = dwSize * 2;
+ dwResult = WszGetModuleFileName(hModule, ssFileName.OpenUnicodeBuffer(dwSize - 1), dwSize);
+ ssFileName.CloseBuffer(dwResult < dwSize ? dwResult : 0);
+
+ if (dwResult == 0)
+ ThrowHR(HRESULT_FROM_GetLastError());
+ }
+
+ // Most of the runtime is not able to handle long filenames. fAllowLongFileNames
+ // has a default value of false, so that callers will not accidentally get long
+ // file names returned.
+ if (!fAllowLongFileNames && ssFileName.BeginsWith(SL(LONG_FILENAME_PREFIX_W)))
+ {
+ ssFileName.Clear();
+ ThrowHR(E_UNEXPECTED);
+ }
+
+ _ASSERTE(dwResult != 0 && dwResult < dwSize);
+ }
+
+ // Returns heap-allocated string in *pwszFileName
+ HRESULT GetModuleFileName(
+ HMODULE hModule,
+ __deref_out_z LPWSTR * pwszFileName,
+ bool fAllowLongFileNames)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pwszFileName));
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ InlineSString<_MAX_PATH> ssFileName;
+ GetModuleFileName(hModule, ssFileName);
+ *pwszFileName = DuplicateStringThrowing(ssFileName.GetUnicode());
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+ }
+
+ void GetFullPathName(
+ SString const & ssFileName,
+ SString & ssPathName,
+ DWORD * pdwFilePartIdx,
+ bool fAllowLongFileNames)
+ {
+ STANDARD_VM_CONTRACT;
+
+ // Get the required buffer length (including terminating NULL).
+ DWORD dwLengthRequired = WszGetFullPathName(ssFileName.GetUnicode(), 0, NULL, NULL);
+
+ if (dwLengthRequired == 0)
+ ThrowHR(HRESULT_FROM_GetLastError());
+
+ LPWSTR wszPathName = ssPathName.OpenUnicodeBuffer(dwLengthRequired - 1);
+ LPWSTR wszFileName = NULL;
+ DWORD dwLengthWritten = WszGetFullPathName(
+ ssFileName.GetUnicode(),
+ dwLengthRequired,
+ wszPathName,
+ &wszFileName);
+
+ // Calculate the index while the buffer is open and the string poiner is stable.
+ if (dwLengthWritten != 0 && dwLengthWritten < dwLengthRequired && pdwFilePartIdx != NULL)
+ *pdwFilePartIdx = static_cast<DWORD>(wszFileName - wszPathName);
+
+ ssPathName.CloseBuffer(dwLengthWritten < dwLengthRequired ? dwLengthWritten : 0);
+
+ if (dwLengthRequired == 0)
+ ThrowHR(HRESULT_FROM_GetLastError());
+
+ // Overly defensive? Perhaps.
+ if (!(dwLengthWritten < dwLengthRequired))
+ ThrowHR(E_UNEXPECTED);
+
+ // Most of the runtime is not able to handle long filenames. fAllowLongFileNames
+ // has a default value of false, so that callers will not accidentally get long
+ // file names returned.
+ if (!fAllowLongFileNames && ssFileName.BeginsWith(SL(LONG_FILENAME_PREFIX_W)))
+ {
+ ssPathName.Clear();
+ if (pdwFilePartIdx != NULL)
+ *pdwFilePartIdx = 0;
+ ThrowHR(E_UNEXPECTED);
+ }
+ }
+} // namespace Win32
+
+} // namespace Util
+} // namespace Clr
diff --git a/src/utilcode/util_nodependencies.cpp b/src/utilcode/util_nodependencies.cpp
new file mode 100644
index 0000000000..f2bb90918c
--- /dev/null
+++ b/src/utilcode/util_nodependencies.cpp
@@ -0,0 +1,1119 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// Util_NoDependencies.cpp
+//
+
+//
+// This contains a bunch of C++ utility classes needed also for UtilCode without dependencies
+// (standalone version without CLR/clr.dll/mscoree.dll dependencies).
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG)
+
+extern RunningOnStatusEnum gRunningOnStatus = RUNNING_ON_STATUS_UNINITED;
+extern BOOL gExInfoAvailable = FALSE;
+extern BOOL gExInfoIsServer = TRUE;
+
+#define NON_SUPPORTED_PLATFORM_MSGBOX_TITLE W("Platform not supported")
+#define NON_SUPPORTED_PLATFORM_MSGBOX_TEXT W("The minimum supported platform is Windows 2000")
+#define NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE 0xBAD1BAD1
+
+//*****************************************************************************
+// One time initialization of the OS version
+//*****************************************************************************
+void InitRunningOnVersionStatus ()
+{
+#ifndef FEATURE_PAL
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ BOOL fSupportedPlatform = FALSE;
+ OSVERSIONINFOEX sVer;
+ DWORDLONG dwlConditionMask;
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 6;
+ sVer.dwMinorVersion = 2;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WIN8;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 6;
+ sVer.dwMinorVersion = 1;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WIN7;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 6;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_VISTA;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 5;
+ sVer.dwMinorVersion = 2;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WIN2003;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+ sVer.dwMajorVersion = 5;
+ sVer.dwMinorVersion = 1;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+ dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WINXP;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 5;
+ sVer.dwMinorVersion = 0;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_PLATFORMID, VER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MAJORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, CLR_VER_MAJORVERSION | CLR_VER_PLATFORMID | CLR_VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WINNT5;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+CHECK_SUPPORTED:
+
+ if (!fSupportedPlatform)
+ {
+ // The current platform isn't supported. Display a message box to this effect and exit.
+ // Note that this should never happen since the .NET Fx setup should not install on
+ // non supported platforms (which is why the message box text isn't localized).
+ UtilMessageBoxCatastrophicNonLocalized(NON_SUPPORTED_PLATFORM_MSGBOX_TITLE, NON_SUPPORTED_PLATFORM_MSGBOX_TEXT, MB_OK | MB_ICONERROR, TRUE);
+ TerminateProcess(GetCurrentProcess(), NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE);
+ }
+
+ gExInfoAvailable = 0;
+ gExInfoIsServer = 0;
+
+ OSVERSIONINFOEX sVerX;
+ ZeroMemory(&sVerX, sizeof(OSVERSIONINFOEX));
+ sVerX.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ sVerX.wProductType = VER_NT_DOMAIN_CONTROLLER;
+
+ dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_PRODUCT_TYPE, VER_EQUAL);
+
+ if(VerifyVersionInfo(&sVerX, CLR_VER_PRODUCT_TYPE, dwlConditionMask))
+ {
+ gExInfoIsServer = 1;
+ }
+
+
+ ZeroMemory(&sVerX, sizeof(OSVERSIONINFOEX));
+ sVerX.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ sVerX.wProductType = VER_NT_SERVER;
+
+ dwlConditionMask = 0;
+ VER_SET_CONDITION(dwlConditionMask, CLR_VER_PRODUCT_TYPE, VER_EQUAL);
+
+ if(VerifyVersionInfo(&sVerX, CLR_VER_PRODUCT_TYPE, dwlConditionMask))
+ {
+ gExInfoIsServer = 1;
+ }
+
+ gExInfoAvailable = 1;
+#else // FEATURE_PAL
+ // UNIXTODO: Do we need version checks for Linux?
+#endif // FEATURE_PAL
+} // InitRunningOnVersionStatus
+
+#ifndef _WIN64
+//------------------------------------------------------------------------------
+// Returns TRUE if we are running on a 64-bit OS in WoW, FALSE otherwise.
+BOOL RunningInWow64()
+{
+ static int s_Wow64Process;
+
+ if (s_Wow64Process == 0)
+ {
+ BOOL fWow64Process = FALSE;
+
+ if (!IsWow64Process(GetCurrentProcess(), &fWow64Process))
+ fWow64Process = FALSE;
+
+ s_Wow64Process = fWow64Process ? 1 : -1;
+ }
+
+ return (s_Wow64Process == 1) ? TRUE : FALSE;
+}
+#endif
+
+#ifndef FEATURE_PAL
+//------------------------------------------------------------------------------
+//
+// GetRegistryLongValue - Reads a configuration LONG value from the registry.
+//
+// Parameters
+// hKeyParent -- Parent key
+// szKey -- key to open
+// szName -- name of the value
+// pValue -- put value here, if found
+// fReadNonVirtualizedKey -- whether to read 64-bit hive on WOW64
+//
+// Returns
+// TRUE -- If the value was found and read
+// FALSE -- The value was not found, could not be read, or was not DWORD
+//
+// Exceptions
+// None
+//------------------------------------------------------------------------------
+BOOL GetRegistryLongValue(HKEY hKeyParent,
+ LPCWSTR szKey,
+ LPCWSTR szName,
+ long *pValue,
+ BOOL fReadNonVirtualizedKey)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD ret; // Return value from registry operation.
+ HKEYHolder hkey; // Registry key.
+ long iValue; // The value to read.
+ DWORD iType; // Type of value to get.
+ DWORD iSize; // Size of buffer.
+ REGSAM samDesired = KEY_READ; // Desired access rights to the key
+
+ if (fReadNonVirtualizedKey)
+ {
+ if (RunningInWow64())
+ {
+ samDesired |= KEY_WOW64_64KEY;
+ }
+ }
+
+ ret = WszRegOpenKeyEx(hKeyParent, szKey, 0, samDesired, &hkey);
+
+ // If we opened the key, see if there is a value.
+ if (ret == ERROR_SUCCESS)
+ {
+ iType = REG_DWORD;
+ iSize = sizeof(long);
+ ret = WszRegQueryValueEx(hkey, szName, NULL, &iType, reinterpret_cast<BYTE*>(&iValue), &iSize);
+
+ if (ret == ERROR_SUCCESS && iType == REG_DWORD && iSize == sizeof(long))
+ { // We successfully read a DWORD value.
+ *pValue = iValue;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+} // GetRegistryLongValue
+
+//----------------------------------------------------------------------------
+//
+// GetCurrentModuleFileName - Retrieve the current module's filename
+//
+// Arguments:
+// pBuffer - output string buffer
+// pcchBuffer - the number of characters of the string buffer
+//
+// Return Value:
+// S_OK on success, else detailed error code.
+//
+// Note:
+//
+//----------------------------------------------------------------------------
+HRESULT GetCurrentModuleFileName(__out_ecount(*pcchBuffer) LPWSTR pBuffer, __inout DWORD *pcchBuffer)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if ((pBuffer == NULL) || (pcchBuffer == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ WCHAR appPath[MAX_PATH + 2];
+
+ DWORD ret = WszGetModuleFileName(NULL, appPath, NumItems(appPath));
+
+ if ((ret == NumItems(appPath)) || (ret == 0))
+ {
+ // The module file name exceeded maxpath, or GetModuleFileName failed.
+ return E_UNEXPECTED;
+ }
+
+ // Pick off the part after the path.
+ WCHAR* appName = wcsrchr(appPath, W('\\'));
+
+ // If no backslash, use the whole name; if there is a backslash, skip it.
+ appName = appName ? appName+1 : appPath;
+
+ if (*pcchBuffer < wcslen(appName))
+ {
+ *pcchBuffer = static_cast<DWORD>(wcslen(appName)) + 1;
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ wcscpy_s(pBuffer, *pcchBuffer, appName);
+ return S_OK;
+}
+
+//----------------------------------------------------------------------------
+//
+// IsCurrentModuleFileNameInAutoExclusionList - decide if the current module's filename
+// is in the AutoExclusionList list
+//
+// Arguments:
+// None
+//
+// Return Value:
+// TRUE or FALSE
+//
+// Note:
+// This function cannot be used in out of process scenarios like DAC because it
+// looks at current module's filename. In OOP we want to use target process's
+// module's filename.
+//
+//----------------------------------------------------------------------------
+BOOL IsCurrentModuleFileNameInAutoExclusionList()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HKEYHolder hKeyHolder;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerAutoExclusionListKey, 0, KEY_READ, &hKeyHolder);
+
+ if (ret != ERROR_SUCCESS)
+ {
+ // there's not even an AutoExclusionList hive
+ return FALSE;
+ }
+
+ WCHAR wszAppName[MAX_PATH];
+ DWORD cchAppName = NumItems(wszAppName);
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ if (GetCurrentModuleFileName(wszAppName, &cchAppName) != S_OK)
+ {
+ // Assume it is not on the exclusion list if we cannot find the module's filename.
+ return FALSE;
+ }
+
+ // Look in AutoExclusionList key for appName get the size of any value stored there.
+ DWORD value, valueType, valueSize = sizeof(value);
+ ret = WszRegQueryValueEx(hKeyHolder, wszAppName, 0, &valueType, reinterpret_cast<BYTE*>(&value), &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_DWORD) && (value == 1))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+} // IsCurrentModuleFileNameInAutoExclusionList
+
+//*****************************************************************************
+// Retrieve information regarding what registered default debugger
+//*****************************************************************************
+void GetDebuggerSettingInfo(SString &ssDebuggerString, BOOL *pfAuto)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ EX_TRY
+ {
+ DWORD cchDebuggerString = MAX_PATH;
+ INDEBUG(DWORD cchOldDebuggerString = cchDebuggerString);
+
+ WCHAR * buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString);
+ HRESULT hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto);
+ ssDebuggerString.CloseBuffer(cchDebuggerString);
+
+ while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ _ASSERTE(cchDebuggerString > cchOldDebuggerString);
+ INDEBUG(cchOldDebuggerString = cchDebuggerString);
+
+ buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString);
+ hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto);
+ ssDebuggerString.CloseBuffer(cchDebuggerString);
+ }
+
+ if (*ssDebuggerString.GetUnicode() == W('\0'))
+ {
+ ssDebuggerString.Clear();
+ }
+
+ if (FAILED(hr))
+ {
+ ssDebuggerString.Clear();
+ if (pfAuto)
+ {
+ *pfAuto = FALSE;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ ssDebuggerString.Clear();
+ if (pfAuto)
+ {
+ *pfAuto = FALSE;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+} // GetDebuggerSettingInfo
+
+//---------------------------------------------------------------------------------------
+//
+// GetDebuggerSettingInfoWorker - retrieve information regarding what registered default debugger
+//
+// Arguments:
+// * wszDebuggerString - [out] the string buffer to store the registered debugger launch
+// string
+// * pcchDebuggerString - [in, out] the size of string buffer in characters
+// * pfAuto - [in] the flag to indicate whether the debugger neeeds to be launched
+// automatically
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+// Notes:
+// * wszDebuggerString can be NULL. When wszDebuggerString is NULL, pcchDebuggerString should
+// * point to a DWORD of zero. pcchDebuggerString cannot be NULL, and the DWORD pointed by
+// * pcchDebuggerString will store the used or required string buffer size in characters.
+HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(pcchDebuggerString != NULL);
+ }
+ CONTRACTL_END;
+
+ if ((pcchDebuggerString == NULL) || ((wszDebuggerString == NULL) && (*pcchDebuggerString != 0)))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Initialize the output values before we start.
+ if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
+ {
+ *wszDebuggerString = W('\0');
+ }
+
+ if (pfAuto != NULL)
+ {
+ *pfAuto = FALSE;
+ }
+
+ HKEYHolder hKeyHolder;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerKey, 0, KEY_READ, &hKeyHolder);
+
+ if (ret != ERROR_SUCCESS)
+ { // Wow, there's not even an AeDebug hive, so no native debugger, no auto.
+ return S_OK;
+ }
+
+ // Look in AeDebug key for "Debugger"; get the size of any value stored there.
+ DWORD valueType, valueSize;
+ ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, 0, &valueType, 0, &valueSize);
+
+ _ASSERTE(pcchDebuggerString != NULL);
+ if ((wszDebuggerString == NULL) || (*pcchDebuggerString < valueSize / sizeof(WCHAR)))
+ {
+ *pcchDebuggerString = valueSize / sizeof(WCHAR) + 1;
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ *pcchDebuggerString = valueSize / sizeof(WCHAR);
+
+ // The size of an empty string with the null terminator is 2.
+ BOOL fIsDebuggerStringEmptry = valueSize <= 2 ? TRUE : FALSE;
+
+ if ((ret != ERROR_SUCCESS) || (valueType != REG_SZ) || fIsDebuggerStringEmptry)
+ {
+ return S_OK;
+ }
+
+ _ASSERTE(wszDebuggerString != NULL);
+ ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, NULL, NULL, reinterpret_cast< LPBYTE >(wszDebuggerString), &valueSize);
+ if (ret != ERROR_SUCCESS)
+ {
+ *wszDebuggerString = W('\0');
+ return S_OK;
+ }
+
+ // The callers are in nothrow scope, so we must swallow exceptions and reset the output parameters to the
+ // default values if exceptions like OOM ever happen.
+ EX_TRY
+ {
+ if (pfAuto != NULL)
+ {
+ BOOL fAuto = FALSE;
+
+ // Get the appname to look up in DebugApplications key.
+ WCHAR wzAppName[MAX_PATH];
+ DWORD cchAppName = NumItems(wzAppName);
+ long iValue;
+
+ // Check DebugApplications setting
+ if ((SUCCEEDED(GetCurrentModuleFileName(wzAppName, &cchAppName))) &&
+ (
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsKey, wzAppName, &iValue, TRUE)
+ ) &&
+ (iValue == 1))
+ {
+ fAuto = TRUE;
+ }
+ else
+ {
+ // Look in AeDebug key for "Auto"; get the size of any value stored there.
+ ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, 0, &valueType, 0, &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_SZ) && (valueSize / sizeof(WCHAR) < MAX_PATH))
+ {
+ WCHAR wzAutoKey[MAX_PATH];
+ valueSize = NumItems(wzAutoKey) * sizeof(WCHAR);
+ WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, NULL, NULL, reinterpret_cast< LPBYTE >(wzAutoKey), &valueSize);
+
+ // The OS's behavior is to consider Auto to be FALSE unless the first character is set
+ // to 1. They don't take into consideration the following characters. Also if the value
+ // isn't present they assume an Auto value of FALSE.
+ if ((wzAutoKey[0] == W('1')) && !IsCurrentModuleFileNameInAutoExclusionList())
+ {
+ fAuto = TRUE;
+ }
+ }
+ }
+
+ *pfAuto = fAuto;
+ }
+ }
+ EX_CATCH
+ {
+ if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
+ {
+ *wszDebuggerString = W('\0');
+ }
+
+ if (pfAuto != NULL)
+ {
+ *pfAuto = FALSE;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return S_OK;
+} // GetDebuggerSettingInfoWorker
+#endif // FEATURE_PAL
+
+#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG)
+
+//*****************************************************************************
+// Convert hex value into a wide string of hex digits
+//*****************************************************************************
+HRESULT GetStr(
+ DWORD hHexNum,
+ __out_ecount((cbHexNum * 2)) LPWSTR szHexNum,
+ DWORD cbHexNum)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE (szHexNum);
+ cbHexNum *= 2; // each nibble is a char
+ while (cbHexNum != 0)
+ {
+ DWORD thisHexDigit = hHexNum % 16;
+ hHexNum /= 16;
+ cbHexNum--;
+ if (thisHexDigit < 10)
+ {
+ *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit + W('0'));
+ }
+ else
+ {
+ *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit - 10 + W('A'));
+ }
+ }
+ return S_OK;
+}
+
+//*****************************************************************************
+// Convert a GUID into a pointer to a Wide char string
+//*****************************************************************************
+int
+GuidToLPWSTR(
+ GUID Guid, // The GUID to convert.
+ __out_ecount(cchGuid) LPWSTR szGuid, // String into which the GUID is stored
+ DWORD cchGuid) // Count in wchars
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+
+ // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD
+ // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.
+
+ if (cchGuid < 39) // 38 chars + 1 null terminating.
+ return 0;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ szGuid[0] = W('{');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^
+ if (FAILED (GetStr(Guid.Data1, szGuid+1 , 4))) return 0;
+
+ szGuid[9] = W('-');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetStr(Guid.Data2, szGuid+10, 2))) return 0;
+
+ szGuid[14] = W('-');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetStr(Guid.Data3, szGuid+15, 2))) return 0;
+
+ szGuid[19] = W('-');
+
+ // Get the last two fields (which are byte arrays).
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ for (i=0; i < 2; ++i)
+ if (FAILED(GetStr(Guid.Data4[i], szGuid + 20 + (i * 2), 1)))
+ return (0);
+
+ szGuid[24] = W('-');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^^^^^
+ for (i=0; i < 6; ++i)
+ if (FAILED(GetStr(Guid.Data4[i+2], szGuid + 25 + (i * 2), 1)))
+ return (0);
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ szGuid[37] = W('}');
+ szGuid[38] = W('\0');
+
+ return 39;
+} // GuidToLPWSTR
+
+//*****************************************************************************
+// Convert wide string of (at most eight) hex digits into a hex value
+//*****************************************************************************
+HRESULT GetHex(
+ DWORD * phHexNum,
+ __in_ecount((cbHexNum * 2)) LPCWSTR szHexNum,
+ DWORD cbHexNum)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE (szHexNum && phHexNum);
+ _ASSERTE(cbHexNum == 1 || cbHexNum == 2 || cbHexNum == 4);
+
+ cbHexNum *= 2; // each nibble is a char
+ DWORD val = 0;
+ for (DWORD i = 0; i < cbHexNum; ++i)
+ {
+ DWORD nibble = 0;
+ if (szHexNum[i] >= W('0') && szHexNum[i] <= W('9'))
+ {
+ nibble = szHexNum[i] - '0';
+ }
+ else if (szHexNum[i] >= W('A') && szHexNum[i] <= W('F'))
+ {
+ nibble = 10 + szHexNum[i] - 'A';
+ }
+ else if (szHexNum[i] >= W('a') && szHexNum[i] <= W('f'))
+ {
+ nibble = 10 + szHexNum[i] - 'a';
+ }
+ else
+ {
+ return E_FAIL;
+ }
+ val = (val << 4) + nibble;
+ }
+ *phHexNum = val;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Parse a Wide char string into a GUID
+//*****************************************************************************
+BOOL
+LPWSTRToGuid(
+ GUID * Guid, // [OUT] The GUID to fill in
+ __in_ecount(cchGuid) LPCWSTR szGuid, // [IN] String to parse
+ DWORD cchGuid) // [IN] Count in wchars in string
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+ DWORD dw;
+
+ // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD
+ // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.
+
+ if (cchGuid < 38) // 38 chars + 1 null terminating.
+ return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ if (szGuid[0] != W('{')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^
+ if (FAILED (GetHex(&dw, szGuid+1 , 4))) return FALSE;
+ Guid->Data1 = dw;
+
+ if (szGuid[9] != W('-')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetHex(&dw, szGuid+10, 2))) return FALSE;
+ Guid->Data2 = (WORD)dw;
+
+ if (szGuid[14] != W('-')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetHex(&dw, szGuid+15, 2))) return FALSE;
+ Guid->Data3 = (WORD)dw;
+
+ if (szGuid[19] != W('-')) return FALSE;
+
+ // Get the last two fields (which are byte arrays).
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ for (i=0; i < 2; ++i)
+ {
+ if (FAILED(GetHex(&dw, szGuid + 20 + (i * 2), 1))) return FALSE;
+ Guid->Data4[i] = (BYTE)dw;
+ }
+
+ if (szGuid[24] != W('-')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^^^^^
+ for (i=0; i < 6; ++i)
+ {
+ if (FAILED(GetHex(&dw, szGuid + 25 + (i * 2), 1))) return FALSE;
+ Guid->Data4[i+2] = (BYTE)dw;
+ }
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ if (szGuid[37] != W('}')) return FALSE;
+
+ return TRUE;
+} // GuidToLPWSTR
+
+
+#ifdef _DEBUG
+// Always write regardless of registry.
+void _cdecl DbgWriteEx(LPCTSTR szFmt, ...)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcBuff[1024];
+ va_list marker;
+
+ va_start(marker, szFmt);
+ _vsnwprintf_s(rcBuff, _countof(rcBuff), _TRUNCATE, szFmt, marker);
+ va_end(marker);
+ WszOutputDebugString(rcBuff);
+}
+#endif //_DEBUG
+
+/**************************************************************************/
+void ConfigDWORD::init(const CLRConfig::ConfigDWORDInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ m_value = CLRConfig::GetConfigValue(info);
+ m_inited = 1;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Takes a const input string, and returns the start & size of the substring that has all
+// leading and trailing whitespace removed. The original string is not modified.
+//
+// Arguments:
+// * pwsz - [in] points to const string we want to trim; [out] points to beginning
+// of trimmed substring of input string
+// * pcch - [in] Points to length in chars of input string (not counting null
+// terminator); [out] Points to length in chars of trimmed substring (not
+// counting null terminator)
+//
+void TrimWhiteSpace(__deref_inout_ecount(*pcch) LPCWSTR *pwsz, __inout LPDWORD pcch)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ _ASSERTE (pwsz != NULL);
+ _ASSERTE (*pwsz != NULL);
+ _ASSERTE (pcch != NULL);
+
+ DWORD cch = *pcch;
+ LPCWSTR wszBeginning = *pwsz;
+ LPCWSTR wszEnd = wszBeginning + (cch - 1);
+
+ while ((cch != 0) && iswspace(*wszBeginning))
+ {
+ wszBeginning++;
+ cch--;
+ }
+
+ while ((cch != 0) && iswspace(*wszEnd))
+ {
+ wszEnd--;
+ cch--;
+ }
+
+ *pwsz = wszBeginning;
+ *pcch = cch;
+} // TrimWhiteSpace
+
+BOOL ThreadWillCreateGuardPage(SIZE_T sizeReservedStack, SIZE_T sizeCommitedStack)
+{
+ // We need to make sure there will be a reserved but never committed page at the end
+ // of the stack. We do here the check NT does when it creates the user stack to decide
+ // if there is going to be a guard page. However, that is not enough, as if we only
+ // have a guard page, we have nothing to protect us from going pass it. Well, in
+ // fact, there is something that we will protect you, there are certain places
+ // (RTLUnwind) in NT that will check that the current frame is within stack limits.
+ // If we are not it will bomb out. We will also bomb out if we touch the hard guard
+ // page.
+ //
+ // For situation B, teb->StackLimit is at the beggining of the user stack (ie
+ // before updating StackLimit it checks if it was able to create a new guard page,
+ // in this case, it can't), which makes the check fail in RtlUnwind.
+ //
+ // Situation A [ Hard guard page | Guard page | user stack]
+ //
+ // Situation B [ Guard page | User stack ]
+ //
+ // Situation C [ User stack ( no room for guard page) ]
+ //
+ // Situation D (W9x) : Guard page or not, w9x has a 64k reserved region below
+ // the stack, we don't need any checks at all
+ //
+ // We really want to be in situation A all the time, so we add one more page
+ // to our requirements (we require guard page + hard guard)
+
+ SYSTEM_INFO sysInfo;
+ ::GetSystemInfo(&sysInfo);
+
+ // OS rounds up sizes the following way to decide if it marks a guard page
+ sizeReservedStack = ALIGN(sizeReservedStack, ((size_t)sysInfo.dwAllocationGranularity)); // Allocation granularity
+ sizeCommitedStack = ALIGN(sizeCommitedStack, ((size_t)sysInfo.dwPageSize)); // Page Size
+
+ // OS wont create guard page, we can't execute managed code safely.
+ // We also have to make sure we have a 'hard' guard, thus we add another
+ // page to the memory we would need comitted.
+ // That is, the following code will check if sizeReservedStack is at least 2 pages
+ // more than sizeCommitedStack.
+ return (sizeReservedStack > sizeCommitedStack + ((size_t)sysInfo.dwPageSize));
+} // ThreadWillCreateGuardPage
+
+//The following characters have special sorting weights when combined with other
+//characters, which means we can't use our fast sorting algorithm on them.
+//Most of these are pretty rare control characters, but apostrophe and hyphen
+//are fairly common and force us down the slower path. This is because we want
+//"word sorting", which means that "coop" and "co-op" sort together, instead of
+//separately as they would if we were doing a string sort.
+// 0x0001 6 3 2 2 0 ;Start Of Heading
+// 0x0002 6 4 2 2 0 ;Start Of Text
+// 0x0003 6 5 2 2 0 ;End Of Text
+// 0x0004 6 6 2 2 0 ;End Of Transmission
+// 0x0005 6 7 2 2 0 ;Enquiry
+// 0x0006 6 8 2 2 0 ;Acknowledge
+// 0x0007 6 9 2 2 0 ;Bell
+// 0x0008 6 10 2 2 0 ;Backspace
+
+// 0x000e 6 11 2 2 0 ;Shift Out
+// 0x000f 6 12 2 2 0 ;Shift In
+// 0x0010 6 13 2 2 0 ;Data Link Escape
+// 0x0011 6 14 2 2 0 ;Device Control One
+// 0x0012 6 15 2 2 0 ;Device Control Two
+// 0x0013 6 16 2 2 0 ;Device Control Three
+// 0x0014 6 17 2 2 0 ;Device Control Four
+// 0x0015 6 18 2 2 0 ;Negative Acknowledge
+// 0x0016 6 19 2 2 0 ;Synchronous Idle
+// 0x0017 6 20 2 2 0 ;End Of Transmission Block
+// 0x0018 6 21 2 2 0 ;Cancel
+// 0x0019 6 22 2 2 0 ;End Of Medium
+// 0x001a 6 23 2 2 0 ;Substitute
+// 0x001b 6 24 2 2 0 ;Escape
+// 0x001c 6 25 2 2 0 ;File Separator
+// 0x001d 6 26 2 2 0 ;Group Separator
+// 0x001e 6 27 2 2 0 ;Record Separator
+// 0x001f 6 28 2 2 0 ;Unit Separator
+
+// 0x0027 6 128 2 2 0 ;Apostrophe-Quote
+// 0x002d 6 130 2 2 0 ;Hyphen-Minus
+
+// 0x007f 6 29 2 2 0 ;Delete
+
+const BYTE
+HighCharHelper::HighCharTable[]= {
+ TRUE, /* 0x0, 0x0 */
+ TRUE, /* 0x1, .*/
+ TRUE, /* 0x2, .*/
+ TRUE, /* 0x3, .*/
+ TRUE, /* 0x4, .*/
+ TRUE, /* 0x5, .*/
+ TRUE, /* 0x6, .*/
+ TRUE, /* 0x7, .*/
+ TRUE, /* 0x8, .*/
+ FALSE, /* 0x9, */
+ FALSE, /* 0xA, */
+ FALSE, /* 0xB, .*/
+ FALSE, /* 0xC, .*/
+ FALSE, /* 0xD, */
+ TRUE, /* 0xE, .*/
+ TRUE, /* 0xF, .*/
+ TRUE, /* 0x10, .*/
+ TRUE, /* 0x11, .*/
+ TRUE, /* 0x12, .*/
+ TRUE, /* 0x13, .*/
+ TRUE, /* 0x14, .*/
+ TRUE, /* 0x15, .*/
+ TRUE, /* 0x16, .*/
+ TRUE, /* 0x17, .*/
+ TRUE, /* 0x18, .*/
+ TRUE, /* 0x19, .*/
+ TRUE, /* 0x1A, */
+ TRUE, /* 0x1B, .*/
+ TRUE, /* 0x1C, .*/
+ TRUE, /* 0x1D, .*/
+ TRUE, /* 0x1E, .*/
+ TRUE, /* 0x1F, .*/
+ FALSE, /*0x20, */
+ FALSE, /*0x21, !*/
+ FALSE, /*0x22, "*/
+ FALSE, /*0x23, #*/
+ FALSE, /*0x24, $*/
+ FALSE, /*0x25, %*/
+ FALSE, /*0x26, &*/
+ TRUE, /*0x27, '*/
+ FALSE, /*0x28, (*/
+ FALSE, /*0x29, )*/
+ FALSE, /*0x2A **/
+ FALSE, /*0x2B, +*/
+ FALSE, /*0x2C, ,*/
+ TRUE, /*0x2D, -*/
+ FALSE, /*0x2E, .*/
+ FALSE, /*0x2F, /*/
+ FALSE, /*0x30, 0*/
+ FALSE, /*0x31, 1*/
+ FALSE, /*0x32, 2*/
+ FALSE, /*0x33, 3*/
+ FALSE, /*0x34, 4*/
+ FALSE, /*0x35, 5*/
+ FALSE, /*0x36, 6*/
+ FALSE, /*0x37, 7*/
+ FALSE, /*0x38, 8*/
+ FALSE, /*0x39, 9*/
+ FALSE, /*0x3A, :*/
+ FALSE, /*0x3B, ;*/
+ FALSE, /*0x3C, <*/
+ FALSE, /*0x3D, =*/
+ FALSE, /*0x3E, >*/
+ FALSE, /*0x3F, ?*/
+ FALSE, /*0x40, @*/
+ FALSE, /*0x41, A*/
+ FALSE, /*0x42, B*/
+ FALSE, /*0x43, C*/
+ FALSE, /*0x44, D*/
+ FALSE, /*0x45, E*/
+ FALSE, /*0x46, F*/
+ FALSE, /*0x47, G*/
+ FALSE, /*0x48, H*/
+ FALSE, /*0x49, I*/
+ FALSE, /*0x4A, J*/
+ FALSE, /*0x4B, K*/
+ FALSE, /*0x4C, L*/
+ FALSE, /*0x4D, M*/
+ FALSE, /*0x4E, N*/
+ FALSE, /*0x4F, O*/
+ FALSE, /*0x50, P*/
+ FALSE, /*0x51, Q*/
+ FALSE, /*0x52, R*/
+ FALSE, /*0x53, S*/
+ FALSE, /*0x54, T*/
+ FALSE, /*0x55, U*/
+ FALSE, /*0x56, V*/
+ FALSE, /*0x57, W*/
+ FALSE, /*0x58, X*/
+ FALSE, /*0x59, Y*/
+ FALSE, /*0x5A, Z*/
+ FALSE, /*0x5B, [*/
+ FALSE, /*0x5C, \*/
+ FALSE, /*0x5D, ]*/
+ FALSE, /*0x5E, ^*/
+ FALSE, /*0x5F, _*/
+ FALSE, /*0x60, `*/
+ FALSE, /*0x61, a*/
+ FALSE, /*0x62, b*/
+ FALSE, /*0x63, c*/
+ FALSE, /*0x64, d*/
+ FALSE, /*0x65, e*/
+ FALSE, /*0x66, f*/
+ FALSE, /*0x67, g*/
+ FALSE, /*0x68, h*/
+ FALSE, /*0x69, i*/
+ FALSE, /*0x6A, j*/
+ FALSE, /*0x6B, k*/
+ FALSE, /*0x6C, l*/
+ FALSE, /*0x6D, m*/
+ FALSE, /*0x6E, n*/
+ FALSE, /*0x6F, o*/
+ FALSE, /*0x70, p*/
+ FALSE, /*0x71, q*/
+ FALSE, /*0x72, r*/
+ FALSE, /*0x73, s*/
+ FALSE, /*0x74, t*/
+ FALSE, /*0x75, u*/
+ FALSE, /*0x76, v*/
+ FALSE, /*0x77, w*/
+ FALSE, /*0x78, x*/
+ FALSE, /*0x79, y*/
+ FALSE, /*0x7A, z*/
+ FALSE, /*0x7B, {*/
+ FALSE, /*0x7C, |*/
+ FALSE, /*0x7D, }*/
+ FALSE, /*0x7E, ~*/
+ TRUE, /*0x7F, */
+}; // HighCharHelper::HighCharTable
diff --git a/src/utilcode/utilcode.settings.targets b/src/utilcode/utilcode.settings.targets
new file mode 100644
index 0000000000..03db64c642
--- /dev/null
+++ b/src/utilcode/utilcode.settings.targets
@@ -0,0 +1,128 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <!--OK to delete NO_NTDLL for devdiv builds.-->
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <!--PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders-->
+ <!--PCH: Both precompiled header and cpp are on the same ..\ path this is likely to be wrong.-->
+ <UtilCodeSrcDir>$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode</UtilCodeSrcDir>
+ <!--PCHCompile>$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\Utilcode\stdafx.cpp</PCHCompile>
+ <PCHObject>stdafx_utilcode.obj</PCHObject-->
+ <ClAdditionalOptions>$(ClAdditionalOptions) -DUNICODE -D_UNICODE $(_CRTIMPFLAGS) -DNO_CRT</ClAdditionalOptions>
+ <ClAdditionalOptions Condition="'FeatureUtilcodeNoDependencies' != 'true'">$(ClAdditionalOptions) -DENABLE_PERF_COUNTERS</ClAdditionalOptions>
+ <UserIncludes>
+ $(UserIncludes);
+ $(VCToolsIncPath);
+ $(_NTDRIVE)$(_NTROOT)\ndp\clr\src\strongname\inc
+ </UserIncludes>
+ </PropertyGroup>
+
+ <!-- Dependencies -->
+ <ItemGroup>
+ <ProjectReference Include="$(ClrSrcDirectory)inc\corguids.nativeproj">
+ <Comment>clrinternal.h</Comment>
+ </ProjectReference>
+ </ItemGroup>
+
+ <!-- Sources common for 'NoDependencies' and 'normal' build -->
+ <ItemGroup>
+ <CppCompile Include="$(UtilCodeSrcDir)\ClrHost_NoDependencies.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\Ex.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\SBuffer.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SString.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SString_COM.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\FString.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\NamespaceUtil.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\MakePath.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SplitPath.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\ClrConfig.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\RegUtil.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\RegistryWrapper.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\Collections.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\Util_NoDependencies.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\GenericStackProbe.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\PostError.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\fstream.cpp" />
+
+ <!-- File format and MetaData -->
+ <CppCompile Include="$(UtilCodeSrcDir)\ClrHelpers.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\StgPool.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\StgPooli.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\StgPoolReadOnly.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\PEDecoder.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\LazyCOW.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\UTSem.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\peinformation.cpp" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\Check.cpp" />
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\Debug.cpp" />
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\Log.cpp" />
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\UtilMessageBox.cpp" />
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\StackTrace.cpp" />
+
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\SafeWrap.cpp" />
+ <DebuggingSupportSources Include="$(UtilCodeSrcDir)\WinFix.cpp" />
+ </ItemGroup>
+
+ <!-- Sources for 'normal' build (can depend on mscoree.dll and other DLLs) -->
+ <ItemGroup Condition="'$(FeatureUtilcodeNoDependencies)' != 'true'">
+ <CppCompile Include="@(DebuggingSupportSources)" />
+ <CppCompile Include="$(UtilCodeSrcDir)\ApiThreadStress.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\ArrayList.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\AssemblyFileHash.cpp" Condition="'$(FeatureCoreClr)' != 'true'"/>
+ <CppCompile Include="$(UtilCodeSrcDir)\BitVector.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\ccomprc.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\CircularLog.cpp" Condition="'$(FeatureCoreClr)' != 'true'"/>
+ <CppCompile Include="$(UtilCodeSrcDir)\ClrHost.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\COMex.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\CycleTimer.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\DelayLoadHelpers.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\GuidFromName.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\jitperf.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\md5.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\MemoryPool.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\Util.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\RangeTree.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\IAllocator.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\LoaderHeap.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\PerfLog.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\OutString.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\ilFormatter.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\OpInfo.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\DacUtil.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\TlbUtils.cpp" Condition="'$(FeatureCominteropTlbSupport)' == 'true'"/>
+ <CppCompile Include="$(UtilCodeSrcDir)\SortVersioning.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\NewApis.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\DownLevel.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\StressLog.cpp" />
+
+ <!-- File format and MetaData -->
+ <CppCompile Include="$(UtilCodeSrcDir)\CorImage.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\format1.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\PrettyPrintSig.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SigBuilder.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SigParser.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\dlwrap.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\LoadRC.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SxSHelpers.cpp" Condition="'$(FeatureCoreClr)' != 'true'"/>
+ <CppCompile Include="$(UtilCodeSrcDir)\TLS.cpp" Condition="'$(FeatureImplicitTls)' != 'true'"/>
+ <CppCompile Include="$(UtilCodeSrcDir)\SecurityWrapper.cpp" />
+ <CppCompile Include="$(UtilCodeSrcDir)\SecurityUtil.cpp" />
+
+ <CppCompile Include="$(UtilCodeSrcDir)\AppXUtil.cpp" Condition="'$(FeatureAppX)' == 'true'"/>
+ </ItemGroup>
+</Project>
diff --git a/src/utilcode/utilmessagebox.cpp b/src/utilcode/utilmessagebox.cpp
new file mode 100644
index 0000000000..737e89b15f
--- /dev/null
+++ b/src/utilcode/utilmessagebox.cpp
@@ -0,0 +1,599 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// UtilMessageBox.cpp
+//
+
+//
+// This module contains the message box utility code for the CLR. It is used
+// by code in the CLR itself as well as other tools that build in the CLR tree.
+// For message boxes inside the ExecutionEngine, EEMessageBox must be used
+// instead of the these APIs.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard header.
+#include <utilcode.h> // Utility helpers.
+#include <corerror.h>
+#include "ndpversion.h"
+#include "../dlls/mscorrc/resource.h"
+#include "ex.h"
+#if !defined(FEATURE_CORESYSTEM)
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_WIN7
+#include "commctrl.h"
+#endif
+
+#if !defined(SELF_NO_HOST) && !defined(FEATURE_CORECLR)
+
+//
+// This should be used for runtime dialog box, because we assume the resource is from mscorrc.dll
+// For tools like ildasm or Shim which uses their own resource file, you need to define IDS_RTL in
+// their resource file and define a function like this and append the style returned from the function
+// to every calls to WszMessageBox.
+//
+UINT GetCLRMBRTLStyle()
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT mbStyle = 0;
+ WCHAR buff[MAX_PATH];
+ if(SUCCEEDED(UtilLoadStringRC(IDS_RTL, buff, MAX_PATH, true))) {
+ if(wcscmp(buff, W("RTL_True")) == 0) {
+ mbStyle = 0x00080000 |0x00100000; // MB_RIGHT || MB_RTLREADING
+ }
+ }
+ return mbStyle;
+}
+
+#endif //!defined(SELF_NO_HOST) && !defined(FEATURE_CORECLR)
+
+BOOL ShouldDisplayMsgBoxOnCriticalFailure()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ // To help find issues, we will always display dialogs for critical failures
+ // under debug builds. This includes asserts and other critical issues.
+ return TRUE;
+#else
+ // Retrieve error mode
+ UINT last = SetErrorMode(0);
+ SetErrorMode(last); //set back to previous value
+
+ // SEM_FAILCRITICALERRORS indicates that the system does not display the critical-error-handler
+ // message box. Instead, the system sends the error to the calling process.
+ return !(last & SEM_FAILCRITICALERRORS);
+#endif // _DEBUG
+}
+
+
+#if !defined(FEATURE_CORESYSTEM) && !defined(FEATURE_CORECLR)
+enum ProbedTaskDialogIndirectState
+{
+ ProbedTaskDialogIndirectState_NotProbed = 0,
+ ProbedTaskDialogIndirectState_NotAvailable = 1,
+ ProbedTaskDialogIndirectState_Available = 2
+};
+
+static ProbedTaskDialogIndirectState siProbedTaskDialogIndirect = ProbedTaskDialogIndirectState_NotProbed;
+#endif // !FEATURE_CORESYSTEM && !FEATURE_CORECLR
+
+
+// We'd like to use TaskDialogIndirect for asserts coming from managed code in particular
+// to display the detailedText in a scrollable way. Also, we'd like to reuse the CLR's
+// plumbing code for the rest of parts of the assert dialog. Note that the simple
+// Win32 MessageBox does not support the detailedText value.
+// If we later refactor MessageBoxImpl into its own DLL, move the lines referencing
+// "Microsoft.Windows.Common-Controls" version 6 in stdafx.h as well.
+int MessageBoxImpl(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR message, // Message
+ LPCWSTR title, // Dialog box title
+ LPCWSTR detailedText, // Details like a stack trace, etc.
+ UINT uType)
+{
+ CONTRACTL
+ {
+ // May pump messages. Callers should be GC_TRIGGERS and MODE_PREEMPTIVE,
+ // but we can't include EE contracts here.
+ THROWS;
+ INJECT_FAULT(return IDCANCEL;);
+
+ // Assert if none of MB_ICON is set
+ PRECONDITION((uType & MB_ICONMASK) != 0);
+ }
+ CONTRACTL_END;
+
+#if defined(FEATURE_CORESYSTEM) || defined (FEATURE_CORECLR)
+ return WszMessageBox(hWnd, message, title, uType);
+#else
+ bool mustUseMessageBox = false; // Mac, Silverlight, pre-Vista? Do we support this type of message box?
+ decltype(TaskDialogIndirect)* pfnTaskDialogIndirect = NULL;
+ ULONG_PTR cookie = NULL; // For activation context.
+ bool activatedActivationContext = false;
+ HModuleHolder hmodComctl32;
+ HANDLE hActCtx = INVALID_HANDLE_VALUE;
+
+ // Note: TaskDialogIndirect is only in the v6 and above versions of comctl32. Windows
+ // stores that library in the WinSxS directory in a directory with
+ // "Microsoft.Windows.Common-Controls" in the name. Your application can only see
+ // this library if the linker has added a manifest dependency on the V6 common controls
+ // to your application. Or, you can create an activation context to make this work,
+ // if your library also has the appropriate manifest dependency.
+ // Also, I'm not going to leave comctl32.dll mapped, to ensure it can't somehow
+ // interfere with older versions. Therefore, re-load comctl32.dll every time through
+ // this method. We will record whether TaskDialogIndirect is available though, so
+ // we can fall back to MessageBox faster.
+
+ // We don't yet have a perfect mapping from all MessageBox behavior to TaskDialogIndirect behavior.
+ // Use MessageBox to avoid most of this complexity.
+ if (((uType & MB_ICONMASK) != MB_ICONWARNING) && (uType & MB_ICONMASK) != MB_ICONERROR ||
+ (uType & MB_TYPEMASK) != MB_ABORTRETRYIGNORE ||
+ (uType & MB_DEFMASK) != 0 ||
+ (uType & MB_MODEMASK) != 0 ||
+ (uType & MB_MISCMASK) != 0)
+ mustUseMessageBox = true;
+ else if (mustUseMessageBox || siProbedTaskDialogIndirect == ProbedTaskDialogIndirectState_NotAvailable)
+ mustUseMessageBox = true;
+ else {
+ // Replace our application's ActivationContext temporarily, load comctl32
+ // & look for TaskDialogIndirect. Don't cache pointer.
+ // The following code was suggested by some Windows experts. We do not want
+ // to add a manifest to our library saying we use comctl32 v6, because that
+ // will mean loading a lot of extra libraries on startup (a significant perf hit).
+ // We could either store the manifest as a resource, or more creatively since
+ // we are effectively a Windows component, rely on %windir%\WindowsShell.manifest.
+ ACTCTX ctx = { sizeof(ACTCTX) };
+ ctx.dwFlags = 0;
+ StackSString manifestPath; // Point this at %windir%\WindowsShell.manifest, for comctl32 version 6.
+ UINT numChars = WszGetWindowsDirectory(manifestPath.OpenUnicodeBuffer(MAX_PATH), MAX_PATH);
+ if (numChars == 0 || numChars >= MAX_PATH)
+ {
+ _ASSERTE(0); // How did this fail?
+ }
+ else {
+ manifestPath.CloseBuffer(numChars);
+ if (manifestPath[manifestPath.GetCount() - 1] != W('\\'))
+ manifestPath.Append(W('\\'));
+ manifestPath.Append(W("WindowsShell.manifest")); // Other Windows components have already loaded this.
+ ctx.lpSource = manifestPath.GetUnicode();
+ hActCtx = CreateActCtx(&ctx);
+ if (hActCtx != INVALID_HANDLE_VALUE)
+ {
+ if (!ActivateActCtx(hActCtx, &cookie))
+ {
+ cookie = NULL;
+ _ASSERTE(0); // Why did ActivateActCtx fail? (We'll continue executing & cope with the failure.)
+ }
+ else {
+ activatedActivationContext = true;
+ // Activation context was replaced - now we can load comctl32 version 6.
+ hmodComctl32 = WszLoadLibrary(W("comctl32.dll"));
+
+ if (hmodComctl32 != INVALID_HANDLE_VALUE) {
+ pfnTaskDialogIndirect = (decltype(TaskDialogIndirect)*)GetProcAddress(hmodComctl32, "TaskDialogIndirect");
+ if (pfnTaskDialogIndirect == NULL) {
+ hmodComctl32.Release();
+ }
+ }
+ }
+ }
+ }
+
+ siProbedTaskDialogIndirect = (pfnTaskDialogIndirect == NULL) ? ProbedTaskDialogIndirectState_NotAvailable : ProbedTaskDialogIndirectState_Available;
+ mustUseMessageBox = (pfnTaskDialogIndirect == NULL);
+ }
+
+ int result;
+ if (mustUseMessageBox) {
+ result = WszMessageBox(hWnd, message, title, uType);
+ }
+ else {
+ _ASSERTE(pfnTaskDialogIndirect != NULL);
+ int nButtonPressed = 0;
+ TASKDIALOGCONFIG config = {0};
+ config.cbSize = sizeof(config);
+ config.hwndParent = hWnd;
+ config.dwCommonButtons = 0;
+ config.pszWindowTitle = title;
+ config.dwFlags = (uType & MB_RTLREADING) ? TDF_RTL_LAYOUT : 0;
+
+ // Set the user-visible icon in the window.
+ _ASSERTE(((uType & MB_ICONMASK) == MB_ICONWARNING) || ((uType & MB_ICONMASK) == MB_ICONERROR));
+ config.pszMainIcon = ((uType & MB_ICONMASK) == MB_ICONWARNING) ? TD_WARNING_ICON : TD_ERROR_ICON;
+
+ config.pszMainInstruction = title;
+ config.pszContent = message;
+ config.pszExpandedInformation = detailedText;
+
+ // Set up the buttons
+ // Note about button hot keys: Windows keeps track of of where the last input came from
+ // (ie, mouse or keyboard). If you use the mouse to interact w/ one dialog box and then use
+ // the keyboard, the next dialog will not include hot keys. This is a Windows feature to
+ // minimize clutter on the screen for mouse users.
+ _ASSERTE((uType & MB_TYPEMASK) == MB_ABORTRETRYIGNORE);
+ StackSString abortLabel, debugLabel, ignoreLabel;
+ const WCHAR *pAbortLabel, *pDebugLabel, *pIgnoreLabel;
+
+ if (abortLabel.LoadResource(CCompRC::Optional, IDS_DIALOG_BOX_ABORT_BUTTON))
+ pAbortLabel = abortLabel.GetUnicode();
+ else
+ pAbortLabel = W("&Abort");
+ if (debugLabel.LoadResource(CCompRC::Optional, IDS_DIALOG_BOX_DEBUG_BUTTON))
+ pDebugLabel = debugLabel.GetUnicode();
+ else
+ pDebugLabel = W("&Debug");
+ if (ignoreLabel.LoadResource(CCompRC::Optional, IDS_DIALOG_BOX_IGNORE_BUTTON))
+ pIgnoreLabel = ignoreLabel.GetUnicode();
+ else
+ pIgnoreLabel = W("&Ignore");
+
+ const TASKDIALOG_BUTTON abortDebugIgnoreButtons[] = {
+ { IDOK, pAbortLabel },
+ { IDRETRY, pDebugLabel },
+ { IDIGNORE, pIgnoreLabel }
+ };
+ config.pButtons = abortDebugIgnoreButtons;
+ config.cButtons = 3;
+
+ HRESULT hr = pfnTaskDialogIndirect(&config, &nButtonPressed, NULL, NULL);
+ _ASSERTE(hr == S_OK);
+ if (hr == S_OK) {
+ result = nButtonPressed;
+ }
+
+ _ASSERTE(result == IDOK || result == IDRETRY || result == IDIGNORE);
+ }
+
+ if (activatedActivationContext) {
+ DeactivateActCtx(0, cookie);
+ ReleaseActCtx(hActCtx); // perf isn’t important so we won’t bother caching the actctx
+ }
+
+ return result;
+#endif
+}
+
+int UtilMessageBoxVA(
+ HWND hWnd, // Handle to Owner Window
+ UINT uText, // Resource Identifier for Text message
+ UINT uTitle, // Resource Identifier for Title
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+ }
+ CONTRACTL_END;
+
+ SString text;
+ SString title;
+ int result = IDCANCEL;
+
+ EX_TRY
+ {
+ text.LoadResource(CCompRC::Error, uText);
+ title.LoadResource(CCompRC::Error, uTitle);
+
+ result = UtilMessageBoxNonLocalizedVA(hWnd, (LPWSTR)text.GetUnicode(),
+ (LPWSTR)title.GetUnicode(), uType, displayForNonInteractive, showFileNameInTitle, NULL, args);
+ }
+ EX_CATCH
+ {
+ result = IDCANCEL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+int UtilMessageBoxNonLocalizedVA(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpTitle, // Title
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort.
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+
+ // Assert if none of MB_ICON is set
+ PRECONDITION((uType & MB_ICONMASK) != 0);
+ }
+ CONTRACTL_END;
+
+ return UtilMessageBoxNonLocalizedVA(hWnd, lpText, lpTitle, NULL, uType, displayForNonInteractive, showFileNameInTitle, pInputFromUser, args);
+}
+
+int UtilMessageBoxNonLocalizedVA(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpTitle, // Title
+ LPCWSTR lpDetails,// Details like a stack trace, etc.
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort.
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+
+ // Assert if none of MB_ICON is set
+ PRECONDITION((uType & MB_ICONMASK) != 0);
+ }
+ CONTRACTL_END;
+
+ int result = IDCANCEL;
+ if (pInputFromUser != NULL)
+ {
+ *pInputFromUser = FALSE;
+ }
+
+ EX_TRY
+ {
+ StackSString formattedMessage;
+ StackSString formattedTitle;
+ SString details(lpDetails);
+ StackSString fileName;
+ BOOL fDisplayMsgBox = TRUE;
+
+ // Format message string using optional parameters
+ formattedMessage.VPrintf(lpText, args);
+
+ // Try to get filename of Module and add it to title
+ if (showFileNameInTitle && WszGetModuleFileName(NULL, fileName.OpenUnicodeBuffer(MAX_PATH), MAX_PATH))
+ {
+ LPCWSTR wszName = NULL;
+ size_t cchName = 0;
+
+ // Close the buffer we opened before the call to WszGetModuleFileName.
+ fileName.CloseBuffer();
+
+ SplitPathInterior(fileName, NULL, NULL, NULL, NULL, &wszName, &cchName, NULL, NULL);
+ formattedTitle.Printf(W("%s - %s"), wszName, lpTitle);
+ }
+ else
+ {
+ formattedTitle.Set(lpTitle);
+ }
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+ // If the current process isn't interactive (a service for example), then we report the message
+ // in the event log and via OutputDebugString.
+ //
+ // We may still however attempt to display the message box if the MB_SERVICE_NOTIFICATION
+ // message box style was specified.
+ if (!RunningInteractive())
+ {
+ HANDLE h;
+ StackSString message;
+
+ message.Printf(W(".NET Runtime version : %s - "), VER_FILEVERSION_STR_L);
+ if (lpTitle)
+ message.Append(lpTitle);
+ if (!formattedMessage.IsEmpty())
+ message.Append(formattedMessage);
+
+ ClrReportEvent(W(".NET Runtime"),
+ EVENTLOG_ERROR_TYPE, // event type
+ 0, // category zero
+ 1024, // event identifier
+ NULL, // no user security identifier
+ message.GetUnicode());
+
+ if(lpTitle != NULL)
+ WszOutputDebugString(lpTitle);
+ if(!formattedMessage.IsEmpty())
+ WszOutputDebugString(formattedMessage);
+
+ // If we are running as a service and displayForNonInteractive is FALSE then IDABORT is
+ // the best value to return as it will most likely cause callers of this API to abort the process.
+ // This is the right thing to do since attaching a debugger doesn't make much sense when the process isn't
+ // running in interactive mode.
+ if(!displayForNonInteractive)
+ {
+ fDisplayMsgBox = FALSE;
+ result = IDABORT;
+ }
+ else
+ {
+ // Include in the MB_DEFAULT_DESKTOP_ONLY style.
+ uType |= MB_DEFAULT_DESKTOP_ONLY;
+ }
+ }
+#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+
+ if (fDisplayMsgBox)
+ {
+ // We normally want to set the reading direction (right-to-left etc.) based on the resources
+ // in use. However, outside the CLR (SELF_NO_HOST) we can't assume we have resources and
+ // in CORECLR we can't even necessarily expect that our CLR callbacks have been initialized.
+ // This code path is used for ASSERT dialogs.
+#if !defined(SELF_NO_HOST) && !defined(FEATURE_CORECLR)
+ uType |= GetCLRMBRTLStyle();
+#endif
+
+ result = MessageBoxImpl(hWnd, formattedMessage, formattedTitle, details, uType);
+
+ if (pInputFromUser != NULL)
+ {
+ *pInputFromUser = TRUE;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ result = IDCANCEL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+int UtilMessageBox(
+ HWND hWnd, // Handle to Owner Window
+ UINT uText, // Resource Identifier for Text message
+ UINT uTitle, // Resource Identifier for Title
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ...) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxVA(hWnd, uText, uTitle, uType, displayForNonInteractive, showFileNameInTitle, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxNonLocalized(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpTitle, // Title message
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ... ) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxNonLocalizedVA(
+ hWnd, lpText, lpTitle, uType, displayForNonInteractive, showFileNameInTitle, NULL, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxCatastrophic(
+ UINT uText, // Text for MessageBox
+ UINT uTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ...)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxCatastrophicVA(uText, uTitle, uType, showFileNameInTitle, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxCatastrophicNonLocalized(
+ LPCWSTR lpText, // Text for MessageBox
+ LPCWSTR lpTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ...)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxCatastrophicNonLocalizedVA(lpText, lpTitle, uType, showFileNameInTitle, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxCatastrophicVA(
+ UINT uText, // Text for MessageBox
+ UINT uTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HWND hwnd = NULL;
+
+ // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going.
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
+
+ if (!ShouldDisplayMsgBoxOnCriticalFailure())
+ return IDABORT;
+
+ // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
+ // owned by the current thread and should prevent interaction with them until dismissed.
+ uType |= MB_TASKMODAL;
+
+ return UtilMessageBoxVA(hwnd, uText, uTitle, uType, TRUE, showFileNameInTitle, args);
+}
+
+int UtilMessageBoxCatastrophicNonLocalizedVA(
+ LPCWSTR lpText, // Text for MessageBox
+ LPCWSTR lpTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HWND hwnd = NULL;
+
+ // We are already in a catastrophic situation so we can tolerate faults as well as SO & GC mode violations to keep going.
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation | SOToleranceViolation);
+
+ if (!ShouldDisplayMsgBoxOnCriticalFailure())
+ return IDABORT;
+
+ // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
+ // owned by the current thread and should prevent interaction with them until dismissed.
+ uType |= MB_TASKMODAL;
+
+ return UtilMessageBoxNonLocalizedVA(hwnd, lpText, lpTitle, uType, TRUE, showFileNameInTitle, NULL, args);
+}
+
diff --git a/src/utilcode/utsem.cpp b/src/utilcode/utsem.cpp
new file mode 100644
index 0000000000..0d3125068d
--- /dev/null
+++ b/src/utilcode/utsem.cpp
@@ -0,0 +1,553 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+/******************************************************************************
+ FILE : UTSEM.CPP
+
+
+
+ Purpose: Part of the utilities library for the VIPER project
+
+ Abstract : Implements the UTSemReadWrite class.
+-------------------------------------------------------------------------------
+Revision History:
+
+
+*******************************************************************************/
+#include "stdafx.h"
+#include "clrhost.h"
+#include "ex.h"
+
+#include <utsem.h>
+#include "contract.h"
+
+// Consider replacing this with a #ifdef INTEROP_DEBUGGING
+#if !defined(SELF_NO_HOST) && defined(_TARGET_X86_)
+// For Interop debugging, the UTSemReadWrite class must inform the debugger
+// that this thread can't be suspended currently. See vm\util.hpp for the
+// implementation of these methods.
+void IncCantStopCount();
+void DecCantStopCount();
+#else
+#define IncCantStopCount()
+#define DecCantStopCount()
+#endif // !SELF_NO_HOST && _TARGET_X86_
+
+/******************************************************************************
+Definitions of the bit fields in UTSemReadWrite::m_dwFlag:
+
+Warning: The code assume that READER_MASK is in the low-order bits of the DWORD.
+******************************************************************************/
+
+const ULONG READERS_MASK = 0x000003FF; // field that counts number of readers
+const ULONG READERS_INCR = 0x00000001; // amount to add to increment number of readers
+
+// The following field is 2 bits long to make it easier to catch errors.
+// (If the number of writers ever exceeds 1, we've got problems.)
+const ULONG WRITERS_MASK = 0x00000C00; // field that counts number of writers
+const ULONG WRITERS_INCR = 0x00000400; // amount to add to increment number of writers
+
+const ULONG READWAITERS_MASK = 0x003FF000; // field that counts number of threads waiting to read
+const ULONG READWAITERS_INCR = 0x00001000; // amount to add to increment number of read waiters
+
+const ULONG WRITEWAITERS_MASK = 0xFFC00000; // field that counts number of threads waiting to write
+const ULONG WRITEWAITERS_INCR = 0x00400000; // amoun to add to increment number of write waiters
+
+// ======================================================================================
+// Spinning support
+
+// Copy of definition from file:..\VM\spinlock.h
+#define CALLER_LIMITS_SPINNING 0
+
+#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
+
+// When we do not have host, we just call OS - see file:..\VM\hosting.cpp#__SwitchToThread
+BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount)
+{
+ // This is just simple implementation that does not support full dwSwitchCount arg
+ _ASSERTE(dwSwitchCount == CALLER_LIMITS_SPINNING);
+ return SwitchToThread();
+}
+
+Volatile<BOOL> g_fInitializedGlobalSystemInfo = FALSE;
+
+// Global System Information
+SYSTEM_INFO g_SystemInfo;
+
+// Configurable constants used across our spin locks
+SpinConstants g_SpinConstants = {
+ 50, // dwInitialDuration
+ 40000, // dwMaximumDuration - ideally (20000 * max(2, numProc)) ... updated in code:InitializeSpinConstants_NoHost
+ 3, // dwBackoffFactor
+ 10 // dwRepetitions
+};
+
+inline void InitializeSpinConstants_NoHost()
+{
+ g_SpinConstants.dwMaximumDuration = max(2, g_SystemInfo.dwNumberOfProcessors) * 20000;
+}
+
+#else //!SELF_NO_HOST || CROSSGEN_COMPILE
+
+// Use VM/CrossGen functions and variables
+BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount);
+extern SYSTEM_INFO g_SystemInfo;
+extern SpinConstants g_SpinConstants;
+
+#endif //!SELF_NO_HOST || CROSSGEN_COMPILE
+
+/******************************************************************************
+Function : UTSemReadWrite::UTSemReadWrite
+
+Abstract: Constructor.
+******************************************************************************/
+UTSemReadWrite::UTSemReadWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
+ if (!g_fInitializedGlobalSystemInfo)
+ {
+ GetSystemInfo(&g_SystemInfo);
+ InitializeSpinConstants_NoHost();
+
+ g_fInitializedGlobalSystemInfo = TRUE;
+ }
+#endif //SELF_NO_HOST && !CROSSGEN_COMPILE
+
+ m_dwFlag = 0;
+ m_pReadWaiterSemaphore = NULL;
+ m_pWriteWaiterEvent = NULL;
+}
+
+
+/******************************************************************************
+Function : UTSemReadWrite::~UTSemReadWrite
+
+Abstract: Destructor
+******************************************************************************/
+UTSemReadWrite::~UTSemReadWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE_MSG((m_dwFlag == (ULONG)0), "Destroying a UTSemReadWrite while in use");
+
+ if (m_pReadWaiterSemaphore != NULL)
+ delete m_pReadWaiterSemaphore;
+
+ if (m_pWriteWaiterEvent != NULL)
+ delete m_pWriteWaiterEvent;
+}
+
+//=======================================================================================
+//
+// Initialize the lock (its semaphore and event)
+//
+HRESULT
+UTSemReadWrite::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ _ASSERTE(m_pReadWaiterSemaphore == NULL);
+ _ASSERTE(m_pWriteWaiterEvent == NULL);
+
+ EX_TRY
+ {
+ CONTRACT_VIOLATION(ThrowsViolation);
+
+ m_pReadWaiterSemaphore = new Semaphore();
+ m_pReadWaiterSemaphore->Create(0, MAXLONG);
+
+ m_pWriteWaiterEvent = new Event();
+ m_pWriteWaiterEvent->CreateAutoEvent(FALSE);
+ }
+ EX_CATCH
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ IfFailGo(hr);
+
+ErrExit:
+ return hr;
+} // UTSemReadWrite::Init
+
+/******************************************************************************
+Function : UTSemReadWrite::LockRead
+
+Abstract: Obtain a shared lock
+******************************************************************************/
+HRESULT UTSemReadWrite::LockRead()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ // Inform CLR that the debugger shouldn't suspend this thread while
+ // holding this lock.
+ IncCantStopCount();
+
+ // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
+ for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
+ {
+ DWORD i = g_SpinConstants.dwInitialDuration;
+
+ do
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag < READERS_MASK)
+ { // There are just readers in the play, try to add one more
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
+ {
+ goto ReadLockAcquired;
+ }
+ }
+
+ if (g_SystemInfo.dwNumberOfProcessors <= 1)
+ { // We do not need to spin on a single processor
+ break;
+ }
+
+ // Delay by approximately 2*i clock cycles (Pentium III).
+ // This is brittle code - future processors may of course execute this
+ // faster or slower, and future code generators may eliminate the loop altogether.
+ // The precise value of the delay is not critical, however, and I can't think
+ // of a better way that isn't machine-dependent.
+ int sum = 0;
+
+ for (int delayCount = i; --delayCount; )
+ {
+ sum += delayCount;
+ YieldProcessor(); // indicate to the processor that we are spining
+ }
+
+ if (sum == 0)
+ {
+ // never executed, just to fool the compiler into thinking sum is live here,
+ // so that it won't optimize away the loop.
+ static char dummy;
+ dummy++;
+ }
+ // exponential backoff: wait a factor longer in the next iteration
+ i *= g_SpinConstants.dwBackoffFactor;
+ } while (i < g_SpinConstants.dwMaximumDuration);
+
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+ // Stop spinning
+
+ // Start waiting
+ for (;;)
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag < READERS_MASK)
+ { // There are just readers in the play, try to add one more
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+ else if ((dwFlag & READERS_MASK) == READERS_MASK)
+ { // The number of readers has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else if ((dwFlag & READWAITERS_MASK) == READWAITERS_MASK)
+ { // The number of readers waiting on semaphore has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else
+ { // Try to add waiting reader and then wait for signal
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READWAITERS_INCR, dwFlag))
+ {
+ m_pReadWaiterSemaphore->Wait(INFINITE, FALSE);
+ break;
+ }
+ }
+ }
+
+ReadLockAcquired:
+ _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero after acquiring read lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero after acquiring write lock");
+ EE_LOCK_TAKEN(this);
+
+ return S_OK;
+} // UTSemReadWrite::LockRead
+
+
+
+/******************************************************************************
+Function : UTSemReadWrite::LockWrite
+
+Abstract: Obtain an exclusive lock
+******************************************************************************/
+HRESULT UTSemReadWrite::LockWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ // Inform CLR that the debugger shouldn't suspend this thread while
+ // holding this lock.
+ IncCantStopCount();
+
+ // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
+ for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
+ {
+ DWORD i = g_SpinConstants.dwInitialDuration;
+
+ do
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag == 0)
+ { // No readers/writers in play, try to add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
+ {
+ goto WriteLockAcquired;
+ }
+ }
+
+ if (g_SystemInfo.dwNumberOfProcessors <= 1)
+ { // We do not need to spin on a single processor
+ break;
+ }
+
+ // Delay by approximately 2*i clock cycles (Pentium III).
+ // This is brittle code - future processors may of course execute this
+ // faster or slower, and future code generators may eliminate the loop altogether.
+ // The precise value of the delay is not critical, however, and I can't think
+ // of a better way that isn't machine-dependent.
+ int sum = 0;
+
+ for (int delayCount = i; --delayCount; )
+ {
+ sum += delayCount;
+ YieldProcessor(); // indicate to the processor that we are spining
+ }
+
+ if (sum == 0)
+ {
+ // never executed, just to fool the compiler into thinking sum is live here,
+ // so that it won't optimize away the loop.
+ static char dummy;
+ dummy++;
+ }
+ // exponential backoff: wait a factor longer in the next iteration
+ i *= g_SpinConstants.dwBackoffFactor;
+ } while (i < g_SpinConstants.dwMaximumDuration);
+
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+ // Stop spinning
+
+ // Start waiting
+ for (;;)
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag == 0)
+ { // No readers/writers in play, try to add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+ else if ((dwFlag & WRITEWAITERS_MASK) == WRITEWAITERS_MASK)
+ { // The number of writers waiting on semaphore has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else
+ { // Try to add waiting writer and then wait for signal
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + WRITEWAITERS_INCR, dwFlag))
+ {
+ m_pWriteWaiterEvent->Wait(INFINITE, FALSE);
+ break;
+ }
+ }
+
+ }
+
+WriteLockAcquired:
+ _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero after acquiring write lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 after acquiring write lock");
+ EE_LOCK_TAKEN(this);
+
+ return S_OK;
+} // UTSemReadWrite::LockWrite
+
+
+
+/******************************************************************************
+Function : UTSemReadWrite::UnlockRead
+
+Abstract: Release a shared lock
+******************************************************************************/
+void UTSemReadWrite::UnlockRead()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ULONG dwFlag;
+
+
+ _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero before releasing read lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero before releasing read lock");
+
+ for (;;)
+ {
+ dwFlag = m_dwFlag;
+
+ if (dwFlag == READERS_INCR)
+ { // we're the last reader, and nobody is waiting
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else if ((dwFlag & READERS_MASK) > READERS_INCR)
+ { // we're not the last reader
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - READERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else
+ {
+ // here, there should be exactly 1 reader (us), and at least one waiting writer.
+ _ASSERTE ((dwFlag & READERS_MASK) == READERS_INCR && "UnlockRead consistency error 1");
+ _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockRead consistency error 2");
+
+ // one or more writers is waiting, do one of them next
+ // (remove a reader (us), remove a write waiter, add a writer
+ if (dwFlag ==
+ InterlockedCompareExchangeT(
+ &m_dwFlag,
+ dwFlag - READERS_INCR - WRITEWAITERS_INCR + WRITERS_INCR,
+ dwFlag))
+ {
+ m_pWriteWaiterEvent->Set();
+ break;
+ }
+ }
+ }
+
+ DecCantStopCount();
+ EE_LOCK_RELEASED(this);
+} // UTSemReadWrite::UnlockRead
+
+
+/******************************************************************************
+Function : UTSemReadWrite::UnlockWrite
+
+Abstract: Release an exclusive lock
+******************************************************************************/
+void UTSemReadWrite::UnlockWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ULONG dwFlag;
+ ULONG count;
+
+ _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero before releasing write lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 before releasing write lock");
+
+ for (;;)
+ {
+ dwFlag = m_dwFlag;
+
+ if (dwFlag == WRITERS_INCR)
+ { // nobody is waiting
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else if ((dwFlag & READWAITERS_MASK) != 0)
+ { // one or more readers are waiting, do them all next
+ count = (dwFlag & READWAITERS_MASK) / READWAITERS_INCR;
+ // remove a writer (us), remove all read waiters, turn them into readers
+ if (dwFlag ==
+ InterlockedCompareExchangeT(
+ &m_dwFlag,
+ dwFlag - WRITERS_INCR - count * READWAITERS_INCR + count * READERS_INCR,
+ dwFlag))
+ {
+ m_pReadWaiterSemaphore->Release(count, NULL);
+ break;
+ }
+ }
+
+ else
+ { // one or more writers is waiting, do one of them next
+ _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockWrite consistency error");
+ // (remove a writer (us), remove a write waiter, add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - WRITEWAITERS_INCR, dwFlag))
+ {
+ m_pWriteWaiterEvent->Set();
+ break;
+ }
+ }
+ }
+
+ DecCantStopCount();
+ EE_LOCK_RELEASED(this);
+} // UTSemReadWrite::UnlockWrite
+
+#ifdef _DEBUG
+
+//=======================================================================================
+BOOL
+UTSemReadWrite::Debug_IsLockedForRead()
+{
+ return ((m_dwFlag & READERS_MASK) != 0);
+}
+
+//=======================================================================================
+BOOL
+UTSemReadWrite::Debug_IsLockedForWrite()
+{
+ return ((m_dwFlag & WRITERS_MASK) != 0);
+}
+
+#endif //_DEBUG
+
diff --git a/src/utilcode/winfix.cpp b/src/utilcode/winfix.cpp
new file mode 100644
index 0000000000..00c6392ec7
--- /dev/null
+++ b/src/utilcode/winfix.cpp
@@ -0,0 +1,515 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// WinWrap.cpp
+//
+
+//
+// This file contains wrapper functions for Win32 API's that take strings.
+//
+// COM+ internally uses UNICODE as the internal state and string format. This
+// file will undef the mapping macros so that one cannot mistakingly call a
+// method that isn't going to work. Instead, you have to call the correct
+// wrapper API.
+//
+//*****************************************************************************
+
+#include "stdafx.h" // Precompiled header key.
+#include "winwrap.h" // Header for macros and functions.
+#include "utilcode.h"
+#include "holder.h"
+#include "ndpversion.h"
+#include "pedecoder.h"
+
+// ====== READ BEFORE ADDING CONTRACTS ==================================================
+// The functions in this file propagate SetLastError codes to their callers.
+// Contracts are not guaranteed to preserve these codes (and no, we're not taking
+// the overhead hit to make them do so. Don't bother asking.)
+//
+// Most of the wrappers have a contract of the form:
+//
+// NOTHROW;
+// INJECT_FAULT(xxx);
+//
+// For such functions, use the special purpose construct:
+//
+// WINWRAPPER_NO_CONTRACT(xxx);
+//
+// For everything else, use STATIC_CONTRACT.
+//
+#undef CONTRACT
+#define CONTRACT $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$
+
+#undef CONTRACTL
+#define CONTRACTL $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$
+
+#ifdef ENABLE_CONTRACTS_IMPL
+static BOOL gWinWrapperContractRecursionBreak = FALSE;
+
+
+class WinWrapperContract
+{
+ public:
+ WinWrapperContract(char *szFunction, char *szFile, int lineNum)
+ {
+ CANNOT_HAVE_CONTRACT;
+
+ m_pClrDebugState = NULL;
+
+ if (gWinWrapperContractRecursionBreak)
+ {
+ return;
+ }
+
+ m_pClrDebugState = GetClrDebugState();
+
+ // Save old debug state
+ m_IncomingClrDebugState = *m_pClrDebugState;
+
+
+ m_pClrDebugState->ViolationMaskReset( ThrowsViolation );
+
+ if (m_pClrDebugState->IsFaultForbid() && !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState)))
+ {
+ gWinWrapperContractRecursionBreak = TRUE;
+
+ CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.",
+ Contract::FAULT_Forbid,
+ Contract::FAULT_Mask,
+ szFunction,
+ szFile,
+ lineNum
+ );
+ }
+
+
+ };
+
+ ~WinWrapperContract()
+ {
+ CANNOT_HAVE_CONTRACT;
+
+ //!!!!!! THIS DESTRUCTOR MUST NOT CHANGE THE GETLASTERROR VALUE !!!!!!
+
+ // Backout all changes to debug state.
+ if (m_pClrDebugState != NULL)
+ {
+ *m_pClrDebugState = m_IncomingClrDebugState;
+ }
+ }
+ private:
+ ClrDebugState *m_pClrDebugState;
+ ClrDebugState m_IncomingClrDebugState;
+
+};
+
+
+
+#endif
+
+#ifdef ENABLE_CONTRACTS_IMPL
+#define WINWRAPPER_NO_CONTRACT(stmt) \
+ STATIC_CONTRACT_NOTHROW; \
+ STATIC_CONTRACT_FAULT; \
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
+ WinWrapperContract __wcontract(__FUNCTION__, __FILE__, __LINE__); \
+ if (0) {stmt} \
+
+#define STATIC_WINWRAPPER_NO_CONTRACT(stmt) \
+ STATIC_CONTRACT_NOTHROW; \
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
+ STATIC_CONTRACT_FAULT; \
+ if (0) {stmt} \
+
+
+#else
+#define WINWRAPPER_NO_CONTRACT(stmt)
+#define STATIC_WINWRAPPER_NO_CONTRACT(stmt)
+#endif
+
+ULONG g_dwMaxDBCSCharByteSize = 0;
+
+#include "psapi.h"
+#include "tlhelp32.h"
+#include "winnls.h"
+
+//********** Globals. *********************************************************
+bool g_fEnsureCharSetInfoInitialized = FALSE; // true if we've detected the platform's character set characteristics
+
+// Detect Unicode support of the operating system, and initialize globals
+void EnsureCharSetInfoInitialized()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ if (!g_fEnsureCharSetInfoInitialized)
+ {
+ // NOTE: Do not use any of the Wsz* wrapper functions right now. They will have
+ // problems.
+
+ // Per Shupak, you're supposed to get the maximum size of a DBCS char
+ // dynamically to work properly on all locales (bug 2757).
+ CPINFO cpInfo;
+ if (GetCPInfo(CP_ACP, &cpInfo))
+ g_dwMaxDBCSCharByteSize = cpInfo.MaxCharSize;
+ else
+ g_dwMaxDBCSCharByteSize = 2;
+
+ VolatileStore(&g_fEnsureCharSetInfoInitialized, true);
+ }
+
+ return;
+}
+
+
+// Running with an interactive workstation.
+BOOL RunningInteractive()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ static int fInteractive = -1;
+ if (fInteractive != -1)
+ return fInteractive != 0;
+
+#if !defined(FEATURE_CORESYSTEM)
+ HWINSTA hwinsta = NULL;
+
+ if ((hwinsta = GetProcessWindowStation() ) != NULL)
+ {
+ DWORD lengthNeeded;
+ USEROBJECTFLAGS flags;
+
+ if (GetUserObjectInformationW (hwinsta, UOI_FLAGS, &flags, sizeof(flags), &lengthNeeded))
+ {
+ if ((flags.dwFlags & WSF_VISIBLE) == 0)
+ fInteractive = 0;
+ }
+ }
+#endif // !FEATURE_CORESYSTEM
+
+ if (fInteractive != 0)
+ fInteractive = 1;
+
+ return fInteractive != 0;
+}
+
+
+// Wrapper function around CheckTokenMembership to determine if the token enables the SID "S-1-5-<rid>".
+// If hToken is NULL, this function uses the thread's impersonation token. If the thread is not impersonating, the
+// process token is used.
+//
+// If the function succeeds, it returns ERROR_SUCCESS, else it returns the error code returned by GetLastError()
+static DWORD TokenEnablesSID(IN HANDLE hToken OPTIONAL, IN DWORD rid, OUT BOOL& fResult)
+{
+ DWORD dwError;
+ SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
+ PSID pSid = NULL;
+ HMODULE hAdvApi32 = NULL;
+ typedef BOOL (WINAPI *CheckTokenMembership_t)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember);
+ CheckTokenMembership_t pfnCheckTokenMembership = NULL;
+
+ hAdvApi32 = WszGetModuleHandle(W("advapi32.dll"));
+ if (hAdvApi32 == NULL)
+ {
+ dwError = ERROR_MOD_NOT_FOUND;
+ goto lExit;
+ }
+ pfnCheckTokenMembership = (CheckTokenMembership_t) GetProcAddress(hAdvApi32, "CheckTokenMembership");
+ if (pfnCheckTokenMembership == NULL)
+ {
+ dwError = GetLastError();
+ goto lExit;
+ }
+
+ fResult = FALSE;
+ if (!AllocateAndInitializeSid(&SIDAuthNT, 1, rid, 0, 0, 0, 0, 0, 0, 0, &pSid))
+ {
+ dwError = GetLastError();
+ goto lExit;
+ }
+ if (!pfnCheckTokenMembership(hToken, pSid, &fResult))
+ {
+ dwError = GetLastError();
+ goto lExit;
+ }
+ dwError = ERROR_SUCCESS;
+
+lExit:
+ if (pSid) FreeSid(pSid);
+ return dwError;
+
+}
+
+// Determines if the process is running as Local System or as a service. Note that
+// the function attempts to determine the process' identity and not the thread's
+// (if the thread is impersonating).
+//
+// Parameters:
+// fIsLocalSystemOrService - TRUE if the function succeeds and the process is
+// running as SYSTEM or as a service
+//
+// Return value:
+//
+// If the function succeeds, it returns ERROR_SUCCESS, else it returns the error
+// code returned by GetLastError()
+//
+// Notes:
+//
+// This function will generally fail if the calling thread is impersonating at the
+// ANONYMOUS level; see the comments in the function.
+//
+DWORD RunningAsLocalSystemOrService(OUT BOOL& fIsLocalSystemOrService)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ static int fLocalSystemOrService = -1;
+ if (fLocalSystemOrService != -1)
+ {
+ fIsLocalSystemOrService = fLocalSystemOrService != 0;
+ return ERROR_SUCCESS;
+ }
+
+ DWORD dwError;
+ HANDLE hThreadToken = NULL;
+ HANDLE hProcessToken = NULL;
+ HANDLE hDuplicatedProcessToken = NULL;
+ BOOL fLocalSystem = FALSE;
+ BOOL fService = FALSE;
+ BOOL bReverted = FALSE;
+
+ if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hThreadToken))
+ {
+ if (RevertToSelf())
+ {
+ bReverted = TRUE;
+ }
+#ifdef _DEBUG
+ else
+ {
+ // For debugging only, continue as the impersonated user; see comment below
+ dwError = GetLastError();
+ }
+#endif // #ifdef _DEBUG
+ }
+#ifdef _DEBUG
+ else
+ {
+ dwError = GetLastError();
+ if (dwError == ERROR_NO_IMPERSONATION_TOKEN || dwError == ERROR_NO_TOKEN)
+ {
+ // The thread is not impersonating; it's safe to continue
+ }
+ else
+ {
+ // The thread could be impersonating, but we won't be able to restore the impersonation
+ // token if we RevertToSelf(). Continue as the impersonated user. OpenProcessToken will
+ // fail (unless the impersonated user is SYSTEM or the same as the process' user).
+ //
+ // Note that this case will occur if the impersonation level is ANONYMOUS, the error
+ // code will be ERROR_CANT_OPEN_ANONYMOUS.
+ }
+ }
+#endif // #ifdef _DEBUG
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hProcessToken))
+ {
+ dwError = GetLastError();
+ goto lExit;
+ }
+
+ if (!DuplicateToken(hProcessToken, SecurityImpersonation, &hDuplicatedProcessToken))
+ {
+ dwError = GetLastError();
+ goto lExit;
+ }
+
+ dwError = TokenEnablesSID(hDuplicatedProcessToken, SECURITY_LOCAL_SYSTEM_RID, fLocalSystem);
+ if (dwError != ERROR_SUCCESS)
+ {
+ goto lExit;
+ }
+ if (fLocalSystem)
+ {
+ goto lExit;
+ }
+
+ dwError = TokenEnablesSID(hDuplicatedProcessToken, SECURITY_SERVICE_RID, fService);
+
+lExit:
+ if (bReverted)
+ {
+ if (!SetThreadToken(NULL, hThreadToken))
+ {
+ DWORD dwLastError = GetLastError();
+ _ASSERT("SetThreadToken failed");
+
+ TerminateProcess(GetCurrentProcess(), dwLastError);
+ }
+ }
+
+ if (hThreadToken) CloseHandle(hThreadToken);
+ if (hProcessToken) CloseHandle(hProcessToken);
+ if (hDuplicatedProcessToken) CloseHandle(hDuplicatedProcessToken);
+
+ if (dwError != ERROR_SUCCESS)
+ {
+ fIsLocalSystemOrService = FALSE; // We don't really know
+ }
+ else
+ {
+ fLocalSystemOrService = (fLocalSystem || fService)? 1 : 0;
+ fIsLocalSystemOrService = fLocalSystemOrService != 0;
+ }
+
+ return dwError;
+
+}
+
+BOOL
+WszCreateProcess(
+ LPCWSTR lpApplicationName,
+ LPCWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation
+ )
+{
+ WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;);
+
+ BOOL fResult;
+ DWORD err;
+ {
+ size_t commandLineLength = wcslen(lpCommandLine) + 1;
+ NewArrayHolder<WCHAR> nonConstCommandLine(new (nothrow) WCHAR[commandLineLength]);
+ if (nonConstCommandLine == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return 0;
+ }
+
+ memcpy(nonConstCommandLine, lpCommandLine, commandLineLength * sizeof(WCHAR));
+
+ fResult = CreateProcessW(lpApplicationName,
+ nonConstCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ (LPWSTR)lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation);
+
+ // At the end of the current scope, the last error code will be overwritten by the destructor of
+ // NewArrayHolder. So we save the error code here, and restore it after the end of the current scope.
+ err = GetLastError();
+ }
+
+ SetLastError(err);
+ return fResult;
+}
+
+
+DWORD
+WszGetWorkingSet()
+{
+ WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;);
+
+ DWORD dwMemUsage = 0;
+
+ // Consider also calling GetProcessWorkingSetSize to get the min & max working
+ // set size. I don't know how to get the current working set though...
+ PROCESS_MEMORY_COUNTERS pmc;
+
+ HINSTANCE hPSapi;
+ typedef BOOL (GET_PROCESS_MEMORY_INFO)(HANDLE, PROCESS_MEMORY_COUNTERS*, DWORD);
+ GET_PROCESS_MEMORY_INFO* pGetProcessMemoryInfo;
+
+ hPSapi = WszLoadLibrary(W("psapi.dll"));
+ if (hPSapi == NULL) {
+ _ASSERTE(0);
+ return 0;
+ }
+
+ pGetProcessMemoryInfo =
+ (GET_PROCESS_MEMORY_INFO*)GetProcAddress(hPSapi, "GetProcessMemoryInfo");
+ // 403746: Prefix correctly complained about
+ // pGetProcessMemoryInfo != NULL assertion.
+ if (pGetProcessMemoryInfo == NULL) {
+ _ASSERTE(0);
+ FreeLibrary(hPSapi);
+ return 0;
+ }
+ PREFIX_ASSUME(pGetProcessMemoryInfo != NULL);
+
+ BOOL r = pGetProcessMemoryInfo(GetCurrentProcess(), &pmc, (DWORD) sizeof(PROCESS_MEMORY_COUNTERS));
+ FreeLibrary(hPSapi);
+ _ASSERTE(r);
+
+ dwMemUsage = (DWORD)pmc.WorkingSetSize;
+
+ return dwMemUsage;
+}
+
+
+SIZE_T
+WszGetPagefileUsage()
+{
+ WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;);
+
+ SIZE_T dwPagefileUsage = 0;
+
+ typedef BOOL (WINAPI FnGetProcessMemoryInfo)(HANDLE, PROCESS_MEMORY_COUNTERS *, DWORD);
+
+ HMODULE hPSApi = WszLoadLibrary(W("Psapi.dll"));
+ if (hPSApi== NULL)
+ {
+ return 0;
+ }
+
+ FnGetProcessMemoryInfo *pfnGetProcessMemoryInfo = reinterpret_cast<FnGetProcessMemoryInfo *>(
+ GetProcAddress(hPSApi, "GetProcessMemoryInfo"));
+
+ if (pfnGetProcessMemoryInfo != NULL)
+ {
+ PROCESS_MEMORY_COUNTERS pmc;
+ ZeroMemory(&pmc, sizeof(pmc));
+
+ if (pfnGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
+ {
+ dwPagefileUsage = pmc.PagefileUsage;
+ }
+ }
+
+ FreeLibrary(hPSApi);
+ hPSApi = NULL;
+
+ return dwPagefileUsage;
+}
+
+DWORD
+WszGetProcessHandleCount()
+{
+ WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;);
+
+ DWORD dwHandleCount = 0;
+
+ if (!GetProcessHandleCount(GetCurrentProcess(), &dwHandleCount))
+ {
+ dwHandleCount = 0;
+ }
+
+ return dwHandleCount;
+}
+