CLR 调试概述
利用公共语言运行时 (CLR) 调试 API,工具供应商可以编写调试器来调试运行于 CLR 环境中的应用程序。 要调试的代码可为 CLR 支持的任何代码种类。CLR 调试 API 主要是使用非托管代码实现的。 因此,调试 API 呈现为一组组件对象模型 (COM) 接口。 API 由以下各项组成:
CLR 实现的 COM 对象和接口的集合。
必须由调试器实现的 COM 回调接口的集合。
CLR 调试方案
以下各部分介绍公共语言运行时调试 API 如何处理典型的调试方案。 请注意,该运行时直接支持某些方案,并且可与当前方法进行互操作来支持其他方案。
进程外调试
在进程外调试中,调试器不在所调试的进程中,而在另一个进程中(即在调试对象外部)。 此方案减少了调试器与调试对象之间的交互。 因此,它可以更准确地描述进程。
CLR 调试 API 直接支持进程外调试。 API 将处理调试器与调试对象托管部分之间的所有通信以支持托管代码调试。
虽然 CLR 调试 API 用于进程外,但某些调试逻辑(例如线程同步)却与调试对象在同一进程内。 大多数情况下,这是应该对调试器保持透明的实现详细信息。 有关线程同步的更多信息,请参见 CLR 调试体系结构。 调试 API 的缺点是在进程外使用时无法用它来检查崩溃转储。
进程内调试
在 .NET Framework 1.0 和 1.1 版本中,CLR 调试 API 支持受限的进程内调试,在受限的进程内调试中,探查器可以使用调试 API 的检查功能。 在 .NET Framework 2.0 中,进程内调试被替换为一组与分析 API 更加一致的功能。 有关这些更改的更多信息,请参见分析概述中的堆栈快照和对象检查功能。
远程进程调试
在远程进程调试中,调试器用户界面与所调试的进程不在同一计算机上。 如果调试器和调试对象在同一计算机上运行时相互影响,此方案可能很有用。 这种影响可能由下列原因引起:
有限资源。
位置依赖性。
影响操作系统的 Bug。
CLR 调试 API 不直接支持远程进程调试。 基于 CLR 调试 API 的调试器仍然必须存在于调试对象进程外。 因此,此解决方案需要在调试对象所在的计算机上有一个代理进程。
非托管代码调试
托管代码和非托管代码通常共存于同一进程中。 通常需要同时调试这两种代码类型。
CLR 调试 API 支持跨越托管代码和非托管代码之间的界限执行单步调试,但是不直接支持非托管代码调试。 然而,通过共享 Win32 调试功能,CLR 调试 API 可以与非托管代码调试器共存。
此外,CLR 调试 API 为调试进程提供了两种选择方案:
“软附加”选择方案,在此选择方案中只调试进程的托管部分。 软附加到进程的调试器随后可以从该进程中分离出来。
“硬附加”选择方案,在此选择方案中将调试进程的托管和非托管部分,并且通过调试 API 公开所有 Win32 调试事件。
混合语言环境
在组件式软件中,可以用不同的语言构建不同的组件。 调试器必须知道语言差异,以便能够用正确的格式显示数据、用正确的语法对表达式进行求值等等。
CLR 调试 API 不对混合语言环境提供任何直接支持,因为 CLR 没有源语言概念。 通过应用调试器的现有源映射功能,应该能够将给定的函数映射到实现该函数所采用的语言。
多个进程和分布式程序
组件式程序可以包括协作组件,这些组件可以在不同的进程上运行,甚至可以在整个网络中的不同计算机上运行。 调试器应该能够跟踪进程之间以及计算机之间的执行逻辑,以提供所发生事件的逻辑视图。
CLR 调试 API 不对多进程调试提供任何直接支持。 而使用 API 的调试器应该直接提供这类支持,并且用于执行此操作的现有方法应该继续有效。
API 类别
调试 API 包括以下三组接口,这三组接口通常都由 CLR 调试器使用,并均以非托管代码的形式实现:
支持对 CLR 应用程序进行调试的接口。
允许访问符号调试信息的接口,该信息通常存储在程序数据库 (PDB) 文件中。
支持在计算机上查询进程和应用程序域的接口。
调试 API 依赖于另外两组接口:
用于处理静态程序信息(比如类和方法类型信息)检查的元数据 API。
用于为托管代码调试器支持源级别调试的符号存储区 API。
调试接口也可以划分为下表中所示的功能类别。
API 类别 |
说明 |
---|---|
注册 |
调试器调用以向 CLR 注册并在发生特定事件时请求通知的接口。 |
通知 |
CLR 用于将各种事件通知调试器并返回请求的信息的回调接口。 这些接口必须由调试器实现。 |
断点 |
调试器调用以检索有关断点的信息的接口。 |
执行 |
调试器调用以控制调试对象执行和访问调用堆栈的接口。 |
信息 |
调试器调用以获取有关调试对象的信息的接口。 |
Enumeration |
调试器调用以枚举对象的接口。 |
修改 |
调试器调用以修改所调试代码的接口。 |
零基础调试的方法
毫无疑问,软件开发人员编写的代码并不总是按照预期行事。 有时它执行一些完全不同的操作! 发生这种情况时,下一个任务是找出原因。尽管我们可能会一直盯着代码看几个小时,但使用调试工具或调试程序会更轻松且更高效。
遗憾的是,调试程序无法神奇地揭示代码中的所有问题或“bug”。 调试意味着在 Visual Studio 等调试工具中逐步运行代码,以找到导致编程错误的确切位置 。 那么就可以了解代码中所需的更正,并且调试工具通常允许进行临时更改,以便可以继续运行该程序。
有效地使用调试程序也是一项需要时间和实践来学习的技能,但从根本上来说这是每个软件开发人员的一项基本任务。 本文随后将介绍调试的核心原则并提供入门提示。
通过问自己正确的问题来澄清问题
在尝试修复之前,这有助于阐明遇到的问题。 我们预计代码已出现了问题,否则你不会试图弄清楚对其进行调试的方法! 因此,开始调试之前,请确保你已确定要解决的问题:
期望代码可执行哪些操作?
相反,发生了什么?
如果运行应用时遇到了错误(异常),这可能是一件好事! 异常是运行代码时遇到的意外事件,通常是某种错误。 调试工具有助于找到代码中发生异常的确切位置并且帮助调查可能的修复方法。
如果发生了其他情况,问题的症状是什么? 是否已经怀疑代码中出现此问题的位置? 例如,如果代码显示了某些文本,但文本不正确,则你知道数据已损坏或设置显示文本的代码存在某种 bug。 通过逐步调试调试程序中的代码,可以检查变量的每个更改,以准确地发现分配时间以及分配不正确值的方式。
检查假设
在调查 bug 或错误之前,请考虑你期望获得某个结果的假设。 即使正在查看调试程序中问题的成因,隐藏或未知的假设也可能妨碍识别问题。 可能有一长串可能的假设! 以下是要询问的一些问题,以质疑你的假设。
是否使用正确的 API(即正确的对象、函数、方法或属性)? 正在使用的 API 可能无法按照你的想法执行操作。 (在调试程序中检查 API 调用之后,解决此问题可能需要转至文档以帮助识别正确的 API。)
是否正确地使用了 API? 也许使用了正确的 API,但方法不正确。
代码是否包含任何拼写错误? 很难发现某些拼写错误(例如变量名的简单拼写错误),尤其是在使用不需要在使用变量之前声明变量的语言时。
是否对代码进行了更改,并假设它与所看到的问题无关?
是否期望对象或变量包含与实际情况不同的特定值(或某种类型的值)?
是否知道代码的含义? 调试其他人的代码通常更加困难。 如果不是你的代码,则可能需要花时间准确地了解代码的作用,然后才能有效地进行调试。
提示
编写代码时,从小型代码开始,从有效的代码开始! (优秀的示例代码在此处很有用。)有时,通过从演示尝试实现的核心任务的一小段代码开始,可以更轻松地修复大型或复杂的代码集。 然后,可以以增量方式来修改或添加代码,从而在每个点测试错误。
CLR Exception---E0434352
什么是CLR Exception---E0434352
CLR异常是.NET应用程序生成的异常类型。异常被封装在从System.exception类派生的类中。它的异常代码是0xE0434352,代码的后面三个字节对应assic码的 "CCR",所以又叫CCR异常。Visual Studio调试器指南---编辑并继续的限制
链接限制
“编辑并继续”有两种类型的链接器限制:
默认情况下,“编辑并继续”在调试会话结束时重新链接到程序,以创建最新的可执行文件。 自动重新链接方面有一些限制。
有些链接器选项禁用“编辑并继续”。
预编译头限制
默认情况下,“编辑并继续”在后台加载并处理预编译头,以加速对代码更改的处理。 加载预编译头需要分配物理内存,如果您正在一台 RAM 有限的计算机上进行编译,这可能会是一个问题。 在 Windows NT 或 Windows 2000 下,可以使用 Windows NT 任务管理器确定调试时可用的物理内存量,从而确定这是否会成为问题。 如果此数量大于预编译头的大小,则“编辑并继续”应没有问题。 如果此数量小于预编译头的大小,可以禁止“编辑并继续”在后台加载预编译头。
对“编辑并继续”禁用预编译头的后台加载
在“工具”菜单上,单击“选项”。
在“选项”对话框中选择“调试”节点,然后选择“编辑并继续”组。
清除“允许预编译”复选框。
单击“确定”。
特性限制
“编辑并继续”不重新生成接口定义 (IDL) 文件。 因此,调试时不反映对 IDL 特性的更改。 若要查看对 IDL 特性更改的结果,必须停止调试并重新生成应用程序。 如果 IDL 特性已更改,“编辑并继续”不生成错误或警告。
关于System.MissingMethodException异常