Roslyn动态编译代码异常栈缺失行号问题求助
解决Roslyn动态编译异常无法获取行号的问题
我来帮你梳理下问题原因,以及对应的解决办法:
问题核心原因
你遇到的情况是:Visual Studio能显示异常行号,但通过StackTrace API拿不到行信息,本质是动态加载的程序集对应的源文件路径在运行时不存在,CLR无法从PDB中关联到有效的源文件信息。具体来说:
- 你设置的
sourceCodePath是"generated.cs",但这个文件并没有实际创建在当前运行目录下 - 虽然你添加了
EmbeddedText嵌入源信息,但CLR默认优先查找物理文件,导致无法直接从PDB中解析嵌入式源内容
两种可行解决方案
方案1:生成临时文件加载(最直接)
让CLR能找到对应的物理源文件和PDB文件,就能正常解析行号:
修改你的编译加载逻辑:
// 创建临时目录存放文件 var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempDir); // 定义文件路径 var assemblyPath = Path.Combine(tempDir, $"{assemblyName}.dll"); var pdbPath = Path.Combine(tempDir, symbolsName); var sourcePath = Path.Combine(tempDir, sourceCodePath); // 把生成的代码写入临时文件 File.WriteAllText(sourcePath, code, encoding); // 编译输出到文件 EmitResult result = compilation.Emit( peStream: File.OpenWrite(assemblyPath), pdbStream: File.OpenWrite(pdbPath), embeddedTexts: embeddedTexts, options: emitOptions); if (result.Success) { Console.WriteLine("Kompilierung erfolgreich!"); try { // 从文件加载程序集 var assembly = Assembly.LoadFrom(assemblyPath); var parsedCodeInfo = new IM3ParsedCodeInfo() { Assembly = assembly, Erfolgreich = true }; return parsedCodeInfo; } catch (Exception ex) { Console.WriteLine("Ausnahme aufgetreten:"); Console.WriteLine(ex); var parsedCodeInfo = new IM3ParsedCodeInfo() { ErrorException = ex, Erfolgreich = false }; return parsedCodeInfo; } }
方案2:自定义AssemblyLoadContext(无临时文件)
如果不想生成物理文件,可以通过自定义加载上下文,让CLR直接从PDB的嵌入式源信息中读取行号:
首先定义自定义加载上下文:
public class DynamicAssemblyLoadContext : AssemblyLoadContext { private readonly byte[] _pdbBytes; public DynamicAssemblyLoadContext(byte[] pdbBytes) : base(isCollectible: true) { _pdbBytes = pdbBytes; } protected override Assembly Load(AssemblyName assemblyName) { throw new NotImplementedException(); } // 重写获取符号流的方法,返回我们的PDB字节流 protected override Stream GetSymbolStream(string assemblyPath) { return new MemoryStream(_pdbBytes); } }
然后修改加载逻辑:
if (result.Success) { Console.WriteLine("Kompilierung erfolgreich!"); try { var loadContext = new DynamicAssemblyLoadContext(symbolsStream.ToArray()); var assembly = loadContext.LoadFromStream(assemblyStream); var parsedCodeInfo = new IM3ParsedCodeInfo() { Assembly = assembly, Erfolgreich = true }; return parsedCodeInfo; } catch (Exception ex) { Console.WriteLine("Ausnahme aufgetreten:"); Console.WriteLine(ex); var parsedCodeInfo = new IM3ParsedCodeInfo() { ErrorException = ex, Erfolgreich = false }; return parsedCodeInfo; } }
额外需要确认的点
- 你的
CSharpCompilationOptions已经设置OptimizationLevel.Debug,这是正确的(Release模式会丢失调试信息) EmitOptions使用的PortablePdb格式是兼容的,无需修改- 捕获异常时
new StackTrace(ex, true)的参数true必须传,这样才会收集文件和行号信息
按照上面的方案修改后,frame.GetFileLineNumber()就能返回正确的行号,stackTrace.ToString()也会包含完整的行信息了。
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

