如何调试 Roslyn 编译生成的 dll?
How to debug dll generated from Roslyn compilation?
我正在使用 Roslyn CSharpCompilation 为我的插件生成 dll 文件 – 文件有 OptimizationLevel.Debug 并且生成了 pdb 文件。接下来,我使用 Assembly.Load 将这些文件加载到我的程序(UWP + .NET Standard 2.0 库)并创建我感兴趣的类型的实例。我的问题是我无法获得 Visual Studio (version 2017 15.7.3) 在我调试的时候找源代码——它像外部库一样踩它,所以当里面抛出异常时我找不到在哪里。我已经厌倦了在 Whosebug 上搜索解决方案,但所有解决方案都不起作用。我检查过这个:
- 生成 Pdb
- VS 中的模块 window 显示已加载符号
- 尝试了不同版本的 Assembly Load/LoadFrom
- 设置“使用
调试选项中的“托管兼容模式”
有什么方法可以使文件可调试吗?也许我必须在编译或更改 VS 中的某些内容时使用一些 roslyn 选项?
下面的代码示例应该对您有所帮助。它基于 thlamare IOC 容器 lamar 的代码生成部分,它是 Jeremy D Miller 制作的 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");
}
我正在使用 Roslyn CSharpCompilation 为我的插件生成 dll 文件 – 文件有 OptimizationLevel.Debug 并且生成了 pdb 文件。接下来,我使用 Assembly.Load 将这些文件加载到我的程序(UWP + .NET Standard 2.0 库)并创建我感兴趣的类型的实例。我的问题是我无法获得 Visual Studio (version 2017 15.7.3) 在我调试的时候找源代码——它像外部库一样踩它,所以当里面抛出异常时我找不到在哪里。我已经厌倦了在 Whosebug 上搜索解决方案,但所有解决方案都不起作用。我检查过这个:
- 生成 Pdb
- VS 中的模块 window 显示已加载符号
- 尝试了不同版本的 Assembly Load/LoadFrom
- 设置“使用 调试选项中的“托管兼容模式”
有什么方法可以使文件可调试吗?也许我必须在编译或更改 VS 中的某些内容时使用一些 roslyn 选项?
下面的代码示例应该对您有所帮助。它基于 thlamare IOC 容器 lamar 的代码生成部分,它是 Jeremy D Miller 制作的 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");
}