How to debug code compiled with Roslyn in a Visual Studio extension inside current Visual Studio host?

≯℡__Kan透↙ 提交于 2019-12-03 08:52:47

Seems like this is actually impossible.

The current visual studio instance cannot debug itself.

I've tried creating a Console Application with roslyn, attaching it to the debugger and then run the generation code from it. But the VisualStudioWorkspace is only available inside VisualStudio (Not serializable and not avalaible througt DTE com interface). So the only solution left was using MBBuildWorkspace. Since it does not have that same behavior as Visual studio workspace, I've abandoned the project.

Here's my code for further references :

Process vsProcess = Process.GetCurrentProcess();

string solutionPath = CurrentWorkspace.CurrentSolution.FilePath;

SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText($@"
            using System;
            using System.Threading.Tasks;

            namespace CodeGenApplication
            {{
                public class Program 
                {{
                    public static void Main(string[] args) 
                    {{

                    Console.ReadLine();
                    int vsProcessId = Int32.Parse(args[0]);
                    CodeGenApp.Test(""{solutionPath.Replace(@"\", @"\\")}"", ""{projectName}"", ""{_codeGenProjectName}"");
                    Console.ReadLine();
                    }}
                }}
            }}");

string assemblyName = Path.GetRandomFileName();

Project codeGenProject = CurrentWorkspace.CurrentSolution.Projects.Where(x => x.Name == _codeGenProjectName).FirstOrDefault();

List<MetadataReference> references = codeGenProject.MetadataReferences.ToList();

CSharpCompilation compilation = CSharpCompilation.Create(

    assemblyName,
    syntaxTrees: new[] { syntaxTree },
    references: references,
    options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));

// Emit assembly to streams.
EmitResult result = compilation.Emit("CodeGenApplication.exe", "CodeGenApplication.pdb");

if (!result.Success)
{
    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
        diagnostic.IsWarningAsError ||
        diagnostic.Severity == DiagnosticSeverity.Error);
}
else
{
    Process codeGenProcess = new Process();
    codeGenProcess.StartInfo.FileName = "CodeGenApplication.exe";
    codeGenProcess.StartInfo.Arguments = vsProcess.Id.ToString();
    codeGenProcess.StartInfo.UseShellExecute = false;
    codeGenProcess.StartInfo.CreateNoWindow = true;
    codeGenProcess.StartInfo.LoadUserProfile = true;
    codeGenProcess.StartInfo.RedirectStandardError = true;
    codeGenProcess.StartInfo.RedirectStandardInput = true;
    codeGenProcess.StartInfo.RedirectStandardOutput = false;
    codeGenProcess.Start();
    foreach (EnvDTE.Process dteProcess in _dte.Debugger.LocalProcesses)
    {
        if (dteProcess.ProcessID == codeGenProcess.Id)
        {
            dteProcess.Attach();
        }
    }
    codeGenProcess.StandardInput.WriteLine("Start");
}
m0sa

You have to attach the debugger to the currently running visual studio host. To do that you need to:

  1. Get hold of the DTE object,
  2. find the (current) process in DTE.Debugger.LocalProcesses
  3. Attach the debugger - example
  4. run method from the compiled assembly

You'd probably want a separate command/button for this, don't just switch on the current build configuration. That's how everybody else does it (e.g. test runners and even Visual Studio). Also, load the compiled Assembly in a new AppDomain, otherwise it'll stick around for ever.

There's also the more "creative" solution of injecting a System.Diagnostics.Debugger.Launch() call to the method you're about to run (modify the correct Compilation.SyntaxTrees before calling Emit) - but seriously, don't do this.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!