├── .gitignore ├── LICENSE ├── README.md ├── nuget ├── ClrMDExports.nuspec ├── nuget.exe └── pack.cmd └── src ├── ClrMDExports.sln └── ClrMDExports ├── ClrMDExports.csproj ├── CrossDomainInvoker.cs ├── DbgEngStream.cs ├── DebuggingContext.cs ├── DelegateBuilder.cs ├── DispatchProxy.cs ├── DispatchProxyGenerator.cs ├── ILLDBDebugClient.cs ├── Init.cs ├── Initialization.cs ├── NativeInvoker.cs ├── Properties └── AssemblyInfo.cs └── StripDmlWriter.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kevin Gosse 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 | # ClrMDExports 2 | 3 | Helpers to easily create WinDbg extensions using ClrMD. 4 | 5 | Create a new class library project, reference the nuget package, then add the `DllExport` attribute to the functions you want to export to WinDbg. Then call `DebuggingContext.Execute` with a callback, to be able to use ClrMD. 6 | 7 | **Note: you need to compile your project for x86 or x64 depending on the bitness of your target. AnyCPU is not supported.** 8 | 9 | ```C# 10 | using System; 11 | using System.Linq; 12 | using System.Runtime.InteropServices; 13 | using ClrMDExports; 14 | using Microsoft.Diagnostics.Runtime; 15 | using RGiesecke.DllExport; 16 | 17 | namespace TestWindbgExtension 18 | { 19 | public class Class1 20 | { 21 | [DllExport("helloworld")] 22 | public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args) 23 | { 24 | // Can't use ClrMD here. Use DebuggingContext.Execute with a callback to switch to another context: 25 | DebuggingContext.Execute(client, args, HelloWorld); 26 | } 27 | 28 | private static void HelloWorld(ClrRuntime runtime, string args) 29 | { 30 | // Can use ClrMD here 31 | Console.WriteLine("The first 10 types on the heap are: "); 32 | 33 | foreach (var obj in runtime.Heap.EnumerateObjects().Take(10)) 34 | { 35 | Console.WriteLine(obj.Type); 36 | } 37 | } 38 | } 39 | } 40 | 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /nuget/ClrMDExports.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ClrMDExports 5 | 2.0.0.0-rc1 6 | ClrMD Exports 7 | Kevin Gosse 8 | Kevin Gosse 9 | false 10 | https://github.com/kevingosse/ClrMDExports/blob/master/LICENSE 11 | https://github.com/kevingosse/ClrMDExports 12 | Helper library to create extensions for WinDbg/LLDB using ClrMD. 13 | Update to ClrMD 2 14 | Copyright 2019-2021 15 | ClrMD WinDBG SOS LLDB 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /nuget/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevingosse/ClrMDExports/86c37a4e71dd11b2ae358a0068b86eeb1bba4e44/nuget/nuget.exe -------------------------------------------------------------------------------- /nuget/pack.cmd: -------------------------------------------------------------------------------- 1 | xcopy /R /Y "..\src\ClrMDExports\Init.cs" ".\contentFiles\cs\net471\" 2 | xcopy /R /Y "..\src\ClrMDExports\Init.cs" ".\contentFiles\cs\netstandard2.0\" 3 | xcopy /R /Y "..\src\ClrMDExports\Init.cs" ".\content\" 4 | 5 | nuget pack -------------------------------------------------------------------------------- /src/ClrMDExports.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClrMDExports", "ClrMDExports\ClrMDExports.csproj", "{779ED776-74EF-4ADA-8E0F-7BE463811483}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {779ED776-74EF-4ADA-8E0F-7BE463811483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {779ED776-74EF-4ADA-8E0F-7BE463811483}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {779ED776-74EF-4ADA-8E0F-7BE463811483}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {779ED776-74EF-4ADA-8E0F-7BE463811483}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {E085A3A4-3D3F-4310-B0FD-595A67818EDD} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/ClrMDExports/ClrMDExports.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net471 5 | ClrMDExports 6 | true 7 | false 8 | false 9 | 10 | 11 | 12 | 13 | xcopy /R /Y "$(TargetPath)" "$(ProjectDir)\..\..\nuget\lib\netstandard2.0\" 14 | xcopy /R /Y "$(TargetPath)" "$(ProjectDir)\..\..\nuget\lib\net471\" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | All 25 | None 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/ClrMDExports/CrossDomainInvoker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using Microsoft.Diagnostics.Runtime; 5 | 6 | namespace ClrMDExports 7 | { 8 | [Serializable] 9 | public delegate void DebuggingMethod(ClrRuntime runtime, string args); 10 | 11 | [Serializable] 12 | public class CrossDomainInvoker 13 | { 14 | private readonly IntPtr _client; 15 | private readonly string _args; 16 | private readonly string _type; 17 | private readonly int _methodMetadataToken; 18 | 19 | public CrossDomainInvoker(IntPtr client, string args, string type, int methodMetadataToken) 20 | { 21 | _client = client; 22 | _args = args; 23 | _type = type; 24 | _methodMetadataToken = methodMetadataToken; 25 | } 26 | 27 | public void Invoke() 28 | { 29 | DebuggingContext.Initialize(_client, true); 30 | 31 | MethodInfo method = null; 32 | var type = Type.GetType(_type); 33 | 34 | if (type == null) 35 | { 36 | Console.WriteLine("Could not load type {0} in child AppDomain", _type); 37 | return; 38 | } 39 | 40 | var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 41 | 42 | method = methods.FirstOrDefault(m => m.MetadataToken == _methodMetadataToken); 43 | 44 | if (method == null) 45 | { 46 | Console.WriteLine("Could not find static method with metadata token {0} in type {1}", _methodMetadataToken, _type); 47 | return; 48 | } 49 | 50 | try 51 | { 52 | method.Invoke(null, new object[] { DebuggingContext.Runtime, _args }); 53 | } 54 | catch (Exception ex) 55 | { 56 | Console.WriteLine("Error while executing the command: " + ex); 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/ClrMDExports/DbgEngStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using Microsoft.Diagnostics.Runtime.Interop; 6 | 7 | namespace ClrMDExports 8 | { 9 | internal class DbgEngStream : Stream 10 | { 11 | private readonly IDebugClient _client; 12 | private readonly IDebugControl _control; 13 | 14 | public DbgEngStream(IDebugClient client) 15 | { 16 | _client = client; 17 | _control = (IDebugControl)client; 18 | } 19 | 20 | public void Clear() 21 | { 22 | while (Marshal.ReleaseComObject(_client) > 0) { } 23 | while (Marshal.ReleaseComObject(_control) > 0) { } 24 | } 25 | 26 | public override bool CanRead => false; 27 | 28 | public override bool CanSeek => false; 29 | 30 | public override bool CanWrite => true; 31 | 32 | public override void Flush() { } 33 | 34 | public override long Length => -1; 35 | 36 | public override long Position 37 | { 38 | get => 0; 39 | set { } 40 | } 41 | 42 | public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException(); 43 | 44 | public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); 45 | 46 | public override void SetLength(long value) => throw new NotImplementedException(); 47 | 48 | public override void Write(byte[] buffer, int offset, int count) 49 | { 50 | string str = new UTF8Encoding().GetString(buffer, offset, count); 51 | _control.ControlledOutput(DEBUG_OUTCTL.ALL_CLIENTS | DEBUG_OUTCTL.DML, DEBUG_OUTPUT.NORMAL, str); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/ClrMDExports/DebuggingContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | using Microsoft.Diagnostics.Runtime; 8 | using Microsoft.Diagnostics.Runtime.Interop; 9 | 10 | namespace ClrMDExports 11 | { 12 | public class DebuggingContext 13 | { 14 | private static AppDomain ChildDomain; 15 | 16 | public static bool IsWinDbg { get; internal set; } 17 | 18 | internal static IDebugClient DebugClient { get; private set; } 19 | internal static DataTarget DataTarget { get; private set; } 20 | internal static ClrRuntime Runtime { get; private set; } 21 | 22 | private static readonly string[] vtable = 23 | { 24 | "QueryInterface", 25 | "AddRef", 26 | "Release", 27 | "GetCoreClrDirectory", 28 | "GetExpression", 29 | "VirtualUnwind", 30 | "SetExceptionCallback", 31 | "ClearExceptionCallback", 32 | 33 | //---------------------------------------------------------------------------- 34 | // IDebugControl2 35 | //---------------------------------------------------------------------------- 36 | 37 | "GetInterrupt", 38 | "OutputVaList", 39 | "GetDebuggeeType", 40 | "GetPageSize", 41 | "GetExecutingProcessorType", 42 | "Execute", 43 | "GetLastEventInformation", 44 | "Disassemble", 45 | 46 | //---------------------------------------------------------------------------- 47 | // IDebugControl4 48 | //---------------------------------------------------------------------------- 49 | 50 | "GetContextStackTrace", 51 | 52 | //---------------------------------------------------------------------------- 53 | // IDebugDataSpaces 54 | //---------------------------------------------------------------------------- 55 | 56 | "ReadVirtual", 57 | "WriteVirtual", 58 | 59 | //---------------------------------------------------------------------------- 60 | // IDebugSymbols 61 | //---------------------------------------------------------------------------- 62 | 63 | "GetSymbolOptions", 64 | "GetNameByOffset", 65 | "GetNumberModules", 66 | "GetModuleByIndex", 67 | "GetModuleByModuleName", 68 | "GetModuleByOffset", 69 | "GetModuleNames", 70 | "GetModuleParameters", 71 | "GetModuleNameString", 72 | "IsPointer64Bit", 73 | "GetLineByOffset", 74 | "GetSourceFileLineOffsets", 75 | "FindSourceFile", 76 | 77 | //---------------------------------------------------------------------------- 78 | // IDebugSystemObjects 79 | //---------------------------------------------------------------------------- 80 | 81 | "GetCurrentProcessId", 82 | "GetCurrentThreadId", 83 | "SetCurrentThreadId", 84 | "GetCurrentThreadSystemId", 85 | "GetThreadIdBySystemId", 86 | "GetThreadContextById", 87 | 88 | //---------------------------------------------------------------------------- 89 | // IDebugRegisters 90 | //---------------------------------------------------------------------------- 91 | 92 | "GetValueByName", 93 | "GetInstructionOffset", 94 | "GetStackOffset", 95 | "GetFrameOffset", 96 | 97 | //---------------------------------------------------------------------------- 98 | // LLDBServices (internal) 99 | //---------------------------------------------------------------------------- 100 | 101 | "GetModuleDirectory" 102 | }; 103 | 104 | public static void Execute(IntPtr client, string args, DebuggingMethod callback) 105 | { 106 | if (!callback.Method.IsStatic) 107 | { 108 | Console.WriteLine("The callback given to DebuggingContext.Execute needs to be a static method"); 109 | return; 110 | } 111 | 112 | if (IsWinDbg) 113 | { 114 | ExecuteWinDbg(client, args, callback); 115 | } 116 | else 117 | { 118 | if (Runtime == null) 119 | { 120 | Initialize(client, false); 121 | } 122 | 123 | callback(Runtime, args); 124 | } 125 | } 126 | 127 | [MethodImpl(MethodImplOptions.NoInlining)] 128 | private static void ExecuteWinDbg(IntPtr client, string args, DebuggingMethod callback) 129 | { 130 | if (ChildDomain == null) 131 | { 132 | var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 133 | 134 | ChildDomain = AppDomain.CreateDomain("WinDbgExtension", AppDomain.CurrentDomain.Evidence, assemblyPath, ".", false); 135 | } 136 | 137 | var invoker = new CrossDomainInvoker(client, args, callback.Method.DeclaringType.AssemblyQualifiedName, callback.Method.MetadataToken); 138 | 139 | ChildDomain.DoCallBack(invoker.Invoke); 140 | } 141 | 142 | internal static void Initialize(IntPtr ptrClient, bool isWinDbg) 143 | { 144 | // On our first call to the API: 145 | // 1. Store a copy of IDebugClient in DebugClient. 146 | // 2. Replace Console's output stream to be the debugger window. 147 | // 3. Create an instance of DataTarget using the IDebugClient. 148 | if (DataTarget == null) 149 | { 150 | if (!isWinDbg) 151 | { 152 | DebugClient = NativeInvoker.GetInstance(ptrClient, vtable); 153 | 154 | var linuxFunctionsType = Type.GetType("Microsoft.Diagnostics.Runtime.LinuxFunctions, Microsoft.Diagnostics.Runtime"); 155 | 156 | var field = typeof(DataTarget).GetField("k__BackingField", BindingFlags.Static | BindingFlags.NonPublic); 157 | 158 | field.SetValue(null, Activator.CreateInstance(linuxFunctionsType)); 159 | 160 | Console.SetOut(new StripDmlWriter(Console.Out)); 161 | } 162 | else 163 | { 164 | DebugClient = (IDebugClient)Marshal.GetUniqueObjectForIUnknown(ptrClient); 165 | 166 | var stream = new StreamWriter(new DbgEngStream(DebugClient)) { AutoFlush = true }; 167 | Console.SetOut(stream); 168 | } 169 | 170 | DataTarget = DataTarget.CreateFromDbgEng(ptrClient); 171 | } 172 | 173 | // If our ClrRuntime instance is null, it means that this is our first call, or 174 | // that the dac wasn't loaded on any previous call. 175 | if (Runtime == null) 176 | { 177 | Runtime = DataTarget.ClrVersions.Single().CreateRuntime(); 178 | } 179 | } 180 | } 181 | } -------------------------------------------------------------------------------- /src/ClrMDExports/DelegateBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Reflection.Emit; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace ClrMDExports 7 | { 8 | public class DelegateBuilder 9 | { 10 | public static Type GenerateDelegateType(MethodInfo targetMethod) 11 | { 12 | var assembly = new AssemblyName 13 | { 14 | Version = new Version(1, 0, 0, 0), 15 | Name = Guid.NewGuid().ToString().Replace("-", string.Empty) 16 | }; 17 | 18 | var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run); 19 | var modbuilder = assemblyBuilder.DefineDynamicModule("MyModule"); 20 | 21 | // Create a delegate that has the same signature as the method we would like to hook up to 22 | var typeBuilder = modbuilder.DefineType("MyDelegateType", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate)); 23 | 24 | var unmanagedFunctionPointerAttributeBuilder = new CustomAttributeBuilder(typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new Type[] { typeof(CallingConvention) }), new object[] { CallingConvention.StdCall }); 25 | typeBuilder.SetCustomAttribute(unmanagedFunctionPointerAttributeBuilder); 26 | 27 | var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) }); 28 | constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); 29 | 30 | // Grab the parameters of the method 31 | var parameters = targetMethod.GetParameters(); 32 | 33 | var paramTypes = new Type[parameters.Length + 1]; 34 | 35 | paramTypes[0] = typeof(IntPtr); 36 | 37 | for (int i = 0; i < parameters.Length; i++) 38 | { 39 | paramTypes[i + 1] = parameters[i].ParameterType; 40 | } 41 | 42 | // Define the Invoke method for the delegate 43 | var methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, targetMethod.ReturnType, paramTypes); 44 | 45 | for (int i = 0; i < parameters.Length; i++) 46 | { 47 | var parameter = parameters[i]; 48 | 49 | var attributes = ParameterAttributes.None; 50 | 51 | UnmanagedType? unmanagedType = null; 52 | 53 | bool outAttribute = false; 54 | 55 | foreach (var customAttribute in parameter.CustomAttributes) 56 | { 57 | if (customAttribute.AttributeType == typeof(MarshalAsAttribute)) 58 | { 59 | attributes |= ParameterAttributes.HasFieldMarshal; 60 | unmanagedType = (UnmanagedType)customAttribute.ConstructorArguments[0].Value; 61 | } 62 | 63 | if (customAttribute.AttributeType == typeof(InAttribute)) 64 | { 65 | attributes |= ParameterAttributes.In; 66 | } 67 | 68 | if (customAttribute.AttributeType == typeof(OutAttribute)) 69 | { 70 | attributes |= ParameterAttributes.Out; 71 | outAttribute = true; 72 | } 73 | } 74 | 75 | var pbr = methodBuilder.DefineParameter(i + 2, attributes, null); 76 | 77 | if (outAttribute) 78 | { 79 | var outAttributeBuilder = new CustomAttributeBuilder(typeof(OutAttribute).GetConstructor(new Type[0]), new object[0]); 80 | pbr.SetCustomAttribute(outAttributeBuilder); 81 | } 82 | 83 | if (unmanagedType != null) 84 | { 85 | var marshalAsAttributeBuilder = new CustomAttributeBuilder(typeof(MarshalAsAttribute).GetConstructor(new Type[] { typeof(UnmanagedType) }), new object[] { unmanagedType.Value }); 86 | pbr.SetCustomAttribute(marshalAsAttributeBuilder); 87 | } 88 | } 89 | 90 | methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); 91 | 92 | return typeBuilder.CreateTypeInfo(); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /src/ClrMDExports/DispatchProxy.cs: -------------------------------------------------------------------------------- 1 | namespace System.Reflection 2 | { 3 | /// 4 | /// DispatchProxy provides a mechanism for the instantiation of proxy objects and handling of 5 | /// their method dispatch. 6 | /// 7 | internal abstract class DispatchProxy 8 | { 9 | protected DispatchProxy() 10 | { 11 | } 12 | 13 | /// 14 | /// Whenever any method on the generated proxy type is called, this method 15 | /// will be invoked to dispatch control. 16 | /// 17 | /// The method the caller invoked 18 | /// The arguments the caller passed to the method 19 | /// The object to return to the caller, or null for void methods 20 | protected abstract object Invoke(MethodInfo targetMethod, object[] args); 21 | 22 | /// 23 | /// Creates an object instance that derives from class 24 | /// and implements interface . 25 | /// 26 | /// The interface the proxy should implement. 27 | /// The base class to use for the proxy class. 28 | /// An object instance that implements . 29 | /// is a class, 30 | /// or is sealed or does not have a parameterless constructor 31 | public static T Create() 32 | where TProxy : DispatchProxy 33 | { 34 | return (T)DispatchProxyGenerator.CreateProxyInstance(typeof(TProxy), typeof(T)); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/ClrMDExports/DispatchProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Reflection.Emit; 9 | using System.Runtime.ExceptionServices; 10 | using System.Threading; 11 | 12 | namespace System.Reflection 13 | { 14 | // Helper class to handle the IL EMIT for the generation of proxies. 15 | // Much of this code was taken directly from the Silverlight proxy generation. 16 | // Differences between this and the Silverlight version are: 17 | // 1. This version is based on DispatchProxy from NET Native and CoreCLR, not RealProxy in Silverlight ServiceModel. 18 | // There are several notable differences between them. 19 | // 2. Both DispatchProxy and RealProxy permit the caller to ask for a proxy specifying a pair of types: 20 | // the interface type to implement, and a base type. But they behave slightly differently: 21 | // - RealProxy generates a proxy type that derives from Object and *implements" all the base type's 22 | // interfaces plus all the interface type's interfaces. 23 | // - DispatchProxy generates a proxy type that *derives* from the base type and implements all 24 | // the interface type's interfaces. This is true for both the CLR version in NET Native and this 25 | // version for CoreCLR. 26 | // 3. DispatchProxy and RealProxy use different type hierarchies for the generated proxies: 27 | // - RealProxy type hierarchy is: 28 | // proxyType : proxyBaseType : object 29 | // Presumably the 'proxyBaseType' in the middle is to allow it to implement the base type's interfaces 30 | // explicitly, preventing collision for same name methods on the base and interface types. 31 | // - DispatchProxy hierarchy is: 32 | // proxyType : baseType (where baseType : DispatchProxy) 33 | // The generated DispatchProxy proxy type does not need to generate implementation methods 34 | // for the base type's interfaces, because the base type already must have implemented them. 35 | // 4. RealProxy required a proxy instance to hold a backpointer to the RealProxy instance to mirror 36 | // the .Net Remoting design that required the proxy and RealProxy to be separate instances. 37 | // But the DispatchProxy design encourages the proxy type to *be* an DispatchProxy. Therefore, 38 | // the proxy's 'this' becomes the equivalent of RealProxy's backpointer to RealProxy, so we were 39 | // able to remove an extraneous field and ctor arg from the DispatchProxy proxies. 40 | // 41 | internal static class DispatchProxyGenerator 42 | { 43 | // Generated proxies have a private Action field that all generated methods 44 | // invoke. It is the first field in the class and the first ctor parameter. 45 | private const int InvokeActionFieldAndCtorParameterIndex = 0; 46 | 47 | // Proxies are requested for a pair of types: base type and interface type. 48 | // The generated proxy will subclass the given base type and implement the interface type. 49 | // We maintain a cache keyed by 'base type' containing a dictionary keyed by interface type, 50 | // containing the generated proxy type for that pair. There are likely to be few (maybe only 1) 51 | // base type in use for many interface types. 52 | // Note: this differs from Silverlight's RealProxy implementation which keys strictly off the 53 | // interface type. But this does not allow the same interface type to be used with more than a 54 | // single base type. The implementation here permits multiple interface types to be used with 55 | // multiple base types, and the generated proxy types will be unique. 56 | // This cache of generated types grows unbounded, one element per unique T/ProxyT pair. 57 | // This approach is used to prevent regenerating identical proxy types for identical T/Proxy pairs, 58 | // which would ultimately be a more expensive leak. 59 | // Proxy instances are not cached. Their lifetime is entirely owned by the caller of DispatchProxy.Create. 60 | private static readonly Dictionary> s_baseTypeAndInterfaceToGeneratedProxyType = new Dictionary>(); 61 | private static readonly ProxyAssembly s_proxyAssembly = new ProxyAssembly(); 62 | private static readonly MethodInfo s_dispatchProxyInvokeMethod = typeof(DispatchProxy).GetTypeInfo().GetDeclaredMethod("Invoke"); 63 | 64 | // Returns a new instance of a proxy the derives from 'baseType' and implements 'interfaceType' 65 | internal static object CreateProxyInstance(Type baseType, Type interfaceType) 66 | { 67 | Debug.Assert(baseType != null); 68 | Debug.Assert(interfaceType != null); 69 | 70 | Type proxiedType = GetProxyType(baseType, interfaceType); 71 | return Activator.CreateInstance(proxiedType, (Action)DispatchProxyGenerator.Invoke); 72 | } 73 | 74 | private static Type GetProxyType(Type baseType, Type interfaceType) 75 | { 76 | lock (s_baseTypeAndInterfaceToGeneratedProxyType) 77 | { 78 | Dictionary interfaceToProxy = null; 79 | if (!s_baseTypeAndInterfaceToGeneratedProxyType.TryGetValue(baseType, out interfaceToProxy)) 80 | { 81 | interfaceToProxy = new Dictionary(); 82 | s_baseTypeAndInterfaceToGeneratedProxyType[baseType] = interfaceToProxy; 83 | } 84 | 85 | Type generatedProxy = null; 86 | if (!interfaceToProxy.TryGetValue(interfaceType, out generatedProxy)) 87 | { 88 | generatedProxy = GenerateProxyType(baseType, interfaceType); 89 | interfaceToProxy[interfaceType] = generatedProxy; 90 | } 91 | 92 | return generatedProxy; 93 | } 94 | } 95 | 96 | // Unconditionally generates a new proxy type derived from 'baseType' and implements 'interfaceType' 97 | private static Type GenerateProxyType(Type baseType, Type interfaceType) 98 | { 99 | // Parameter validation is deferred until the point we need to create the proxy. 100 | // This prevents unnecessary overhead revalidating cached proxy types. 101 | TypeInfo baseTypeInfo = baseType.GetTypeInfo(); 102 | 103 | // The interface type must be an interface, not a class 104 | if (!interfaceType.GetTypeInfo().IsInterface) 105 | { 106 | // "T" is the generic parameter seen via the public contract 107 | throw new ArgumentException("InterfaceType_Must_Be_Interface " + interfaceType.FullName); 108 | } 109 | 110 | // The base type cannot be sealed because the proxy needs to subclass it. 111 | if (baseTypeInfo.IsSealed) 112 | { 113 | // "TProxy" is the generic parameter seen via the public contract 114 | throw new ArgumentException("BaseType_Cannot_Be_Sealed " + baseTypeInfo.FullName); 115 | } 116 | 117 | // The base type cannot be abstract 118 | if (baseTypeInfo.IsAbstract) 119 | { 120 | throw new ArgumentException("BaseType_Cannot_Be_Abstract " + baseType.FullName); 121 | } 122 | 123 | // The base type must have a public default ctor 124 | if (!baseTypeInfo.DeclaredConstructors.Any(c => c.IsPublic && c.GetParameters().Length == 0)) 125 | { 126 | throw new ArgumentException("BaseType_Must_Have_Default_Ctor " + baseType.FullName); 127 | } 128 | 129 | // Create a type that derives from 'baseType' provided by caller 130 | ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType); 131 | 132 | foreach (Type t in interfaceType.GetTypeInfo().ImplementedInterfaces) 133 | pb.AddInterfaceImpl(t); 134 | 135 | pb.AddInterfaceImpl(interfaceType); 136 | 137 | Type generatedProxyType = pb.CreateType(); 138 | return generatedProxyType; 139 | } 140 | 141 | // All generated proxy methods call this static helper method to dispatch. 142 | // Its job is to unpack the arguments and the 'this' instance and to dispatch directly 143 | // to the (abstract) DispatchProxy.Invoke() method. 144 | private static void Invoke(object[] args) 145 | { 146 | PackedArgs packed = new PackedArgs(args); 147 | MethodBase method = s_proxyAssembly.ResolveMethodToken(packed.DeclaringType, packed.MethodToken); 148 | if (method.IsGenericMethodDefinition) 149 | method = ((MethodInfo)method).MakeGenericMethod(packed.GenericTypes); 150 | 151 | // Call (protected method) DispatchProxy.Invoke() 152 | try 153 | { 154 | Debug.Assert(s_dispatchProxyInvokeMethod != null); 155 | object returnValue = s_dispatchProxyInvokeMethod.Invoke(packed.DispatchProxy, 156 | new object[] { method, packed.Args }); 157 | packed.ReturnValue = returnValue; 158 | } 159 | catch (TargetInvocationException tie) 160 | { 161 | ExceptionDispatchInfo.Capture(tie.InnerException).Throw(); 162 | } 163 | } 164 | 165 | private class PackedArgs 166 | { 167 | internal const int DispatchProxyPosition = 0; 168 | internal const int DeclaringTypePosition = 1; 169 | internal const int MethodTokenPosition = 2; 170 | internal const int ArgsPosition = 3; 171 | internal const int GenericTypesPosition = 4; 172 | internal const int ReturnValuePosition = 5; 173 | 174 | internal static readonly Type[] PackedTypes = new Type[] { typeof(object), typeof(Type), typeof(int), typeof(object[]), typeof(Type[]), typeof(object) }; 175 | 176 | private object[] _args; 177 | internal PackedArgs() : this(new object[PackedTypes.Length]) { } 178 | internal PackedArgs(object[] args) { _args = args; } 179 | 180 | internal DispatchProxy DispatchProxy { get { return (DispatchProxy)_args[DispatchProxyPosition]; } } 181 | internal Type DeclaringType { get { return (Type)_args[DeclaringTypePosition]; } } 182 | internal int MethodToken { get { return (int)_args[MethodTokenPosition]; } } 183 | internal object[] Args { get { return (object[])_args[ArgsPosition]; } } 184 | internal Type[] GenericTypes { get { return (Type[])_args[GenericTypesPosition]; } } 185 | internal object ReturnValue { /*get { return args[ReturnValuePosition]; }*/ set { _args[ReturnValuePosition] = value; } } 186 | } 187 | 188 | internal static class IgnoreAccessChecksToAttributeBuilder 189 | { 190 | /// 191 | /// Generate the declaration for the IgnoresAccessChecksToAttribute type. 192 | /// This attribute will be both defined and used in the dynamic assembly. 193 | /// Each usage identifies the name of the assembly containing non-public 194 | /// types the dynamic assembly needs to access. Normally those types 195 | /// would be inaccessible, but this attribute allows them to be visible. 196 | /// It works like a reverse InternalsVisibleToAttribute. 197 | /// This method returns the ConstructorInfo of the generated attribute. 198 | /// 199 | public static ConstructorInfo AddToModule(ModuleBuilder mb) 200 | { 201 | TypeBuilder attributeTypeBuilder = 202 | mb.DefineType("System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute", 203 | TypeAttributes.Public | TypeAttributes.Class, 204 | typeof(Attribute)); 205 | 206 | // Create backing field as: 207 | // private string assemblyName; 208 | FieldBuilder assemblyNameField = 209 | attributeTypeBuilder.DefineField("assemblyName", typeof(string), FieldAttributes.Private); 210 | 211 | // Create ctor as: 212 | // public IgnoresAccessChecksToAttribute(string) 213 | ConstructorBuilder constructorBuilder = attributeTypeBuilder.DefineConstructor(MethodAttributes.Public, 214 | CallingConventions.HasThis, 215 | new Type[] { assemblyNameField.FieldType }); 216 | 217 | ILGenerator il = constructorBuilder.GetILGenerator(); 218 | 219 | // Create ctor body as: 220 | // this.assemblyName = {ctor parameter 0} 221 | il.Emit(OpCodes.Ldarg_0); 222 | il.Emit(OpCodes.Ldarg, 1); 223 | il.Emit(OpCodes.Stfld, assemblyNameField); 224 | 225 | // return 226 | il.Emit(OpCodes.Ret); 227 | 228 | // Define property as: 229 | // public string AssemblyName {get { return this.assemblyName; } } 230 | PropertyBuilder getterPropertyBuilder = attributeTypeBuilder.DefineProperty( 231 | "AssemblyName", 232 | PropertyAttributes.None, 233 | CallingConventions.HasThis, 234 | returnType: typeof(string), 235 | parameterTypes: null); 236 | 237 | MethodBuilder getterMethodBuilder = attributeTypeBuilder.DefineMethod( 238 | "get_AssemblyName", 239 | MethodAttributes.Public, 240 | CallingConventions.HasThis, 241 | returnType: typeof(string), 242 | parameterTypes: null); 243 | 244 | // Generate body: 245 | // return this.assemblyName; 246 | il = getterMethodBuilder.GetILGenerator(); 247 | il.Emit(OpCodes.Ldarg_0); 248 | il.Emit(OpCodes.Ldfld, assemblyNameField); 249 | il.Emit(OpCodes.Ret); 250 | 251 | // Generate the AttributeUsage attribute for this attribute type: 252 | // [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 253 | TypeInfo attributeUsageTypeInfo = typeof(AttributeUsageAttribute).GetTypeInfo(); 254 | 255 | // Find the ctor that takes only AttributeTargets 256 | ConstructorInfo attributeUsageConstructorInfo = 257 | attributeUsageTypeInfo.DeclaredConstructors 258 | .Single(c => c.GetParameters().Count() == 1 && 259 | c.GetParameters()[0].ParameterType == typeof(AttributeTargets)); 260 | 261 | // Find the property to set AllowMultiple 262 | PropertyInfo allowMultipleProperty = 263 | attributeUsageTypeInfo.DeclaredProperties 264 | .Single(f => string.Equals(f.Name, "AllowMultiple")); 265 | 266 | // Create a builder to construct the instance via the ctor and property 267 | CustomAttributeBuilder customAttributeBuilder = 268 | new CustomAttributeBuilder(attributeUsageConstructorInfo, 269 | new object[] { AttributeTargets.Assembly }, 270 | new PropertyInfo[] { allowMultipleProperty }, 271 | new object[] { true }); 272 | 273 | // Attach this attribute instance to the newly defined attribute type 274 | attributeTypeBuilder.SetCustomAttribute(customAttributeBuilder); 275 | 276 | // Make the TypeInfo real so the constructor can be used. 277 | return attributeTypeBuilder.CreateTypeInfo().DeclaredConstructors.Single(); 278 | } 279 | } 280 | 281 | private class ProxyAssembly 282 | { 283 | private AssemblyBuilder _ab; 284 | private ModuleBuilder _mb; 285 | private int _typeId = 0; 286 | 287 | // Maintain a MethodBase-->int, int-->MethodBase mapping to permit generated code 288 | // to pass methods by token 289 | private Dictionary _methodToToken = new Dictionary(); 290 | private List _methodsByToken = new List(); 291 | private HashSet _ignoresAccessAssemblyNames = new HashSet(); 292 | private ConstructorInfo _ignoresAccessChecksToAttributeConstructor; 293 | 294 | public ProxyAssembly() 295 | { 296 | _ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("ProxyBuilder"), AssemblyBuilderAccess.Run); 297 | _mb = _ab.DefineDynamicModule("testmod"); 298 | } 299 | 300 | // Gets or creates the ConstructorInfo for the IgnoresAccessChecksAttribute. 301 | // This attribute is both defined and referenced in the dynamic assembly to 302 | // allow access to internal types in other assemblies. 303 | internal ConstructorInfo IgnoresAccessChecksAttributeConstructor 304 | { 305 | get 306 | { 307 | if (_ignoresAccessChecksToAttributeConstructor == null) 308 | { 309 | _ignoresAccessChecksToAttributeConstructor = IgnoreAccessChecksToAttributeBuilder.AddToModule(_mb); 310 | } 311 | 312 | return _ignoresAccessChecksToAttributeConstructor; 313 | } 314 | } 315 | public ProxyBuilder CreateProxy(string name, Type proxyBaseType) 316 | { 317 | int nextId = Interlocked.Increment(ref _typeId); 318 | TypeBuilder tb = _mb.DefineType(name + "_" + nextId, TypeAttributes.Public, proxyBaseType); 319 | return new ProxyBuilder(this, tb, proxyBaseType); 320 | } 321 | 322 | // Generates an instance of the IgnoresAccessChecksToAttribute to 323 | // identify the given assembly as one which contains internal types 324 | // the dynamic assembly will need to reference. 325 | internal void GenerateInstanceOfIgnoresAccessChecksToAttribute(string assemblyName) 326 | { 327 | // Add this assembly level attribute: 328 | // [assembly: System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute(assemblyName)] 329 | ConstructorInfo attributeConstructor = IgnoresAccessChecksAttributeConstructor; 330 | CustomAttributeBuilder customAttributeBuilder = 331 | new CustomAttributeBuilder(attributeConstructor, new object[] { assemblyName }); 332 | _ab.SetCustomAttribute(customAttributeBuilder); 333 | } 334 | 335 | // Ensures the type we will reference from the dynamic assembly 336 | // is visible. Non-public types need to emit an attribute that 337 | // allows access from the dynamic assembly. 338 | internal void EnsureTypeIsVisible(Type type) 339 | { 340 | TypeInfo typeInfo = type.GetTypeInfo(); 341 | if (!typeInfo.IsVisible) 342 | { 343 | string assemblyName = typeInfo.Assembly.GetName().Name; 344 | if (!_ignoresAccessAssemblyNames.Contains(assemblyName)) 345 | { 346 | GenerateInstanceOfIgnoresAccessChecksToAttribute(assemblyName); 347 | _ignoresAccessAssemblyNames.Add(assemblyName); 348 | } 349 | } 350 | } 351 | 352 | internal void GetTokenForMethod(MethodBase method, out Type type, out int token) 353 | { 354 | type = method.DeclaringType; 355 | token = 0; 356 | if (!_methodToToken.TryGetValue(method, out token)) 357 | { 358 | _methodsByToken.Add(method); 359 | token = _methodsByToken.Count - 1; 360 | _methodToToken[method] = token; 361 | } 362 | } 363 | 364 | internal MethodBase ResolveMethodToken(Type type, int token) 365 | { 366 | Debug.Assert(token >= 0 && token < _methodsByToken.Count); 367 | return _methodsByToken[token]; 368 | } 369 | } 370 | 371 | private class ProxyBuilder 372 | { 373 | private static readonly MethodInfo s_delegateInvoke = typeof(Action).GetTypeInfo().GetDeclaredMethod("Invoke"); 374 | 375 | private ProxyAssembly _assembly; 376 | private TypeBuilder _tb; 377 | private Type _proxyBaseType; 378 | private List _fields; 379 | 380 | internal ProxyBuilder(ProxyAssembly assembly, TypeBuilder tb, Type proxyBaseType) 381 | { 382 | _assembly = assembly; 383 | _tb = tb; 384 | _proxyBaseType = proxyBaseType; 385 | 386 | _fields = new List(); 387 | _fields.Add(tb.DefineField("invoke", typeof(Action), FieldAttributes.Private)); 388 | } 389 | 390 | private void Complete() 391 | { 392 | Type[] args = new Type[_fields.Count]; 393 | for (int i = 0; i < args.Length; i++) 394 | { 395 | args[i] = _fields[i].FieldType; 396 | } 397 | 398 | ConstructorBuilder cb = _tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, args); 399 | ILGenerator il = cb.GetILGenerator(); 400 | 401 | // chained ctor call 402 | ConstructorInfo baseCtor = _proxyBaseType.GetTypeInfo().DeclaredConstructors.SingleOrDefault(c => c.IsPublic && c.GetParameters().Length == 0); 403 | Debug.Assert(baseCtor != null); 404 | 405 | il.Emit(OpCodes.Ldarg_0); 406 | il.Emit(OpCodes.Call, baseCtor); 407 | 408 | // store all the fields 409 | for (int i = 0; i < args.Length; i++) 410 | { 411 | il.Emit(OpCodes.Ldarg_0); 412 | il.Emit(OpCodes.Ldarg, i + 1); 413 | il.Emit(OpCodes.Stfld, _fields[i]); 414 | } 415 | 416 | il.Emit(OpCodes.Ret); 417 | } 418 | 419 | internal Type CreateType() 420 | { 421 | this.Complete(); 422 | return _tb.CreateTypeInfo().AsType(); 423 | } 424 | 425 | internal void AddInterfaceImpl(Type iface) 426 | { 427 | // If necessary, generate an attribute to permit visibility 428 | // to internal types. 429 | _assembly.EnsureTypeIsVisible(iface); 430 | 431 | _tb.AddInterfaceImplementation(iface); 432 | 433 | // AccessorMethods -> Metadata mappings. 434 | var propertyMap = new Dictionary(MethodInfoEqualityComparer.Instance); 435 | foreach (PropertyInfo pi in iface.GetRuntimeProperties()) 436 | { 437 | var ai = new PropertyAccessorInfo(pi.GetMethod, pi.SetMethod); 438 | if (pi.GetMethod != null) 439 | propertyMap[pi.GetMethod] = ai; 440 | if (pi.SetMethod != null) 441 | propertyMap[pi.SetMethod] = ai; 442 | } 443 | 444 | var eventMap = new Dictionary(MethodInfoEqualityComparer.Instance); 445 | foreach (EventInfo ei in iface.GetRuntimeEvents()) 446 | { 447 | var ai = new EventAccessorInfo(ei.AddMethod, ei.RemoveMethod, ei.RaiseMethod); 448 | if (ei.AddMethod != null) 449 | eventMap[ei.AddMethod] = ai; 450 | if (ei.RemoveMethod != null) 451 | eventMap[ei.RemoveMethod] = ai; 452 | if (ei.RaiseMethod != null) 453 | eventMap[ei.RaiseMethod] = ai; 454 | } 455 | 456 | foreach (MethodInfo mi in iface.GetRuntimeMethods()) 457 | { 458 | MethodBuilder mdb = AddMethodImpl(mi); 459 | PropertyAccessorInfo associatedProperty; 460 | if (propertyMap.TryGetValue(mi, out associatedProperty)) 461 | { 462 | if (MethodInfoEqualityComparer.Instance.Equals(associatedProperty.InterfaceGetMethod, mi)) 463 | associatedProperty.GetMethodBuilder = mdb; 464 | else 465 | associatedProperty.SetMethodBuilder = mdb; 466 | } 467 | 468 | EventAccessorInfo associatedEvent; 469 | if (eventMap.TryGetValue(mi, out associatedEvent)) 470 | { 471 | if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceAddMethod, mi)) 472 | associatedEvent.AddMethodBuilder = mdb; 473 | else if (MethodInfoEqualityComparer.Instance.Equals(associatedEvent.InterfaceRemoveMethod, mi)) 474 | associatedEvent.RemoveMethodBuilder = mdb; 475 | else 476 | associatedEvent.RaiseMethodBuilder = mdb; 477 | } 478 | } 479 | 480 | foreach (PropertyInfo pi in iface.GetRuntimeProperties()) 481 | { 482 | PropertyAccessorInfo ai = propertyMap[pi.GetMethod ?? pi.SetMethod]; 483 | PropertyBuilder pb = _tb.DefineProperty(pi.Name, pi.Attributes, pi.PropertyType, pi.GetIndexParameters().Select(p => p.ParameterType).ToArray()); 484 | if (ai.GetMethodBuilder != null) 485 | pb.SetGetMethod(ai.GetMethodBuilder); 486 | if (ai.SetMethodBuilder != null) 487 | pb.SetSetMethod(ai.SetMethodBuilder); 488 | } 489 | 490 | foreach (EventInfo ei in iface.GetRuntimeEvents()) 491 | { 492 | EventAccessorInfo ai = eventMap[ei.AddMethod ?? ei.RemoveMethod]; 493 | EventBuilder eb = _tb.DefineEvent(ei.Name, ei.Attributes, ei.EventHandlerType); 494 | if (ai.AddMethodBuilder != null) 495 | eb.SetAddOnMethod(ai.AddMethodBuilder); 496 | if (ai.RemoveMethodBuilder != null) 497 | eb.SetRemoveOnMethod(ai.RemoveMethodBuilder); 498 | if (ai.RaiseMethodBuilder != null) 499 | eb.SetRaiseMethod(ai.RaiseMethodBuilder); 500 | } 501 | } 502 | 503 | private MethodBuilder AddMethodImpl(MethodInfo mi) 504 | { 505 | ParameterInfo[] parameters = mi.GetParameters(); 506 | Type[] paramTypes = ParamTypes(parameters, false); 507 | 508 | MethodBuilder mdb = _tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, mi.ReturnType, paramTypes); 509 | if (mi.ContainsGenericParameters) 510 | { 511 | Type[] ts = mi.GetGenericArguments(); 512 | string[] ss = new string[ts.Length]; 513 | for (int i = 0; i < ts.Length; i++) 514 | { 515 | ss[i] = ts[i].Name; 516 | } 517 | GenericTypeParameterBuilder[] genericParameters = mdb.DefineGenericParameters(ss); 518 | for (int i = 0; i < genericParameters.Length; i++) 519 | { 520 | genericParameters[i].SetGenericParameterAttributes(ts[i].GetTypeInfo().GenericParameterAttributes); 521 | } 522 | } 523 | ILGenerator il = mdb.GetILGenerator(); 524 | 525 | ParametersArray args = new ParametersArray(il, paramTypes); 526 | 527 | // object[] args = new object[paramCount]; 528 | il.Emit(OpCodes.Nop); 529 | GenericArray argsArr = new GenericArray(il, ParamTypes(parameters, true).Length); 530 | 531 | for (int i = 0; i < parameters.Length; i++) 532 | { 533 | // args[i] = argi; 534 | if (!parameters[i].IsOut || !parameters[i].ParameterType.IsByRef || parameters[i].IsIn) 535 | { 536 | argsArr.BeginSet(i); 537 | args.Get(i); 538 | argsArr.EndSet(parameters[i].ParameterType); 539 | } 540 | } 541 | 542 | // object[] packed = new object[PackedArgs.PackedTypes.Length]; 543 | GenericArray packedArr = new GenericArray(il, PackedArgs.PackedTypes.Length); 544 | 545 | // packed[PackedArgs.DispatchProxyPosition] = this; 546 | packedArr.BeginSet(PackedArgs.DispatchProxyPosition); 547 | il.Emit(OpCodes.Ldarg_0); 548 | packedArr.EndSet(typeof(DispatchProxy)); 549 | 550 | // packed[PackedArgs.DeclaringTypePosition] = typeof(iface); 551 | MethodInfo Type_GetTypeFromHandle = typeof(Type).GetRuntimeMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }); 552 | int methodToken; 553 | Type declaringType; 554 | _assembly.GetTokenForMethod(mi, out declaringType, out methodToken); 555 | packedArr.BeginSet(PackedArgs.DeclaringTypePosition); 556 | il.Emit(OpCodes.Ldtoken, declaringType); 557 | il.Emit(OpCodes.Call, Type_GetTypeFromHandle); 558 | packedArr.EndSet(typeof(object)); 559 | 560 | // packed[PackedArgs.MethodTokenPosition] = iface method token; 561 | packedArr.BeginSet(PackedArgs.MethodTokenPosition); 562 | il.Emit(OpCodes.Ldc_I4, methodToken); 563 | packedArr.EndSet(typeof(int)); 564 | 565 | // packed[PackedArgs.ArgsPosition] = args; 566 | packedArr.BeginSet(PackedArgs.ArgsPosition); 567 | argsArr.Load(); 568 | packedArr.EndSet(typeof(object[])); 569 | 570 | // packed[PackedArgs.GenericTypesPosition] = mi.GetGenericArguments(); 571 | if (mi.ContainsGenericParameters) 572 | { 573 | packedArr.BeginSet(PackedArgs.GenericTypesPosition); 574 | Type[] genericTypes = mi.GetGenericArguments(); 575 | GenericArray typeArr = new GenericArray(il, genericTypes.Length); 576 | for (int i = 0; i < genericTypes.Length; ++i) 577 | { 578 | typeArr.BeginSet(i); 579 | il.Emit(OpCodes.Ldtoken, genericTypes[i]); 580 | il.Emit(OpCodes.Call, Type_GetTypeFromHandle); 581 | typeArr.EndSet(typeof(Type)); 582 | } 583 | typeArr.Load(); 584 | packedArr.EndSet(typeof(Type[])); 585 | } 586 | 587 | // Call static DispatchProxyHelper.Invoke(object[]) 588 | il.Emit(OpCodes.Ldarg_0); 589 | il.Emit(OpCodes.Ldfld, _fields[InvokeActionFieldAndCtorParameterIndex]); // delegate 590 | packedArr.Load(); 591 | il.Emit(OpCodes.Call, s_delegateInvoke); 592 | 593 | for (int i = 0; i < parameters.Length; i++) 594 | { 595 | if (parameters[i].ParameterType.IsByRef) 596 | { 597 | args.BeginSet(i); 598 | argsArr.Get(i); 599 | args.EndSet(i, typeof(object)); 600 | } 601 | } 602 | 603 | if (mi.ReturnType != typeof(void)) 604 | { 605 | packedArr.Get(PackedArgs.ReturnValuePosition); 606 | Convert(il, typeof(object), mi.ReturnType, false); 607 | } 608 | 609 | il.Emit(OpCodes.Ret); 610 | 611 | _tb.DefineMethodOverride(mdb, mi); 612 | return mdb; 613 | } 614 | 615 | private static Type[] ParamTypes(ParameterInfo[] parms, bool noByRef) 616 | { 617 | Type[] types = new Type[parms.Length]; 618 | for (int i = 0; i < parms.Length; i++) 619 | { 620 | types[i] = parms[i].ParameterType; 621 | if (noByRef && types[i].IsByRef) 622 | types[i] = types[i].GetElementType(); 623 | } 624 | return types; 625 | } 626 | 627 | // TypeCode does not exist in ProjectK or ProjectN. 628 | // This lookup method was copied from PortableLibraryThunks\Internal\PortableLibraryThunks\System\TypeThunks.cs 629 | // but returns the integer value equivalent to its TypeCode enum. 630 | private static int GetTypeCode(Type type) 631 | { 632 | if (type == null) 633 | return 0; // TypeCode.Empty; 634 | 635 | if (type == typeof(bool)) 636 | return 3; // TypeCode.Boolean; 637 | 638 | if (type == typeof(char)) 639 | return 4; // TypeCode.Char; 640 | 641 | if (type == typeof(sbyte)) 642 | return 5; // TypeCode.SByte; 643 | 644 | if (type == typeof(byte)) 645 | return 6; // TypeCode.Byte; 646 | 647 | if (type == typeof(short)) 648 | return 7; // TypeCode.Int16; 649 | 650 | if (type == typeof(ushort)) 651 | return 8; // TypeCode.UInt16; 652 | 653 | if (type == typeof(int)) 654 | return 9; // TypeCode.Int32; 655 | 656 | if (type == typeof(uint)) 657 | return 10; // TypeCode.UInt32; 658 | 659 | if (type == typeof(long)) 660 | return 11; // TypeCode.Int64; 661 | 662 | if (type == typeof(ulong)) 663 | return 12; // TypeCode.UInt64; 664 | 665 | if (type == typeof(float)) 666 | return 13; // TypeCode.Single; 667 | 668 | if (type == typeof(double)) 669 | return 14; // TypeCode.Double; 670 | 671 | if (type == typeof(decimal)) 672 | return 15; // TypeCode.Decimal; 673 | 674 | if (type == typeof(DateTime)) 675 | return 16; // TypeCode.DateTime; 676 | 677 | if (type == typeof(string)) 678 | return 18; // TypeCode.String; 679 | 680 | if (type.GetTypeInfo().IsEnum) 681 | return GetTypeCode(Enum.GetUnderlyingType(type)); 682 | 683 | return 1; // TypeCode.Object; 684 | } 685 | 686 | private static OpCode[] s_convOpCodes = new OpCode[] { 687 | OpCodes.Nop,//Empty = 0, 688 | OpCodes.Nop,//Object = 1, 689 | OpCodes.Nop,//DBNull = 2, 690 | OpCodes.Conv_I1,//Boolean = 3, 691 | OpCodes.Conv_I2,//Char = 4, 692 | OpCodes.Conv_I1,//SByte = 5, 693 | OpCodes.Conv_U1,//Byte = 6, 694 | OpCodes.Conv_I2,//Int16 = 7, 695 | OpCodes.Conv_U2,//UInt16 = 8, 696 | OpCodes.Conv_I4,//Int32 = 9, 697 | OpCodes.Conv_U4,//UInt32 = 10, 698 | OpCodes.Conv_I8,//Int64 = 11, 699 | OpCodes.Conv_U8,//UInt64 = 12, 700 | OpCodes.Conv_R4,//Single = 13, 701 | OpCodes.Conv_R8,//Double = 14, 702 | OpCodes.Nop,//Decimal = 15, 703 | OpCodes.Nop,//DateTime = 16, 704 | OpCodes.Nop,//17 705 | OpCodes.Nop,//String = 18, 706 | }; 707 | 708 | private static OpCode[] s_ldindOpCodes = new OpCode[] { 709 | OpCodes.Nop,//Empty = 0, 710 | OpCodes.Nop,//Object = 1, 711 | OpCodes.Nop,//DBNull = 2, 712 | OpCodes.Ldind_I1,//Boolean = 3, 713 | OpCodes.Ldind_I2,//Char = 4, 714 | OpCodes.Ldind_I1,//SByte = 5, 715 | OpCodes.Ldind_U1,//Byte = 6, 716 | OpCodes.Ldind_I2,//Int16 = 7, 717 | OpCodes.Ldind_U2,//UInt16 = 8, 718 | OpCodes.Ldind_I4,//Int32 = 9, 719 | OpCodes.Ldind_U4,//UInt32 = 10, 720 | OpCodes.Ldind_I8,//Int64 = 11, 721 | OpCodes.Ldind_I8,//UInt64 = 12, 722 | OpCodes.Ldind_R4,//Single = 13, 723 | OpCodes.Ldind_R8,//Double = 14, 724 | OpCodes.Nop,//Decimal = 15, 725 | OpCodes.Nop,//DateTime = 16, 726 | OpCodes.Nop,//17 727 | OpCodes.Ldind_Ref,//String = 18, 728 | }; 729 | 730 | private static OpCode[] s_stindOpCodes = new OpCode[] { 731 | OpCodes.Nop,//Empty = 0, 732 | OpCodes.Nop,//Object = 1, 733 | OpCodes.Nop,//DBNull = 2, 734 | OpCodes.Stind_I1,//Boolean = 3, 735 | OpCodes.Stind_I2,//Char = 4, 736 | OpCodes.Stind_I1,//SByte = 5, 737 | OpCodes.Stind_I1,//Byte = 6, 738 | OpCodes.Stind_I2,//Int16 = 7, 739 | OpCodes.Stind_I2,//UInt16 = 8, 740 | OpCodes.Stind_I4,//Int32 = 9, 741 | OpCodes.Stind_I4,//UInt32 = 10, 742 | OpCodes.Stind_I8,//Int64 = 11, 743 | OpCodes.Stind_I8,//UInt64 = 12, 744 | OpCodes.Stind_R4,//Single = 13, 745 | OpCodes.Stind_R8,//Double = 14, 746 | OpCodes.Nop,//Decimal = 15, 747 | OpCodes.Nop,//DateTime = 16, 748 | OpCodes.Nop,//17 749 | OpCodes.Stind_Ref,//String = 18, 750 | }; 751 | 752 | private static void Convert(ILGenerator il, Type source, Type target, bool isAddress) 753 | { 754 | Debug.Assert(!target.IsByRef); 755 | if (target == source) 756 | return; 757 | 758 | TypeInfo sourceTypeInfo = source.GetTypeInfo(); 759 | TypeInfo targetTypeInfo = target.GetTypeInfo(); 760 | 761 | if (source.IsByRef) 762 | { 763 | Debug.Assert(!isAddress); 764 | Type argType = source.GetElementType(); 765 | Ldind(il, argType); 766 | Convert(il, argType, target, isAddress); 767 | return; 768 | } 769 | if (targetTypeInfo.IsValueType) 770 | { 771 | if (sourceTypeInfo.IsValueType) 772 | { 773 | OpCode opCode = s_convOpCodes[GetTypeCode(target)]; 774 | Debug.Assert(!opCode.Equals(OpCodes.Nop)); 775 | il.Emit(opCode); 776 | } 777 | else 778 | { 779 | Debug.Assert(sourceTypeInfo.IsAssignableFrom(targetTypeInfo)); 780 | il.Emit(OpCodes.Unbox, target); 781 | if (!isAddress) 782 | Ldind(il, target); 783 | } 784 | } 785 | else if (targetTypeInfo.IsAssignableFrom(sourceTypeInfo)) 786 | { 787 | if (sourceTypeInfo.IsValueType || source.IsGenericParameter) 788 | { 789 | if (isAddress) 790 | Ldind(il, source); 791 | il.Emit(OpCodes.Box, source); 792 | } 793 | } 794 | else 795 | { 796 | //Debug.Assert(sourceTypeInfo.IsAssignableFrom(targetTypeInfo) || targetTypeInfo.IsInterface || sourceTypeInfo.IsInterface); 797 | if (target.IsGenericParameter) 798 | { 799 | il.Emit(OpCodes.Unbox_Any, target); 800 | } 801 | else 802 | { 803 | il.Emit(OpCodes.Castclass, target); 804 | } 805 | } 806 | } 807 | 808 | private static void Ldind(ILGenerator il, Type type) 809 | { 810 | OpCode opCode = s_ldindOpCodes[GetTypeCode(type)]; 811 | if (!opCode.Equals(OpCodes.Nop)) 812 | { 813 | il.Emit(opCode); 814 | } 815 | else 816 | { 817 | il.Emit(OpCodes.Ldobj, type); 818 | } 819 | } 820 | 821 | private static void Stind(ILGenerator il, Type type) 822 | { 823 | OpCode opCode = s_stindOpCodes[GetTypeCode(type)]; 824 | if (!opCode.Equals(OpCodes.Nop)) 825 | { 826 | il.Emit(opCode); 827 | } 828 | else 829 | { 830 | il.Emit(OpCodes.Stobj, type); 831 | } 832 | } 833 | 834 | private class ParametersArray 835 | { 836 | private ILGenerator _il; 837 | private Type[] _paramTypes; 838 | internal ParametersArray(ILGenerator il, Type[] paramTypes) 839 | { 840 | _il = il; 841 | _paramTypes = paramTypes; 842 | } 843 | 844 | internal void Get(int i) 845 | { 846 | _il.Emit(OpCodes.Ldarg, i + 1); 847 | } 848 | 849 | internal void BeginSet(int i) 850 | { 851 | _il.Emit(OpCodes.Ldarg, i + 1); 852 | } 853 | 854 | internal void EndSet(int i, Type stackType) 855 | { 856 | Debug.Assert(_paramTypes[i].IsByRef); 857 | Type argType = _paramTypes[i].GetElementType(); 858 | Convert(_il, stackType, argType, false); 859 | Stind(_il, argType); 860 | } 861 | } 862 | 863 | private class GenericArray 864 | { 865 | private ILGenerator _il; 866 | private LocalBuilder _lb; 867 | internal GenericArray(ILGenerator il, int len) 868 | { 869 | _il = il; 870 | _lb = il.DeclareLocal(typeof(T[])); 871 | 872 | il.Emit(OpCodes.Ldc_I4, len); 873 | il.Emit(OpCodes.Newarr, typeof(T)); 874 | il.Emit(OpCodes.Stloc, _lb); 875 | } 876 | 877 | internal void Load() 878 | { 879 | _il.Emit(OpCodes.Ldloc, _lb); 880 | } 881 | 882 | internal void Get(int i) 883 | { 884 | _il.Emit(OpCodes.Ldloc, _lb); 885 | _il.Emit(OpCodes.Ldc_I4, i); 886 | _il.Emit(OpCodes.Ldelem_Ref); 887 | } 888 | 889 | internal void BeginSet(int i) 890 | { 891 | _il.Emit(OpCodes.Ldloc, _lb); 892 | _il.Emit(OpCodes.Ldc_I4, i); 893 | } 894 | 895 | internal void EndSet(Type stackType) 896 | { 897 | Convert(_il, stackType, typeof(T), false); 898 | _il.Emit(OpCodes.Stelem_Ref); 899 | } 900 | } 901 | 902 | private sealed class PropertyAccessorInfo 903 | { 904 | public MethodInfo InterfaceGetMethod { get; } 905 | public MethodInfo InterfaceSetMethod { get; } 906 | public MethodBuilder GetMethodBuilder { get; set; } 907 | public MethodBuilder SetMethodBuilder { get; set; } 908 | 909 | public PropertyAccessorInfo(MethodInfo interfaceGetMethod, MethodInfo interfaceSetMethod) 910 | { 911 | InterfaceGetMethod = interfaceGetMethod; 912 | InterfaceSetMethod = interfaceSetMethod; 913 | } 914 | } 915 | 916 | private sealed class EventAccessorInfo 917 | { 918 | public MethodInfo InterfaceAddMethod { get; } 919 | public MethodInfo InterfaceRemoveMethod { get; } 920 | public MethodInfo InterfaceRaiseMethod { get; } 921 | public MethodBuilder AddMethodBuilder { get; set; } 922 | public MethodBuilder RemoveMethodBuilder { get; set; } 923 | public MethodBuilder RaiseMethodBuilder { get; set; } 924 | 925 | public EventAccessorInfo(MethodInfo interfaceAddMethod, MethodInfo interfaceRemoveMethod, MethodInfo interfaceRaiseMethod) 926 | { 927 | InterfaceAddMethod = interfaceAddMethod; 928 | InterfaceRemoveMethod = interfaceRemoveMethod; 929 | InterfaceRaiseMethod = interfaceRaiseMethod; 930 | } 931 | } 932 | 933 | private sealed class MethodInfoEqualityComparer : EqualityComparer 934 | { 935 | public static readonly MethodInfoEqualityComparer Instance = new MethodInfoEqualityComparer(); 936 | 937 | private MethodInfoEqualityComparer() { } 938 | 939 | public sealed override bool Equals(MethodInfo left, MethodInfo right) 940 | { 941 | if (ReferenceEquals(left, right)) 942 | return true; 943 | 944 | if (left == null) 945 | return right == null; 946 | else if (right == null) 947 | return false; 948 | 949 | // This assembly should work in netstandard1.3, 950 | // so we cannot use MemberInfo.MetadataToken here. 951 | // Therefore, it compares honestly referring ECMA-335 I.8.6.1.6 Signature Matching. 952 | if (!Equals(left.DeclaringType, right.DeclaringType)) 953 | return false; 954 | 955 | if (!Equals(left.ReturnType, right.ReturnType)) 956 | return false; 957 | 958 | if (left.CallingConvention != right.CallingConvention) 959 | return false; 960 | 961 | if (left.IsStatic != right.IsStatic) 962 | return false; 963 | 964 | if (left.Name != right.Name) 965 | return false; 966 | 967 | Type[] leftGenericParameters = left.GetGenericArguments(); 968 | Type[] rightGenericParameters = right.GetGenericArguments(); 969 | if (leftGenericParameters.Length != rightGenericParameters.Length) 970 | return false; 971 | 972 | for (int i = 0; i < leftGenericParameters.Length; i++) 973 | { 974 | if (!Equals(leftGenericParameters[i], rightGenericParameters[i])) 975 | return false; 976 | } 977 | 978 | ParameterInfo[] leftParameters = left.GetParameters(); 979 | ParameterInfo[] rightParameters = right.GetParameters(); 980 | if (leftParameters.Length != rightParameters.Length) 981 | return false; 982 | 983 | for (int i = 0; i < leftParameters.Length; i++) 984 | { 985 | if (!Equals(leftParameters[i].ParameterType, rightParameters[i].ParameterType)) 986 | return false; 987 | } 988 | 989 | return true; 990 | } 991 | 992 | public sealed override int GetHashCode(MethodInfo obj) 993 | { 994 | if (obj == null) 995 | return 0; 996 | 997 | int hashCode = obj.DeclaringType.GetHashCode(); 998 | hashCode ^= obj.Name.GetHashCode(); 999 | foreach (ParameterInfo parameter in obj.GetParameters()) 1000 | { 1001 | hashCode ^= parameter.ParameterType.GetHashCode(); 1002 | } 1003 | 1004 | return hashCode; 1005 | } 1006 | } 1007 | } 1008 | } 1009 | } -------------------------------------------------------------------------------- /src/ClrMDExports/ILLDBDebugClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Diagnostics.Runtime.Interop; 2 | 3 | namespace ClrMDExports 4 | { 5 | internal interface ILLDBDebugClient : IDebugClient, IDebugClient2, IDebugClient3, IDebugClient4, IDebugClient5, IDebugClient6, 6 | IDebugControl, IDebugControl2, IDebugControl3, IDebugControl4, IDebugControl5, IDebugControl6, 7 | IDebugSymbols, IDebugSymbols2, IDebugSymbols3, IDebugSymbols4, IDebugSymbols5, 8 | IDebugAdvanced, IDebugAdvanced2, IDebugAdvanced3, 9 | IDebugRegisters, IDebugRegisters2, 10 | IDebugBreakpoint, IDebugBreakpoint2, IDebugBreakpoint3, 11 | IDebugDataSpaces, IDebugDataSpaces2, IDebugDataSpaces3, IDebugDataSpaces4, 12 | IDebugSymbolGroup, IDebugSymbolGroup2, 13 | IDebugSystemObjects, IDebugSystemObjects2, //IDebugSystemObjects3, 14 | IDebugOutputCallbacks, IDebugOutputCallbacks2, 15 | IDebugInputCallbacks, 16 | IDebugDataSpacesPtr, 17 | IDebugEventCallbacksWide, 18 | IDebugOutputCallbacksWide, 19 | IDebugEventContextCallbacks 20 | { 21 | } 22 | } -------------------------------------------------------------------------------- /src/ClrMDExports/Init.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace ClrMDExports 7 | { 8 | internal class Init 9 | { 10 | [RGiesecke.DllExport.DllExport("DebugExtensionInitialize")] 11 | public static int DebugExtensionInitialize(ref uint version, ref uint flags) 12 | { 13 | var extensionFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 14 | 15 | try 16 | { 17 | var file = Path.Combine(extensionFolder, "ClrMDExports.dll"); 18 | 19 | var assemblyName = AssemblyName.GetAssemblyName(file); 20 | 21 | ForceAssemblyLoad(assemblyName); 22 | } 23 | catch (Exception ex) 24 | { 25 | Console.WriteLine("Could not load ClrMDExports: " + ex); 26 | } 27 | 28 | InitializeDebuggingContext(); 29 | 30 | // Set the extension version to 1, which expects exports with this signature: 31 | // void _stdcall function(IDebugClient *client, const char *args) 32 | version = DEBUG_EXTENSION_VERSION(1, 0); 33 | flags = 0; 34 | return 0; 35 | } 36 | 37 | [MethodImpl(MethodImplOptions.NoInlining)] 38 | private static void InitializeDebuggingContext() 39 | { 40 | Private.Initialization.IsWinDbg = true; 41 | } 42 | 43 | private static void ForceAssemblyLoad(AssemblyName assemblyName) 44 | { 45 | var codeBase = assemblyName.CodeBase; 46 | 47 | if (codeBase.StartsWith("file://")) 48 | { 49 | codeBase = codeBase.Substring(8).Replace('/', '\\'); 50 | } 51 | 52 | ResolveEventHandler assemblyResolve = (sender, args) => args.Name == assemblyName.FullName ? Assembly.LoadFrom(codeBase) : null; 53 | 54 | AppDomain.CurrentDomain.AssemblyResolve += assemblyResolve; 55 | 56 | Assembly.Load(assemblyName.FullName); 57 | 58 | AppDomain.CurrentDomain.AssemblyResolve -= assemblyResolve; 59 | } 60 | 61 | static uint DEBUG_EXTENSION_VERSION(uint major, uint minor) 62 | { 63 | return ((((major) & 0xffff) << 16) | ((minor) & 0xffff)); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/ClrMDExports/Initialization.cs: -------------------------------------------------------------------------------- 1 | namespace ClrMDExports.Private 2 | { 3 | public class Initialization 4 | { 5 | public static bool IsWinDbg 6 | { 7 | set => DebuggingContext.IsWinDbg = value; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ClrMDExports/NativeInvoker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace ClrMDExports 9 | { 10 | internal class NativeInvoker : DispatchProxy 11 | { 12 | private readonly ConcurrentDictionary<(string, string), Type> _delegateTypes = new ConcurrentDictionary<(string, string), Type>(); 13 | 14 | private IntPtr _address; 15 | private Type _interfaceType; 16 | private string[] _vtable; 17 | 18 | public static T GetInstance(IntPtr address, string[] vtable = null) 19 | { 20 | var proxy = DispatchProxy.Create(); 21 | 22 | if (vtable == null) 23 | { 24 | vtable = typeof(T).GetMethods() 25 | .Select(m => m.Name) 26 | .ToArray(); 27 | } 28 | 29 | (proxy as NativeInvoker).Configure(typeof(T), address, vtable); 30 | 31 | return proxy; 32 | } 33 | 34 | internal void Configure(Type interfaceType, IntPtr address, string[] vtable) 35 | { 36 | _interfaceType = interfaceType; 37 | _address = address; 38 | _vtable = vtable; 39 | } 40 | 41 | 42 | protected override unsafe object Invoke(MethodInfo targetMethod, object[] args) 43 | { 44 | var delegateType = _delegateTypes.GetOrAdd((targetMethod.DeclaringType.FullName, targetMethod.Name), _ => DelegateBuilder.GenerateDelegateType(targetMethod)); 45 | 46 | var vtable = *(long**)_address; 47 | 48 | int offset = -1; 49 | 50 | for (int i = 0; i < _vtable.Length; i++) 51 | { 52 | if (_vtable[i] == targetMethod.Name) 53 | { 54 | offset = i; 55 | break; 56 | } 57 | } 58 | 59 | if (offset == -1) 60 | { 61 | Console.WriteLine("Could not find {0} in the vtable for {1}", targetMethod.Name, _interfaceType.Name); 62 | } 63 | 64 | var func = Marshal.GetDelegateForFunctionPointer(new IntPtr(vtable[offset]), delegateType); 65 | 66 | var argsWithSelf = new object[args.Length + 1]; 67 | 68 | argsWithSelf[0] = _address; 69 | 70 | for (int i = 0; i < args.Length; i++) 71 | { 72 | argsWithSelf[i + 1] = args[i]; 73 | } 74 | 75 | var result = func.DynamicInvoke(argsWithSelf); 76 | 77 | for (int i = 0; i < args.Length; i++) 78 | { 79 | args[i] = argsWithSelf[i + 1]; 80 | } 81 | 82 | return result; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/ClrMDExports/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("ClrMDExports")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ClrMDExports")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 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("f5a1b095-d321-4734-aef5-8ad353c7b3d4")] 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 | -------------------------------------------------------------------------------- /src/ClrMDExports/StripDmlWriter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace ClrMDExports 6 | { 7 | internal class StripDmlWriter : TextWriter 8 | { 9 | private readonly TextWriter _out; 10 | 11 | public StripDmlWriter(TextWriter console) 12 | { 13 | _out = console; 14 | } 15 | 16 | public override Encoding Encoding => _out.Encoding; 17 | 18 | public override void Write(string value) 19 | { 20 | if (value != null) 21 | { 22 | _out.Write(Regex.Replace(value, @"<[^>]*>", string.Empty)); 23 | } 24 | } 25 | } 26 | } --------------------------------------------------------------------------------