├── .gitattributes ├── .gitignore ├── Demo ├── Class1.cs ├── Demo.csproj └── Properties │ └── AssemblyInfo.cs ├── DemoAssembly ├── App.config ├── Class1.cs ├── DemoAssembly.csproj └── Properties │ └── AssemblyInfo.cs ├── DotNetReflectiveLoading.sln ├── DotNetReflectiveLoading ├── DotNetReflectiveLoading.vcxproj ├── DotNetReflectiveLoading.vcxproj.filters ├── clr.cpp ├── clr.hpp ├── main.cpp └── utils.hpp ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /Demo/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Demo 7 | { 8 | public class DemoClass 9 | { 10 | public DemoClass() {} 11 | 12 | public static void Board(string msg) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Demo/Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {AE2A3AE2-F933-4D8A-94E7-9E9CD18C3B33} 9 | Library 10 | Properties 11 | Demo 12 | Demo 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile5 17 | v4.0 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 51 | -------------------------------------------------------------------------------- /Demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("Demo")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("Demo")] 14 | [assembly: AssemblyCopyright("Copyright © 2017")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | -------------------------------------------------------------------------------- /DemoAssembly/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /DemoAssembly/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Forms; 6 | using System.Runtime.InteropServices; 7 | #if NET_v4 8 | using System.Runtime.ExceptionServices; 9 | #endif 10 | using System.Runtime.CompilerServices; 11 | 12 | 13 | [assembly:RuntimeCompatibility(WrapNonExceptionThrows = true)] 14 | namespace DemoAssembly 15 | { 16 | 17 | public class Demo 18 | { 19 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 20 | private delegate void Fptr([MarshalAs(UnmanagedType.LPWStr)]string val); 21 | 22 | [UnmanagedFunctionPointer(CallingConvention.ThisCall)] 23 | [return: MarshalAs(UnmanagedType.I4)] 24 | private delegate int ClsPtr(IntPtr tptr); 25 | 26 | [UnmanagedFunctionPointer(CallingConvention.ThisCall)] 27 | [return: MarshalAs(UnmanagedType.I4)] 28 | private delegate int ClsPtr2(IntPtr tptr, [MarshalAs(UnmanagedType.LPWStr)]string val); 29 | 30 | private IntPtr is_this_; 31 | 32 | public Demo() 33 | { 34 | } 35 | 36 | public static void Popup(string msg) 37 | { 38 | MessageBox.Show(msg); 39 | } 40 | 41 | public void Callback(Int64 ptr) 42 | { 43 | IntPtr p = (IntPtr)ptr; 44 | Fptr cb = (Fptr)Marshal.GetDelegateForFunctionPointer(p, typeof(Fptr)); 45 | 46 | cb("Made it here!"); 47 | 48 | } 49 | 50 | public string GetString() 51 | { 52 | return "This is what we return."; 53 | } 54 | 55 | 56 | #if NET_v4 57 | [HandleProcessCorruptedStateExceptions] 58 | #endif 59 | public void ExceptionCallback(Int64 ptr) 60 | { 61 | IntPtr p = (IntPtr)ptr; 62 | Fptr ecb = (Fptr)Marshal.GetDelegateForFunctionPointer(p, typeof(Fptr)); 63 | try 64 | { 65 | ecb("This will segfault!"); 66 | } 67 | catch(Exception e) 68 | { 69 | MessageBox.Show("Handled!" + e); 70 | } 71 | } 72 | 73 | 74 | public bool AddClass(Int64 tptr) 75 | { 76 | is_this_ = (IntPtr)tptr; 77 | return true; 78 | } 79 | 80 | public void InvokeMethod(Int64 fptr) 81 | { 82 | ClsPtr p = (ClsPtr)Marshal.GetDelegateForFunctionPointer((IntPtr)fptr, typeof(ClsPtr)); 83 | int res = p(is_this_); 84 | 85 | MessageBox.Show("Value: " + res); 86 | } 87 | 88 | public void InvokeExcepted(Int64 fptr) 89 | { 90 | ClsPtr2 p = (ClsPtr2)Marshal.GetDelegateForFunctionPointer((IntPtr)fptr, typeof(ClsPtr2)); 91 | try 92 | { 93 | int res = p(is_this_, "Here we go..."); 94 | } 95 | catch(Exception e) 96 | { 97 | MessageBox.Show("Handled CPP exception!"); 98 | } 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /DemoAssembly/DemoAssembly.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2B18471E-FCBF-418B-8FF5-A86F1103F036} 8 | Library 9 | Properties 10 | DemoAssembly 11 | DemoAssembly 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 64 | 65 | 66 | $(DefineConstants);NET_v4; 67 | 68 | 69 | -------------------------------------------------------------------------------- /DemoAssembly/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DemoAssembly")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DemoAssembly")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2b18471e-fcbf-418b-8ff5-a86f1103f036")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DotNetReflectiveLoading.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DotNetReflectiveLoading", "DotNetReflectiveLoading\DotNetReflectiveLoading.vcxproj", "{FCE55E19-374D-4CEC-AC96-694D3642F29E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoAssembly", "DemoAssembly\DemoAssembly.csproj", "{2B18471E-FCBF-418B-8FF5-A86F1103F036}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Debug|Any CPU.ActiveCfg = Debug|Win32 21 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Debug|x64.ActiveCfg = Debug|x64 22 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Debug|x64.Build.0 = Debug|x64 23 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Debug|x86.ActiveCfg = Debug|Win32 24 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Debug|x86.Build.0 = Debug|Win32 25 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Release|Any CPU.ActiveCfg = Release|Win32 26 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Release|x64.ActiveCfg = Release|x64 27 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Release|x64.Build.0 = Release|x64 28 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Release|x86.ActiveCfg = Release|Win32 29 | {FCE55E19-374D-4CEC-AC96-694D3642F29E}.Release|x86.Build.0 = Release|Win32 30 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Debug|x64.ActiveCfg = Debug|Any CPU 33 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Debug|x64.Build.0 = Debug|Any CPU 34 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Debug|x86.ActiveCfg = Debug|Any CPU 35 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Debug|x86.Build.0 = Debug|Any CPU 36 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Release|x64.ActiveCfg = Release|Any CPU 39 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Release|x64.Build.0 = Release|Any CPU 40 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Release|x86.ActiveCfg = Release|Any CPU 41 | {2B18471E-FCBF-418B-8FF5-A86F1103F036}.Release|x86.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /DotNetReflectiveLoading/DotNetReflectiveLoading.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {FCE55E19-374D-4CEC-AC96-694D3642F29E} 23 | DotNetReflectiveLoading 24 | 8.1 25 | 26 | 27 | 28 | Application 29 | true 30 | v140 31 | MultiByte 32 | 33 | 34 | Application 35 | false 36 | v140 37 | true 38 | MultiByte 39 | 40 | 41 | Application 42 | true 43 | v140 44 | MultiByte 45 | 46 | 47 | Application 48 | false 49 | v140 50 | true 51 | MultiByte 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Level3 75 | Disabled 76 | true 77 | 78 | 79 | mscoree.lib;%(AdditionalDependencies) 80 | 81 | 82 | 83 | 84 | Level3 85 | Disabled 86 | true 87 | 88 | 89 | mscoree.lib;%(AdditionalDependencies) 90 | 91 | 92 | 93 | 94 | Level3 95 | MaxSpeed 96 | true 97 | true 98 | true 99 | 100 | 101 | true 102 | true 103 | mscoree.lib;%(AdditionalDependencies) 104 | 105 | 106 | 107 | 108 | Level3 109 | MaxSpeed 110 | true 111 | true 112 | true 113 | 114 | 115 | true 116 | true 117 | mscoree.lib;%(AdditionalDependencies) 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /DotNetReflectiveLoading/DotNetReflectiveLoading.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /DotNetReflectiveLoading/clr.cpp: -------------------------------------------------------------------------------- 1 | #include "clr.hpp" 2 | #include "utils.hpp" 3 | #include 4 | 5 | namespace clr { 6 | ClrAssembly::ClrAssembly(mscorlib::_AssemblyPtr p) : p_(p) 7 | { 8 | } 9 | 10 | mscorlib::_TypePtr ClrAssembly::find_type(const std::wstring& clsname) 11 | { 12 | mscorlib::_TypePtr pClsType = nullptr; 13 | mscorlib::_TypePtr* pTypes = nullptr; 14 | BSTR pName = L""; 15 | HRESULT hr = S_OK; 16 | bool found = false; 17 | SAFEARRAY* pArray = nullptr; 18 | long lower_bound = 0; 19 | long upper_bound = 0; 20 | 21 | if (FAILED((hr = p_->GetTypes(&pArray)))) { 22 | LOG_ERROR("Failed to get types!", hr); 23 | return false; 24 | } 25 | SafeArrayGetLBound(pArray, 1, &lower_bound); 26 | SafeArrayGetUBound(pArray, 1, &upper_bound); 27 | SafeArrayAccessData(pArray, (void**)&pTypes); 28 | auto elem_count = upper_bound - lower_bound + 1; 29 | for (auto i = 0; i < elem_count; ++i) { 30 | pClsType = pTypes[i]; 31 | if (FAILED((hr = pClsType->get_FullName(&pName)))) { 32 | LOG_ERROR("Failed to query for name!", hr); 33 | break; 34 | } 35 | 36 | if (pName == clsname) { 37 | found = true; 38 | break; 39 | } 40 | } 41 | SafeArrayUnaccessData(pArray); 42 | if (!found) 43 | return nullptr; 44 | 45 | return pClsType; 46 | 47 | } 48 | 49 | std::unique_ptr ClrAssembly::construct(const std::wstring & classname) 50 | { 51 | std::unique_ptr cls; 52 | HRESULT hr = S_OK; 53 | bool found = false; 54 | mscorlib::_TypePtr pClsType = nullptr; 55 | bstr_t pName(classname.c_str()); 56 | variant_t var; 57 | 58 | if (FAILED((hr = p_->CreateInstance(pName, &var)))) { 59 | LOG_ERROR("Failed to create class instance!", hr); 60 | return nullptr; 61 | } 62 | 63 | pClsType = find_type(classname); 64 | if (pClsType == nullptr) { 65 | LOG("Failed to find class!"); 66 | return nullptr; 67 | } 68 | 69 | cls = std::make_unique(pClsType, var); 70 | return cls; 71 | } 72 | 73 | 74 | std::wstring ClrDomain::find_runtime() 75 | { 76 | HRESULT hr = S_OK; 77 | int g_maj = 0; 78 | int g_min = 0; 79 | int g_build = 0; 80 | std::wstring ver = clr_default_version; 81 | 82 | if (!pMeta_) 83 | return ver; 84 | 85 | IEnumUnknown* pRuntimes = nullptr; 86 | ICLRRuntimeInfo* pInfo = nullptr; 87 | ULONG fetched = 0; 88 | 89 | if (FAILED((hr = pMeta_->EnumerateInstalledRuntimes(&pRuntimes)))) { 90 | LOG_ERROR("Failed to enumerate installed runtimes!", hr); 91 | return ver; 92 | } 93 | 94 | while (SUCCEEDED((hr = pRuntimes->Next(1, (IUnknown**)&pInfo, &fetched))) && 0 != fetched) { 95 | wchar_t ver_string[clr_ver_reservation] = { 0 }; 96 | DWORD ver_size = clr_ver_reservation; 97 | int c_min = 0, c_maj = 0, c_build = 0; 98 | if (FAILED((hr = pInfo->GetVersionString(ver_string, &ver_size)))) { 99 | LOG_ERROR("Failed to get version string!", hr); 100 | continue; 101 | } 102 | swscanf_s(ver_string, L"v%d.%d.%d", &c_maj, &c_min, &c_build); 103 | if (c_maj > g_maj) { 104 | g_maj = c_maj; 105 | g_min = c_min; 106 | g_build = c_build; 107 | ver = ver_string; 108 | } 109 | else if (c_maj == g_maj) { 110 | if (c_min > g_min || (c_min == g_min && c_build > g_build)) { 111 | g_min = c_min; 112 | g_build = c_build; 113 | ver = ver_string; 114 | } 115 | } 116 | } 117 | 118 | return ver; 119 | } 120 | 121 | ClrDomain::ClrDomain() 122 | { 123 | HRESULT hr = S_OK; 124 | BOOL loadable = FALSE; 125 | LOG("Runtime initialization started..."); 126 | 127 | if (FAILED((hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(pMeta_.GetAddressOf()))))) { 128 | LOG_ERROR("Failed to initialize metahost!", hr); 129 | throw EXCEPT("Host initialization failed!"); 130 | } 131 | 132 | auto clr_version = find_runtime(); 133 | 134 | if (FAILED((hr = pMeta_->GetRuntime(clr_version.c_str(), IID_PPV_ARGS(pRuntime_.GetAddressOf()))))) { 135 | LOG_ERROR("Runtime initialization failed!", hr); 136 | throw EXCEPT("Runtime init failed!"); 137 | } 138 | 139 | if (FAILED((hr = pRuntime_->IsLoadable(&loadable)) || !loadable)) { 140 | LOG_ERROR("Runtime not loadable!", hr); 141 | throw EXCEPT("Runtime not loadable!"); 142 | } 143 | 144 | if (FAILED((hr = pRuntime_->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(pHost_.GetAddressOf()))))) { 145 | LOG_ERROR("Failed to get runtime host!", hr); 146 | throw EXCEPT("Unable to host application!"); 147 | } 148 | 149 | if (FAILED((hr = pHost_->Start()))) { 150 | LOG_ERROR("Host failed to start!", hr); 151 | throw EXCEPT("Host start failed!"); 152 | } 153 | 154 | LOG("Initialization Complete!"); 155 | } 156 | 157 | ClrDomain::~ClrDomain() 158 | { 159 | pHost_->Stop(); 160 | } 161 | 162 | std::unique_ptr ClrDomain::load(std::vector& mod) 163 | { 164 | std::unique_ptr clr; 165 | IUnknownPtr pDomainThunk = nullptr; 166 | mscorlib::_AppDomainPtr pAppDomain = nullptr; 167 | mscorlib::_AssemblyPtr pAsm = nullptr; 168 | HRESULT hr = S_OK; 169 | SAFEARRAY* pModContainer = nullptr; 170 | 171 | auto modSize = mod.size(); 172 | if (modSize > ULONG_MAX) { 173 | LOG("Failed to load module, file size is too large!"); 174 | return nullptr; 175 | } 176 | 177 | if (FAILED((hr = pHost_->GetDefaultDomain(&pDomainThunk)))) { 178 | LOG_ERROR("Failed to get default appdomain!", hr); 179 | return nullptr; 180 | } 181 | 182 | if (FAILED((hr = pDomainThunk->QueryInterface(IID_PPV_ARGS(&pAppDomain))))) { 183 | LOG_ERROR("Failed to get app domain interface from thunk!", hr); 184 | return nullptr; 185 | } 186 | 187 | if (nullptr == (pModContainer = SafeArrayCreateVector(VT_UI1, 0, static_cast(modSize)))) { 188 | LOG("Failed to allocate safe array vector!"); 189 | return nullptr; 190 | } 191 | 192 | unsigned char* buf = nullptr; 193 | if (FAILED((hr = SafeArrayAccessData(pModContainer, reinterpret_cast(&buf))))) { 194 | LOG_ERROR("Failed to access safe array!", hr); 195 | return nullptr; 196 | } 197 | 198 | memcpy(buf, mod.data(), mod.size()); 199 | SafeArrayUnaccessData(pModContainer); 200 | 201 | if (FAILED((hr = pAppDomain->Load_3(pModContainer, &pAsm)))) { 202 | LOG_ERROR("Failed to load assembly!", hr); 203 | return nullptr; 204 | } 205 | 206 | 207 | arr_.push_back(std::shared_ptr(pModContainer, [](auto p) { if (p) SafeArrayDestroy(p); })); 208 | clr = std::make_unique(pAsm); 209 | 210 | return clr; 211 | } 212 | ClrClass::ClrClass(mscorlib::_TypePtr pt, variant_t inst) : pType_(pt), instance_(inst) 213 | { 214 | } 215 | } -------------------------------------------------------------------------------- /DotNetReflectiveLoading/clr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #import raw_interfaces_only \ 12 | high_property_prefixes("_get","_put","_putref") \ 13 | rename("ReportEvent", "InteropServices_ReportEvent") 14 | 15 | namespace clr { 16 | constexpr wchar_t clr_default_version[] = /*L"v2.0.50727"; */L"v4.0.30319"; 17 | constexpr unsigned long clr_ver_reservation = 30; 18 | constexpr mscorlib::BindingFlags mem_fn_call = static_cast(mscorlib::BindingFlags_InvokeMethod | mscorlib::BindingFlags_Instance | mscorlib::BindingFlags_Public); 19 | constexpr mscorlib::BindingFlags static_fn_call = static_cast(mscorlib::BindingFlags_InvokeMethod | mscorlib::BindingFlags_Static | mscorlib::BindingFlags_Public); 20 | 21 | class ClrClass { 22 | private: 23 | mscorlib::_TypePtr pType_; 24 | variant_t instance_; 25 | 26 | public: 27 | ClrClass(mscorlib::_TypePtr pt, variant_t inst); 28 | 29 | template 30 | variant_t invoke_method(const std::wstring& name, Args&&... args) { 31 | variant_t ret; 32 | HRESULT hr = S_OK; 33 | bstr_t fn_name(name.c_str()); 34 | std::array var_args{ variant_t(args)... }; 35 | auto arglist = SafeArrayCreateVector(VT_VARIANT, 0, var_args.size()); 36 | std::shared_ptr arglist_ptr(arglist, [](auto p) { if (p) SafeArrayDestroy(p); }); 37 | for (auto i = 0; i < sizeof...(args); ++i) { 38 | LONG tmp = i; 39 | if (FAILED((hr = SafeArrayPutElement(arglist, &tmp, &var_args[i])))) { 40 | throw std::runtime_error("Failed to add element to safe array!"); 41 | } 42 | } 43 | if (FAILED((hr = pType_->InvokeMember_3(fn_name, mem_fn_call, nullptr, instance_, arglist, &ret)))) { 44 | throw std::runtime_error("Failed to invoke method!"); 45 | } 46 | return ret; 47 | } 48 | }; 49 | 50 | class ClrAssembly { 51 | private: 52 | mscorlib::_AssemblyPtr p_; 53 | SAFEARRAY* mod_; 54 | public: 55 | ClrAssembly(mscorlib::_AssemblyPtr p); 56 | mscorlib::_TypePtr find_type(const std::wstring& clsname); 57 | std::unique_ptr construct(const std::wstring& classname); 58 | 59 | template 60 | variant_t invoke_static(const std::wstring& clsName, const std::wstring& methodName, Args&&... args) { 61 | variant_t ret; 62 | variant_t v_empty; 63 | HRESULT hr = S_OK; 64 | bstr_t fn_name(methodName.c_str()); 65 | std::array var_args{ variant_t(args)... }; 66 | auto pType = find_type(clsName); 67 | if (nullptr == pType) 68 | throw std::runtime_error("Failed to find type!"); 69 | auto arglist = SafeArrayCreateVector(VT_VARIANT, 0, var_args.size()); 70 | std::shared_ptr arglist_ptr(arglist, [](auto p) { if (p) SafeArrayDestroy(p); }); 71 | for (auto i = 0; i < sizeof...(args); ++i) { 72 | LONG tmp = i; 73 | if (FAILED((hr = SafeArrayPutElement(arglist, &tmp, &var_args[i])))) { 74 | throw std::runtime_error("Failed to add element to safe array!"); 75 | } 76 | } 77 | if (FAILED((hr = pType->InvokeMember_3(fn_name, static_fn_call, nullptr, v_empty, arglist, &ret)))) { 78 | throw std::runtime_error("Failed to invoke method!"); 79 | } 80 | 81 | return ret; 82 | } 83 | }; 84 | 85 | class ClrDomain { 86 | private: 87 | Microsoft::WRL::ComPtr pMeta_; 88 | Microsoft::WRL::ComPtr pRuntime_; 89 | Microsoft::WRL::ComPtr pHost_; 90 | std::vector> arr_; 91 | std::wstring find_runtime(); 92 | public: 93 | ClrDomain(); 94 | ~ClrDomain(); 95 | std::unique_ptr load(std::vector& mod); 96 | }; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /DotNetReflectiveLoading/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "clr.hpp" 3 | 4 | void callback0(const wchar_t* p) 5 | { 6 | std::wcout << L"Callback 0: Message: " << p << std::endl; 7 | } 8 | 9 | void callback1(const wchar_t* p) 10 | { 11 | std::wcout << L"Callback 1: " << p << std::endl; 12 | auto tmp = *((char*)0); 13 | } 14 | 15 | 16 | class CppClassEx { 17 | public: 18 | int fn() { 19 | return 10; 20 | } 21 | 22 | int excepted(const wchar_t* p) { 23 | std::wcout << L"Hello from unmanaged C++ land! " << p << std::endl; 24 | throw std::runtime_error("Failsauce!"); 25 | return 20; 26 | } 27 | }; 28 | 29 | int wmain(int argc, wchar_t** argv) 30 | { 31 | DWORD bytes = 0; 32 | std::vector vec; 33 | 34 | auto hf = CreateFileW(L"DemoAssembly.dll", GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 35 | if (INVALID_HANDLE_VALUE == hf) 36 | return 0; 37 | 38 | auto size = GetFileSize(hf, nullptr); 39 | if (size == INVALID_FILE_SIZE) 40 | return -1; 41 | vec.resize(size, 0x00); 42 | 43 | if (!ReadFile(hf, vec.data(), size, &bytes, nullptr) || bytes == 0) { 44 | std::cout << "Something went wrong! " << GetLastError() << std::endl; 45 | return -1; 46 | } 47 | 48 | CloseHandle(hf); 49 | 50 | clr::ClrDomain dom; 51 | 52 | auto res = dom.load(vec); 53 | if (!res) { 54 | std::cout << "Failed to load module!" << std::endl; 55 | return -2; 56 | } 57 | 58 | res->invoke_static(L"DemoAssembly.Demo", L"Popup", L"Hah, Messagebox!"); 59 | 60 | auto cls = res->construct(L"DemoAssembly.Demo"); 61 | if(!cls) { 62 | std::cout << "Class construction failed!" << std::endl; 63 | return -3; 64 | } 65 | 66 | LONGLONG fp = (LONGLONG)callback0; 67 | cls->invoke_method(L"Callback", fp); 68 | auto var = cls->invoke_method(L"GetString"); 69 | auto bst = static_cast(var); 70 | std::wcout << L"Message from CLR: " << static_cast(bst) << std::endl; 71 | 72 | fp = (LONGLONG)callback1; 73 | cls->invoke_method(L"ExceptionCallback", fp); 74 | 75 | CppClassEx example; 76 | 77 | auto r = cls->invoke_method(L"AddClass", ((LONGLONG)&example)); 78 | if (!static_cast(r)) 79 | return -4; 80 | 81 | union { 82 | decltype(&CppClassEx::fn) f; 83 | decltype(&CppClassEx::excepted) ex; 84 | void* p; 85 | } u; 86 | 87 | u.f = &CppClassEx::fn; 88 | 89 | cls->invoke_method(L"InvokeMethod", ((LONGLONG)(u.p))); 90 | 91 | u.ex = &CppClassEx::excepted; 92 | cls->invoke_method(L"InvokeExcepted", ((LONGLONG)u.p)); 93 | 94 | system("pause"); 95 | return 0; 96 | } -------------------------------------------------------------------------------- /DotNetReflectiveLoading/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _DEBUG 4 | #include 5 | #define LOG(x) do {std::fstream f("LOG.txt", std::ios::app); f << __FILE__ << " : " << __LINE__ << " - " << x << std::endl; f.close();} while(0) 6 | #define LOG_ERROR(x, y) do {std::fstream f("LOG.txt", std::ios::app); f << __FILE__ << " : " << __LINE__ << " - " << x << " - 0x" << std::hex << y << std::endl; f.close();} while(0) 7 | #define EXCEPT(x) std::runtime_error(x) 8 | #else 9 | #define LOG(x) __LINE__ 10 | #define EXCEPT(x) std::runtime_error(std::to_string(__LINE__)) 11 | #define LOG_ERROR(x, y) (void*)0 12 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Aaron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DotNetReflectiveLoading 2 | 3 | ## Purpose 4 | 5 | This project is the culmination of playing around with the native application CLR hosting APIs. It provides the ability to 6 | reflectively load and execute code from .NET assemblies, and will be a bit of an exploration of the COM and reflection facilities 7 | underpinning .NET on Windows. 8 | 9 | ## Use 10 | 11 | At the present time, this project has a few limitations (e.g., it doesn't support constructors that take arguments currently), 12 | up to and including the fact that it only supports reflective loading (as opposed to loading from disk), and is still a bit 13 | of a work in progress. 14 | 15 | It currently contains three class definitions: 16 | 17 | ClrDomain - which manages the AppDomain and some hosting facilities 18 | ClrAssembly - which manages an individual loaded assembly 19 | ClrClass - which manages an individual class instance 20 | 21 | The following snippet of code will load an assembly: 22 | 23 | ```cpp 24 | 25 | std::vector vec; 26 | 27 | // ... load assembly from file into vec here 28 | 29 | clr::ClrDomain dom; 30 | 31 | auto asm = dom.load(vec); 32 | if(!asm) { 33 | std::cout << "Failed to load assembly!" << std::endl; 34 | return0; 35 | } 36 | 37 | ``` 38 | 39 | At this point, the variable asm contains a std::unique_ptr to a clr::ClrAssembly class. 40 | 41 | Assuming for a second that we have an assembly that looks like this: 42 | 43 | ```csharp 44 | 45 | namespace TestLib 46 | { 47 | public class Class1 48 | { 49 | private string stuff; 50 | public Class1() 51 | { 52 | stuff = "Herp derp string"; 53 | System.Console.WriteLine("Success"); 54 | } 55 | public void mb(string text) { 56 | System.Windows.Forms.MessageBox.Show(text); 57 | 58 | } 59 | 60 | public static void drop_file(string filename, string filecontents) 61 | { 62 | FileStream tmp = File.Open(filename, FileMode.Create); 63 | byte[] buf = Encoding.ASCII.GetBytes(filecontents); 64 | tmp.Write(buf, 0, buf.Length); 65 | tmp.Close(); 66 | } 67 | } 68 | } 69 | 70 | ``` 71 | 72 | We can invoke the static method without much work at all, by providing the class name, function name, and arguments: 73 | 74 | ```cpp 75 | 76 | asm->invoke_static(L"TestLib.Class1", L"drop_file", L"stuff.txt", L"Lorem Ipsum"); 77 | 78 | ``` 79 | 80 | At this point, we should now have a file named "stuff.txt", containing the Lorem Ipsum text. Now, 81 | supposing we want to actually construct an instance of the class, and create a message box: 82 | 83 | ```cpp 84 | 85 | auto cls = asm->construct(L"TestLib.Class1"); // Constructor will get invoked here 86 | if(!cls) 87 | return 0; 88 | 89 | cls->invoke_method(L"mb", L"Success!"); 90 | 91 | ``` 92 | 93 | At this point, the popup should display. 94 | --------------------------------------------------------------------------------