在调试时从 .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 代码的自动编译。 在单步执行外部代码或使用“调用堆栈”窗口时,可自动进行编译。

在单步执行已在外部实现的代码时,此调试程序会自动反向编译该代码并显示当前的执行点。 如果要单步执行外部代码,请禁用仅我的代码

无需禁用“仅我的代码”,即可通过“调用堆栈”窗口进行反向编译。

若要从“调用堆栈”窗口自动进行编译:

  1. 在打开“调用堆栈”窗口进行调试时,选择显示外部代码

  2. 在“调用堆栈”窗口中,双击任一堆栈帧。 调试器会反向编译该代码,然后直接导航到当前执行点。

    显示外部代码的“调用堆栈”窗口的屏幕截图。

    所有反向编译的代码也显示在解决方案资源管理器中的外部源节点下,以便在需要时轻松浏览外部文件。

    显示反编译程序集的外部源节点的屏幕截图。

    可调试反向编译后的代码并设置断点。

若要禁用外部代码的自动反向编译,请转到工具 > 选项 > 调试 > 常规,并取消选择按需自动反向编译到源(仅托管)

为程序集生成和嵌入源代码

除了针对特定位置生成源代码之外,还可以针对给定的 .NET 程序集生成所有源代码。 若要完成此任务,请转到“模块”窗口,然后在 .NET 程序集的上下文菜单中,选择“将源反向编译到符号文件”命令。 Visual Studio 为程序集生成一个符号文件,然后将源代码嵌入到该符号文件中。 在后续步骤中,可以提取嵌入的源代码。

“模块”窗口中包含“反向编译源代码”命令的程序集上下文菜单的屏幕截图。

提取和查看嵌入的源代码

可使用“模块”窗口内上下文菜单中的“提取源代码”命令来提取嵌入在符号文件中的源文件 。

“模块”窗口包含“提取源代码”命令的程序集上下文菜单的屏幕截图。

提取的源文件将作为杂项文件添加到解决方案中。 在 Visual Studio 中,杂项文件功能默认处于关闭状态。 可通过工具>选项>环境>文档>在解决方案资源管理器中显示杂项文件复选框来启用此功能。 如果未启用此功能,则无法打开提取的源代码。

已启用“杂项文件”选项的工具选项页面的屏幕截图。

提取的源文件将显示在“解决方案资源管理器”的“杂项文件”中。

包含“杂项文件”的“解决方案资源管理器”的屏幕截图。

对于 .NET 库或为 SourceLink 启用的 NuGet 包,还可以单步执行源代码、设置断点并使用所有调试器功能。 有关详细信息,请参阅使用源链接启用调试和诊断使用 SourceLink 提高调试时工作效率

已知的限制

需要中断模式

仅当调试器处于中断模式并且应用程序暂停时,才可以使用反向编译功能生成源代码。 例如,Visual Studio 遇到断点或异常时会进入中断模式。 通过使用“全部中断”命令(全部中断图标),可以轻松触发 Visual Studio 在下次运行代码时中断。

反向编译的限制

从 .NET 程序集中使用的中间格式 (IL) 生成源代码有一些固有的限制。 因此,生成的源代码看起来与原始源代码不同。 大多数存在差异的地方都在于运行时不需要原始源代码中的信息。 例如,运行时不需要空格、注释和局部变量名称等信息。 建议使用生成的源代码来了解程序的执行方式,而不是用于替代原始源代码。

调试优化或发布的程序集

在调试从使用编译器优化功能编译的程序集反向编译的代码时,可能会遇到以下问题:

  • 断点可能并不总是绑定到匹配的源位置。
  • 单步执行可能并不总是进行到正确的位置。
  • 局部变量的名称可能不正确。
  • 某些变量可能无法求值。

可在 GitHub 问题中找到更多详细信息:ICSharpCode.Decompiler 与 VS 调试器集成

反向编译的可靠性

反向编译尝试失败的概率相对较小。 此行为是由于 ILSpy 中存在序列点 null 引用错误而导致的。 我们通过捕获这些问题并重现失败过程来减少反向编译失败。

可在 GitHub 问题中找到更多详细信息:ICSharpCode.Decompiler 与 VS 调试器集成

异步代码的限制

使用异步/等待代码模式反向编译模块的结果可能不完整或完全失败。 异步/等待和暂停状态机的 ILSpy 实现仅部分实现。

可在 GitHub 问题中找到更多详细信息:PDB 生成器状态

仅我的代码

仅我的代码 (JMC) 设置使 Visual Studio 可以单步跳过系统、框架、库和其他非用户调用。 在调试会话期间,模块窗口会显示调试器将哪些代码模块视为“我的代码”(用户代码)。

对优化或发布模块进行反向编译会生成非用户代码。 例如,如果调试器中断了反向编译的非用户代码,则会显示“无源代码”窗口。 要禁用“仅我的代码”,请导航至工具>选项(或调试>选项)>调试>常规,然后取消选择启用’仅我的代码’

提取的源代码

从程序集中提取的源代码具有以下限制:

  • 所生成文件的名称和位置不可配置。
  • 这些文件是临时文件,Visual Studio 会将其删除。
  • 这些文件放在单个文件夹中,并且不使用原始源文件的任何文件夹层次结构。
  • 每个文件的文件名都包含该文件的校验和哈希。

生成的代码仅采用 C#

反向编译仅生成 C# 源代码文件。 没有以任何其他语言生成文件的选项。