├── .gitattributes ├── .gitignore ├── Assemblies ├── Microsoft.DebugEngineHost.dll ├── Microsoft.MICore.dll ├── Techl.Mono.Cecil.dll ├── Techl.Mono.Debugger.Soft.dll └── Techl.dll ├── AssemblyInfo.cs ├── CHANGELOG.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE.txt ├── MonoRemoteDebugger.Debugger ├── Contracts │ └── IDebugSession.cs ├── DebugEngineHost │ └── HostOutputWindowEx.cs ├── DebugHelper.cs ├── Enums │ └── SteppingTypeEnum.cs ├── MIDebugEngine │ ├── AD7.Impl │ │ ├── AD7Enums.cs │ │ ├── AD7Events.cs │ │ └── EngineConstants.cs │ └── Engine.Impl │ │ ├── DebuggedThread.cs │ │ ├── EngineUtils.cs │ │ ├── MITextPosition.cs │ │ ├── Structures.cs │ │ └── WorkerThread.cs ├── MonoRemoteDebugger.Debugger.csproj ├── RoslynHelper.cs ├── TypeSummary.cs ├── VisualStudio │ ├── AD7BoundBreakpoint.cs │ ├── AD7DocumentContext.cs │ ├── AD7Engine.cs │ ├── AD7Expression.cs │ ├── AD7Guids.cs │ ├── AD7PendingBreakpoint.cs │ ├── AD7ProgramNode.cs │ ├── AD7ProgramProvider.cs │ ├── AD7StackFrame.cs │ ├── AD7Thread.cs │ ├── AsyncDispatcher.cs │ ├── DebuggedMonoThread.cs │ ├── DebuggedProcess.cs │ ├── EngineCallback.cs │ ├── MonoBreakpointLocation.cs │ ├── MonoDocument.cs │ ├── MonoProcess.cs │ └── MonoProperty.cs ├── app.config └── packages.config ├── MonoRemoteDebugger.Server ├── App.config ├── MonoRemoteDebugger.Server.csproj └── Program.cs ├── MonoRemoteDebugger.SharedLib ├── ApplicationType.cs ├── Command.cs ├── ExtensionMethods.cs ├── GlobalConfig.cs ├── MessageBase.cs ├── MonoLogger.cs ├── MonoRemoteDebugger.SharedLib.csproj ├── Server │ ├── ClientSession.cs │ ├── MonoDebugServer.cs │ ├── MonoDesktopProcess.cs │ ├── MonoProcess.cs │ ├── MonoUtils.cs │ ├── MonoWebProcess.cs │ └── Pdb2MdbGenerator.cs ├── StartDebuggingMessage.cs ├── StatusMessage.cs ├── TcpCommunication.cs └── ZipFile.cs ├── MonoRemoteDebugger.VSExtension ├── MonoClient │ ├── DebugClient.cs │ ├── DebugSession.cs │ ├── MonoServerDiscovery.cs │ └── MonoServerInformation.cs ├── MonoRemoteDebugger.VSExtension.csproj ├── MonoRemoteDebuggerInstaller.cs ├── MonoVisualStudioExtension.cs ├── Resources │ ├── ArrowDown.png │ ├── Images.png │ ├── MonoLogo.png │ ├── MonoRemoteDebugger.png │ └── Resources.xaml ├── Settings │ ├── UserSettings.cs │ └── UserSettingsManager.cs ├── VSCommandTable.cs ├── VSCommandTable.vsct ├── VSPackage.cs ├── Views │ ├── ServersFound.xaml │ ├── ServersFound.xaml.cs │ └── ServersFoundViewModel.cs ├── app.config ├── source.extension.cs ├── source.extension.ico ├── source.extension.resx └── source.extension.vsixmanifest ├── MonoRemoteDebugger.sln ├── Output └── Debug │ └── Microsoft.CodeAnalysis.CSharp.dll ├── README.md ├── UpdateVSIXManifest.ps1 └── appveyor.yml /.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 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | -------------------------------------------------------------------------------- /Assemblies/Microsoft.DebugEngineHost.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/Assemblies/Microsoft.DebugEngineHost.dll -------------------------------------------------------------------------------- /Assemblies/Microsoft.MICore.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/Assemblies/Microsoft.MICore.dll -------------------------------------------------------------------------------- /Assemblies/Techl.Mono.Cecil.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/Assemblies/Techl.Mono.Cecil.dll -------------------------------------------------------------------------------- /Assemblies/Techl.Mono.Debugger.Soft.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/Assemblies/Techl.Mono.Debugger.Soft.dll -------------------------------------------------------------------------------- /Assemblies/Techl.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/Assemblies/Techl.dll -------------------------------------------------------------------------------- /AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("MonoRemoteDebugger")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("Techl.com")] 8 | [assembly: AssemblyProduct("MonoRemoteDebugger")] 9 | [assembly: AssemblyCopyright("Copyright 2019 Techl.com")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | 13 | [assembly: ComVisible(false)] 14 | [assembly: Guid("3edd3e10-0982-4a3c-9715-f5d6bac172ac")] 15 | [assembly: AssemblyVersion(MonoRemoteDebugger.VSExtension.Vsix.Version)] 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | - [ ] Watch complex objects. 4 | - [ ] Support breakpoints on worker threads. 5 | 6 | Features that have a checkmark are complete and available for 7 | download in the 8 | [CI build](http://vsixgallery.com/extension/27D183E9-5D2B-44D6-9EC8-2DB329096DF7/). 9 | 10 | # Changelog 11 | 12 | These are the changes to each version that has been released 13 | on the official Visual Studio extension gallery. 14 | 15 | ## 1.5.2 16 | 17 | - [x] Fix Server bug on debug exit. 18 | - [x] BackgroundLoad 19 | 20 | ## 1.5.1 21 | 22 | - [x] Fix VS crash on exit(#51) 23 | 24 | ## 1.5.0 25 | 26 | - [x] Change to support AsyncPackage for VS2019 27 | - [x] Support Start Command line arguments 28 | - [x] Remove project warnings 29 | - [x] Support Enter key for connecting on connection dialog 30 | 31 | ## 1.4.0 32 | 33 | - [x] Support VS2019 34 | - [x] Support big file(#49) 35 | 36 | ## 1.3.1-beta2 37 | 38 | - [x] Fix problem of GTK application(#48) 39 | - [x] nuget update 40 | 41 | ## 1.3.0 42 | **2017-07-04** 43 | 44 | - [x] [Added support for Windows 10 with Mono 64Bit](https://github.com/techl/MonoRemoteDebugger/pull/39/commits/912b5c4f9fac23d21ae2b1313ec08cf68522c57b) 45 | - [x] [New Feature: Debug last content if AppHash is equal. No content transfer needed (reason: faster debugging for embedded devices if content hasn't changed)](https://github.com/techl/MonoRemoteDebugger/pull/39/commits/f4d256c806278ec9bf86c7f799ebe08a2ab90de6) 46 | - [x] [Support for complex objects (arrays, struct, class, local, parameters) and callstack added](https://github.com/techl/MonoRemoteDebugger/pull/39/commits/60215b17fc7667a96d24d1dec091fe3d2f841fbb) 47 | - [x] [Better printing support for string, struct, object, jagged array and multidimensional array](https://github.com/techl/MonoRemoteDebugger/pull/39/commits/83f84c1fd7c38e9fa9ac2a6a8dade60427a7e171) 48 | 49 | ## 1.2.1 50 | **2017-05-26** 51 | 52 | - [x] Support VS2013 in vsix 53 | 54 | ## 1.2.0 55 | **2017-03-30** 56 | 57 | - [x] Support changing MonoRemoteDebugger Server port 58 | 59 | ## 1.1.0 60 | **2017-03-28** 61 | 62 | - [x] Support Visual Studio 2017 63 | 64 | ## 1.0.11 65 | **2016-08-02** 66 | 67 | - [x] Fixed the bug "the parameter is incorrect" which is occurred if the project is inside solution folders. 68 | 69 | ## 1.0.10 70 | **2016-06-29** 71 | 72 | - [x] Fixed the bug running without Xamarin extension. 73 | - [x] Cleared potential mono dll conflicts. 74 | - [x] Support VS15 75 | 76 | ## 1.0.9 77 | **2016-06-07** 78 | 79 | - [x] Fixed the bug that if project name has spaces, it doesn't work. 80 | 81 | ## 1.0.8 82 | **2016-06-06** 83 | 84 | - [x] Changed Mono.Debugger.Soft.dll name for avoiding conflict. 85 | 86 | ## 1.0.7 87 | 88 | **2016-05-14** 89 | 90 | - [x] Fixed VS hang bug. 91 | 92 | ## 1.0.6 93 | 94 | **2016-05-14** 95 | 96 | - [x] Fixed a bug which cannot find mono path on windows 10 x64 97 | 98 | ## 1.0.5 99 | 100 | - [x] Bug fixed 101 | 102 | ## 1.0.4 103 | 104 | - [x] Fixed conflict with Xamarin VS Extension. 105 | 106 | ## 1.0.3 107 | 108 | - [x] Bug fixed 109 | 110 | 111 | ## 1.0.2 112 | 113 | - [x] Support Visual Studio Output Window 114 | 115 | - [x] Support deleting Break Point 116 | 117 | ## 1.0.1 118 | 119 | - [x] Support Continue operation 120 | 121 | ## 1.0.0 122 | 123 | - [x] Initial Release 124 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Looking to contribute something? **Here's how you can help.** 4 | 5 | Please take a moment to review this document in order to make the contribution 6 | process easy and effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of 9 | the developers managing and developing this open source project. In return, 10 | they should reciprocate that respect in addressing your issue or assessing 11 | patches and features. 12 | 13 | 14 | ## Using the issue tracker 15 | 16 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 17 | [features requests](#feature-requests) and 18 | [submitting pull requests](#pull-requests), but please respect the 19 | following restrictions: 20 | 21 | * Please **do not** use the issue tracker for personal support requests. Stack 22 | Overflow is a better place to get help. 23 | 24 | * Please **do not** derail or troll issues. Keep the discussion on topic and 25 | respect the opinions of others. 26 | 27 | * Please **do not** open issues or pull requests which *belongs to* third party 28 | components. 29 | 30 | 31 | ## Bug reports 32 | 33 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 34 | Good bug reports are extremely helpful, so thanks! 35 | 36 | Guidelines for bug reports: 37 | 38 | 1. **Use the GitHub issue search** — check if the issue has already been 39 | reported. 40 | 41 | 2. **Check if the issue has been fixed** — try to reproduce it using the 42 | latest `master` or development branch in the repository. 43 | 44 | 3. **Isolate the problem** — ideally create an 45 | [SSCCE](http://www.sscce.org/) and a live example. 46 | Uploading the project on cloud storage (OneDrive, DropBox, et el.) 47 | or creating a sample GitHub repository is also helpful. 48 | 49 | 50 | A good bug report shouldn't leave others needing to chase you up for more 51 | information. Please try to be as detailed as possible in your report. What is 52 | your environment? What steps will reproduce the issue? What browser(s) and OS 53 | experience the problem? Do other browsers show the bug differently? What 54 | would you expect to be the outcome? All these details will help people to fix 55 | any potential bugs. 56 | 57 | Example: 58 | 59 | > Short and descriptive example bug report title 60 | > 61 | > A summary of the issue and the Visual Studio, browser, OS environments 62 | > in which it occurs. If suitable, include the steps required to reproduce the bug. 63 | > 64 | > 1. This is the first step 65 | > 2. This is the second step 66 | > 3. Further steps, etc. 67 | > 68 | > `` - a link to the project/file uploaded on cloud storage or other publicly accessible medium. 69 | > 70 | > Any other information you want to share that is relevant to the issue being 71 | > reported. This might include the lines of code that you have identified as 72 | > causing the bug, and potential solutions (and your opinions on their 73 | > merits). 74 | 75 | 76 | ## Feature requests 77 | 78 | Feature requests are welcome. But take a moment to find out whether your idea 79 | fits with the scope and aims of the project. It's up to *you* to make a strong 80 | case to convince the project's developers of the merits of this feature. Please 81 | provide as much detail and context as possible. 82 | 83 | 84 | ## Pull requests 85 | 86 | Good pull requests, patches, improvements and new features are a fantastic 87 | help. They should remain focused in scope and avoid containing unrelated 88 | commits. 89 | 90 | **Please ask first** before embarking on any significant pull request (e.g. 91 | implementing features, refactoring code, porting to a different language), 92 | otherwise you risk spending a lot of time working on something that the 93 | project's developers might not want to merge into the project. 94 | 95 | Please adhere to the [coding guidelines](#code-guidelines) used throughout the 96 | project (indentation, accurate comments, etc.) and any other requirements 97 | (such as test coverage). 98 | 99 | Adhering to the following process is the best way to get your work 100 | included in the project: 101 | 102 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 103 | and configure the remotes: 104 | 105 | ```bash 106 | # Clone your fork of the repo into the current directory 107 | git clone https://github.com//.git 108 | # Navigate to the newly cloned directory 109 | cd 110 | # Assign the original repo to a remote called "upstream" 111 | git remote add upstream https://github.com/madskristensen/.git 112 | ``` 113 | 114 | 2. If you cloned a while ago, get the latest changes from upstream: 115 | 116 | ```bash 117 | git checkout master 118 | git pull upstream master 119 | ``` 120 | 121 | 3. Create a new topic branch (off the main project development branch) to 122 | contain your feature, change, or fix: 123 | 124 | ```bash 125 | git checkout -b 126 | ``` 127 | 128 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 129 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 130 | or your code is unlikely be merged into the main project. Use Git's 131 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 132 | feature to tidy up your commits before making them public. Also, prepend name of the feature 133 | to the commit message. For instance: "SCSS: Fixes compiler results for IFileListener.\nFixes `#123`" 134 | 135 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 136 | 137 | ```bash 138 | git pull [--rebase] upstream master 139 | ``` 140 | 141 | 6. Push your topic branch up to your fork: 142 | 143 | ```bash 144 | git push origin 145 | ``` 146 | 147 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 148 | with a clear title and description against the `master` branch. 149 | 150 | 151 | ## Code guidelines 152 | 153 | - Always use proper indentation. 154 | - In Visual Studio under `Tools > Options > Text Editor > C# > Advanced`, make sure 155 | `Place 'System' directives first when sorting usings` option is enabled (checked). 156 | - Before committing, organize usings for each updated C# source file. Either you can 157 | right-click editor and select `Organize Usings > Remove and sort` OR use extension 158 | like [BatchFormat](http://visualstudiogallery.msdn.microsoft.com/a7f75c34-82b4-4357-9c66-c18e32b9393e). 159 | - Before committing, run Code Analysis in `Debug` configuration and follow the guidelines 160 | to fix CA issues. Code Analysis commits can be made separately. 161 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Installed product versions 2 | - Visual Studio: [example 2015 Professional] 3 | - This extension: [example 1.1.21] 4 | 5 | ### Description 6 | Replace this text with a short description 7 | 8 | ### Steps to recreate 9 | 1. Replace this 10 | 2. text with 11 | 3. the steps 12 | 4. to recreate 13 | 14 | ### Current behavior 15 | Explain what it's doing and why it's wrong 16 | 17 | ### Expected behavior 18 | Explain what it should be doing after it's fixed. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 Christian Giesswein 2 | Copyright 2016 Techl.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/Contracts/IDebugSession.cs: -------------------------------------------------------------------------------- 1 | namespace MonoRemoteDebugger.Contracts 2 | { 3 | public interface IDebugSession 4 | { 5 | void Disconnect(); 6 | } 7 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/DebugEngineHost/HostOutputWindowEx.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MonoRemoteDebugger.Debugger.DebugEngineHost 11 | { 12 | public static class HostOutputWindowEx 13 | { 14 | // Use an extra class so that we have a seperate class which depends on VS interfaces 15 | private static class VsImpl 16 | { 17 | internal static void SetText(string outputMessage) 18 | { 19 | int hr; 20 | 21 | var outputWindow = (IVsOutputWindow)Package.GetGlobalService(typeof(SVsOutputWindow)); 22 | if (outputWindow == null) 23 | return; 24 | 25 | IVsOutputWindowPane pane; 26 | Guid guidDebugOutputPane = VSConstants.GUID_OutWindowDebugPane; 27 | hr = outputWindow.GetPane(ref guidDebugOutputPane, out pane); 28 | if (hr < 0) 29 | return; 30 | 31 | //pane.Clear(); 32 | //pane.Activate(); 33 | 34 | hr = pane.OutputString(outputMessage); 35 | //if (hr < 0) 36 | // return; 37 | 38 | //var shell = (IVsUIShell)Package.GetGlobalService(typeof(SVsUIShell)); 39 | //if (shell == null) 40 | // return; 41 | 42 | //Guid commandSet = VSConstants.GUID_VSStandardCommandSet97; 43 | //object inputVariant = null; 44 | //shell.PostExecCommand(commandSet, (uint)VSConstants.VSStd97CmdID.OutputWindow, 0, ref inputVariant); 45 | } 46 | } 47 | 48 | /// 49 | /// Write text to the Debug VS Output window pane directly. This is used to write information before the session create event. 50 | /// 51 | /// 52 | public static void WriteLaunchError(string outputMessage) 53 | { 54 | try 55 | { 56 | VsImpl.SetText(outputMessage); 57 | } 58 | catch (Exception) 59 | { 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/DebugHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using NLog; 5 | 6 | namespace MonoRemoteDebugger.Debugger 7 | { 8 | public static class DebugHelper 9 | { 10 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 11 | 12 | internal static void TraceEnteringMethod([CallerMemberName] string callerMember = "") 13 | { 14 | MethodBase mth = new StackTrace().GetFrame(1).GetMethod(); 15 | if (mth.ReflectedType != null) 16 | { 17 | string className = mth.ReflectedType.Name; 18 | logger.Trace(className + " (entering) : " + callerMember); 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/Enums/SteppingTypeEnum.cs: -------------------------------------------------------------------------------- 1 | namespace MonoRemoteDebugger.Debugger.Enums 2 | { 3 | internal enum SteppingTypeEnum 4 | { 5 | None, 6 | StepInto, 7 | StepOut, 8 | StepOver 9 | } 10 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/MIDebugEngine/AD7.Impl/AD7Enums.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using Microsoft.VisualStudio.Debugger.Interop; 8 | 9 | 10 | // These classes use a generic enumerator implementation to create the various enumerators required by the engine. 11 | // They allow the enumeration of everything from programs to breakpoints. 12 | 13 | namespace Microsoft.MIDebugEngine 14 | { 15 | #region Base Class 16 | internal class AD7Enum where I : class 17 | { 18 | private readonly T[] _data; 19 | private uint _position; 20 | 21 | public AD7Enum(T[] data) 22 | { 23 | _data = data; 24 | _position = 0; 25 | } 26 | 27 | public int Clone(out I ppEnum) 28 | { 29 | ppEnum = null; 30 | return Constants.E_NOTIMPL; 31 | } 32 | 33 | public int GetCount(out uint pcelt) 34 | { 35 | pcelt = (uint)_data.Length; 36 | return Constants.S_OK; 37 | } 38 | 39 | public int Next(uint celt, T[] rgelt, out uint celtFetched) 40 | { 41 | return Move(celt, rgelt, out celtFetched); 42 | } 43 | 44 | public int Reset() 45 | { 46 | lock (this) 47 | { 48 | _position = 0; 49 | 50 | return Constants.S_OK; 51 | } 52 | } 53 | 54 | public int Skip(uint celt) 55 | { 56 | uint celtFetched; 57 | 58 | return Move(celt, null, out celtFetched); 59 | } 60 | 61 | private int Move(uint celt, T[] rgelt, out uint celtFetched) 62 | { 63 | lock (this) 64 | { 65 | int hr = Constants.S_OK; 66 | celtFetched = (uint)_data.Length - _position; 67 | 68 | if (celt > celtFetched) 69 | { 70 | hr = Constants.S_FALSE; 71 | } 72 | else if (celt < celtFetched) 73 | { 74 | celtFetched = celt; 75 | } 76 | 77 | if (rgelt != null) 78 | { 79 | for (int c = 0; c < celtFetched; c++) 80 | { 81 | rgelt[c] = _data[_position + c]; 82 | } 83 | } 84 | 85 | _position += celtFetched; 86 | 87 | return hr; 88 | } 89 | } 90 | } 91 | #endregion Base Class 92 | 93 | internal class AD7ProgramEnum : AD7Enum, IEnumDebugPrograms2 94 | { 95 | public AD7ProgramEnum(IDebugProgram2[] data) : base(data) 96 | { 97 | } 98 | 99 | public int Next(uint celt, IDebugProgram2[] rgelt, ref uint celtFetched) 100 | { 101 | return Next(celt, rgelt, out celtFetched); 102 | } 103 | } 104 | 105 | internal class AD7FrameInfoEnum : AD7Enum, IEnumDebugFrameInfo2 106 | { 107 | public AD7FrameInfoEnum(FRAMEINFO[] data) 108 | : base(data) 109 | { 110 | } 111 | 112 | public int Next(uint celt, FRAMEINFO[] rgelt, ref uint celtFetched) 113 | { 114 | return Next(celt, rgelt, out celtFetched); 115 | } 116 | } 117 | 118 | internal class AD7PropertyInfoEnum : AD7Enum, IEnumDebugPropertyInfo2 119 | { 120 | public AD7PropertyInfoEnum(DEBUG_PROPERTY_INFO[] data) 121 | : base(data) 122 | { 123 | } 124 | } 125 | 126 | internal class AD7ThreadEnum : AD7Enum, IEnumDebugThreads2 127 | { 128 | public AD7ThreadEnum(IDebugThread2[] threads) 129 | : base(threads) 130 | { 131 | } 132 | 133 | public int Next(uint celt, IDebugThread2[] rgelt, ref uint celtFetched) 134 | { 135 | return Next(celt, rgelt, out celtFetched); 136 | } 137 | } 138 | 139 | internal class AD7ModuleEnum : AD7Enum, IEnumDebugModules2 140 | { 141 | public AD7ModuleEnum(IDebugModule2[] modules) 142 | : base(modules) 143 | { 144 | } 145 | 146 | public int Next(uint celt, IDebugModule2[] rgelt, ref uint celtFetched) 147 | { 148 | return Next(celt, rgelt, out celtFetched); 149 | } 150 | } 151 | 152 | internal class AD7PropertyEnum : AD7Enum, IEnumDebugPropertyInfo2 153 | { 154 | public AD7PropertyEnum(DEBUG_PROPERTY_INFO[] properties) 155 | : base(properties) 156 | { 157 | } 158 | } 159 | 160 | internal class AD7CodeContextEnum : AD7Enum, IEnumDebugCodeContexts2 161 | { 162 | public AD7CodeContextEnum(IDebugCodeContext2[] codeContexts) 163 | : base(codeContexts) 164 | { 165 | } 166 | 167 | public int Next(uint celt, IDebugCodeContext2[] rgelt, ref uint celtFetched) 168 | { 169 | return Next(celt, rgelt, out celtFetched); 170 | } 171 | } 172 | 173 | internal class AD7BoundBreakpointsEnum : AD7Enum, IEnumDebugBoundBreakpoints2 174 | { 175 | public AD7BoundBreakpointsEnum(IDebugBoundBreakpoint2[] breakpoints) 176 | : base(breakpoints) 177 | { 178 | } 179 | 180 | public int Next(uint celt, IDebugBoundBreakpoint2[] rgelt, ref uint celtFetched) 181 | { 182 | return Next(celt, rgelt, out celtFetched); 183 | } 184 | } 185 | 186 | internal class AD7ErrorBreakpointsEnum : AD7Enum, IEnumDebugErrorBreakpoints2 187 | { 188 | public AD7ErrorBreakpointsEnum(IDebugErrorBreakpoint2[] breakpoints) 189 | : base(breakpoints) 190 | { 191 | } 192 | 193 | public int Next(uint celt, IDebugErrorBreakpoint2[] rgelt, ref uint celtFetched) 194 | { 195 | return Next(celt, rgelt, out celtFetched); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/MIDebugEngine/AD7.Impl/EngineConstants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Microsoft.MIDebugEngine 9 | { 10 | static public class EngineConstants 11 | { 12 | /// 13 | /// This is the engine GUID of the engine. It needs to be changed here and in the registration 14 | /// when creating a new engine. 15 | /// 16 | public const string EngineId = "{ea6637c6-17df-45b5-a183-0951c54243bc}"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/MIDebugEngine/Engine.Impl/MITextPosition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using MICore; 5 | using Microsoft.VisualStudio.Debugger.Interop; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Microsoft.MIDebugEngine 14 | { 15 | internal class MITextPosition 16 | { 17 | public string FileName { get; private set; } 18 | public TEXT_POSITION BeginPosition { get; private set; } 19 | public TEXT_POSITION EndPosition { get; private set; } 20 | 21 | public MITextPosition(string filename, TEXT_POSITION beginPosition, TEXT_POSITION endPosition) 22 | { 23 | this.FileName = filename; 24 | this.BeginPosition = beginPosition; 25 | this.EndPosition = endPosition; 26 | } 27 | 28 | public static MITextPosition TryParse(TupleValue miTuple) 29 | { 30 | string filename = miTuple.TryFindString("fullname"); 31 | if (string.IsNullOrEmpty(filename)) 32 | { 33 | filename = miTuple.TryFindString("file"); 34 | } 35 | if (!string.IsNullOrEmpty(filename)) 36 | { 37 | filename = DebuggedProcess.UnixPathToWindowsPath(filename); 38 | } 39 | 40 | if (string.IsNullOrWhiteSpace(filename)) 41 | return null; 42 | 43 | uint? line = miTuple.TryFindUint("line"); 44 | if (!line.HasValue || line.Value == 0) 45 | return null; 46 | 47 | uint lineValue = line.Value; 48 | 49 | Microsoft.VisualStudio.Debugger.Interop.TEXT_POSITION startPosition = new Microsoft.VisualStudio.Debugger.Interop.TEXT_POSITION(); 50 | startPosition.dwLine = lineValue - 1; 51 | startPosition.dwColumn = 0; 52 | 53 | uint? startColumn = miTuple.TryFindUint("col"); 54 | if (startColumn > 0) 55 | { 56 | startPosition.dwColumn = startColumn.Value - 1; 57 | } 58 | 59 | Microsoft.VisualStudio.Debugger.Interop.TEXT_POSITION endPosition = startPosition; 60 | uint? endLine = miTuple.TryFindUint("end-line"); 61 | if (endLine > 0) 62 | { 63 | endPosition.dwLine = endLine.Value - 1; 64 | 65 | uint? endCol = miTuple.TryFindUint("end-col"); 66 | if (endCol > 0) 67 | { 68 | endPosition.dwColumn = endCol.Value - 1; 69 | } 70 | } 71 | 72 | return new MITextPosition(filename, startPosition, endPosition); 73 | } 74 | 75 | public string GetFileExtension() 76 | { 77 | int lastDotIndex = this.FileName.LastIndexOf('.'); 78 | if (lastDotIndex < 0) 79 | return string.Empty; 80 | if (this.FileName.IndexOf('\\', lastDotIndex) >= 0) 81 | return string.Empty; 82 | 83 | return this.FileName.Substring(lastDotIndex); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/MIDebugEngine/Engine.Impl/Structures.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using Microsoft.VisualStudio.Debugger.Interop; 8 | using System.Collections.ObjectModel; 9 | using MICore; 10 | //using MICore; 11 | 12 | namespace Microsoft.MIDebugEngine 13 | { 14 | //internal class ThreadContext 15 | //{ 16 | // public ThreadContext(ulong? addr, MITextPosition textPosition, string function, uint level, string from) 17 | // { 18 | // pc = addr; 19 | // sp = 0; 20 | // TextPosition = textPosition; 21 | // Function = function; 22 | // Level = level; 23 | // From = from; 24 | // } 25 | 26 | // /// 27 | // /// [Optional] Program counter. This will be null for an annotated frame. 28 | // /// 29 | // public ulong? pc { get; private set; } 30 | // public ulong sp { get; private set; } 31 | // public string Function { get; private set; } 32 | // public MITextPosition TextPosition { get; private set; } 33 | 34 | // public string From { get; private set; } 35 | 36 | // public uint Level { get; private set; } 37 | 38 | // /// 39 | // /// Finds the module for this context 40 | // /// 41 | // /// process of the context 42 | // /// [Optional] Module, if the frame has one 43 | // internal DebuggedModule FindModule(DebuggedProcess process) 44 | // { 45 | // // CLRDBG TODO: Use module id to find the module 46 | 47 | // //techcap TODO 48 | // //if (this.pc.HasValue) 49 | // //{ 50 | // // return process.ResolveAddress(this.pc.Value); 51 | // //} 52 | 53 | // return null; 54 | // } 55 | //} 56 | 57 | public class OutputMessage 58 | { 59 | public enum Severity 60 | { 61 | Error, 62 | Warning 63 | }; 64 | 65 | public readonly string Message; 66 | public readonly enum_MESSAGETYPE MessageType; 67 | public readonly Severity SeverityValue; 68 | 69 | /// 70 | /// Error HRESULT to send to the debug package. 0 (S_OK) if there is no associated error code. 71 | /// 72 | public readonly uint ErrorCode; 73 | 74 | public OutputMessage(string message, enum_MESSAGETYPE messageType, Severity severity, uint errorCode = 0) 75 | { 76 | this.Message = message; 77 | this.MessageType = messageType; 78 | this.SeverityValue = severity; 79 | this.ErrorCode = errorCode; 80 | } 81 | } 82 | 83 | //public interface ISampleEngineCallback 84 | //{ 85 | // void OnModuleLoad(DebuggedModule module); 86 | // void OnModuleUnload(DebuggedModule module); 87 | // void OnThreadStart(DebuggedThread thread); 88 | // void OnThreadExit(DebuggedThread thread, uint exitCode); 89 | // void OnProcessExit(uint exitCode); 90 | // void OnOutputString(string outputString); 91 | // void OnOutputMessage(OutputMessage outputMessage); 92 | 93 | // /// 94 | // /// Raises an error event to Visual Studio, which will show a message box. Note that this function should never throw. 95 | // /// 96 | // /// [Required] message to send 97 | // void OnError(string message); 98 | // void OnBreakpoint(DebuggedThread thread, ReadOnlyCollection clients); 99 | // void OnException(DebuggedThread thread, string name, string description, uint code, Guid? exceptionCategory = null, ExceptionBreakpointState state = ExceptionBreakpointState.None); 100 | // void OnStepComplete(DebuggedThread thread); 101 | // void OnAsyncBreakComplete(DebuggedThread thread); 102 | // void OnLoadComplete(DebuggedThread thread); 103 | // //void OnProgramDestroy(uint exitCode); 104 | // void OnSymbolSearch(DebuggedModule module, string status, uint dwStatsFlags); 105 | // void OnBreakpointBound(Object objPendingBreakpoint); 106 | // void OnEntryPoint(DebuggedThread thread); 107 | //}; 108 | 109 | public class Constants 110 | { 111 | public const int S_OK = 0; 112 | public const int S_FALSE = 1; 113 | public const int E_NOTIMPL = unchecked((int)0x80004001); 114 | public const int E_FAIL = unchecked((int)0x80004005); 115 | public const int E_ABORT = unchecked((int)(0x80004004)); 116 | public const int RPC_E_SERVERFAULT = unchecked((int)(0x80010105)); 117 | }; 118 | 119 | public class RegisterGroup 120 | { 121 | public readonly string Name; 122 | internal int Count { get; set; } 123 | 124 | public RegisterGroup(string name) 125 | { 126 | Name = name; 127 | Count = 0; 128 | } 129 | } 130 | 131 | public class RegisterDescription 132 | { 133 | public readonly string Name; 134 | public RegisterGroup Group { get; private set; } 135 | public readonly int Index; 136 | public RegisterDescription(string name, RegisterGroup group, int i) 137 | { 138 | Name = name; 139 | Group = group; 140 | Index = i; 141 | Group.Count++; 142 | } 143 | }; 144 | } 145 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/RoslynHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | using Microsoft.CodeAnalysis.Text; 8 | using Mono.Debugger.Soft; 9 | using MonoRemoteDebugger.Debugger.VisualStudio; 10 | using NLog; 11 | using Location = Microsoft.CodeAnalysis.Location; 12 | using System.IO; 13 | using Microsoft.MIDebugEngine; 14 | using Microsoft.VisualStudio.Debugger.Interop; 15 | 16 | namespace MonoRemoteDebugger.Debugger 17 | { 18 | internal class RoslynHelper 19 | { 20 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 21 | 22 | internal static MITextPosition GetStatementRange(string fileName, int startLine, int startColumn) 23 | { 24 | try 25 | { 26 | logger.Trace("Line: {0} Column: {1} Source: {2}", startLine, startColumn, fileName); 27 | 28 | using (var stream = File.OpenRead(fileName)) 29 | { 30 | SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(SourceText.From(stream), path: fileName); 31 | SourceText text = syntaxTree.GetText(); 32 | var root = (CompilationUnitSyntax)syntaxTree.GetRoot(); 33 | 34 | var span = new TextSpan(text.Lines[startLine - 1].Start + startColumn, 1); 35 | SyntaxNode node = root.FindNode(span, false, false); 36 | 37 | if (node is BlockSyntax) 38 | return MapBlockSyntax(span, node, fileName); 39 | while (node is TypeSyntax || node is MemberAccessExpressionSyntax) 40 | node = node.Parent; 41 | 42 | Location location = node.GetLocation(); 43 | FileLinePositionSpan mapped = location.GetMappedLineSpan(); 44 | 45 | return new MITextPosition(fileName, 46 | new TEXT_POSITION() { dwLine = (uint)mapped.StartLinePosition.Line, dwColumn = (uint)mapped.StartLinePosition.Character }, 47 | new TEXT_POSITION() { dwLine = (uint)mapped.EndLinePosition.Line, dwColumn = (uint)mapped.EndLinePosition.Character }); 48 | } 49 | } 50 | catch (Exception ex) 51 | { 52 | logger.Trace($"Exception : {ex}"); 53 | return null; 54 | } 55 | } 56 | 57 | private static MITextPosition MapBlockSyntax(TextSpan span, SyntaxNode node, string fileName) 58 | { 59 | var block = (BlockSyntax)node; 60 | bool start = Math.Abs(block.SpanStart - span.Start) < Math.Abs(block.Span.End - span.Start); 61 | 62 | Location location = block.GetLocation(); 63 | FileLinePositionSpan mapped = location.GetMappedLineSpan(); 64 | 65 | if (start) 66 | { 67 | return new MITextPosition(fileName, 68 | new TEXT_POSITION() { dwLine = (uint)mapped.StartLinePosition.Line, dwColumn = (uint)mapped.StartLinePosition.Character }, 69 | new TEXT_POSITION() { dwLine = (uint)mapped.StartLinePosition.Line, dwColumn = (uint)mapped.StartLinePosition.Character + 1 }); 70 | } 71 | 72 | return new MITextPosition(fileName, 73 | new TEXT_POSITION() { dwLine = (uint)mapped.EndLinePosition.Line, dwColumn = (uint)mapped.EndLinePosition.Character - 1 }, 74 | new TEXT_POSITION() { dwLine = (uint)mapped.EndLinePosition.Line, dwColumn = (uint)mapped.EndLinePosition.Character }); 75 | } 76 | 77 | internal static void GetILOffset(AD7PendingBreakpoint bp, MethodMirror methodMirror, out int ilOffset) 78 | { 79 | List locations = methodMirror.Locations.ToList(); 80 | 81 | foreach (Mono.Debugger.Soft.Location location in locations) 82 | { 83 | int line = location.LineNumber; 84 | int column = location.ColumnNumber; 85 | 86 | if (line != bp.StartLine + 1) 87 | continue; 88 | //if (column != bp.StartColumn) 89 | // continue; 90 | 91 | ilOffset = location.ILOffset; 92 | 93 | Console.WriteLine(location.ColumnNumber); 94 | return; 95 | } 96 | 97 | throw new Exception("Cant bind breakpoint"); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/TypeSummary.cs: -------------------------------------------------------------------------------- 1 | using Mono.Debugger.Soft; 2 | 3 | namespace MonoRemoteDebugger.Debugger 4 | { 5 | public class TypeSummary 6 | { 7 | private MethodMirror[] _methods; 8 | 9 | public TypeMirror TypeMirror { get; set; } 10 | 11 | public MethodMirror[] Methods 12 | { 13 | get 14 | { 15 | lock (this) 16 | { 17 | if (_methods == null && TypeMirror != null) 18 | _methods = TypeMirror.GetMethods(); 19 | } 20 | 21 | return _methods; 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7BoundBreakpoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.Debugger.Interop; 4 | 5 | namespace MonoRemoteDebugger.Debugger.VisualStudio 6 | { 7 | internal class AD7BoundBreakpoint : IDebugBoundBreakpoint2, IDebugBreakpointResolution2 8 | { 9 | private readonly AD7Engine _engine; 10 | private readonly AD7PendingBreakpoint _pendingBreakpoint; 11 | 12 | public AD7BoundBreakpoint(AD7Engine engine, AD7PendingBreakpoint pendingBreakpoint) 13 | { 14 | _engine = engine; 15 | _pendingBreakpoint = pendingBreakpoint; 16 | } 17 | 18 | public int Delete() 19 | { 20 | return _pendingBreakpoint.Delete(); 21 | } 22 | 23 | public int Enable(int fEnable) 24 | { 25 | return _pendingBreakpoint.Enable(fEnable); 26 | } 27 | 28 | public int GetBreakpointResolution(out IDebugBreakpointResolution2 ppBPResolution) 29 | { 30 | ppBPResolution = this; 31 | return VSConstants.S_OK; 32 | } 33 | 34 | public int GetHitCount(out uint pdwHitCount) 35 | { 36 | pdwHitCount = 0; 37 | return VSConstants.S_OK; 38 | } 39 | 40 | public int GetPendingBreakpoint(out IDebugPendingBreakpoint2 ppPendingBreakpoint) 41 | { 42 | ppPendingBreakpoint = _pendingBreakpoint; 43 | return VSConstants.S_OK; 44 | } 45 | 46 | public int GetState(enum_BP_STATE[] pState) 47 | { 48 | pState[0] = 0; 49 | if (_pendingBreakpoint.Deleted) 50 | { 51 | pState[0] = enum_BP_STATE.BPS_DELETED; 52 | } 53 | else if (_pendingBreakpoint.Enabled) 54 | { 55 | pState[0] = enum_BP_STATE.BPS_ENABLED; 56 | } 57 | else if (!_pendingBreakpoint.Enabled) 58 | { 59 | pState[0] = enum_BP_STATE.BPS_DISABLED; 60 | } 61 | return VSConstants.S_OK; 62 | } 63 | 64 | public int SetCondition(BP_CONDITION bpCondition) 65 | { 66 | throw new NotImplementedException(); 67 | } 68 | 69 | public int SetHitCount(uint dwHitCount) 70 | { 71 | throw new NotImplementedException(); 72 | } 73 | 74 | public int SetPassCount(BP_PASSCOUNT bpPassCount) 75 | { 76 | throw new NotImplementedException(); 77 | } 78 | 79 | public int GetBreakpointType(enum_BP_TYPE[] pBPType) 80 | { 81 | pBPType[0] = enum_BP_TYPE.BPT_CODE; 82 | return VSConstants.S_OK; 83 | } 84 | 85 | public int GetResolutionInfo(enum_BPRESI_FIELDS dwFields, BP_RESOLUTION_INFO[] pBPResolutionInfo) 86 | { 87 | if (dwFields == enum_BPRESI_FIELDS.BPRESI_ALLFIELDS) 88 | { 89 | pBPResolutionInfo[0].dwFields = enum_BPRESI_FIELDS.BPRESI_PROGRAM; 90 | pBPResolutionInfo[0].pProgram = _engine; 91 | } 92 | 93 | return VSConstants.S_OK; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7DocumentContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.Debugger.Interop; 4 | using Microsoft.MIDebugEngine; 5 | using MICore; 6 | 7 | namespace MonoRemoteDebugger.Debugger.VisualStudio 8 | { 9 | internal class AD7DocumentContext : IDebugDocumentContext2, IDebugCodeContext2 10 | { 11 | private readonly MITextPosition _textPosition; 12 | //private AD7MemoryAddress _codeContext; 13 | 14 | public string FileName { get; private set; } 15 | 16 | public AD7DocumentContext(MITextPosition textPosition)//, AD7MemoryAddress codeContext) 17 | { 18 | _textPosition = textPosition; 19 | //_codeContext = codeContext; 20 | } 21 | 22 | #region IDebugDocumentContext2 Members 23 | 24 | // Compares this document context to a given array of document contexts. 25 | int IDebugDocumentContext2.Compare(enum_DOCCONTEXT_COMPARE Compare, IDebugDocumentContext2[] rgpDocContextSet, uint dwDocContextSetLen, out uint pdwDocContext) 26 | { 27 | dwDocContextSetLen = 0; 28 | pdwDocContext = 0; 29 | 30 | return Constants.E_NOTIMPL; 31 | } 32 | 33 | // Retrieves a list of all code contexts associated with this document context. 34 | // The engine sample only supports one code context per document context and 35 | // the code contexts are always memory addresses. 36 | int IDebugDocumentContext2.EnumCodeContexts(out IEnumDebugCodeContexts2 ppEnumCodeCxts) 37 | { 38 | ppEnumCodeCxts = null; 39 | //try 40 | //{ 41 | // AD7MemoryAddress[] codeContexts = new AD7MemoryAddress[1]; 42 | // codeContexts[0] = _codeContext; 43 | // ppEnumCodeCxts = new AD7CodeContextEnum(codeContexts); 44 | // return Constants.S_OK; 45 | //} 46 | //catch (MIException e) 47 | //{ 48 | // return e.HResult; 49 | //} 50 | //catch (Exception e) 51 | //{ 52 | // return EngineUtils.UnexpectedException(e); 53 | //} 54 | return VSConstants.S_FALSE; 55 | } 56 | 57 | // Gets the document that contains this document context. 58 | // This method is for those debug engines that supply documents directly to the IDE. Since the sample engine 59 | // does not do this, this method returns E_NOTIMPL. 60 | int IDebugDocumentContext2.GetDocument(out IDebugDocument2 ppDocument) 61 | { 62 | ppDocument = null; 63 | return Constants.E_FAIL; 64 | 65 | //ppDocument = null; // new MonoDocument(_pendingBreakpoint); 66 | //return VSConstants.S_OK; 67 | } 68 | 69 | // Gets the language associated with this document context. 70 | public int GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage) 71 | { 72 | // CLRDBG TODO: Add 'language' to the MI 73 | 74 | string fileExtension = _textPosition.GetFileExtension(); 75 | 76 | if (fileExtension.Equals(".cs", StringComparison.OrdinalIgnoreCase)) 77 | { 78 | pbstrLanguage = "C#"; 79 | pguidLanguage = AD7Guids.guidLanguageCs; 80 | } 81 | // NOTE: Use a case sensitive comparison, since '.C' can be used for C++ on unix 82 | else if (fileExtension == ".c") 83 | { 84 | pbstrLanguage = "C"; 85 | pguidLanguage = AD7Guids.guidLanguageC; 86 | } 87 | else 88 | { 89 | pbstrLanguage = "C++"; 90 | pguidLanguage = AD7Guids.guidLanguageCpp; 91 | } 92 | 93 | return Constants.S_OK; 94 | } 95 | 96 | // Gets the displayable name of the document that contains this document context. 97 | int IDebugDocumentContext2.GetName(enum_GETNAME_TYPE gnType, out string pbstrFileName) 98 | { 99 | pbstrFileName = _textPosition.FileName; 100 | return Constants.S_OK; 101 | } 102 | 103 | // Gets the source code range of this document context. 104 | // A source range is the entire range of source code, from the current statement back to just after the previous s 105 | // statement that contributed code. The source range is typically used for mixing source statements, including 106 | // comments, with code in the disassembly window. 107 | // Sincethis engine does not support the disassembly window, this is not implemented. 108 | int IDebugDocumentContext2.GetSourceRange(TEXT_POSITION[] pBegPosition, TEXT_POSITION[] pEndPosition) 109 | { 110 | throw new NotImplementedException("This method is not implemented"); 111 | } 112 | 113 | // Gets the file statement range of the document context. 114 | // A statement range is the range of the lines that contributed the code to which this document context refers. 115 | int IDebugDocumentContext2.GetStatementRange(TEXT_POSITION[] pBegPosition, TEXT_POSITION[] pEndPosition) 116 | { 117 | try 118 | { 119 | pBegPosition[0].dwColumn = _textPosition.BeginPosition.dwColumn; 120 | pBegPosition[0].dwLine = _textPosition.BeginPosition.dwLine; 121 | 122 | pEndPosition[0].dwColumn = _textPosition.EndPosition.dwColumn; 123 | pEndPosition[0].dwLine = _textPosition.EndPosition.dwLine; 124 | } 125 | catch (MIException e) 126 | { 127 | return e.HResult; 128 | } 129 | catch (Exception e) 130 | { 131 | return EngineUtils.UnexpectedException(e); 132 | } 133 | 134 | return Constants.S_OK; 135 | } 136 | 137 | // Moves the document context by a given number of statements or lines. 138 | // This is used primarily to support the Autos window in discovering the proximity statements around 139 | // this document context. 140 | int IDebugDocumentContext2.Seek(int nCount, out IDebugDocumentContext2 ppDocContext) 141 | { 142 | ppDocContext = null; 143 | return Constants.E_NOTIMPL; 144 | } 145 | 146 | #endregion 147 | 148 | 149 | 150 | 151 | public int Add(ulong dwCount, out IDebugMemoryContext2 ppMemCxt) 152 | { 153 | throw new NotImplementedException(); 154 | } 155 | 156 | public int GetDocumentContext(out IDebugDocumentContext2 ppSrcCxt) 157 | { 158 | ppSrcCxt = this; 159 | return VSConstants.S_OK; 160 | } 161 | 162 | public int GetInfo(enum_CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO[] pinfo) 163 | { 164 | pinfo[0].dwFields = enum_CONTEXT_INFO_FIELDS.CIF_MODULEURL | enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS; 165 | 166 | if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_MODULEURL) != 0) 167 | { 168 | pinfo[0].bstrModuleUrl = FileName; 169 | pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_MODULEURL; 170 | } 171 | 172 | if ((dwFields & enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS) != 0) 173 | { 174 | 175 | pinfo[0].bstrAddress = _textPosition.BeginPosition.dwLine.ToString(); 176 | pinfo[0].dwFields |= enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS; 177 | } 178 | 179 | return VSConstants.S_OK; 180 | } 181 | 182 | public int Subtract(ulong dwCount, out IDebugMemoryContext2 ppMemCxt) 183 | { 184 | throw new NotImplementedException(); 185 | } 186 | 187 | public int Compare(enum_CONTEXT_COMPARE Compare, IDebugMemoryContext2[] rgpMemoryContextSet, 188 | uint dwMemoryContextSetLen, out uint pdwMemoryContext) 189 | { 190 | throw new NotImplementedException(); 191 | } 192 | 193 | // Gets the user-displayable name for this context 194 | // This is not supported by the sample engine. 195 | public int GetName(out string pbstrName) 196 | { 197 | throw new NotImplementedException(); 198 | } 199 | } 200 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7Engine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.VisualStudio; 5 | using Microsoft.VisualStudio.Debugger.Interop; 6 | using Microsoft.MIDebugEngine; 7 | 8 | namespace MonoRemoteDebugger.Debugger.VisualStudio 9 | { 10 | [ComVisible(true)] 11 | [Guid(AD7Guids.EngineString)] 12 | public class AD7Engine : IDebugEngine2, IDebugEngineLaunch2, IDebugProgram3 13 | { 14 | private readonly AsyncDispatcher _dispatcher = new AsyncDispatcher(); 15 | private Guid _programId; 16 | 17 | public static AD7Engine Instance { get; private set; } 18 | 19 | public DebuggedProcess DebuggedProcess { get; private set; } 20 | public EngineCallback Callback { get; private set; } 21 | public MonoProcess RemoteProcess { get; private set; } 22 | 23 | public AD7Engine() 24 | { 25 | //This call is to initialize the global service provider while we are still on the main thread. 26 | //Do not remove this this, even though the return value goes unused. 27 | var globalProvider = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider; 28 | 29 | Instance = this; 30 | } 31 | 32 | public int Attach(IDebugProgram2[] rgpPrograms, IDebugProgramNode2[] rgpProgramNodes, uint celtPrograms, 33 | IDebugEventCallback2 pCallback, enum_ATTACH_REASON dwReason) 34 | { 35 | DebugHelper.TraceEnteringMethod(); 36 | 37 | rgpPrograms[0].GetProgramId(out _programId); 38 | _dispatcher.Queue(() => DebuggedProcess.Attach()); 39 | _dispatcher.Queue(() => DebuggedProcess.WaitForAttach()); 40 | 41 | Callback.EngineCreated(); 42 | Callback.ProgramCreated(); 43 | 44 | this.ProgramCreateEventSent = true; 45 | 46 | return VSConstants.S_OK; 47 | } 48 | 49 | public int CreatePendingBreakpoint(IDebugBreakpointRequest2 pBPRequest, out IDebugPendingBreakpoint2 ppPendingBP) 50 | { 51 | DebugHelper.TraceEnteringMethod(); 52 | 53 | AD7PendingBreakpoint breakpoint = DebuggedProcess.AddPendingBreakpoint(pBPRequest); 54 | ppPendingBP = breakpoint; 55 | 56 | return VSConstants.S_OK; 57 | } 58 | 59 | public int CauseBreak() 60 | { 61 | DebugHelper.TraceEnteringMethod(); 62 | _dispatcher.Queue(() => DebuggedProcess.Break()); 63 | return VSConstants.S_OK; 64 | } 65 | 66 | public int ContinueFromSynchronousEvent(IDebugEvent2 pEvent) 67 | { 68 | DebugHelper.TraceEnteringMethod(); 69 | return VSConstants.S_OK; 70 | } 71 | 72 | public int DestroyProgram(IDebugProgram2 pProgram) 73 | { 74 | DebugHelper.TraceEnteringMethod(); 75 | return VSConstants.S_OK; 76 | } 77 | 78 | public int EnumPrograms(out IEnumDebugPrograms2 ppEnum) 79 | { 80 | DebugHelper.TraceEnteringMethod(); 81 | ppEnum = null; 82 | return VSConstants.S_OK; 83 | } 84 | 85 | public int GetEngineId(out Guid pguidEngine) 86 | { 87 | DebugHelper.TraceEnteringMethod(); 88 | pguidEngine = AD7Guids.EngineGuid; 89 | return VSConstants.S_OK; 90 | } 91 | 92 | public int RemoveAllSetExceptions(ref Guid guidType) 93 | { 94 | DebugHelper.TraceEnteringMethod(); 95 | return VSConstants.S_OK; 96 | } 97 | 98 | public int RemoveSetException(EXCEPTION_INFO[] pException) 99 | { 100 | DebugHelper.TraceEnteringMethod(); 101 | return VSConstants.S_OK; 102 | } 103 | 104 | public int SetException(EXCEPTION_INFO[] pException) 105 | { 106 | DebugHelper.TraceEnteringMethod(); 107 | return VSConstants.S_OK; 108 | } 109 | 110 | public int SetLocale(ushort wLangID) 111 | { 112 | DebugHelper.TraceEnteringMethod(); 113 | return VSConstants.S_OK; 114 | } 115 | 116 | public int SetMetric(string pszMetric, object varValue) 117 | { 118 | DebugHelper.TraceEnteringMethod(); 119 | return VSConstants.S_OK; 120 | } 121 | 122 | public int SetRegistryRoot(string pszRegistryRoot) 123 | { 124 | DebugHelper.TraceEnteringMethod(); 125 | return VSConstants.S_OK; 126 | } 127 | 128 | public int LaunchSuspended(string pszServer, IDebugPort2 port, string exe, string args, string dir, 129 | string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, 130 | uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process) 131 | { 132 | DebugHelper.TraceEnteringMethod(); 133 | 134 | Callback = new EngineCallback(this, ad7Callback); 135 | DebuggedProcess = new DebuggedProcess(this, IPAddress.Parse(args), Callback); 136 | DebuggedProcess.ApplicationClosed += _debuggedProcess_ApplicationClosed; 137 | DebuggedProcess.StartDebugging(); 138 | 139 | process = RemoteProcess = new MonoProcess(port); 140 | return VSConstants.S_OK; 141 | } 142 | 143 | public int ResumeProcess(IDebugProcess2 pProcess) 144 | { 145 | DebugHelper.TraceEnteringMethod(); 146 | IDebugPort2 port; 147 | pProcess.GetPort(out port); 148 | Guid id; 149 | pProcess.GetProcessId(out id); 150 | var defaultPort = (IDebugDefaultPort2) port; 151 | IDebugPortNotify2 notify; 152 | defaultPort.GetPortNotify(out notify); 153 | 154 | int result = notify.AddProgramNode(new AD7ProgramNode(DebuggedProcess, id)); 155 | 156 | return VSConstants.S_OK; 157 | } 158 | 159 | public int TerminateProcess(IDebugProcess2 pProcess) 160 | { 161 | DebugHelper.TraceEnteringMethod(); 162 | _dispatcher.Queue(() => DebuggedProcess.Terminate()); 163 | _dispatcher.Stop(); 164 | Callback.ProgramDestroyed(this); 165 | return VSConstants.S_OK; 166 | } 167 | 168 | public int CanTerminateProcess(IDebugProcess2 pProcess) 169 | { 170 | DebugHelper.TraceEnteringMethod(); 171 | return VSConstants.S_OK; 172 | } 173 | 174 | public int CanDetach() 175 | { 176 | return VSConstants.S_OK; 177 | } 178 | 179 | public int Continue(IDebugThread2 pThread) 180 | { 181 | // VS Code currently isn't providing a thread Id in certain cases. Work around this by handling null values. 182 | AD7Thread thread = pThread as AD7Thread; 183 | _dispatcher.Queue(() => DebuggedProcess.Continue(thread?.GetDebuggedThread())); 184 | return VSConstants.S_OK; 185 | } 186 | 187 | public int Detach() 188 | { 189 | _dispatcher.Queue(() => DebuggedProcess.Terminate()); 190 | return VSConstants.S_OK; 191 | } 192 | 193 | public int Terminate() 194 | { 195 | _dispatcher.Queue(() => DebuggedProcess.Terminate()); 196 | return VSConstants.S_OK; 197 | } 198 | 199 | public int Attach(IDebugEventCallback2 pCallback) 200 | { 201 | throw new NotImplementedException(); 202 | } 203 | 204 | public int EnumCodeContexts(IDebugDocumentPosition2 pDocPos, out IEnumDebugCodeContexts2 ppEnum) 205 | { 206 | ppEnum = null; 207 | return VSConstants.E_NOTIMPL; 208 | } 209 | 210 | public int EnumCodePaths(string pszHint, IDebugCodeContext2 pStart, IDebugStackFrame2 pFrame, int fSource, 211 | out IEnumCodePaths2 ppEnum, out IDebugCodeContext2 ppSafety) 212 | { 213 | ppEnum = null; 214 | ppSafety = null; 215 | return VSConstants.E_NOTIMPL; 216 | } 217 | 218 | public int EnumModules(out IEnumDebugModules2 ppEnum) 219 | { 220 | ppEnum = null; 221 | return VSConstants.E_NOTIMPL; 222 | } 223 | 224 | public int EnumThreads(out IEnumDebugThreads2 ppEnum) 225 | { 226 | ppEnum = null; 227 | return VSConstants.E_NOTIMPL; 228 | } 229 | 230 | public int Execute() 231 | { 232 | return VSConstants.E_NOTIMPL; 233 | } 234 | 235 | public int Step(IDebugThread2 pThread, enum_STEPKIND sk, enum_STEPUNIT stepUnit) 236 | { 237 | var thread = (AD7Thread) pThread; 238 | _dispatcher.Queue(() => DebuggedProcess.Step(thread, sk, stepUnit)); 239 | return VSConstants.S_OK; 240 | } 241 | 242 | public int ExecuteOnThread(IDebugThread2 pThread) 243 | { 244 | var thread = (AD7Thread) pThread; 245 | _dispatcher.Queue(() => DebuggedProcess.Execute(thread)); 246 | return VSConstants.S_OK; 247 | } 248 | 249 | public int GetDebugProperty(out IDebugProperty2 ppProperty) 250 | { 251 | throw new NotImplementedException(); 252 | } 253 | 254 | public int GetDisassemblyStream(enum_DISASSEMBLY_STREAM_SCOPE dwScope, IDebugCodeContext2 pCodeContext, 255 | out IDebugDisassemblyStream2 ppDisassemblyStream) 256 | { 257 | ppDisassemblyStream = null; 258 | return VSConstants.E_NOTIMPL; 259 | } 260 | 261 | public int GetENCUpdate(out object ppUpdate) 262 | { 263 | ppUpdate = null; 264 | return VSConstants.E_NOTIMPL; 265 | } 266 | 267 | public int GetEngineInfo(out string pbstrEngine, out Guid pguidEngine) 268 | { 269 | throw new NotImplementedException(); 270 | } 271 | 272 | public int GetMemoryBytes(out IDebugMemoryBytes2 ppMemoryBytes) 273 | { 274 | ppMemoryBytes = null; 275 | return VSConstants.E_NOTIMPL; 276 | } 277 | 278 | public int GetName(out string pbstrName) 279 | { 280 | pbstrName = null; 281 | return VSConstants.E_NOTIMPL; 282 | } 283 | 284 | public int GetProcess(out IDebugProcess2 ppProcess) 285 | { 286 | ppProcess = null; 287 | return VSConstants.E_NOTIMPL; 288 | } 289 | 290 | public int GetProgramId(out Guid pguidProgramId) 291 | { 292 | pguidProgramId = _programId; 293 | return VSConstants.S_OK; 294 | } 295 | 296 | public int WriteDump(enum_DUMPTYPE DUMPTYPE, string pszDumpUrl) 297 | { 298 | throw new NotImplementedException(); 299 | } 300 | 301 | private void _debuggedProcess_ApplicationClosed(object sender, EventArgs e) 302 | { 303 | _dispatcher.Stop(); 304 | Callback.ProgramDestroyed(this); 305 | } 306 | 307 | internal bool ProgramCreateEventSent 308 | { 309 | get; 310 | private set; 311 | } 312 | } 313 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7Expression.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio; 2 | using Microsoft.VisualStudio.Debugger.Interop; 3 | 4 | namespace MonoRemoteDebugger.Debugger.VisualStudio 5 | { 6 | internal class AD7Expression : IDebugExpression2 7 | { 8 | private readonly MonoProperty _monoProperty; 9 | 10 | public AD7Expression(MonoProperty monoProperty) 11 | { 12 | _monoProperty = monoProperty; 13 | } 14 | 15 | public int Abort() 16 | { 17 | return VSConstants.E_NOTIMPL; 18 | } 19 | 20 | public int EvaluateAsync(enum_EVALFLAGS dwFlags, IDebugEventCallback2 pExprCallback) 21 | { 22 | return VSConstants.E_NOTIMPL; 23 | } 24 | 25 | public int EvaluateSync(enum_EVALFLAGS dwFlags, uint dwTimeout, IDebugEventCallback2 pExprCallback, 26 | out IDebugProperty2 ppResult) 27 | { 28 | ppResult = _monoProperty; 29 | return VSConstants.S_OK; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7Guids.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MonoRemoteDebugger.Debugger.VisualStudio 4 | { 5 | public static class AD7Guids 6 | { 7 | public const string EngineString = "8BF3AB9F-3864-449A-93AB-E7B0935FC8F5"; 8 | public const string ProgramProviderString = "CA171DED-5920-4ACD-93C2-BD9E4FA10CA0"; 9 | 10 | public const string EngineName = "MonoRemoteDebugger"; 11 | 12 | public static readonly Guid ProgramProviderGuid = new Guid(ProgramProviderString); 13 | public static readonly Guid EngineGuid = new Guid(EngineString); 14 | 15 | 16 | // Language guid for C++. Used when the language for a document context or a stack frame is requested. 17 | static private Guid s_guidLanguageCpp = new Guid("3a12d0b7-c26c-11d0-b442-00a0244a1dd2"); 18 | static public Guid guidLanguageCpp 19 | { 20 | get { return s_guidLanguageCpp; } 21 | } 22 | 23 | static private Guid s_guidLanguageCs = new Guid("{3F5162F8-07C6-11D3-9053-00C04FA302A1}"); 24 | static public Guid guidLanguageCs 25 | { 26 | get { return s_guidLanguageCs; } 27 | } 28 | 29 | static private Guid s_guidLanguageC = new Guid("63A08714-FC37-11D2-904C-00C04FA302A1"); 30 | static public Guid guidLanguageC 31 | { 32 | get { return s_guidLanguageC; } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7PendingBreakpoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | using Microsoft.CodeAnalysis.Text; 9 | using Microsoft.VisualStudio; 10 | using Microsoft.VisualStudio.Debugger.Interop; 11 | using Mono.Debugger.Soft; 12 | using Location = Microsoft.CodeAnalysis.Location; 13 | using System.IO; 14 | using NLog; 15 | using Microsoft.MIDebugEngine; 16 | 17 | namespace MonoRemoteDebugger.Debugger.VisualStudio 18 | { 19 | internal class AD7PendingBreakpoint : IDebugPendingBreakpoint2 20 | { 21 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 22 | 23 | private readonly AD7Engine _engine; 24 | private readonly IDebugBreakpointRequest2 _pBPRequest; 25 | private AD7BoundBreakpoint _boundBreakpoint; 26 | private BP_REQUEST_INFO _bpRequestInfo; 27 | 28 | public AD7PendingBreakpoint(AD7Engine engine, IDebugBreakpointRequest2 pBPRequest) 29 | { 30 | var requestInfo = new BP_REQUEST_INFO[1]; 31 | pBPRequest.GetRequestInfo(enum_BPREQI_FIELDS.BPREQI_BPLOCATION, requestInfo); 32 | _bpRequestInfo = requestInfo[0]; 33 | _pBPRequest = pBPRequest; 34 | _engine = engine; 35 | 36 | Enabled = true; 37 | 38 | var docPosition = 39 | (IDebugDocumentPosition2) Marshal.GetObjectForIUnknown(_bpRequestInfo.bpLocation.unionmember2); 40 | 41 | string documentName; 42 | docPosition.GetFileName(out documentName); 43 | var startPosition = new TEXT_POSITION[1]; 44 | var endPosition = new TEXT_POSITION[1]; 45 | docPosition.GetRange(startPosition, endPosition); 46 | 47 | DocumentName = documentName; 48 | StartLine = (int) startPosition[0].dwLine; 49 | StartColumn = (int) startPosition[0].dwColumn; 50 | 51 | EndLine = (int) endPosition[0].dwLine; 52 | EndColumn = (int) endPosition[0].dwColumn; 53 | } 54 | 55 | public bool Bound { get; set; } 56 | public bool Enabled { get; private set; } 57 | public bool Deleted { get; private set; } 58 | public int StartLine { get; private set; } 59 | public int StartColumn { get; private set; } 60 | public int EndLine { get; private set; } 61 | public int EndColumn { get; private set; } 62 | public string DocumentName { get; set; } 63 | public AD7Thread CurrentThread { get; set; } 64 | public EventRequest LastRequest { get; set; } 65 | 66 | public int Bind() 67 | { 68 | try 69 | { 70 | _boundBreakpoint = new AD7BoundBreakpoint(_engine, this); 71 | return VSConstants.S_OK; 72 | } 73 | catch 74 | { 75 | return VSConstants.S_FALSE; 76 | } 77 | } 78 | 79 | public int CanBind(out IEnumDebugErrorBreakpoints2 ppErrorEnum) 80 | { 81 | ppErrorEnum = null; 82 | if (_bpRequestInfo.bpLocation.bpLocationType == (uint) enum_BP_LOCATION_TYPE.BPLT_CODE_FILE_LINE) 83 | return VSConstants.S_OK; 84 | 85 | return VSConstants.S_FALSE; 86 | } 87 | 88 | public int Delete() 89 | { 90 | Deleted = true; 91 | 92 | try 93 | { 94 | LastRequest?.Disable(); 95 | } 96 | catch (VMDisconnectedException) { } 97 | 98 | _engine.DebuggedProcess.DeletePendingBreakpoint(this); 99 | 100 | return VSConstants.S_OK; 101 | } 102 | 103 | public int Enable(int fEnable) 104 | { 105 | Enabled = fEnable != 0; 106 | return VSConstants.S_OK; 107 | } 108 | 109 | public int EnumBoundBreakpoints(out IEnumDebugBoundBreakpoints2 ppEnum) 110 | { 111 | ppEnum = new AD7BoundBreakpointsEnum(new[] {_boundBreakpoint}); 112 | return VSConstants.S_OK; 113 | } 114 | 115 | public int EnumErrorBreakpoints(enum_BP_ERROR_TYPE bpErrorType, out IEnumDebugErrorBreakpoints2 ppEnum) 116 | { 117 | ppEnum = null; 118 | return VSConstants.E_NOTIMPL; 119 | } 120 | 121 | public int GetBreakpointRequest(out IDebugBreakpointRequest2 ppBPRequest) 122 | { 123 | ppBPRequest = _pBPRequest; 124 | return VSConstants.S_OK; 125 | } 126 | 127 | public int GetState(PENDING_BP_STATE_INFO[] pState) 128 | { 129 | if (Deleted) 130 | { 131 | pState[0].state = enum_PENDING_BP_STATE.PBPS_DELETED; 132 | } 133 | else if (Enabled) 134 | { 135 | pState[0].state = enum_PENDING_BP_STATE.PBPS_ENABLED; 136 | } 137 | else if (!Enabled) 138 | { 139 | pState[0].state = enum_PENDING_BP_STATE.PBPS_DISABLED; 140 | } 141 | return VSConstants.S_OK; 142 | } 143 | 144 | public int SetCondition(BP_CONDITION bpCondition) 145 | { 146 | throw new NotImplementedException(); 147 | } 148 | 149 | public int SetPassCount(BP_PASSCOUNT bpPassCount) 150 | { 151 | throw new NotImplementedException(); 152 | } 153 | 154 | public int Virtualize(int fVirtualize) 155 | { 156 | return VSConstants.S_OK; 157 | } 158 | 159 | internal bool TryBind(Dictionary types, out MonoBreakpointLocation breakpointLocation) 160 | { 161 | try 162 | { 163 | using (var stream = File.OpenRead(DocumentName)) 164 | { 165 | SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(SourceText.From(stream), path:DocumentName); 166 | TextLine textLine = syntaxTree.GetText().Lines[StartLine]; 167 | Location location = syntaxTree.GetLocation(textLine.Span); 168 | SyntaxTree sourceTree = location.SourceTree; 169 | SyntaxNode node = location.SourceTree.GetRoot().FindNode(location.SourceSpan, true, true); 170 | 171 | var method = GetParentMethod(node.Parent); 172 | string methodName = method.Identifier.Text; 173 | 174 | var cl = GetParentMethod(method); 175 | string className = cl.Identifier.Text; 176 | 177 | var ns = GetParentMethod(method); 178 | string nsname = ns.Name.ToString(); 179 | 180 | string name = string.Format("{0}.{1}", nsname, className); 181 | TypeSummary summary; 182 | if (types.TryGetValue(name, out summary)) 183 | { 184 | MethodMirror methodMirror = summary.Methods.FirstOrDefault(x => x.Name == methodName); 185 | 186 | if (methodMirror != null) 187 | { 188 | breakpointLocation = new MonoBreakpointLocation 189 | { 190 | Method = methodMirror, 191 | Offset = 0, 192 | }; 193 | return true; 194 | } 195 | } 196 | } 197 | } 198 | catch(Exception ex) 199 | { 200 | logger.Trace($"Exception : {ex}"); 201 | } 202 | 203 | breakpointLocation = null; 204 | return false; 205 | } 206 | 207 | private T GetParentMethod(SyntaxNode node) where T : SyntaxNode 208 | { 209 | if (node == null) 210 | return null; 211 | 212 | if (node is T) 213 | return node as T; 214 | return GetParentMethod(node.Parent); 215 | } 216 | } 217 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7ProgramNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.Debugger.Interop; 4 | using Microsoft.MIDebugEngine; 5 | 6 | namespace MonoRemoteDebugger.Debugger.VisualStudio 7 | { 8 | public class AD7ProgramNode : IDebugProgramNode2 9 | { 10 | private readonly DebuggedProcess _process; 11 | private readonly Guid _processId; 12 | 13 | public AD7ProgramNode(DebuggedProcess process, Guid processId) 14 | { 15 | _process = process; 16 | _processId = processId; 17 | } 18 | 19 | public int Attach_V7(IDebugProgram2 pMDMProgram, IDebugEventCallback2 pCallback, uint dwReason) 20 | { 21 | DebugHelper.TraceEnteringMethod(); 22 | return VSConstants.E_NOTIMPL; 23 | } 24 | 25 | public int DetachDebugger_V7() 26 | { 27 | DebugHelper.TraceEnteringMethod(); 28 | return VSConstants.E_NOTIMPL; 29 | } 30 | 31 | public int GetHostMachineName_V7(out string pbstrHostMachineName) 32 | { 33 | DebugHelper.TraceEnteringMethod(); 34 | pbstrHostMachineName = null; 35 | return VSConstants.E_NOTIMPL; 36 | } 37 | 38 | public int GetEngineInfo(out string pbstrEngine, out Guid pguidEngine) 39 | { 40 | DebugHelper.TraceEnteringMethod(); 41 | pbstrEngine = AD7Guids.EngineName; 42 | pguidEngine = AD7Guids.EngineGuid; 43 | return VSConstants.S_OK; 44 | } 45 | 46 | public int GetHostName(enum_GETHOSTNAME_TYPE dwHostNameType, out string pbstrHostName) 47 | { 48 | DebugHelper.TraceEnteringMethod(); 49 | pbstrHostName = null; 50 | _process.StartDebugging(); 51 | return VSConstants.S_OK; 52 | } 53 | 54 | public int GetHostPid(AD_PROCESS_ID[] pHostProcessId) 55 | { 56 | DebugHelper.TraceEnteringMethod(); 57 | pHostProcessId[0].ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; 58 | pHostProcessId[0].guidProcessId = _processId; 59 | return VSConstants.S_OK; 60 | } 61 | 62 | public int GetProgramName(out string pbstrProgramName) 63 | { 64 | DebugHelper.TraceEnteringMethod(); 65 | pbstrProgramName = null; 66 | return VSConstants.E_NOTIMPL; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7ProgramProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Microsoft.VisualStudio; 4 | using Microsoft.VisualStudio.Debugger.Interop; 5 | 6 | namespace MonoRemoteDebugger.Debugger.VisualStudio 7 | { 8 | [ComVisible(true)] 9 | [Guid(AD7Guids.ProgramProviderString)] 10 | public class AD7ProgramProvider : IDebugProgramProvider2 11 | { 12 | public int GetProviderProcessData(enum_PROVIDER_FLAGS Flags, IDebugDefaultPort2 pPort, AD_PROCESS_ID ProcessId, 13 | CONST_GUID_ARRAY EngineFilter, PROVIDER_PROCESS_DATA[] pProcess) 14 | { 15 | DebugHelper.TraceEnteringMethod(); 16 | return VSConstants.S_OK; 17 | } 18 | 19 | public int GetProviderProgramNode(enum_PROVIDER_FLAGS Flags, IDebugDefaultPort2 pPort, AD_PROCESS_ID ProcessId, 20 | ref Guid guidEngine, ulong programId, out IDebugProgramNode2 ppProgramNode) 21 | { 22 | DebugHelper.TraceEnteringMethod(); 23 | ppProgramNode = null; 24 | return VSConstants.S_OK; 25 | } 26 | 27 | public int SetLocale(ushort wLangID) 28 | { 29 | DebugHelper.TraceEnteringMethod(); 30 | return VSConstants.S_OK; 31 | } 32 | 33 | public int WatchForProviderEvents(enum_PROVIDER_FLAGS Flags, IDebugDefaultPort2 pPort, AD_PROCESS_ID ProcessId, 34 | CONST_GUID_ARRAY EngineFilter, ref Guid guidLaunchingEngine, IDebugPortNotify2 pEventCallback) 35 | { 36 | DebugHelper.TraceEnteringMethod(); 37 | return VSConstants.S_OK; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7StackFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.VisualStudio; 5 | using Microsoft.VisualStudio.Debugger.Interop; 6 | using Mono.Debugger.Soft; 7 | using Microsoft.MIDebugEngine; 8 | using System.Diagnostics; 9 | 10 | namespace MonoRemoteDebugger.Debugger.VisualStudio 11 | { 12 | internal class AD7StackFrame : IDebugStackFrame2, IDebugExpressionContext2 13 | { 14 | public AD7Engine Engine { get; private set; } 15 | public AD7Thread Thread { get; private set; } 16 | public Mono.Debugger.Soft.StackFrame ThreadContext { get; private set; } 17 | 18 | private string _functionName; 19 | private MITextPosition _textPosition; 20 | 21 | private readonly AD7DocumentContext docContext; 22 | private readonly List LocalVariables; 23 | 24 | public AD7StackFrame(AD7Engine engine, AD7Thread thread, Mono.Debugger.Soft.StackFrame threadContext) 25 | { 26 | Debug.Assert(threadContext != null, "ThreadContext is null"); 27 | 28 | Engine = engine; 29 | Thread = thread; 30 | ThreadContext = threadContext; 31 | 32 | _textPosition = RoslynHelper.GetStatementRange(ThreadContext.FileName, ThreadContext.LineNumber, ThreadContext.ColumnNumber); 33 | _functionName = threadContext.Method.Name; 34 | 35 | if (_textPosition != null) 36 | { 37 | docContext = new AD7DocumentContext(_textPosition); 38 | } 39 | 40 | this.LocalVariables = threadContext.GetVisibleVariables().Select(x => new MonoProperty(threadContext, x)).ToList(); 41 | } 42 | 43 | public int ParseText(string pszCode, enum_PARSEFLAGS dwFlags, uint nRadix, out IDebugExpression2 ppExpr, 44 | out string pbstrError, out uint pichError) 45 | { 46 | pbstrError = ""; 47 | pichError = 0; 48 | ppExpr = null; 49 | string lookup = pszCode; 50 | 51 | Debug.WriteLine($"ParseText(pszCode={pszCode}, dwFlags={dwFlags}, nRadix={nRadix})"); 52 | try 53 | { 54 | //############## 55 | // Step01: local 56 | //############## 57 | 58 | var local = ThreadContext.Method.GetLocals().Where(x => x.Name == pszCode).SingleOrDefault(); 59 | if (local != null) 60 | { 61 | //Debug.WriteLine($"=> ParseText.LocalVariable: pszCode={pszCode}, Name={local?.Name}, Type={local?.Type?.FullName}"); 62 | ppExpr = new AD7Expression(new MonoProperty(ThreadContext, local)); 63 | return VSConstants.S_OK; 64 | } 65 | 66 | //############################## 67 | // Step02: fields and properties 68 | //############################## 69 | 70 | var declaringType = ThreadContext.Method?.DeclaringType; 71 | if (declaringType != null) 72 | { 73 | var field = declaringType.GetFields().Where(x => x.Name == pszCode).FirstOrDefault(); 74 | 75 | if (field != null) 76 | { 77 | //Debug.WriteLine($"=> ParseText.Field: pszCode={pszCode}, Name={field?.Name}, Type={field?.FieldType?.FullName}"); 78 | ppExpr = new AD7Expression(new MonoProperty(ThreadContext, field)); 79 | return VSConstants.S_OK; 80 | } 81 | 82 | var property = declaringType.GetProperties().Where(x => x.Name == pszCode).FirstOrDefault(); 83 | 84 | if (property != null) 85 | { 86 | //Debug.WriteLine($"=> ParseText.Property: pszCode={pszCode}, Name={property?.Name}, Type={property?.PropertyType?.FullName}"); 87 | ppExpr = new AD7Expression(new MonoProperty(ThreadContext, property)); 88 | return VSConstants.S_OK; 89 | } 90 | } 91 | } 92 | catch (Exception ex) 93 | { 94 | pbstrError = "Error: " + ex.Message; 95 | pichError = (uint)pbstrError.Length; 96 | return VSConstants.S_FALSE; 97 | } 98 | 99 | pbstrError = "Unsupported Expression"; 100 | pichError = (uint)pbstrError.Length; 101 | return VSConstants.S_FALSE; 102 | } 103 | 104 | public int EnumProperties(enum_DEBUGPROP_INFO_FLAGS dwFields, uint nRadix, ref Guid guidFilter, uint dwTimeout, 105 | out uint pcelt, out IEnumDebugPropertyInfo2 ppEnum) 106 | { 107 | ppEnum = new AD7PropertyInfoEnum(LocalVariables.Select(x => x.GetDebugPropertyInfo(dwFields)).ToArray()); 108 | ppEnum.GetCount(out pcelt); 109 | return VSConstants.S_OK; 110 | } 111 | 112 | public int GetCodeContext(out IDebugCodeContext2 ppCodeCxt) 113 | { 114 | ppCodeCxt = docContext; 115 | return VSConstants.S_OK; 116 | } 117 | 118 | public int GetDebugProperty(out IDebugProperty2 ppProperty) 119 | { 120 | ppProperty = null; // _locals; 121 | return VSConstants.S_OK; 122 | } 123 | 124 | public int GetDocumentContext(out IDebugDocumentContext2 ppCxt) 125 | { 126 | ppCxt = docContext; 127 | return VSConstants.S_OK; 128 | } 129 | 130 | public int GetExpressionContext(out IDebugExpressionContext2 ppExprCxt) 131 | { 132 | ppExprCxt = this; 133 | return VSConstants.S_OK; 134 | } 135 | 136 | public int GetInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, FRAMEINFO[] pFrameInfo) 137 | { 138 | pFrameInfo[0] = GetFrameInfo(dwFieldSpec); 139 | return VSConstants.S_OK; 140 | } 141 | 142 | public int GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage) 143 | { 144 | if (docContext != null) 145 | return docContext.GetLanguageInfo(ref pbstrLanguage, ref pguidLanguage); 146 | else 147 | return Constants.S_OK; 148 | //pbstrLanguage = AD7Guids.LanguageName; 149 | //pguidLanguage = AD7Guids.guidLanguageCpp; 150 | //return VSConstants.S_OK; 151 | } 152 | 153 | public int GetName(out string pbstrName) 154 | { 155 | pbstrName = ThreadContext.FileName; 156 | return VSConstants.S_OK; 157 | } 158 | 159 | public int GetPhysicalStackRange(out ulong paddrMin, out ulong paddrMax) 160 | { 161 | paddrMin = 0; 162 | paddrMax = 0; 163 | return VSConstants.S_OK; 164 | } 165 | 166 | public int GetThread(out IDebugThread2 ppThread) 167 | { 168 | ppThread = Thread; 169 | return VSConstants.S_OK; 170 | } 171 | 172 | internal FRAMEINFO GetFrameInfo(enum_FRAMEINFO_FLAGS dwFieldSpec) 173 | { 174 | // CallstackInfo 175 | var frameInfo = new FRAMEINFO(); 176 | 177 | var parameters = string.Join(", ", ThreadContext.Location.Method.GetParameters().Select(x => x.ParameterType.Name + " " + x.Name)); 178 | 179 | // TODO get modul name instead of filename 180 | frameInfo.m_bstrFuncName = $"{ThreadContext.FileName}!{ThreadContext.Location.Method.Name}({parameters}) Line {ThreadContext.Location.LineNumber}"; 181 | frameInfo.m_bstrModule = ThreadContext.FileName; 182 | frameInfo.m_pFrame = this; 183 | frameInfo.m_fHasDebugInfo = 1; 184 | frameInfo.m_fStaleCode = 0; 185 | frameInfo.m_bstrReturnType = ThreadContext.Location.Method.ReturnType.Name; 186 | 187 | frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_STALECODE; 188 | frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO; 189 | frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_MODULE; 190 | frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_FUNCNAME; 191 | frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_RETURNTYPE; 192 | 193 | if (docContext != null) 194 | { 195 | string pbstrLanguage = null; 196 | Guid pguidLanguage = default(Guid); 197 | docContext.GetLanguageInfo(ref pbstrLanguage, ref pguidLanguage); 198 | frameInfo.m_bstrLanguage = pbstrLanguage; 199 | frameInfo.m_dwValidFields |= enum_FRAMEINFO_FLAGS.FIF_LANGUAGE; 200 | } 201 | 202 | return frameInfo; 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AD7Thread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.VisualStudio; 4 | using Microsoft.VisualStudio.Debugger.Interop; 5 | using Mono.Debugger.Soft; 6 | using MonoRemoteDebugger.Debugger.VisualStudio; 7 | 8 | namespace Microsoft.MIDebugEngine 9 | { 10 | internal class AD7Thread : IDebugThread2 11 | { 12 | private readonly AD7Engine _engine; 13 | private readonly DebuggedThread _debuggedThread; 14 | private string _threadName = "Mono Thread"; 15 | 16 | public ThreadMirror ThreadMirror { get; private set; } 17 | 18 | public AD7Thread(AD7Engine engine, DebuggedThread debuggedThread)//ThreadMirror threadMirror) 19 | { 20 | _engine = engine; 21 | 22 | ThreadMirror = debuggedThread.Thread; 23 | _debuggedThread = debuggedThread; 24 | } 25 | 26 | public int CanSetNextStatement(IDebugStackFrame2 pStackFrame, IDebugCodeContext2 pCodeContext) 27 | { 28 | return VSConstants.S_FALSE; 29 | } 30 | 31 | public int EnumFrameInfo(enum_FRAMEINFO_FLAGS dwFieldSpec, uint nRadix, out IEnumDebugFrameInfo2 ppEnum) 32 | { 33 | StackFrame[] stackFrames = ThreadMirror.GetFrames(); 34 | ppEnum = new AD7FrameInfoEnum(stackFrames.Select(x => new AD7StackFrame(_engine, this, x).GetFrameInfo(dwFieldSpec)).ToArray()); 35 | return VSConstants.S_OK; 36 | } 37 | 38 | public int GetLogicalThread(IDebugStackFrame2 pStackFrame, out IDebugLogicalThread2 ppLogicalThread) 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | 43 | public int GetName(out string pbstrName) 44 | { 45 | pbstrName = _threadName; 46 | return VSConstants.S_OK; 47 | } 48 | 49 | public int GetProgram(out IDebugProgram2 ppProgram) 50 | { 51 | ppProgram = _engine; 52 | return VSConstants.S_OK; 53 | } 54 | 55 | public int GetThreadId(out uint pdwThreadId) 56 | { 57 | pdwThreadId = 1234; 58 | return VSConstants.S_OK; 59 | } 60 | 61 | public int GetThreadProperties(enum_THREADPROPERTY_FIELDS dwFields, THREADPROPERTIES[] ptp) 62 | { 63 | return VSConstants.S_OK; 64 | } 65 | 66 | public int Resume(out uint pdwSuspendCount) 67 | { 68 | pdwSuspendCount = 0; 69 | return VSConstants.E_NOTIMPL; 70 | } 71 | 72 | public int SetNextStatement(IDebugStackFrame2 pStackFrame, IDebugCodeContext2 pCodeContext) 73 | { 74 | return VSConstants.S_FALSE; 75 | } 76 | 77 | public int SetThreadName(string pszName) 78 | { 79 | return VSConstants.E_NOTIMPL; 80 | } 81 | 82 | public int Suspend(out uint pdwSuspendCount) 83 | { 84 | pdwSuspendCount = 0; 85 | return VSConstants.E_NOTIMPL; 86 | } 87 | 88 | internal DebuggedThread GetDebuggedThread() 89 | { 90 | return _debuggedThread; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/AsyncDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace MonoRemoteDebugger.Debugger.VisualStudio 8 | { 9 | internal class AsyncDispatcher 10 | { 11 | private readonly BlockingCollection actions = new BlockingCollection(); 12 | private readonly CancellationTokenSource cts = new CancellationTokenSource(); 13 | 14 | public AsyncDispatcher() 15 | { 16 | Task.Factory.StartNew(Run); 17 | } 18 | 19 | private void Run() 20 | { 21 | try 22 | { 23 | foreach (Action action in actions.GetConsumingEnumerable(cts.Token)) 24 | { 25 | action(); 26 | } 27 | } 28 | catch (Exception ex) 29 | { 30 | Trace.WriteLine(ex.ToString()); 31 | } 32 | } 33 | 34 | public void Queue(Action action) 35 | { 36 | actions.Add(action); 37 | } 38 | 39 | internal void Stop() 40 | { 41 | cts.Cancel(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/DebuggedMonoThread.cs: -------------------------------------------------------------------------------- 1 | namespace MonoRemoteDebugger.Debugger 2 | { 3 | internal class DebuggedMonoThread 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/EngineCallback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Debugger.Interop; 3 | using Microsoft.MIDebugEngine; 4 | 5 | namespace MonoRemoteDebugger.Debugger.VisualStudio 6 | { 7 | public class EngineCallback //: MICore.IDeviceAppLauncherEventCallback 8 | { 9 | private readonly IDebugEventCallback2 _eventCallback; 10 | private readonly AD7Engine _engine; 11 | 12 | public EngineCallback(AD7Engine engine, IDebugEventCallback2 pCallback) 13 | { 14 | _engine = engine; 15 | _eventCallback = pCallback; 16 | } 17 | 18 | public void Send(IDebugEvent2 eventObject, string iidEvent, IDebugProgram2 program, IDebugThread2 thread) 19 | { 20 | uint attributes; 21 | Guid riidEvent = new Guid(iidEvent); 22 | 23 | EngineUtils.RequireOk(eventObject.GetAttributes(out attributes)); 24 | EngineUtils.RequireOk(_eventCallback.Event(_engine, null, program, thread, eventObject, ref riidEvent, attributes)); 25 | } 26 | 27 | public void Send(IDebugEvent2 eventObject, string iidEvent, IDebugThread2 thread) 28 | { 29 | IDebugProgram2 program = _engine; 30 | if (!_engine.ProgramCreateEventSent) 31 | { 32 | // Any events before programe create shouldn't include the program 33 | program = null; 34 | } 35 | 36 | Send(eventObject, iidEvent, program, thread); 37 | } 38 | 39 | public void EngineCreated() 40 | { 41 | var iid = new Guid(AD7EngineCreateEvent.IID); 42 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, null, new AD7EngineCreateEvent(_engine), ref iid, 43 | AD7AsynchronousEvent.Attributes); 44 | } 45 | 46 | public void ProgramCreated() 47 | { 48 | var iid = new Guid(AD7ProgramCreateEvent.IID); 49 | _eventCallback.Event(_engine, null, _engine, null, new AD7ProgramCreateEvent(), ref iid, 50 | AD7AsynchronousEvent.Attributes); 51 | } 52 | 53 | public void EngineLoaded() 54 | { 55 | var iid = new Guid(AD7LoadCompleteEvent.IID); 56 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, null, new AD7LoadCompleteEvent(), ref iid, 57 | AD7StoppingEvent.Attributes); 58 | } 59 | 60 | internal void DebugEntryPoint() 61 | { 62 | var iid = new Guid(AD7EntryPointEvent.IID); 63 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, null, new AD7EntryPointEvent(), ref iid, AD7AsynchronousEvent.Attributes); 64 | } 65 | 66 | internal void ProgramDestroyed(IDebugProgram2 program) 67 | { 68 | var iid = new Guid(AD7ProgramDestroyEvent.IID); 69 | _eventCallback.Event(_engine, null, program, null, new AD7ProgramDestroyEvent(0), ref iid, AD7AsynchronousEvent.Attributes); 70 | } 71 | 72 | internal void BoundBreakpoint(AD7PendingBreakpoint breakpoint) 73 | { 74 | var iid = new Guid(AD7BreakpointBoundEvent.IID); 75 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, null, new AD7BreakpointBoundEvent(breakpoint), ref iid, 76 | AD7AsynchronousEvent.Attributes); 77 | } 78 | 79 | internal void BreakpointHit(AD7PendingBreakpoint breakpoint, AD7Thread thread) 80 | { 81 | var iid = new Guid(AD7BreakpointEvent.IID); 82 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, thread, new AD7BreakpointEvent(breakpoint), ref iid, 83 | AD7StoppingEvent.Attributes); 84 | } 85 | 86 | internal void ThreadStarted(AD7Thread thread) 87 | { 88 | var iid = new Guid(Microsoft.MIDebugEngine.AD7ThreadCreateEvent.IID); 89 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, thread, new Microsoft.MIDebugEngine.AD7ThreadCreateEvent(), ref iid, 90 | AD7StoppingEvent.Attributes); 91 | } 92 | 93 | internal void StepCompleted(AD7Thread thread) 94 | { 95 | var iid = new Guid(Microsoft.MIDebugEngine.AD7StepCompleteEvent.IID); 96 | _eventCallback.Event(_engine, _engine.RemoteProcess, _engine, thread, new Microsoft.MIDebugEngine.AD7StepCompleteEvent(), ref iid, 97 | AD7StoppingEvent.Attributes); 98 | } 99 | 100 | public void OnError(string message) 101 | { 102 | SendMessage(message, OutputMessage.Severity.Error, isAsync: true); 103 | } 104 | 105 | /// 106 | /// Sends an error to the user, blocking until the user dismisses the error 107 | /// 108 | /// string to display to the user 109 | public void OnErrorImmediate(string message) 110 | { 111 | SendMessage(message, OutputMessage.Severity.Error, isAsync: false); 112 | } 113 | 114 | private void SendMessage(string message, OutputMessage.Severity severity, bool isAsync) 115 | { 116 | try 117 | { 118 | // IDebugErrorEvent2 is used to report error messages to the user when something goes wrong in the debug engine. 119 | // The sample engine doesn't take advantage of this. 120 | 121 | AD7MessageEvent eventObject = new AD7MessageEvent(new OutputMessage(message, enum_MESSAGETYPE.MT_MESSAGEBOX, severity), isAsync); 122 | Send(eventObject, AD7MessageEvent.IID, null); 123 | } 124 | catch 125 | { 126 | // Since we are often trying to report an exception, if something goes wrong we don't want to take down the process, 127 | // so ignore the failure. 128 | } 129 | } 130 | 131 | //public void OnWarning(string message) 132 | //{ 133 | // SendMessage(message, OutputMessage.Severity.Warning, isAsync: true); 134 | //} 135 | 136 | //public void OnCustomDebugEvent(Guid guidVSService, Guid sourceId, int messageCode, object parameter1, object parameter2) 137 | //{ 138 | // var eventObject = new AD7CustomDebugEvent(guidVSService, sourceId, messageCode, parameter1, parameter2); 139 | // Send(eventObject, AD7CustomDebugEvent.IID, null); 140 | //} 141 | } 142 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/MonoBreakpointLocation.cs: -------------------------------------------------------------------------------- 1 | using Mono.Debugger.Soft; 2 | 3 | namespace MonoRemoteDebugger.Debugger.VisualStudio 4 | { 5 | internal class MonoBreakpointLocation 6 | { 7 | public MethodMirror Method { get; set; } 8 | public long Offset { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/MonoDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.VisualStudio; 4 | using Microsoft.VisualStudio.Debugger.Interop; 5 | 6 | namespace MonoRemoteDebugger.Debugger.VisualStudio 7 | { 8 | internal class MonoDocument : IDebugDocument2 9 | { 10 | private readonly AD7PendingBreakpoint _pendingBreakpoint; 11 | 12 | public MonoDocument(AD7PendingBreakpoint pendingBreakpoint) 13 | { 14 | _pendingBreakpoint = pendingBreakpoint; 15 | } 16 | 17 | public int GetDocumentClassId(out Guid pclsid) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | 22 | public int GetName(enum_GETNAME_TYPE gnType, out string pbstrFileName) 23 | { 24 | gnType = enum_GETNAME_TYPE.GN_FILENAME; 25 | pbstrFileName = Path.GetFileName(_pendingBreakpoint.DocumentName); 26 | return VSConstants.S_OK; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/VisualStudio/MonoProcess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.Debugger.Interop; 4 | 5 | namespace MonoRemoteDebugger.Debugger.VisualStudio 6 | { 7 | public class MonoProcess : IDebugProcess3 8 | { 9 | private readonly IDebugPort2 _port; 10 | 11 | public MonoProcess(IDebugPort2 pPort) 12 | { 13 | Id = Guid.NewGuid(); 14 | _port = pPort; 15 | } 16 | 17 | public Guid Id { get; private set; } 18 | 19 | public int Attach(IDebugEventCallback2 pCallback, Guid[] rgguidSpecificEngines, uint celtSpecificEngines, 20 | int[] rghrEngineAttach) 21 | { 22 | DebugHelper.TraceEnteringMethod(); 23 | return VSConstants.E_NOTIMPL; 24 | } 25 | 26 | public int CanDetach() 27 | { 28 | DebugHelper.TraceEnteringMethod(); 29 | return VSConstants.E_NOTIMPL; 30 | } 31 | 32 | public int CauseBreak() 33 | { 34 | DebugHelper.TraceEnteringMethod(); 35 | return VSConstants.E_NOTIMPL; 36 | } 37 | 38 | public int Continue(IDebugThread2 pThread) 39 | { 40 | DebugHelper.TraceEnteringMethod(); 41 | return VSConstants.E_NOTIMPL; 42 | } 43 | 44 | public int Detach() 45 | { 46 | DebugHelper.TraceEnteringMethod(); 47 | return VSConstants.E_NOTIMPL; 48 | } 49 | 50 | public int DisableENC(EncUnavailableReason reason) 51 | { 52 | DebugHelper.TraceEnteringMethod(); 53 | throw new NotImplementedException(); 54 | } 55 | 56 | public int EnumPrograms(out IEnumDebugPrograms2 ppEnum) 57 | { 58 | DebugHelper.TraceEnteringMethod(); 59 | ppEnum = null; 60 | return VSConstants.E_NOTIMPL; 61 | } 62 | 63 | public int EnumThreads(out IEnumDebugThreads2 ppEnum) 64 | { 65 | DebugHelper.TraceEnteringMethod(); 66 | ppEnum = null; 67 | return VSConstants.E_NOTIMPL; 68 | } 69 | 70 | public int Execute(IDebugThread2 pThread) 71 | { 72 | DebugHelper.TraceEnteringMethod(); 73 | return VSConstants.E_NOTIMPL; 74 | } 75 | 76 | public int GetAttachedSessionName(out string pbstrSessionName) 77 | { 78 | DebugHelper.TraceEnteringMethod(); 79 | throw new NotImplementedException(); 80 | } 81 | 82 | public int GetDebugReason(enum_DEBUG_REASON[] pReason) 83 | { 84 | DebugHelper.TraceEnteringMethod(); 85 | throw new NotImplementedException(); 86 | } 87 | 88 | public int GetENCAvailableState(EncUnavailableReason[] pReason) 89 | { 90 | DebugHelper.TraceEnteringMethod(); 91 | throw new NotImplementedException(); 92 | } 93 | 94 | public int GetEngineFilter(GUID_ARRAY[] pEngineArray) 95 | { 96 | DebugHelper.TraceEnteringMethod(); 97 | throw new NotImplementedException(); 98 | } 99 | 100 | public int GetHostingProcessLanguage(out Guid pguidLang) 101 | { 102 | DebugHelper.TraceEnteringMethod(); 103 | throw new NotImplementedException(); 104 | } 105 | 106 | public int GetInfo(enum_PROCESS_INFO_FIELDS Fields, PROCESS_INFO[] pProcessInfo) 107 | { 108 | DebugHelper.TraceEnteringMethod(); 109 | throw new NotImplementedException(); 110 | } 111 | 112 | public int GetName(enum_GETNAME_TYPE gnType, out string pbstrName) 113 | { 114 | DebugHelper.TraceEnteringMethod(); 115 | throw new NotImplementedException(); 116 | } 117 | 118 | public int GetPhysicalProcessId(AD_PROCESS_ID[] pProcessId) 119 | { 120 | DebugHelper.TraceEnteringMethod(); 121 | pProcessId[0].ProcessIdType = (uint) enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; 122 | pProcessId[0].guidProcessId = Id; 123 | return VSConstants.S_OK; 124 | } 125 | 126 | public int GetPort(out IDebugPort2 ppPort) 127 | { 128 | DebugHelper.TraceEnteringMethod(); 129 | ppPort = _port; 130 | return VSConstants.S_OK; 131 | } 132 | 133 | public int GetProcessId(out Guid pguidProcessId) 134 | { 135 | DebugHelper.TraceEnteringMethod(); 136 | pguidProcessId = Id; 137 | return VSConstants.S_OK; 138 | } 139 | 140 | public int GetServer(out IDebugCoreServer2 ppServer) 141 | { 142 | DebugHelper.TraceEnteringMethod(); 143 | throw new NotImplementedException(); 144 | } 145 | 146 | public int SetHostingProcessLanguage(ref Guid guidLang) 147 | { 148 | DebugHelper.TraceEnteringMethod(); 149 | throw new NotImplementedException(); 150 | } 151 | 152 | public int Step(IDebugThread2 pThread, enum_STEPKIND sk, enum_STEPUNIT Step) 153 | { 154 | DebugHelper.TraceEnteringMethod(); 155 | return VSConstants.S_OK; 156 | } 157 | 158 | public int Terminate() 159 | { 160 | DebugHelper.TraceEnteringMethod(); 161 | return VSConstants.S_OK; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Debugger/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Server/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Server/MonoRemoteDebugger.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 1.5.2 5 | Exe 6 | net461 7 | false 8 | false 9 | true 10 | 11 | 12 | 13 | ..\Output\Release\Server\ 14 | 15 | 16 | 17 | ..\Output\Debug\Server\ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using MonoRemoteDebugger.SharedLib; 2 | using MonoRemoteDebugger.SharedLib.Server; 3 | using System; 4 | using System.Reflection; 5 | 6 | namespace MonoRemoteDebugger.Server 7 | { 8 | internal class Program 9 | { 10 | private static void Main(string[] args) 11 | { 12 | Console.WriteLine($"MonoRemoteDebugger.Server v{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}"); 13 | 14 | MonoLogger.Setup(); 15 | 16 | MonoUtils.EnsurePdb2MdbCallWorks(); 17 | 18 | using (var server = new MonoDebugServer()) 19 | { 20 | server.StartAnnouncing(); 21 | server.Start(); 22 | 23 | server.WaitForExit(); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/ApplicationType.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | [DataContract] 6 | public enum ApplicationType 7 | { 8 | [EnumMember] Desktopapplication, 9 | [EnumMember] Webapplication 10 | } 11 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Command.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | [DataContract] 6 | public enum Command : byte 7 | { 8 | [EnumMember] 9 | DebugContent, 10 | [EnumMember] 11 | StartedMono, 12 | [EnumMember] 13 | Shutdown, 14 | [EnumMember] 15 | DebugLastContent 16 | } 17 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | internal static class ExtensionMethods 6 | { 7 | internal static bool IsSocketConnected(this Socket s) 8 | { 9 | bool part1 = s.Poll(1000, SelectMode.SelectRead); 10 | bool part2 = (s.Available == 0); 11 | if (part1 && part2) 12 | { 13 | return false; 14 | } 15 | return true; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/GlobalConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Techl; 7 | 8 | namespace MonoRemoteDebugger.SharedLib 9 | { 10 | public class GlobalConfig 11 | { 12 | #region Current Members 13 | private static readonly GlobalConfig _Current = new GlobalConfig(); 14 | public static GlobalConfig Current 15 | { 16 | get 17 | { 18 | return _Current; 19 | } 20 | } 21 | #endregion 22 | 23 | public int ServerPort { get; set; } = AppSettings.Get("ServerPort", 13001); 24 | public int DebuggerAgentPort { get; set; } = AppSettings.Get("DebuggerAgentPort", 11000); 25 | public string LibMonoApplicationPath { get; set; } = AppSettings.Get("LibMonoApplicationPath", ""); 26 | public string ShellScriptInstallPath { get; set; } = AppSettings.Get("ShellScriptInstallPath", ""); 27 | public int SkipLastUsedContentDirectories { get; set; } = AppSettings.Get("SkipLastUsedContentDirectories", 3); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/MessageBase.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | [DataContract] 6 | public class MessageBase 7 | { 8 | [DataMember] 9 | public Command Command { get; set; } 10 | 11 | [DataMember] 12 | public object Payload { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/MonoLogger.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using NLog; 3 | using NLog.Common; 4 | using NLog.Config; 5 | using NLog.Targets; 6 | 7 | namespace MonoRemoteDebugger.SharedLib 8 | { 9 | public static class MonoLogger 10 | { 11 | public static string LoggerPath { get; private set; } 12 | 13 | public static void Setup() 14 | { 15 | var basePath = new FileInfo(typeof(MonoLogger).Assembly.Location).Directory.FullName; 16 | var logPath = Path.Combine(basePath, "Log"); 17 | if (!Directory.Exists(logPath)) 18 | Directory.CreateDirectory(logPath); 19 | LoggerPath = Path.Combine(logPath, "MonoRemoteDebugger.log"); 20 | 21 | var config = new LoggingConfiguration(); 22 | var target = new NLog.Targets.DebuggerTarget(); 23 | config.AddTarget("file", target); 24 | config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, target)); 25 | 26 | var fileTarget = new FileTarget { FileName = LoggerPath }; 27 | config.AddTarget("file", fileTarget); 28 | config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, fileTarget)); 29 | var console = new ColoredConsoleTarget(); 30 | config.AddTarget("file", console); 31 | config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, console)); 32 | 33 | LogManager.Configuration = config; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/MonoRemoteDebugger.SharedLib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 0.0.16 4 | net461 5 | Copyright © EMCT Co., Ltd. 2022 6 | default 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/ClientSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Xml; 7 | using System.Linq; 8 | using NLog; 9 | 10 | namespace MonoRemoteDebugger.SharedLib.Server 11 | { 12 | internal class ClientSession 13 | { 14 | private readonly TcpCommunication communication; 15 | private readonly Logger logger = LogManager.GetCurrentClassLogger(); 16 | private readonly IPAddress remoteEndpoint; 17 | private readonly string tempContentDirectory; 18 | private readonly int skipLastUsedContentDirectories; 19 | private Process proc; 20 | 21 | private string directoryName; 22 | private string targetExe; 23 | private string Arguments; 24 | 25 | public ClientSession(Socket socket) 26 | { 27 | var basePath = new FileInfo(typeof(MonoLogger).Assembly.Location).Directory.FullName; 28 | tempContentDirectory = Path.Combine(basePath, "Temp"); 29 | 30 | remoteEndpoint = ((IPEndPoint)socket.RemoteEndPoint).Address; 31 | communication = new TcpCommunication(socket); 32 | 33 | skipLastUsedContentDirectories = GlobalConfig.Current.SkipLastUsedContentDirectories; 34 | } 35 | 36 | public void HandleSession() 37 | { 38 | try 39 | { 40 | logger.Trace("New Session from {0}", remoteEndpoint); 41 | 42 | while (communication.IsConnected) 43 | { 44 | if (proc != null && proc.HasExited) 45 | return; 46 | 47 | MessageBase msg = communication.Receive(); 48 | 49 | if (msg == null) 50 | { 51 | logger.Info("Null-Message received"); 52 | return; 53 | } 54 | 55 | switch (msg.Command) 56 | { 57 | case Command.DebugLastContent: 58 | case Command.DebugContent: 59 | logger.Info($"{msg.Command.ToString()}-Message received"); 60 | var successful = StartDebugging((StartDebuggingMessage)msg.Payload); 61 | communication.Send(Command.StartedMono, new StatusMessage() { Successful = successful }); 62 | break; 63 | case Command.Shutdown: 64 | logger.Info("Shutdown-Message received"); 65 | return; 66 | } 67 | } 68 | } 69 | catch (XmlException xmlException) 70 | { 71 | logger.Info("CommunicationError : " + xmlException); 72 | } 73 | catch (Exception ex) 74 | { 75 | logger.Error(ex); 76 | } 77 | finally 78 | { 79 | try 80 | { 81 | if (proc != null && !proc.HasExited) 82 | proc.Kill(); 83 | } 84 | catch { } 85 | } 86 | } 87 | 88 | private bool StartDebugging(StartDebuggingMessage msg) 89 | { 90 | if (!Directory.Exists(tempContentDirectory)) 91 | { 92 | Directory.CreateDirectory(tempContentDirectory); 93 | } 94 | 95 | targetExe = msg.FileName; 96 | Arguments = msg.Arguments; 97 | 98 | directoryName = Path.Combine(tempContentDirectory, msg.AppHash); 99 | 100 | if (msg.DebugContent == null || msg.DebugContent.Length == 0) 101 | { 102 | logger.Trace($"Check if content is already available from {remoteEndpoint}: {directoryName}"); 103 | 104 | if (!Directory.Exists(directoryName)) 105 | { 106 | logger.Trace("Content not found. Request new content from {0} ...", remoteEndpoint); 107 | return false; 108 | } 109 | } 110 | else 111 | { 112 | logger.Trace("Receiving content from {0}", remoteEndpoint); 113 | 114 | var zipFileName = directoryName + ".zip"; 115 | 116 | File.WriteAllBytes(zipFileName, msg.DebugContent); 117 | ZipFile.ExtractToDirectory(zipFileName, directoryName); 118 | 119 | foreach (string file in Directory.GetFiles(directoryName, "*vshost*")) 120 | { 121 | File.Delete(file); 122 | } 123 | 124 | File.Delete(zipFileName); 125 | logger.Trace("Extracted content from {0} to {1}", remoteEndpoint, directoryName); 126 | 127 | var generator = new Pdb2MdbGenerator(); 128 | 129 | string binaryDirectory = msg.AppType == ApplicationType.Desktopapplication 130 | ? directoryName 131 | : Path.Combine(directoryName, "bin"); 132 | 133 | logger.Trace($"AppType: {msg.AppType} => choosing binaryDirectory={binaryDirectory}"); 134 | 135 | generator.GeneratePdb2Mdb(binaryDirectory); 136 | } 137 | 138 | StartMono(msg.AppType); 139 | 140 | return true; 141 | } 142 | 143 | private void StartMono(ApplicationType type) 144 | { 145 | MonoProcess proc = MonoProcess.Start(type, targetExe); 146 | proc.Arguments = Arguments; 147 | proc.ProcessStarted += MonoProcessStarted; 148 | this.proc = proc.Start(directoryName); 149 | this.proc.EnableRaisingEvents = true; 150 | this.proc.Exited += _proc_Exited; 151 | } 152 | 153 | private void MonoProcessStarted(object sender, EventArgs e) 154 | { 155 | var web = sender as MonoWebProcess; 156 | if (web != null) 157 | { 158 | Process.Start(web.Url); 159 | } 160 | } 161 | 162 | private void _proc_Exited(object sender, EventArgs e) 163 | { 164 | Console.WriteLine("Program closed: " + proc.ExitCode); 165 | try 166 | { 167 | var oldTempDirectories = Directory.GetDirectories(tempContentDirectory) 168 | .OrderByDescending(x => new DirectoryInfo(x).LastWriteTime) 169 | .Skip(skipLastUsedContentDirectories); // keep last X directories 170 | 171 | foreach (string oldDirectory in oldTempDirectories) 172 | { 173 | if (oldDirectory != directoryName) 174 | { 175 | Directory.Delete(oldDirectory, true); 176 | } 177 | } 178 | } 179 | catch (Exception ex) 180 | { 181 | logger.Error(ex, $"Can't delete old temp directories from {tempContentDirectory}!"); 182 | } 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/MonoDebugServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using NLog; 8 | 9 | namespace MonoRemoteDebugger.SharedLib.Server 10 | { 11 | public class MonoDebugServer : IDisposable 12 | { 13 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 14 | private readonly CancellationTokenSource cts = new CancellationTokenSource(); 15 | 16 | private Task listeningTask; 17 | private TcpListener tcp; 18 | 19 | public void Start() 20 | { 21 | tcp = new TcpListener(IPAddress.Any, GlobalConfig.Current.ServerPort); 22 | tcp.Start(); 23 | 24 | listeningTask = Task.Factory.StartNew(() => StartListening(cts.Token), cts.Token); 25 | } 26 | 27 | private void StartListening(CancellationToken token) 28 | { 29 | while (true) 30 | { 31 | logger.Info("Waiting for client"); 32 | if (tcp == null) 33 | { 34 | token.ThrowIfCancellationRequested(); 35 | return; 36 | } 37 | 38 | TcpClient client = tcp.AcceptTcpClient(); 39 | token.ThrowIfCancellationRequested(); 40 | 41 | logger.Info("Accepted client: " + client.Client.RemoteEndPoint); 42 | var clientSession = new ClientSession(client.Client); 43 | 44 | Task.Factory.StartNew(clientSession.HandleSession, token).Wait(); 45 | } 46 | } 47 | 48 | public void Stop() 49 | { 50 | cts.Cancel(); 51 | if (tcp != null && tcp.Server != null) 52 | { 53 | tcp.Server.Close(0); 54 | tcp = null; 55 | } 56 | if (listeningTask != null) 57 | { 58 | try 59 | { 60 | if (!Task.WaitAll(new Task[] { listeningTask }, 5000)) 61 | logger.Error("listeningTask timeout!!!"); 62 | } 63 | catch (Exception ex) 64 | { 65 | logger.Error(ex.ToString()); 66 | } 67 | } 68 | 69 | logger.Info("Closed MonoDebugServer"); 70 | } 71 | 72 | public void StartAnnouncing() 73 | { 74 | Task.Factory.StartNew(() => 75 | { 76 | try 77 | { 78 | CancellationToken token = cts.Token; 79 | logger.Trace("Start announcing"); 80 | using (var client = new UdpClient()) 81 | { 82 | var ip = new IPEndPoint(IPAddress.Broadcast, 15000); 83 | client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); 84 | 85 | while (true) 86 | { 87 | token.ThrowIfCancellationRequested(); 88 | byte[] bytes = Encoding.ASCII.GetBytes("MonoServer"); 89 | client.Send(bytes, bytes.Length, ip); 90 | Thread.Sleep(100); 91 | } 92 | } 93 | } 94 | catch (OperationCanceledException) 95 | { 96 | } 97 | catch (Exception ex) 98 | { 99 | logger.Trace(ex); 100 | } 101 | }); 102 | } 103 | 104 | public void WaitForExit() 105 | { 106 | listeningTask.Wait(); 107 | } 108 | 109 | #region IDisposable Members 110 | protected bool disposed = false; 111 | protected virtual void Dispose(bool disposing) 112 | { 113 | if (this.disposed) 114 | return; 115 | 116 | if (disposing) 117 | { 118 | //Dispose managed resources 119 | Stop(); 120 | } 121 | 122 | //Dispose unmanaged resources here. 123 | 124 | disposed = true; 125 | } 126 | 127 | public void Dispose() 128 | { 129 | Dispose(true); 130 | GC.SuppressFinalize(this); 131 | } 132 | 133 | ~MonoDebugServer() 134 | { 135 | Dispose(false); 136 | } 137 | #endregion 138 | } 139 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/MonoDesktopProcess.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | 4 | namespace MonoRemoteDebugger.SharedLib.Server 5 | { 6 | internal class MonoDesktopProcess : MonoProcess 7 | { 8 | private readonly string _targetExe; 9 | 10 | public MonoDesktopProcess(string targetExe) 11 | { 12 | _targetExe = targetExe; 13 | } 14 | 15 | internal override Process Start(string workingDirectory) 16 | { 17 | string monoBin = MonoUtils.GetMonoPath(); 18 | var dirInfo = new DirectoryInfo(workingDirectory); 19 | 20 | string args = GetProcessArgs(); 21 | ProcessStartInfo procInfo = GetProcessStartInfo(workingDirectory, monoBin); 22 | procInfo.Arguments = args + string.Format(" --config \"{0}.config\" \"{0}\" {1}", _targetExe, Arguments); 23 | 24 | _proc = Process.Start(procInfo); 25 | RaiseProcessStarted(); 26 | return _proc; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/MonoProcess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using Techl; 8 | 9 | namespace MonoRemoteDebugger.SharedLib.Server 10 | { 11 | public abstract class MonoProcess 12 | { 13 | public string Arguments { get; set; } 14 | protected Process _proc; 15 | public event EventHandler ProcessStarted; 16 | internal abstract Process Start(string workingDirectory); 17 | 18 | protected void RaiseProcessStarted() 19 | { 20 | EventHandler handler = ProcessStarted; 21 | if (handler != null) 22 | handler(this, EventArgs.Empty); 23 | } 24 | 25 | protected string GetProcessArgs() 26 | { 27 | IPAddress ip = IPAddress.Any; 28 | string args = 29 | string.Format( 30 | @"--debugger-agent=address={0}:{1},transport=dt_socket,server=y --debug=mdb-optimizations", ip, GlobalConfig.Current.DebuggerAgentPort); 31 | return args; 32 | } 33 | 34 | protected ProcessStartInfo GetProcessStartInfo(string workingDirectory, string monoBin) 35 | { 36 | var dirInfo = new DirectoryInfo(workingDirectory); 37 | var procInfo = new ProcessStartInfo(monoBin); 38 | procInfo.WorkingDirectory = dirInfo.FullName; 39 | return procInfo; 40 | } 41 | 42 | public static IPAddress GetLocalIp() 43 | { 44 | IPAddress[] adresses = Dns.GetHostEntry(Dns.GetHostName()).AddressList; 45 | IPAddress adr = adresses.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork); 46 | return adr; 47 | } 48 | 49 | internal static MonoProcess Start(ApplicationType type, string _targetExe) 50 | { 51 | if (type == ApplicationType.Desktopapplication) 52 | return new MonoDesktopProcess(_targetExe); 53 | if (type == ApplicationType.Webapplication) 54 | return new MonoWebProcess(); 55 | 56 | throw new Exception("Unknown ApplicationType"); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/MonoUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Microsoft.Win32; 5 | using NLog; 6 | 7 | namespace MonoRemoteDebugger.SharedLib.Server 8 | { 9 | public static class MonoUtils 10 | { 11 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 12 | 13 | const PlatformID PlatFormIDUnixUnderNET1 = (PlatformID)128; 14 | 15 | public static void EnsurePdb2MdbCallWorks() 16 | { 17 | var platform = Environment.OSVersion.Platform; 18 | logger.Trace($"Platform={platform}, GetMonoPath={GetMonoPath()}, GetMonoXsp4={GetMonoXsp4()}, GetPdb2MdbPath={GetPdb2MdbPath()}"); 19 | 20 | var fileName = MonoUtils.GetPdb2MdbPath(); 21 | 22 | try 23 | { 24 | StartProcess(fileName, string.Empty); 25 | } 26 | catch (Exception ex) 27 | { 28 | logger.Info($"Can not find and run {fileName}! Calling {nameof(AddShellScriptToMonoApp)} ..."); 29 | if (!AddShellScriptToMonoApp()) 30 | { 31 | logger.Error(ex, $"Can not find and run {fileName}!"); 32 | } 33 | } 34 | } 35 | 36 | private static bool AddShellScriptToMonoApp() 37 | { 38 | try 39 | { 40 | var libMonoApplicationPath = GlobalConfig.Current.LibMonoApplicationPath; 41 | if (string.IsNullOrWhiteSpace(libMonoApplicationPath) || !Directory.Exists(libMonoApplicationPath)) 42 | { 43 | logger.Error($"{nameof(AddShellScriptToMonoApp)}: Path {libMonoApplicationPath} from 'App.config/configuration/appSettings/{nameof(GlobalConfig.Current.LibMonoApplicationPath)}' not found!"); 44 | return false; 45 | } 46 | 47 | var shellScriptInstallPath = GlobalConfig.Current.ShellScriptInstallPath; 48 | if (string.IsNullOrWhiteSpace(shellScriptInstallPath) || !Directory.Exists(shellScriptInstallPath)) 49 | { 50 | logger.Error($"{nameof(AddShellScriptToMonoApp)}: Path {shellScriptInstallPath} from 'App.config/configuration/appSettings/{nameof(GlobalConfig.Current.ShellScriptInstallPath)}' not found!"); 51 | return false; 52 | } 53 | 54 | if (Environment.OSVersion.Platform == PlatformID.Unix) 55 | { 56 | AddShellScriptToMonoApp(libMonoApplicationPath, shellScriptInstallPath, MonoUtils.GetPdb2MdbPath()); 57 | AddShellScriptToMonoApp(libMonoApplicationPath, shellScriptInstallPath, MonoUtils.GetMonoXsp4()); 58 | } 59 | else 60 | { 61 | logger.Error($"Workaround for missing {MonoUtils.GetPdb2MdbPath()} is implemented only for unix (support for embedded linux)!"); 62 | } 63 | } 64 | catch (Exception ex) 65 | { 66 | logger.Error(ex, $"{nameof(AddShellScriptToMonoApp)} failed!"); 67 | return false; 68 | } 69 | 70 | return true; 71 | } 72 | 73 | private static void AddShellScriptToMonoApp(string libMonoApplicationPath, string shellScriptInstallPath, string programExeName) 74 | { 75 | var programExePath = Path.Combine(libMonoApplicationPath, $"{programExeName}.exe"); 76 | logger.Trace($"Add shell script {shellScriptInstallPath}{programExeName} for {programExePath} ..."); 77 | File.WriteAllText($"{shellScriptInstallPath}{programExeName}", $@"#!/bin/sh{Environment.NewLine}{shellScriptInstallPath}mono {programExePath} ""$@"""); 78 | StartProcess("chmod", $"+x {shellScriptInstallPath}{programExeName}"); 79 | } 80 | 81 | private static void StartProcess(string fileName, string args) 82 | { 83 | var procInfo = new ProcessStartInfo(fileName, args); 84 | procInfo.UseShellExecute = false; 85 | procInfo.CreateNoWindow = true; 86 | Process proc = Process.Start(procInfo); 87 | proc.WaitForExit(); 88 | } 89 | 90 | public static string GetMonoPath() 91 | { 92 | var p = Environment.OSVersion.Platform; 93 | if (p == PlatformID.Unix || p == PlatformID.MacOSX || p == PlatFormIDUnixUnderNET1) 94 | { 95 | return "mono"; 96 | } 97 | 98 | return Path.Combine(GetMonoRootPathWindows(), @"bin\mono.exe"); 99 | } 100 | 101 | public static string GetMonoXsp4() 102 | { 103 | var p = Environment.OSVersion.Platform; 104 | if (p == PlatformID.Unix || p == PlatformID.MacOSX || p == PlatFormIDUnixUnderNET1) 105 | { 106 | return "xsp4"; 107 | } 108 | 109 | return Path.Combine(GetMonoRootPathWindows(), @"bin\Xsp4.bat"); 110 | } 111 | 112 | public static string GetPdb2MdbPath() 113 | { 114 | var p = Environment.OSVersion.Platform; 115 | if (p == PlatformID.Unix || p == PlatformID.MacOSX || p == PlatFormIDUnixUnderNET1) 116 | { 117 | return "pdb2mdb"; 118 | } 119 | 120 | return Path.Combine(GetMonoRootPathWindows(), @"bin\pdb2mdb.bat"); 121 | } 122 | 123 | private static string GetMonoRootPathWindows() 124 | { 125 | try 126 | { 127 | RegistryKey localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default); 128 | RegistryKey monoKey = localMachine.OpenSubKey(@"Software\Novell\Mono\"); //legacy 129 | if (monoKey == null) 130 | { 131 | monoKey = localMachine.OpenSubKey(@"Software\Mono\"); // on windows 10 x64 with Mono 64-Bit 132 | } 133 | if (monoKey == null) 134 | { 135 | monoKey = localMachine.OpenSubKey(@"Software\WOW6432Node\Mono\"); // on windows 10 x64 with Mono 32Bit 136 | } 137 | if(monoKey == null) 138 | { 139 | logger.Error("Cannot find monoKey in Windows registry. pdb2mdb not found. Please install Mono!"); 140 | return String.Empty; 141 | } 142 | 143 | var monoVersion = monoKey.GetValue("DefaultCLR") as string; //legacy 144 | if (string.IsNullOrEmpty(monoVersion)) // on windows 10 145 | { 146 | monoVersion = monoKey.GetValue("Version") as string; 147 | 148 | return (string)monoKey.GetValue("SdkInstallRoot"); 149 | } 150 | else 151 | { 152 | RegistryKey versionKey = localMachine.OpenSubKey(string.Format(@"Software\Novell\Mono\{0}", monoVersion)); 153 | var path = (string)versionKey.GetValue("SdkInstallRoot"); 154 | return path; 155 | } 156 | } 157 | catch(Exception ex) 158 | { 159 | logger.Error(ex.ToString()); 160 | } 161 | return string.Empty; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/MonoWebProcess.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Threading.Tasks; 3 | using NLog; 4 | 5 | namespace MonoRemoteDebugger.SharedLib.Server 6 | { 7 | internal class MonoWebProcess : MonoProcess 8 | { 9 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 10 | public string Url { get; private set; } 11 | 12 | internal override Process Start(string workingDirectory) 13 | { 14 | string monoBin = MonoUtils.GetMonoXsp4(); 15 | string args = GetProcessArgs(); 16 | ProcessStartInfo procInfo = GetProcessStartInfo(workingDirectory, monoBin); 17 | 18 | procInfo.Arguments = Arguments; 19 | procInfo.CreateNoWindow = true; 20 | procInfo.UseShellExecute = false; 21 | procInfo.EnvironmentVariables["MONO_OPTIONS"] = args; 22 | procInfo.RedirectStandardOutput = true; 23 | 24 | _proc = Process.Start(procInfo); 25 | Task.Run(() => 26 | { 27 | while (!_proc.StandardOutput.EndOfStream) 28 | { 29 | string line = _proc.StandardOutput.ReadLine(); 30 | 31 | if (line.StartsWith("Listening on address")) 32 | { 33 | string url = line.Substring(line.IndexOf(":") + 2).Trim(); 34 | if (url == "0.0.0.0") 35 | Url = "localhost"; 36 | else 37 | Url = url; 38 | } 39 | else if (line.StartsWith("Listening on port")) 40 | { 41 | string port = line.Substring(line.IndexOf(":") + 2).Trim(); 42 | port = port.Substring(0, port.IndexOf(" ")); 43 | Url += ":" + port; 44 | 45 | if (line.Contains("non-secure")) 46 | Url = "http://" + Url; 47 | else 48 | Url = "https://" + Url; 49 | 50 | RaiseProcessStarted(); 51 | } 52 | 53 | 54 | logger.Trace(line); 55 | } 56 | }); 57 | 58 | return _proc; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/Server/Pdb2MdbGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using NLog; 8 | 9 | namespace MonoRemoteDebugger.SharedLib.Server 10 | { 11 | internal class Pdb2MdbGenerator 12 | { 13 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 14 | 15 | internal void GeneratePdb2Mdb(string directoryName) 16 | { 17 | logger.Trace(directoryName); 18 | IEnumerable files = 19 | Directory.GetFiles(directoryName, "*.dll") 20 | .Concat(Directory.GetFiles(directoryName, "*.exe")) 21 | .Where(x => !x.Contains("vshost")); 22 | logger.Trace(files.Count()); 23 | 24 | var dirInfo = new DirectoryInfo(directoryName); 25 | 26 | Parallel.ForEach(files, file => 27 | { 28 | try 29 | { 30 | string fileNameWithoutExt = Path.GetFileNameWithoutExtension(file); 31 | string pdbFile = Path.Combine(Path.GetDirectoryName(file), fileNameWithoutExt + ".pdb"); 32 | if (File.Exists(pdbFile)) 33 | { 34 | logger.Trace("Generate mdp for: " + file); 35 | var procInfo = new ProcessStartInfo(MonoUtils.GetPdb2MdbPath(), $"\"{Path.GetFileName(file)}\""); 36 | procInfo.WorkingDirectory = dirInfo.FullName; 37 | procInfo.UseShellExecute = false; 38 | procInfo.CreateNoWindow = true; 39 | Process proc = Process.Start(procInfo); 40 | proc.WaitForExit(); 41 | } 42 | } 43 | catch (Exception ex) 44 | { 45 | logger.Trace(ex); 46 | } 47 | }); 48 | 49 | logger.Trace("Transformed Debuginformation pdb2mdb"); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/StartDebuggingMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | [DataContract] 6 | public class StartDebuggingMessage 7 | { 8 | [DataMember] 9 | public string FileName { get; set; } 10 | 11 | [DataMember] 12 | public string Arguments { get; set; } 13 | 14 | [DataMember] 15 | public ApplicationType AppType { get; set; } 16 | 17 | [DataMember] 18 | public string AppHash { get; set; } 19 | 20 | [DataMember] 21 | public byte[] DebugContent { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/StatusMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | [DataContract] 6 | public class StatusMessage 7 | { 8 | [DataMember] 9 | public bool Successful { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/TcpCommunication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.Sockets; 6 | using System.Runtime.Serialization; 7 | using System.Threading.Tasks; 8 | using NLog; 9 | 10 | namespace MonoRemoteDebugger.SharedLib 11 | { 12 | public class TcpCommunication 13 | { 14 | private readonly DataContractSerializer _serializer; 15 | private readonly Socket _socket; 16 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 17 | 18 | public TcpCommunication(Socket socket) 19 | { 20 | _socket = socket; 21 | List contracts = 22 | GetType() 23 | .Assembly.GetTypes() 24 | .Where(x => x.GetCustomAttributes(typeof (DataContractAttribute), true).Any()) 25 | .ToList(); 26 | _serializer = new DataContractSerializer(typeof (MessageBase), contracts); 27 | } 28 | 29 | public bool IsConnected 30 | { 31 | get { return _socket.IsSocketConnected(); } 32 | } 33 | 34 | public void Send(Command cmd, object payload) 35 | { 36 | using (var ms = new MemoryStream()) 37 | { 38 | _serializer.WriteObject(ms, new MessageBase {Command = cmd, Payload = payload}); 39 | byte[] buffer = ms.ToArray(); 40 | _socket.Send(BitConverter.GetBytes(buffer.Length)); 41 | _socket.Send(buffer); 42 | } 43 | } 44 | 45 | public MessageBase Receive() 46 | { 47 | var buffer = new byte[sizeof (int)]; 48 | int received = _socket.Receive(buffer); 49 | int size = BitConverter.ToInt32(buffer, 0); 50 | return ReceiveContent(size); 51 | } 52 | 53 | private MessageBase ReceiveContent(int size) 54 | { 55 | try 56 | { 57 | using (var ms = new MemoryStream()) 58 | { 59 | int totalReceived = 0; 60 | while (totalReceived != size) 61 | { 62 | var buffer = new byte[Math.Min(1024 * 10, size - totalReceived)]; 63 | int received = _socket.Receive(buffer); 64 | totalReceived += received; 65 | ms.Write(buffer, 0, received); 66 | } 67 | 68 | ms.Seek(0, SeekOrigin.Begin); 69 | return _serializer.ReadObject(ms) as MessageBase; 70 | } 71 | } 72 | catch (Exception ex) 73 | { 74 | logger.Error(ex, $"ReceiveContent({size}) failed!"); 75 | return null; 76 | } 77 | } 78 | 79 | public Task ReceiveAsync() 80 | { 81 | return Task.Factory.StartNew(() => Receive()); 82 | } 83 | 84 | public void Disconnect() 85 | { 86 | if (_socket != null) 87 | { 88 | _socket.Close(1); 89 | _socket.Dispose(); 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.SharedLib/ZipFile.cs: -------------------------------------------------------------------------------- 1 | using Ionic.Zip; 2 | 3 | namespace MonoRemoteDebugger.SharedLib 4 | { 5 | public static class ZipFile 6 | { 7 | public static void CreateFromDirectory(string directory, string targetZip) 8 | { 9 | using (var zip = new Ionic.Zip.ZipFile()) 10 | { 11 | zip.UseZip64WhenSaving = Zip64Option.AsNecessary; 12 | zip.AddDirectory(directory); 13 | zip.Save(targetZip); 14 | } 15 | } 16 | 17 | public static void ExtractToDirectory(string ZipFileName, string targetDirectory) 18 | { 19 | using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(ZipFileName)) 20 | { 21 | foreach (ZipEntry e in zip) 22 | { 23 | e.Extract(targetDirectory); 24 | } 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/MonoClient/DebugClient.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | using System.Threading.Tasks; 4 | using MonoRemoteDebugger.SharedLib; 5 | using MonoRemoteDebugger.SharedLib.Server; 6 | 7 | namespace MonoRemoteDebugger.VSExtension.MonoClient 8 | { 9 | public class DebugClient 10 | { 11 | private readonly ApplicationType _type; 12 | 13 | public DebugClient(ApplicationType type, string targetExe, string arguments, string outputDirectory, string appHash) 14 | { 15 | _type = type; 16 | TargetExe = targetExe; 17 | Arguments = arguments; 18 | OutputDirectory = outputDirectory; 19 | AppHash = appHash; 20 | } 21 | 22 | public string TargetExe { get; set; } 23 | public string Arguments { get; set; } 24 | public string OutputDirectory { get; set; } 25 | public string AppHash { get; set; } 26 | public IPAddress CurrentServer { get; private set; } 27 | 28 | public async Task ConnectToServerAsync(string ipAddress) 29 | { 30 | CurrentServer = IPAddress.Parse(ipAddress); 31 | 32 | var tcp = new TcpClient(); 33 | await tcp.ConnectAsync(CurrentServer, GlobalConfig.Current.ServerPort); 34 | return new DebugSession(this, _type, tcp.Client); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/MonoClient/DebugSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Sockets; 4 | using System.Threading.Tasks; 5 | using MonoRemoteDebugger.Contracts; 6 | using MonoRemoteDebugger.SharedLib; 7 | 8 | namespace MonoRemoteDebugger.VSExtension.MonoClient 9 | { 10 | public class DebugSession : IDebugSession 11 | { 12 | private readonly TcpCommunication communication; 13 | private readonly ApplicationType type; 14 | 15 | public DebugSession(DebugClient debugClient, ApplicationType type, Socket socket) 16 | { 17 | Client = debugClient; 18 | this.type = type; 19 | communication = new TcpCommunication(socket); 20 | } 21 | 22 | public DebugClient Client { get; private set; } 23 | 24 | public void Disconnect() 25 | { 26 | communication.Disconnect(); 27 | } 28 | 29 | public void TransferFiles() 30 | { 31 | var info = new DirectoryInfo(Client.OutputDirectory); 32 | if (!info.Exists) 33 | throw new DirectoryNotFoundException("Directory not found"); 34 | 35 | string targetZip = Path.Combine(info.FullName, "DebugContent.zip"); 36 | if (File.Exists(targetZip)) 37 | File.Delete(targetZip); 38 | 39 | ZipFile.CreateFromDirectory(info.FullName, targetZip); 40 | 41 | communication.Send(Command.DebugContent, new StartDebuggingMessage 42 | { 43 | AppType = type, 44 | DebugContent = File.ReadAllBytes(targetZip), 45 | FileName = Client.TargetExe, 46 | Arguments = Client.Arguments, 47 | AppHash = Client.AppHash 48 | }); 49 | 50 | File.Delete(targetZip); 51 | Console.WriteLine("Finished transmitting"); 52 | } 53 | 54 | public Task TransferFilesAsync() 55 | { 56 | return Task.Run(new Action(TransferFiles)); 57 | } 58 | 59 | public void RestartDebugging() 60 | { 61 | communication.Send(Command.DebugLastContent, new StartDebuggingMessage 62 | { 63 | AppType = type, 64 | DebugContent = new byte[0], 65 | FileName = Client.TargetExe, 66 | Arguments = Client.Arguments, 67 | AppHash = Client.AppHash 68 | }); 69 | 70 | Console.WriteLine("RestartDebugging transmitting"); 71 | } 72 | 73 | public async Task RestartDebuggingAsync(int delay) 74 | { 75 | RestartDebugging(); 76 | 77 | try 78 | { 79 | var msg = await WaitForAnswerAsync(delay); 80 | return (msg.Command == Command.StartedMono && ((StatusMessage)msg.Payload).Successful); 81 | } 82 | catch (Exception) 83 | { 84 | } 85 | 86 | return false; 87 | } 88 | 89 | public async Task WaitForAnswerAsync(int _delay=10000) 90 | { 91 | Task delay = Task.Delay(_delay); 92 | Task msg = await Task.WhenAny(communication.ReceiveAsync(), delay); 93 | 94 | if (msg is Task) 95 | return await (msg as Task); 96 | 97 | if (msg == delay) 98 | throw new Exception("Did not receive an answer in time..."); 99 | throw new Exception("Cant start debugging"); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/MonoClient/MonoServerDiscovery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace MonoRemoteDebugger.VSExtension.MonoClient 10 | { 11 | public class MonoServerDiscovery 12 | { 13 | public async Task SearchServerAsync(CancellationToken token) 14 | { 15 | using (var udp = new UdpClient(new IPEndPoint(IPAddress.Any, 15000))) 16 | { 17 | Task result = await Task.WhenAny(udp.ReceiveAsync(), Task.Delay(500, token)); 18 | var task = result as Task; 19 | if (task != null) 20 | { 21 | UdpReceiveResult udpResult = await task; 22 | string msg = Encoding.Default.GetString(udpResult.Buffer); 23 | return new MonoServerInformation { Message = msg, IpAddress = udpResult.RemoteEndPoint.Address }; 24 | } 25 | } 26 | 27 | return null; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/MonoClient/MonoServerInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace MonoRemoteDebugger.VSExtension.MonoClient 5 | { 6 | public class MonoServerInformation 7 | { 8 | public IPAddress IpAddress { get; set; } 9 | 10 | public string Message { get; set; } 11 | 12 | public DateTime LastMessage { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/MonoRemoteDebuggerInstaller.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Microsoft.Win32; 3 | using MonoRemoteDebugger.Debugger.VisualStudio; 4 | 5 | namespace MonoRemoteDebugger.VSExtension 6 | { 7 | public static class MonoRemoteDebuggerInstaller 8 | { 9 | private const string ENGINE_PATH = @"AD7Metrics\Engine\"; 10 | private const string CLSID_PATH = @"CLSID\"; 11 | 12 | public static void RegisterDebugEngine(string dllPath, RegistryKey rootKey) 13 | { 14 | using (RegistryKey engine = rootKey.OpenSubKey(@"AD7Metrics\Engine\", true)) 15 | { 16 | string engineGuid = AD7Guids.EngineGuid.ToString("B").ToUpper(); 17 | using (RegistryKey engineKey = engine.CreateSubKey(engineGuid)) 18 | { 19 | engineKey.SetValue("CLSID", AD7Guids.EngineGuid.ToString("B").ToUpper()); 20 | engineKey.SetValue("ProgramProvider", AD7Guids.ProgramProviderGuid.ToString("B").ToUpper()); 21 | engineKey.SetValue("Attach", 1, RegistryValueKind.DWord); 22 | engineKey.SetValue("AddressBP", 0, RegistryValueKind.DWord); 23 | engineKey.SetValue("AutoSelectPriority", 4, RegistryValueKind.DWord); 24 | engineKey.SetValue("CallstackBP", 1, RegistryValueKind.DWord); 25 | engineKey.SetValue("Name", AD7Guids.EngineName); 26 | engineKey.SetValue("PortSupplier", AD7Guids.ProgramProviderGuid.ToString("B").ToUpper()); 27 | engineKey.SetValue("AlwaysLoadLocal", 1, RegistryValueKind.DWord); 28 | } 29 | } 30 | 31 | using (RegistryKey clsid = rootKey.OpenSubKey(CLSID_PATH, true)) 32 | { 33 | using (RegistryKey clsidKey = clsid.CreateSubKey(AD7Guids.EngineGuid.ToString("B").ToUpper())) 34 | { 35 | clsidKey.SetValue("Assembly", Assembly.GetExecutingAssembly().GetName().Name); 36 | clsidKey.SetValue("Class", typeof(AD7Engine).FullName); 37 | clsidKey.SetValue("InprocServer32", @"c:\windows\system32\mscoree.dll"); 38 | clsidKey.SetValue("CodeBase", dllPath); 39 | } 40 | 41 | using ( 42 | RegistryKey programProviderKey = 43 | clsid.CreateSubKey(AD7Guids.ProgramProviderGuid.ToString("B").ToUpper())) 44 | { 45 | programProviderKey.SetValue("Assembly", Assembly.GetExecutingAssembly().GetName().Name); 46 | programProviderKey.SetValue("Class", typeof(AD7ProgramProvider).FullName); 47 | programProviderKey.SetValue("InprocServer32", @"c:\windows\system32\mscoree.dll"); 48 | programProviderKey.SetValue("CodeBase", dllPath); 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/MonoVisualStudioExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using EnvDTE; 6 | using EnvDTE80; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using MonoRemoteDebugger.SharedLib; 10 | using MonoRemoteDebugger.Debugger; 11 | using MonoRemoteDebugger.Debugger.VisualStudio; 12 | using MonoRemoteDebugger.VSExtension.MonoClient; 13 | using NLog; 14 | using IServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; 15 | using Task = System.Threading.Tasks.Task; 16 | using Microsoft.MIDebugEngine; 17 | using System.Collections.Generic; 18 | using System.Security.Cryptography; 19 | 20 | namespace MonoRemoteDebugger.VSExtension 21 | { 22 | internal class MonoVisualStudioExtension 23 | { 24 | private readonly DTE _dte; 25 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 26 | 27 | public MonoVisualStudioExtension(DTE dTE) 28 | { 29 | _dte = dTE; 30 | } 31 | 32 | internal void BuildSolution() 33 | { 34 | var sb = (SolutionBuild2) _dte.Solution.SolutionBuild; 35 | sb.Build(true); 36 | } 37 | 38 | private Project GetStartupProject() 39 | { 40 | var sb = (SolutionBuild2) _dte.Solution.SolutionBuild; 41 | string project = ((Array) sb.StartupProjects).Cast().First(); 42 | 43 | try 44 | { 45 | var projects = Projects(_dte.Solution); 46 | foreach (var p in projects) 47 | { 48 | if (p.UniqueName == project) 49 | return p; 50 | } 51 | //startupProject = _dte.Solution.Item(project); 52 | } 53 | catch (ArgumentException aex) 54 | { 55 | throw new ArgumentException($"The parameter '{project}' is incorrect.", aex); 56 | } 57 | 58 | throw new ArgumentException($"The parameter '{project}' is incorrect."); 59 | } 60 | 61 | public static IList Projects(Solution solution) 62 | { 63 | Projects projects = solution.Projects; 64 | List list = new List(); 65 | var item = projects.GetEnumerator(); 66 | while (item.MoveNext()) 67 | { 68 | var project = item.Current as Project; 69 | if (project == null) 70 | { 71 | continue; 72 | } 73 | 74 | if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder) 75 | { 76 | list.AddRange(GetSolutionFolderProjects(project)); 77 | } 78 | else 79 | { 80 | list.Add(project); 81 | } 82 | } 83 | 84 | return list; 85 | } 86 | 87 | private static IEnumerable GetSolutionFolderProjects(Project solutionFolder) 88 | { 89 | List list = new List(); 90 | for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++) 91 | { 92 | var subProject = solutionFolder.ProjectItems.Item(i).SubProject; 93 | if (subProject == null) 94 | { 95 | continue; 96 | } 97 | 98 | // If this is another solution folder, do a recursive call, otherwise add 99 | if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder) 100 | { 101 | list.AddRange(GetSolutionFolderProjects(subProject)); 102 | } 103 | else 104 | { 105 | list.Add(subProject); 106 | } 107 | } 108 | return list; 109 | } 110 | 111 | internal string GetAssemblyPath(Project vsProject) 112 | { 113 | string fullPath = vsProject.Properties.Item("FullPath").Value.ToString(); 114 | string outputPath = 115 | vsProject.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString(); 116 | string outputDir = Path.Combine(fullPath, outputPath); 117 | string outputFileName = vsProject.Properties.Item("OutputFileName").Value.ToString(); 118 | string assemblyPath = Path.Combine(outputDir, outputFileName); 119 | return assemblyPath; 120 | } 121 | 122 | internal string GetArguments(Project vsProject) 123 | { 124 | try 125 | { 126 | //TODO: Need to support Common Project System https://github.com/Microsoft/VSProjectSystem/blob/master/doc/automation/finding_CPS_in_a_VS_project.md 127 | return vsProject.ConfigurationManager.ActiveConfiguration.Properties.Item("StartArguments").Value.ToString(); 128 | } 129 | catch { } 130 | 131 | return null; 132 | } 133 | 134 | internal async Task AttachDebuggerAsync(string ipAddress, int timeout=10000) 135 | { 136 | ThreadHelper.ThrowIfNotOnUIThread(); 137 | Project startupProject = GetStartupProject(); 138 | string path = GetAssemblyPath(startupProject); 139 | string arguments = GetArguments(startupProject); 140 | string targetExe = Path.GetFileName(path); 141 | string outputDirectory = Path.GetDirectoryName(path); 142 | string appHash = ComputeHash(path); 143 | 144 | bool isWeb = ((object[])startupProject.ExtenderNames).Any(x => x.ToString() == "WebApplication"); 145 | ApplicationType appType = isWeb ? ApplicationType.Webapplication : ApplicationType.Desktopapplication; 146 | if (appType == ApplicationType.Webapplication) 147 | outputDirectory += @"\..\..\"; 148 | 149 | var client = new DebugClient(appType, targetExe, arguments, outputDirectory, appHash); 150 | DebugSession session = await client.ConnectToServerAsync(ipAddress); 151 | var debugSessionStarted = await session.RestartDebuggingAsync(timeout); 152 | 153 | if (!debugSessionStarted) 154 | { 155 | await session.TransferFilesAsync(); 156 | await session.WaitForAnswerAsync(timeout); 157 | } 158 | 159 | IntPtr pInfo = GetDebugInfo(ipAddress, targetExe, outputDirectory); 160 | var sp = new ServiceProvider((IServiceProvider) _dte); 161 | try 162 | { 163 | var dbg = (IVsDebugger) sp.GetService(typeof (SVsShellDebugger)); 164 | int hr = dbg.LaunchDebugTargets(1, pInfo); 165 | Marshal.ThrowExceptionForHR(hr); 166 | 167 | DebuggedProcess.Instance.AssociateDebugSession(session); 168 | } 169 | catch(Exception ex) 170 | { 171 | logger.Error(ex); 172 | string msg; 173 | var sh = (IVsUIShell) sp.GetService(typeof (SVsUIShell)); 174 | sh.GetErrorInfo(out msg); 175 | 176 | if (!string.IsNullOrWhiteSpace(msg)) 177 | { 178 | logger.Error(msg); 179 | } 180 | throw; 181 | } 182 | finally 183 | { 184 | if (pInfo != IntPtr.Zero) 185 | Marshal.FreeCoTaskMem(pInfo); 186 | } 187 | } 188 | 189 | public static string ComputeHash(string file) 190 | { 191 | using (FileStream stream = File.OpenRead(file)) 192 | { 193 | var sha = new SHA256Managed(); 194 | byte[] checksum = sha.ComputeHash(stream); 195 | return BitConverter.ToString(checksum).Replace("-", string.Empty); 196 | } 197 | } 198 | 199 | private IntPtr GetDebugInfo(string args, string targetExe, string outputDirectory) 200 | { 201 | var info = new VsDebugTargetInfo(); 202 | info.cbSize = (uint) Marshal.SizeOf(info); 203 | info.dlo = DEBUG_LAUNCH_OPERATION.DLO_CreateProcess; 204 | 205 | info.bstrExe = Path.Combine(outputDirectory, targetExe); 206 | info.bstrCurDir = outputDirectory; 207 | info.bstrArg = args; // no command line parameters 208 | info.bstrRemoteMachine = null; // debug locally 209 | info.grfLaunch = (uint) __VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd; 210 | info.fSendStdoutToOutputWindow = 0; 211 | info.clsidCustom = AD7Guids.EngineGuid; 212 | info.grfLaunch = 0; 213 | 214 | IntPtr pInfo = Marshal.AllocCoTaskMem((int) info.cbSize); 215 | Marshal.StructureToPtr(info, pInfo, false); 216 | return pInfo; 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Resources/ArrowDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/MonoRemoteDebugger.VSExtension/Resources/ArrowDown.png -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Resources/Images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/MonoRemoteDebugger.VSExtension/Resources/Images.png -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Resources/MonoLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/MonoRemoteDebugger.VSExtension/Resources/MonoLogo.png -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Resources/MonoRemoteDebugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/MonoRemoteDebugger.VSExtension/Resources/MonoRemoteDebugger.png -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Resources/Resources.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Settings/UserSettings.cs: -------------------------------------------------------------------------------- 1 | namespace MonoRemoteDebugger.VSExtension.Settings 2 | { 3 | public class UserSettings 4 | { 5 | public UserSettings() 6 | { 7 | LastIp = "127.0.0.1"; 8 | LastTimeout = 10000; 9 | } 10 | 11 | public string LastIp { get; set; } 12 | public int LastTimeout { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Settings/UserSettingsManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Settings; 3 | using Newtonsoft.Json; 4 | using NLog; 5 | 6 | namespace MonoRemoteDebugger.VSExtension.Settings 7 | { 8 | public class UserSettingsManager 9 | { 10 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 11 | private static readonly UserSettingsManager manager = new UserSettingsManager(); 12 | private WritableSettingsStore store; 13 | 14 | private UserSettingsManager() 15 | { 16 | } 17 | 18 | public static UserSettingsManager Instance 19 | { 20 | get { return manager; } 21 | } 22 | 23 | public UserSettings Load() 24 | { 25 | var result = new UserSettings(); 26 | 27 | if (store.CollectionExists("MonoRemoteDebugger")) 28 | { 29 | try 30 | { 31 | string content = store.GetString("MonoRemoteDebugger", "Settings"); 32 | result = JsonConvert.DeserializeObject(content); 33 | return result; 34 | } 35 | catch (Exception ex) 36 | { 37 | logger.Error(ex); 38 | } 39 | } 40 | 41 | return result; 42 | } 43 | 44 | public void Save(UserSettings settings) 45 | { 46 | string json = JsonConvert.SerializeObject(settings); 47 | if (!store.CollectionExists("MonoRemoteDebugger")) 48 | store.CreateCollection("MonoRemoteDebugger"); 49 | store.SetString("MonoRemoteDebugger", "Settings", json); 50 | } 51 | 52 | public static void Initialize(WritableSettingsStore configurationSettingsStore) 53 | { 54 | Instance.store = configurationSettingsStore; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/VSCommandTable.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by Extensibility Tools v1.10.211 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace MonoRemoteDebugger.VSExtension 7 | { 8 | using System; 9 | 10 | /// 11 | /// Helper class that exposes all GUIDs used across VS Package. 12 | /// 13 | internal sealed partial class PackageGuids 14 | { 15 | public const string guidMonoDebugger_VS2013PkgString = "27d183e9-5d2b-44d6-9ec8-2db329096df7"; 16 | public const string guidMonoDebugger_VS2013CmdSetString = "9ef3ef5e-965c-4443-a78a-947849fba55a"; 17 | public const string guidImagesString = "5f91711c-12a6-4a4e-875a-352c3cf839c8"; 18 | public static Guid guidMonoDebugger_VS2013Pkg = new Guid(guidMonoDebugger_VS2013PkgString); 19 | public static Guid guidMonoDebugger_VS2013CmdSet = new Guid(guidMonoDebugger_VS2013CmdSetString); 20 | public static Guid guidImages = new Guid(guidImagesString); 21 | } 22 | /// 23 | /// Helper class that encapsulates all CommandIDs uses across VS Package. 24 | /// 25 | internal sealed partial class PackageIds 26 | { 27 | public const int TopLevelMenuGroup = 0x1020; 28 | public const int cmdRemodeDebugCode = 0x0100; 29 | public const int cmdLocalDebugCode = 0x0101; 30 | public const int menuIDMainMenu = 0x0102; 31 | public const int cmdOpenLogFile = 0x0104; 32 | public const int publish = 0x0001; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/VSCommandTable.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | MonoRemoteDebugger 14 | MonoRemoteDebugger 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 35 | 36 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/VSPackage.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using EnvDTE80; 3 | using Microsoft.MIDebugEngine; 4 | using Microsoft.VisualStudio; 5 | using Microsoft.VisualStudio.Settings; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using Microsoft.VisualStudio.Shell.Settings; 9 | using Microsoft.Win32; 10 | using MonoRemoteDebugger.SharedLib; 11 | using MonoRemoteDebugger.SharedLib.Server; 12 | using MonoRemoteDebugger.VSExtension.Settings; 13 | using MonoRemoteDebugger.VSExtension.Views; 14 | using NLog; 15 | using System; 16 | using System.ComponentModel.Design; 17 | using System.Diagnostics; 18 | using System.IO; 19 | using System.Linq; 20 | using System.Runtime.InteropServices; 21 | using System.Threading; 22 | using System.Windows; 23 | using Process = System.Diagnostics.Process; 24 | using Task = System.Threading.Tasks.Task; 25 | 26 | namespace MonoRemoteDebugger.VSExtension 27 | { 28 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 29 | [InstalledProductRegistration("#110", "#112", Vsix.Version, IconResourceID = 400)] 30 | [ProvideMenuResource("Menus.ctmenu", 1)] 31 | [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string, PackageAutoLoadFlags.BackgroundLoad)] 32 | [Guid(PackageGuids.guidMonoDebugger_VS2013PkgString)] 33 | public sealed class VSPackage : AsyncPackage 34 | { 35 | private static readonly Logger logger = LogManager.GetCurrentClassLogger(); 36 | private MonoVisualStudioExtension monoExtension; 37 | private MonoDebugServer server = new MonoDebugServer(); 38 | 39 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 40 | { 41 | var settingsManager = new ShellSettingsManager(this); 42 | var configurationSettingsStore = settingsManager.GetWritableSettingsStore(SettingsScope.UserSettings); 43 | UserSettingsManager.Initialize(configurationSettingsStore); 44 | MonoLogger.Setup(); 45 | base.Initialize(); 46 | var dte = await GetServiceAsync(typeof(DTE)) as DTE; 47 | monoExtension = new MonoVisualStudioExtension(dte); 48 | TryRegisterAssembly(); 49 | 50 | 51 | Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary 52 | { 53 | Source = new Uri("/MonoRemoteDebugger.VSExtension;component/Resources/Resources.xaml", UriKind.Relative) 54 | }); 55 | 56 | var mcs = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 57 | InstallMenu(mcs); 58 | 59 | } 60 | 61 | private void InstallMenu(OleMenuCommandService mcs) 62 | { 63 | if (mcs != null) 64 | { 65 | var debugLocally = new CommandID(PackageGuids.guidMonoDebugger_VS2013CmdSet, PackageIds.cmdLocalDebugCode); 66 | var localCmd = new OleMenuCommand(DebugLocalClicked, debugLocally); 67 | localCmd.BeforeQueryStatus += cmd_BeforeQueryStatus; 68 | mcs.AddCommand(localCmd); 69 | 70 | 71 | var menuCommandID = new CommandID(PackageGuids.guidMonoDebugger_VS2013CmdSet, PackageIds.cmdRemodeDebugCode); 72 | var cmd = new OleMenuCommand(DebugRemoteClicked, menuCommandID); 73 | cmd.BeforeQueryStatus += cmd_BeforeQueryStatus; 74 | mcs.AddCommand(cmd); 75 | 76 | var cmdOpenLogFileId = new CommandID(PackageGuids.guidMonoDebugger_VS2013CmdSet, PackageIds.cmdOpenLogFile); 77 | var openCmd = new OleMenuCommand(OpenLogFile, cmdOpenLogFileId); 78 | openCmd.BeforeQueryStatus += (o, e) => openCmd.Enabled = File.Exists(MonoLogger.LoggerPath); 79 | mcs.AddCommand(openCmd); 80 | } 81 | } 82 | 83 | private void OpenLogFile(object sender, EventArgs e) 84 | { 85 | if (File.Exists(MonoLogger.LoggerPath)) 86 | { 87 | Process.Start(MonoLogger.LoggerPath); 88 | } 89 | } 90 | 91 | private void TryRegisterAssembly() 92 | { 93 | try 94 | { 95 | RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(@"CLSID\{8BF3AB9F-3864-449A-93AB-E7B0935FC8F5}"); 96 | 97 | if (regKey != null) 98 | return; 99 | 100 | string location = typeof(DebuggedProcess).Assembly.Location; 101 | 102 | string regasm = @"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe"; 103 | if (!Environment.Is64BitOperatingSystem) 104 | regasm = @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe"; 105 | 106 | var p = new ProcessStartInfo(regasm, location); 107 | p.Verb = "runas"; 108 | p.RedirectStandardOutput = true; 109 | p.UseShellExecute = false; 110 | p.CreateNoWindow = true; 111 | 112 | Process proc = Process.Start(p); 113 | while (!proc.HasExited) 114 | { 115 | string txt = proc.StandardOutput.ReadToEnd(); 116 | } 117 | 118 | using (RegistryKey config = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration)) 119 | { 120 | MonoRemoteDebuggerInstaller.RegisterDebugEngine(location, config); 121 | } 122 | } 123 | catch (UnauthorizedAccessException) 124 | { 125 | MessageBox.Show( 126 | "Failed finish installation of MonoRemoteDebugger - Please run Visual Studio once als Administrator...", 127 | "MonoRemoteDebugger", MessageBoxButton.OK, MessageBoxImage.Error); 128 | } 129 | catch (Exception ex) 130 | { 131 | logger.Error(ex); 132 | } 133 | } 134 | 135 | private void cmd_BeforeQueryStatus(object sender, EventArgs e) 136 | { 137 | var menuCommand = sender as OleMenuCommand; 138 | if (menuCommand != null) 139 | { 140 | var dte = GetService(typeof(DTE)) as DTE; 141 | var sb = (SolutionBuild2)dte.Solution.SolutionBuild; 142 | menuCommand.Visible = sb.StartupProjects != null; 143 | if (menuCommand.Visible) 144 | menuCommand.Enabled = ((Array)sb.StartupProjects).Cast().Count() == 1; 145 | } 146 | } 147 | 148 | private async void DebugLocalClicked(object sender, EventArgs e) 149 | { 150 | try 151 | { 152 | if (server != null) 153 | { 154 | server.Stop(); 155 | server = null; 156 | } 157 | 158 | monoExtension.BuildSolution(); 159 | 160 | using (server = new MonoDebugServer()) 161 | { 162 | server.Start(); 163 | await monoExtension.AttachDebuggerAsync(MonoProcess.GetLocalIp().ToString()); 164 | } 165 | } 166 | catch (Exception ex) 167 | { 168 | logger.Error(ex); 169 | if (server != null) 170 | server.Stop(); 171 | MessageBox.Show(ex.Message, "MonoRemoteDebugger", MessageBoxButton.OK, MessageBoxImage.Error); 172 | } 173 | } 174 | 175 | private async void DebugRemoteClicked(object sender, EventArgs e) 176 | { 177 | var dlg = new ServersFound(); 178 | 179 | if (dlg.ShowDialog().GetValueOrDefault()) 180 | { 181 | try 182 | { 183 | int timeout = dlg.ViewModel.AwaitTimeout; 184 | monoExtension.BuildSolution(); 185 | if (dlg.ViewModel.SelectedServer != null) 186 | await monoExtension.AttachDebuggerAsync(dlg.ViewModel.SelectedServer.IpAddress.ToString(), timeout); 187 | else if (!string.IsNullOrWhiteSpace(dlg.ViewModel.ManualIp)) 188 | await monoExtension.AttachDebuggerAsync(dlg.ViewModel.ManualIp, timeout); 189 | } 190 | catch (Exception ex) 191 | { 192 | logger.Error(ex); 193 | MessageBox.Show(ex.Message, "MonoRemoteDebugger", MessageBoxButton.OK, MessageBoxImage.Error); 194 | } 195 | } 196 | } 197 | } 198 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Views/ServersFound.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Views/ServersFound.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace MonoRemoteDebugger.VSExtension.Views 4 | { 5 | /// 6 | /// Interaktionslogik für ServersFound.xaml 7 | /// 8 | public partial class ServersFound : Window 9 | { 10 | public ServersFound() 11 | { 12 | InitializeComponent(); 13 | WindowStartupLocation = WindowStartupLocation.CenterOwner; 14 | 15 | ViewModel = new ServersFoundViewModel(); 16 | DataContext = ViewModel; 17 | Closing += (o, e) => ViewModel.StopLooking(); 18 | } 19 | 20 | public ServersFoundViewModel ViewModel { get; set; } 21 | 22 | private void Connect(object sender, RoutedEventArgs e) 23 | { 24 | DialogResult = true; 25 | } 26 | 27 | private void Cancel(object sender, RoutedEventArgs e) 28 | { 29 | DialogResult = false; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/Views/ServersFoundViewModel.cs: -------------------------------------------------------------------------------- 1 | using MonoRemoteDebugger.SharedLib; 2 | using MonoRemoteDebugger.VSExtension.MonoClient; 3 | using MonoRemoteDebugger.VSExtension.Settings; 4 | using System; 5 | using System.Collections.ObjectModel; 6 | using System.Linq; 7 | using System.Net.Sockets; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Windows; 11 | 12 | namespace MonoRemoteDebugger.VSExtension.Views 13 | { 14 | public class ServersFoundViewModel : IDisposable 15 | { 16 | private readonly CancellationTokenSource cts = new CancellationTokenSource(); 17 | 18 | public ServersFoundViewModel() 19 | { 20 | Servers = new ObservableCollection(); 21 | UserSettings settings = UserSettingsManager.Instance.Load(); 22 | ManualIp = settings.LastIp; 23 | AwaitTimeout = settings.LastTimeout; 24 | var task = LookupServersAsync(cts.Token); 25 | } 26 | 27 | public ObservableCollection Servers { get; set; } 28 | public MonoServerInformation SelectedServer { get; set; } 29 | public string ManualIp { get; set; } 30 | public int AwaitTimeout { get; set; } 31 | 32 | public int ServerPort 33 | { 34 | get 35 | { 36 | return GlobalConfig.Current.ServerPort; 37 | } 38 | set 39 | { 40 | GlobalConfig.Current.ServerPort = value; 41 | } 42 | } 43 | 44 | public int DebuggerAgentPort 45 | { 46 | get 47 | { 48 | return GlobalConfig.Current.DebuggerAgentPort; 49 | } 50 | set 51 | { 52 | GlobalConfig.Current.DebuggerAgentPort = value; 53 | } 54 | } 55 | 56 | private async Task LookupServersAsync(CancellationToken token) 57 | { 58 | var discovery = new MonoServerDiscovery(); 59 | 60 | try 61 | { 62 | while (!token.IsCancellationRequested) 63 | { 64 | token.ThrowIfCancellationRequested(); 65 | MonoServerInformation server = await discovery.SearchServerAsync(token); 66 | if (server != null) 67 | { 68 | MonoServerInformation exists = Servers.FirstOrDefault(x => Equals(x.IpAddress, server.IpAddress)); 69 | if (exists == null) 70 | { 71 | Servers.Add(server); 72 | server.LastMessage = DateTime.Now; 73 | } 74 | else 75 | { 76 | exists.LastMessage = DateTime.Now; 77 | } 78 | } 79 | else 80 | { 81 | await Task.Delay(1000); 82 | } 83 | 84 | foreach (MonoServerInformation deadServer in Servers.Where(x => ((DateTime.Now - x.LastMessage).TotalSeconds > 5)).ToList()) 85 | Servers.Remove(deadServer); 86 | } 87 | } 88 | catch (SocketException ex) 89 | { 90 | if (ex.SocketErrorCode == SocketError.AddressAlreadyInUse) 91 | MessageBox.Show("Port 15000 is in use."); 92 | else 93 | MessageBox.Show(ex.ToString()); 94 | } 95 | catch (Exception ex) 96 | { 97 | MessageBox.Show(ex.ToString()); 98 | } 99 | } 100 | 101 | public void StopLooking() 102 | { 103 | UserSettings settings = UserSettingsManager.Instance.Load(); 104 | settings.LastIp = ManualIp; 105 | //Check if value of timeout is greater than 0 before save. If not set default 10sec. 106 | settings.LastTimeout = AwaitTimeout > 0 ? AwaitTimeout : 10000; 107 | UserSettingsManager.Instance.Save(settings); 108 | 109 | cts.Cancel(); 110 | } 111 | 112 | #region IDisposable Members 113 | protected bool disposed = false; 114 | protected virtual void Dispose(bool disposing) 115 | { 116 | if (this.disposed) 117 | return; 118 | 119 | if (disposing) 120 | { 121 | //Dispose managed resources 122 | cts.Dispose(); 123 | } 124 | 125 | //Dispose unmanaged resources here. 126 | 127 | disposed = true; 128 | } 129 | 130 | public void Dispose() 131 | { 132 | Dispose(true); 133 | GC.SuppressFinalize(this); 134 | } 135 | 136 | ~ServersFoundViewModel() 137 | { 138 | Dispose(false); 139 | } 140 | #endregion 141 | 142 | } 143 | } -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by Extensibility Tools v1.10.211 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace MonoRemoteDebugger.VSExtension 7 | { 8 | static class Vsix 9 | { 10 | public const string Id = "27D183E9-5D2B-44D6-9EC8-2DB329096DF7"; 11 | public const string Name = "MonoRemoteDebugger"; 12 | public const string Description = @"MonoRemoteDebugger"; 13 | public const string Language = "en-US"; 14 | public const string Version = "1.5.2"; 15 | public const string Author = "Techl.com"; 16 | public const string Tags = "mono, debugger, remote"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/source.extension.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/MonoRemoteDebugger.VSExtension/source.extension.ico -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/source.extension.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | MonoRemoteDebugger 122 | 123 | 124 | MonoRemoteDebugger 125 | 126 | 127 | 128 | source.extension.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.VSExtension/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | MonoRemoteDebugger 7 | MonoRemoteDebugger 8 | https://github.com/techl/MonoRemoteDebugger 9 | LICENSE.txt 10 | Resources\MonoRemoteDebugger.png 11 | mono, debugger, remote 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /MonoRemoteDebugger.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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E7AA95CC-1EAA-4E4B-81BE-CF921C5F219F}" 7 | ProjectSection(SolutionItems) = preProject 8 | appveyor.yml = appveyor.yml 9 | AssemblyInfo.cs = AssemblyInfo.cs 10 | CHANGELOG.md = CHANGELOG.md 11 | LICENSE.txt = LICENSE.txt 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoRemoteDebugger.Server", "MonoRemoteDebugger.Server\MonoRemoteDebugger.Server.csproj", "{134655A3-EF22-4E4A-AE8D-9EE1282733A7}" 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoRemoteDebugger.SharedLib", "MonoRemoteDebugger.SharedLib\MonoRemoteDebugger.SharedLib.csproj", "{FE76AEE5-C4E3-45ED-AA18-29DF82AB405C}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoRemoteDebugger.VSExtension", "MonoRemoteDebugger.VSExtension\MonoRemoteDebugger.VSExtension.csproj", "{7A273453-436B-4226-BCD4-9D02687944A0}" 20 | ProjectSection(ProjectDependencies) = postProject 21 | {134655A3-EF22-4E4A-AE8D-9EE1282733A7} = {134655A3-EF22-4E4A-AE8D-9EE1282733A7} 22 | {FE76AEE5-C4E3-45ED-AA18-29DF82AB405C} = {FE76AEE5-C4E3-45ED-AA18-29DF82AB405C} 23 | {C99840F2-29F7-44FC-A152-3CDD7FC41B21} = {C99840F2-29F7-44FC-A152-3CDD7FC41B21} 24 | EndProjectSection 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoRemoteDebugger.Debugger", "MonoRemoteDebugger.Debugger\MonoRemoteDebugger.Debugger.csproj", "{C99840F2-29F7-44FC-A152-3CDD7FC41B21}" 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assemblies", "Assemblies", "{866CF2EE-574D-49C6-89EB-637B56693D24}" 29 | ProjectSection(SolutionItems) = preProject 30 | Assemblies\Microsoft.DebugEngineHost.dll = Assemblies\Microsoft.DebugEngineHost.dll 31 | Assemblies\Microsoft.MICore.dll = Assemblies\Microsoft.MICore.dll 32 | Assemblies\Techl.dll = Assemblies\Techl.dll 33 | Assemblies\Techl.Mono.Cecil.dll = Assemblies\Techl.Mono.Cecil.dll 34 | Assemblies\Techl.Mono.Debugger.Soft.dll = Assemblies\Techl.Mono.Debugger.Soft.dll 35 | EndProjectSection 36 | EndProject 37 | Global 38 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 39 | Debug|Any CPU = Debug|Any CPU 40 | Release|Any CPU = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 43 | {134655A3-EF22-4E4A-AE8D-9EE1282733A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {134655A3-EF22-4E4A-AE8D-9EE1282733A7}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {134655A3-EF22-4E4A-AE8D-9EE1282733A7}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {134655A3-EF22-4E4A-AE8D-9EE1282733A7}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {FE76AEE5-C4E3-45ED-AA18-29DF82AB405C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {FE76AEE5-C4E3-45ED-AA18-29DF82AB405C}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {FE76AEE5-C4E3-45ED-AA18-29DF82AB405C}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {FE76AEE5-C4E3-45ED-AA18-29DF82AB405C}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {7A273453-436B-4226-BCD4-9D02687944A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {7A273453-436B-4226-BCD4-9D02687944A0}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {7A273453-436B-4226-BCD4-9D02687944A0}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {7A273453-436B-4226-BCD4-9D02687944A0}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {C99840F2-29F7-44FC-A152-3CDD7FC41B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {C99840F2-29F7-44FC-A152-3CDD7FC41B21}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {C99840F2-29F7-44FC-A152-3CDD7FC41B21}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {C99840F2-29F7-44FC-A152-3CDD7FC41B21}.Release|Any CPU.Build.0 = Release|Any CPU 59 | EndGlobalSection 60 | GlobalSection(SolutionProperties) = preSolution 61 | HideSolutionNode = FALSE 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /Output/Debug/Microsoft.CodeAnalysis.CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techl/MonoRemoteDebugger/da9f10cfaca66a5b78ffb2b1f278006e251e6253/Output/Debug/Microsoft.CodeAnalysis.CSharp.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MonoRemoteDebugger 2 | ============ 3 | 4 | MonoRemoteDebugger enables linux remote debugging using Visual Studio 2015-2019 5 | 6 | [![Build status](https://ci.appveyor.com/api/projects/status/y25a6ymkwrt268s1?svg=true)](https://ci.appveyor.com/project/techcap/monoremotedebugger) 7 | 8 | Usage 9 | --- 10 | Download MonoRemoteDebugger.Server on the linux machine. 11 | > wget https://github.com/techl/MonoRemoteDebugger/releases/download/v1.5.2/MonoRemoteDebugger.Server.zip 12 | 13 | Extract MonoRemoteDebugger.Server 14 | > unzip -d MonoRemoteDebugger.Server MonoRemoteDebugger.Server.zip 15 | 16 | Run MonoRemoteDebugger.Server on the linux machine. 17 | > cd MonoRemoteDebugger.Server 18 | 19 | > mono MonoRemoteDebugger.Server.exe 20 | 21 |
22 | 23 | 24 | Install MonoRemoteDebugger extension. You can find also in the Visual Studio Gallery. 25 | 26 | Run Visual Studio 2019. 27 | 28 | Toolbar -> Extensions -> MonoRemoteDebugger -> Debug with Mono (remote) 29 | 30 | Type remote IP Address . 31 | 32 | Click Connect button. 33 | 34 | Then the program will run and hit the breakpoint which you set on Visual Studio. 35 | 36 | Enjoy you debugging. 37 | 38 |
39 | 40 | ### Known Issue 41 | Not supported breakpoint on user thread. 42 | 43 | Not supported Visual Basic, F#. 44 | 45 | Unstable on .Net Core Common Project System 46 | 47 | ### Version History 48 | 49 | 50 |
51 | 52 | This project is based on [MonoDebugger](https://github.com/giessweinapps/MonoDebugger). Thanks to Christian Giesswein. 53 | -------------------------------------------------------------------------------- /UpdateVSIXManifest.ps1: -------------------------------------------------------------------------------- 1 | $file= "AssemblyInfo.cs" 2 | $content = Get-Content $file 3 | $pattern = 'AssemblyVersion\("(.*?)"\)' 4 | $match = [System.Text.RegularExpressions.Regex]::Match($content, $pattern) 5 | Write-Host "AssemblyInfo.cs Version is: " $match.Groups[1] 6 | 7 | $vsix = "MonoDebugger.VS2013\VisualStudio\source.extension.vsixmanifest" 8 | $content = Get-Content $vsix 9 | $content = [System.Text.RegularExpressions.Regex]::Replace($content, '" Version="(.*?)"', '" Version="'+$match.Groups[1]+'"') 10 | Write-Host $content 11 | 12 | Set-Content -Value $content -Path $vsix -encoding UTF8 -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2015 2 | 3 | install: 4 | - ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex 5 | 6 | before_build: 7 | - ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion 8 | #- ps: Vsix-TokenReplacement MonoRemoteDebugger.VSExtension\obj\Debug\extension.cs 'Version = "([0-9\\.]+)"' 'Version = "{version}"' 9 | 10 | build_script: 11 | - nuget restore -Verbosity quiet 12 | - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 13 | 14 | after_test: 15 | - ps: Vsix-PushArtifacts | Vsix-PublishToGallery 16 | --------------------------------------------------------------------------------