使用托管调试助手诊断错误

注意

本文特定于 .NET Framework。 它不适用于 .NET 的较新版本实现,包括 .NET 6 及更高版本。

托管调试助手 (MDA) 是调试助辅程序,它与公共语言运行时 (CLR) 一起工作以提供关于运行时状态的信息。 这些助手可生成关于你无法通过其他方式捕获的运行时事件的信息性消息。 可以使用 MDA 隔离在托管代码和非托管代码之间转换时发生的、难以发现的应用程序 Bug。

可通过向 Windows 注册表添加注册表项或设置环境变量,启用或禁用所有 MDA。 可使用应用程序配置设置来启用特定的 MDA。 可以在应用程序的配置文件中为某些单独的 MDA 设置其他配置设置。 由于将在加载运行时后分析这些配置文件,因此必须在托管应用程序启动之前启用 MDA。 不能为已经启动的应用程序启用 MDA。

下表列出了 .NET Framework 附带的 MDA:

MDA
asynchronousThreadAbort
bindingFailure
callbackOnCollectedDelegate
contextSwitchDeadlock
dangerousThreadingAPI
dateTimeInvalidLocalFormat
dirtyCastAndCallOnInterface
disconnectedContext
dllMainReturnsFalse
exceptionSwallowedOnCallFromCom
failedQI
fatalExecutionEngineError
gcManagedToUnmanaged
gcUnmanagedToManaged
illegalPrepareConstrainedRegion
invalidApartmentStateChange
invalidCERCall
invalidFunctionPointerInDelegate
invalidGCHandleCookie
invalidIUnknown
invalidMemberDeclaration
invalidOverlappedToPinvoke
invalidVariant
jitCompilationStart
loaderLock
loadFromContext
marshalCleanupError
marshaling
memberInfoCacheCreation
moduloObjectHashcode
nonComVisibleBaseClass
notMarshalable
openGenericCERCall
overlappedFreeError
pInvokeLog
pInvokeStackImbalance
raceOnRCWCleanup
reentrancy
releaseHandleFailed
reportAvOnComRelease
streamWriterBufferedDataLost
virtualCERCall

默认情况下,.NET Framework 会为所有托管调试器激活 MDA 的子集。 通过在“调试”菜单上选择“Windows”“异常设置”,然后展开“托管调试助手”列表,可以查看 Visual Studio 中的默认集。

Visual Studio 中的“异常设置”窗口

启用和禁用 MDA

可使用注册表项、环境变量和应用程序配置设置,启用和禁用 MDA。 若要使用应用程序配置设置,必须启用注册表项或环境变量。

提示

可以在收到 MDA 通知时阻止 Visual Studio 显示 MDA 对话框,而无需禁用 MDA。 为此,请选择“调试”菜单上的“Windows”“异常设置”,展开“托管调试助手”列表,然后为单个 MDA 选中或清除“引发时中断”复选框。

注册表项

如要启用 MDA,可在 Windows 注册表中添加 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\MDA 子项(类型为 REG_SZ,值为 1)。 将以下示例复制到名为 MDAEnable.reg 的文本文件中。打开 Windows 注册表编辑器 (RegEdit.exe),从“文件”菜单选择“导入”。 选择 MDAEnable.reg 文件以在该计算机上启用 MDA。 将子项设置为字符串值 1(不是 DWORD 值 1)能够从 ApplicationName.suffix.mda.config 文件读取 MDA 设置。 例如,Notepad 的 MDA 配置文件将被命名为 notepad.exe.mda.config。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
"MDA"="1"

如果计算机在 64 位操作系统上运行 32 位应用程序,应按下方所示设置 MDA 密钥:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]
"MDA"="1"

有关详细信息,请参阅特定于应用程序的配置设置。 可以使用 COMPLUS_MDA 环境变量替代注册表设置。 有关详细信息,请参阅环境变量

若要禁用 MDA,请使用 Windows 注册表编辑器将 MDA 子项设置为 0(零)。

默认情况下,即使未添加该注册表项,有些 MDA 也会在运行附加到调试器的应用程序时启用。 可以按本节前面所述,运行 MDADisable.reg 文件来禁用这些助手。

环境变量

还可以通过环境变量 COMPLUS_MDA 控制 MDA 激活,此变量可替代注册表项。 COMPLUS_MDA 字符串是一个区分大小写、以分号分隔的 MDA 名称列表或其他特殊控制字符串。 在托管或非托管调试器下启动将默认启用一组 MDA。 这是通过在环境变量或注册表项的值前面,隐式附加默认在调试器下启用的、以分号分隔的 MDA 的列表来实现的。 特殊控制字符串如下所示:

  • 0 - 停用所有 MDA。

  • 1 - 从 ApplicationName.mda.configg 读取 MDA 设置1

  • managedDebugger - 显式激活在调试器下启动托管可执行文件时隐式激活的所有 MDA。

  • unmanagedDebugger - 显式激活在调试器下启动非托管可执行文件时隐式激活的所有 MDA。

如果存在冲突的设置,则最新的设置将重写先前的设置:

  • COMPLUS_MDA=0 禁用所有 MDA,包括那些在调试器下隐式启用的 MDA。

  • 除了调试器下隐式启用的所有 MDA,COMPLUS_MDA=gcUnmanagedToManaged 还启用 gcUnmanagedToManaged

  • COMPLUS_MDA=0;gcUnmanagedToManaged 启用 gcUnmanagedToManaged,但是禁用将在调试器下隐式启用的 MDA。

特定于应用程序的配置设置

可以在应用程序的 MDA 配置文件中单独地启用、禁用和配置某些助手。 若要使用用于配置 MDA 的应用程序配置文件,必须设置 MDA 注册表项或 COMPLUS_MDA 环境变量。 应用程序配置文件通常与应用程序的可执行文件 (.exe) 位于同一目录中。 文件名采用的格式为 ApplicationName.mda.config;例如,notepad.exe.mda.config。在应用程序配置文件中启用的助手可能具有设计用于控制该助手行为的属性或元素。

下面的示例演示如何启用和配置 marshaling

<mdaConfig>
  <assistants>
    <marshaling>
      <methodFilter>
        <match name="*"/>
      </methodFilter>
      <fieldFilter>
        <match name="*"/>
      </fieldFilter>
    </marshaling>
  </assistants>
</mdaConfig>

对于应用程序中每个托管到非托管的转换,Marshaling MDA 会发出有关正封送到非托管类型的托管类型信息。 Marshaling MDA 还能够分别筛选 methodFilter 和 fieldFilter 子元素中提供的方法和结构字段的名称。

下面的示例演示如何使用 MDA 的默认设置来启用多个 MDA:

<mdaConfig>
  <assistants>
    <illegalPrepareConstrainedRegion />
    <invalidCERCall />
    <openGenericCERCall />
    <virtualCERCall />
  </assistants>
</mdaConfig>

重要

若在配置文件中指定了多个助手,则必须按字母顺序列出这些助手。 例如,若要同时启用 virtualCERCallinvalidCERCall MDA,则必须先添加 <invalidCERCall /> 项,再添加 <virtualCERCall /> 项。 如果这些项未按字母顺序排列,将显示未处理的无效配置文件异常消息。

MDA 异常

启用 MDA 后,即使不在调试程序下执行代码,MDA 也会处于活动状态。 如果在调试器不存在时引发 MDA 事件,尽管这不是未经处理的异常,事件消息也会出现在未经处理的异常对话框中。 若要避免出现该对话框,请在代码未在调试环境中执行时,移除 MDA 启用设置。

在 Visual Studio 集成开发环境 (IDE) 中执行代码时,可以避免针对特定 MDA 事件出现的异常对话框。 为此,请在“调试”菜单上选择“Windows”“异常设置”。 在“异常设置”窗口中,展开“托管调试助手”列表,然后清除单个 MDA 的“引发时中断”复选框。 也可以使用此对话框启用 MDA 异常对话框的显示。

MDA 输出

MDA 输出类似于下面的示例,此示例显示了来自 PInvokeStackImbalance MDA 的输出:

对 PInvoke 函数“MDATest!MDATest.Program::StdCall”的调用使堆栈失衡。 这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。 检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

请参阅