调试时从 .NET 程序集生成源代码
调试 .NET 应用程序时,你可能会发现需要查看尚未拥有的源代码。 例如,中断异常或使用调用堆栈导航到源位置。
备注
- 源代码生成(反编译)仅适用于 .NET 应用程序,并且基于开放源代码 ILSpy 项目。
- 反编译仅在 Visual Studio 2019 16.5 及更高版本中可用。
- 将 SuppressIldasmAttribute 属性应用于程序集或模块可防止 Visual Studio 尝试反编译。 尽管该属性在 .NET 6 及更高版本中已过时,但 Visual Studio 会遵循该属性。
生成源代码
进行调试但没有可用的源代码时,Visual Studio 会显示“找不到源代码”文档,或者,如果没有用于程序集的符号,则显示“未加载任何符号”文档。 这两个文档都有一个 反编译源代码 选项,该选项为当前位置生成 C# 代码。 然后,生成的 C# 代码可以像任何其他源代码一样使用。 可以查看代码、检查变量、设置断点等。
未加载任何符号
下图显示了“未加载任何符号”消息。
的屏幕截图
找不到源
下图显示了“找不到源代码”消息。
自动反编译代码
从 Visual Studio 2022 版本 17.7 开始,Visual Studio 调试器支持自动编译外部 .NET 代码。 在步入外部代码或使用“调用堆栈”窗口时,您可以自动反编译。
如果进入已在外部实现的代码,调试器会自动反编译它,并显示当前的执行位置。 如果要单步执行外部代码,请禁用仅我的代码。
可以从“调用堆栈”窗口反编译,而无需禁用“仅我的代码”。
要从“调用堆栈”窗口自动反编译,请执行以下操作:
打开“调用堆栈”窗口进行调试时,选择“显示外部代码”。
在“调用堆栈”窗口中,双击任何堆栈帧。 调试器将反编译代码,然后直接导航到当前执行点。
解决方案资源管理器中的“外部源”节点下也显示了所有反编译代码,以便在需要时轻松浏览外部文件。
可以调试反编译代码并设置断点。
若要禁用外部代码的自动反编译,请转到“工具”>“选项”>“调试”>“常规”,并取消选择“在需要时自动反编译到源(仅限托管)”。
为程序集生成和嵌入源代码
除了为特定位置生成源代码外,还可以为给定的 .NET 程序集生成所有源代码。 若要执行此任务,请转到 模块 窗口,然后从 .NET 程序集的上下文菜单中选择“将源反编译到符号文件 命令。 Visual Studio 为程序集生成符号文件,然后将源嵌入符号文件中。 在后续步骤中,可以提取嵌入的源代码。
提取并查看嵌入的源代码
可以使用 “提取源代码” 命令在 “模块”窗口的上下文菜单中提取嵌入在符号文件中的源文件。
提取的源文件已被添加到解决方案中,作为 杂项文件。 默认情况下,Visual Studio 中的杂项文件功能处于关闭状态。 可以从 工具>选项>环境>文档>解决方案 资源管理器中的“显示杂项文件”复选框启用此功能。 如果未启用此功能,则无法打开提取的源代码。
提取的源文件显示在 解决方案资源管理器杂项文件中。
SourceLink
对于支持 SourceLink 的 .NET 库或 NuGet 包,您还可以进入源代码、设置断点并使用所有调试功能。 有关详细信息,请参阅 使用源链接 启用调试和诊断,使用 SourceLink提高调试时工作效率。
已知限制
需要中断模式
仅当调试器处于中断模式且应用程序暂停时,才能使用反编译生成源代码。 例如,Visual Studio 遇到断点或异常时会进入中断模式。 可以使用命令 Break All(图标)轻松触发 Visual Studio 在代码下次运行时暂停。
反向编译的限制
从 .NET 程序集中使用的中间格式(IL)生成源代码具有一些固有的限制。 因此,生成的源代码与原始源代码不同。 大多数差异位于运行时不需要原始源代码中信息的位置。 例如,运行时不需要空格、注释和局部变量的名称等信息。 建议使用生成的源来了解程序的执行方式,而不是原始源代码的替代方法。
调试优化或发布的程序集
当调试从使用编译器优化编译的程序集中反编译的代码时,可能会遇到以下问题:
- 断点可能并不总是绑定到匹配的源位置。
- 单步执行可能并不总是进行到正确的位置。
- 局部变量可能没有准确的名称。
- 某些变量可能不可用于评估。
GitHub 问题中可以找到更多详细信息:ICSharpCode.Decompiler 集成到 VS Debugger。
反向编译的可靠性
进行反编译尝试时,仅有一小部分可能会失败。 此行为是由于 ILSpy 中的序列点 null 引用错误导致的。 我们通过捕获这些问题并重现失败过程来减少反向编译失败。
GitHub 问题中可以找到更多详细信息:ICSharpCode.Decompiler 集成到 VS Debugger。
异步代码的限制
使用异步/await 代码模式反编译模块的结果可能不完整或完全失败。 异步/等待和暂停状态机的 ILSpy 实现仅部分实现。
可在 GitHub 问题中找到更多详细信息:PDB 生成器状态。
仅我的代码
“仅我的代码”(JMC) 设置允许 Visual Studio 跳过系统代码、框架代码、库代码以及其他非用户代码调用。 在调试会话期间,“模块”窗口显示调试器将视为“我的代码”(用户代码)的代码模块。
对优化或发布模块进行反编译会生成非用户代码。 例如,如果调试器中断了反向编译的非用户代码,则会显示“无源代码”窗口。 若要禁用“仅我的代码”,请导航到“工具>选项(或 调试>选项)>调试>常规,然后取消选择 ”启用仅我的代码“。
提取的资源
从程序集中提取的源代码具有以下限制:
- 生成的文件的名称和位置不可配置。
- 这些文件是临时的,由 Visual Studio 删除。
- 这些文件放置在单个文件夹中,不使用原始源的任何文件夹层次结构。
- 每个文件的文件名都包含该文件的校验和哈希。
生成的代码仅为 C#
反编译仅生成 C# 中的源代码文件。 无法选择以任何其他语言生成文件。