Как отладить dll, сгенерированную из компиляции Roslyn? - PullRequest
0 голосов
/ 01 июня 2018

Я использую Roslyn CSharpCompilation для генерации dll-файлов для моих плагинов - файлы имеют OptimizationLevel.Debug и файл pdb генерируется.Затем я загружаю эти файлы в свою программу (библиотеки UWP + .NET Standard 2.0) с использованием Assembly.Load и создаю экземпляр интересующих меня типов. Моя проблема в том, что я не могу получить Visual Studio (версия 2017 года 15.7.3) найти исходный код во время отладки - он работает как внешняя библиотека, поэтому, когда возникает исключение, я не могу найти где.Я устал искать решение на stackoverflow, но все решение не работает.Я проверил это:

  • Pdb генерируется
  • Окно модуля в VS показывает, что символы загружены
  • Пробовал другую версию Assembly Load / LoadFrom
  • Настройка «Использовать режим управляемой совместимости» в параметрах отладки

Есть ли способ сделать файл отлаживаемым?Может быть, я должен использовать какой-то параметр Roslyn при компиляции или изменить что-то в VS?

1 Ответ

0 голосов
/ 21 августа 2018

Пример кода ниже должен помочь вам на вашем пути.Он основан на части генерации кода контейнера IOC thlamare , преемника StructureMap, созданного Джереми Д. Миллером.

Я только добавил возможности отладки.Хитрость заключалась в том, чтобы сделать исходный текст встраиваемым, выбрать правильные форматы и установить значения кодировки, где это необходимо.

Проверьте оригинальную работу для получения дополнительной информации, например, о добавлении ссылок.

public Assembly CreateAssembly(string code)
{
    var encoding = Encoding.UTF8;

    var assemblyName = Path.GetRandomFileName();
    var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
    var sourceCodePath = "generated.cs";

    var buffer = encoding.GetBytes(code);
    var sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);

    var syntaxTree = CSharpSyntaxTree.ParseText(
        sourceText, 
        new CSharpParseOptions(), 
        path: sourceCodePath);

    var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
    var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, sourceCodePath, encoding);

    var optimizationLevel = OptimizationLevel.Debug;

    CSharpCompilation compilation = CSharpCompilation.Create(
        assemblyName,
        syntaxTrees: new[] { encoded },
        references: references,
        options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            .WithOptimizationLevel(optimizationLevel)
            .WithPlatform(Platform.AnyCpu)
    );

    using (var assemblyStream = new MemoryStream())
    using (var symbolsStream = new MemoryStream())
    {
        var emitOptions = new EmitOptions(
                debugInformationFormat: DebugInformationFormat.PortablePdb,
                pdbFilePath: symbolsName);

        var embeddedTexts = new List<EmbeddedText>
        {
            EmbeddedText.FromSource(sourceCodePath, sourceText),
        };

        EmitResult result = compilation.Emit(
            peStream: assemblyStream,
            pdbStream: symbolsStream,
            embeddedTexts: embeddedTexts,
            options: emitOptions);

        if (!result.Success)
        {
            var errors = new List<string>();

            IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                diagnostic.IsWarningAsError ||
                diagnostic.Severity == DiagnosticSeverity.Error);

            foreach (Diagnostic diagnostic in failures)
                errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");

            throw new Exception(String.Join("\n", errors));
        }

        Console.WriteLine(code);

        assemblyStream.Seek(0, SeekOrigin.Begin);
        symbolsStream?.Seek(0, SeekOrigin.Begin);

        var assembly = AssemblyLoadContext.Default.LoadFromStream(assemblyStream, symbolsStream);
        return assembly;
    }
}

Использование:

[Test]
public void Verify()
{
    var code =
        @"namespace Debuggable
        {
            public class HelloWorld
            {
                public string Greet(string name)
                {
                    var result = ""Hello, "" + name;
                    return result;
                }
            }
        }
        ";

    var codeGenerator = new CodeGenerator();
    var assembly = codeGenerator.CreateAssembly(code);

    dynamic instance = assembly.CreateInstance("Debuggable.HelloWorld");

    // Set breakpoint here
    string result = instance.Greet("Roslyn");

    result.Should().Be("Hello, Roslyn");
}
...