适用于 C++/WinRT 的 Visual Studio 本机调试可视化 (natvis)

C++/WinRT Visual Studio 扩展 (VSIX) 提供 C++/WinRT 投影类型的 Visual Studio 本机调试可视化 (natvis)。 这提供了类似于 C# 调试的体验。

注意

有关 C++/WinRT Visual Studio 扩展 (VSIX) 的详细信息,请参阅 C++/WinRT 和 VSIX 的 Visual Studio 支持

启用 natvis

Natvis 对于调试版本是自动启用的,因为在定义 _DEBUG 符号时会定义 WINRT_NATVIS 。

对于发布版本,下面介绍了如何选择它。

  • 使用定义的符号 WINRT_NATVIS 编译代码。 这样会导出 WINRT_abi_val 函数,该函数为调试可视化工具提供在目标进程计算属性值的入口点。
  • 生成完整的 PDB。 这是因为调试可视化工具使用 Visual Studio C++ 表达式计算器,而后者又需要所显示的属性类型的符号定义。
  • 可视化的类型必须报告在可发现的元数据中定义的运行时类或接口。 它通过其 IInspectable::GetRuntimeClassName 的实现执行此操作。

综上所述,调试可视化工具最适合可在 C:\Windows\System32\WinMetadata 文件中找到元数据的 Windows 系统类型。 但它也支持用户定义的类型和远程调试,前提是你可正确地定位 .winmd 文件。

使用自定义元数据

调试可视化工具会查找用户定义的元数据(.winmd文件)以及进程 .exe。 它使用与 RoGetMetaDataFile 的算法相似的算法,探测完全限定的类型名称的后续子字符串。 例如,如果可视化的类型为 Contoso.Controls.Widget,则可视化工具会依次查找:

  • Contoso.Controls.Widget.winmd
  • Contoso.Controls.winmd
  • Contoso.winmd

使用自定义元数据进行远程调试

进行远程调试时,进程 .exe 不在本地执行,因此自定义元数据(在上一部分中提及)搜索失败。 在这种情况下,可视化工具会回退到本地缓存文件夹 (%TEMP%),以查找合适的 .winmd 文件。 如果找到,它会记录该文件的大小和日期,然后在远程调试目标中搜索同一 .winmd 以及该二进制文件。 如有必要,将下载远程文件,更新本地缓存。 此策略确保本地缓存的 .winmd 始终处于最新状态,并提供了一种方法,可以在无法远程找到 . 时winmd 手动缓存它(例如,如果 F5 部署未将其放在此处)。

有关缓存行为的示例,请参阅下面的故障排除部分。

疑难解答

调试可视化工具使用 Visual Studio C++ 表达式计算器,调用导出的 WINRT_abi_val 函数以获取属性值。 可视化工具通常可以捕获未处理的异常,并适当降级,同时在 Visual Studio 的“监视”窗口中显示“<Object uninitialized or information unavailable>”。

当可视化工具尝试计算其生存期范围外(如在构造之前)的局部变量时,这会非常有用。 在某些上下文(如单元测试)中,会安装未处理的异常筛选器。 这可能导致在 C++ 表达式计算器出现故障时进程终止。 为了防止出现故障,可视化工具在 WINRT_abi_val 中多次调用 VirtualQuery

诊断

如果属性未正确显示,请在 Visual Studio 打开详细的 natvis 诊断(“工具”>“选项”>“调试”>“输出窗口”>“Natvis 诊断消息”),然后查看“输出”窗口中的 natvis 错误 。

以下摘录显示了探测 .winmd 文件的数次尝试,再从远程目标将其下载到本地缓存文件夹,然后加载该 .winmd 文件。

Natvis C++/WinRT: Looking for C:\Users\...\AppData\Local\DevelopmentFiles\ffcddd4f-cfc0-44cb-b736-0b2d026def77VS.Debug_x64....\Consoso.Controls.Widget.winmd
Natvis C++/WinRT: Looking for C:\Users\...\AppData\Local\DevelopmentFiles\ffcddd4f-cfc0-44cb-b736-0b2d026def77VS.Debug_x64....\Consoso.Controls.winmd
Natvis C++/WinRT: Downloading C:\Users\...\AppData\Local\DevelopmentFiles\ffcddd4f-cfc0-44cb-b736-0b2d026def77VS.Debug_x64....\Consoso.Controls.winmd
Natvis C++/WinRT: Loaded C:\Users\...\AppData\Local\Temp\Consoso.Controls.winmd

如果可视化工具未找到 .winmd 文件,则会生成此错误:

Natvis C++/WinRT: Could not find metadata for Consoso.Controls.Widget

还有许多其他生成诊断的错误情况。

如果元数据可用,输出诊断将显示如下所示的许多调用:

Natvis C++/WinRT: WINRT_abi_val(*(::IUnknown**)0x32dd4ffc18, L"{96369F54-8EB6-48F0-ABCE-C1B211E627C3}", 0).s,sh
Natvis C++/WinRT: WINRT_abi_val(*(::IUnknown**)0x32dd4ffc18, L"{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}", -2).s,sh

第一个是对 IStringable.ToString 的调用,以获取复杂类型的字符串表示形式(未扩展的显示值)。

第二个是对 IInspectable::GetRuntimeClassName 的调用,以反映该类型的属性。

后续的 WINRT_abi_val 调用是对在该类型上发现的每个接口的属性计算。

调用 WINRT_abi_val

可使用 Visual Studio 的即时/命令窗口直接调用 WINRT_abi_val 以排除故障 。

例如,给定一个计划的变量 stringable,你可以计算其 IStringable.ToString

>? WINRT_abi_val((::IUnknown*)&stringable, L"{96369F54-8EB6-48F0-ABCE-C1B211E627C3}", 0).s,sh
L"string"