Visualización de depuración nativa de Visual Studio (natvis) para C++/WinRT

La extensión de Visual Studio C++/WinRT (VSIX) proporciona a Visual Studio visualización de depuración nativa (natvis) de tipos proyectados de C++/WinRT. Esto proporciona una experiencia similar a la depuración de C#.

Nota

Para más información sobre la extensión de C++/WinRT Visual Studio (VSIX), consulte la sección Compatibilidad de Visual Studio con C++/WinRT, XAML, la extensión VSIX y el paquete NuGet.

Habilitación de natvis

Natvis se activa automáticamente para una compilación de depuración porque WINRT_NATVIS se define cuando se define el símbolo _DEBUG.

Aquí se indica cómo participar en ella para una compilación de versión.

  • Compile el código con el símbolo WINRT_NATVIS definido. Al hacerlo, exporta una función WINRT_abi_val, que proporciona el punto de entrada para que el visualizador de depuración evalúe los valores de propiedad en el proceso de destino.
  • Genere un PDB completo. Esto se debe a que el visualizador de depuración usa el evaluador de expresiones de C++ de Visual Studio, que, a su vez, requiere definiciones simbólicas para los tipos de propiedad que se muestran.
  • Un tipo visualizado debe notificar una clase en tiempo de ejecución o una interfaz definida en metadatos reconocibles. Lo hace mediante su implementación de IInspectable::GetRuntimeClassName.

Teniendo en cuenta lo anterior, el visualizador de depuración funciona mejor con los tipos de sistema de Windows para los que se pueden encontrar metadatos en la carpeta C:\Windows\System32\WinMetadata. Sin embargo, también puede admitir tipos definidos por el usuario y depuración remota, siempre que localice correctamente los archivos .winmd.

Uso de metadatos personalizados

El visualizador de depuración busca metadatos definidos por el usuario (archivos .winmd) junto con el proceso .exe. Usa un algoritmo similar al de RoGetMetaDataFile, que sondea si hay subcadenas sucesivas del typename completo. Por ejemplo, si el tipo que se visualiza es Contoso.Controls.Widget, el visualizador busca lo siguiente, en esta secuencia:

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

Depuración remota con metadatos personalizados

Cuando se realiza una depuración remota, el .exe del proceso no es local, por lo que se produce un error en la búsqueda de metadatos personalizados (que se ha mencionado en la sección anterior). En ese caso, el visualizador reserva en una carpeta de caché local (%TEMP%) un archivo .winmd adecuado. Si encuentra alguno, registra su tamaño y fecha y, después, busca en el destino de depuración remota junto el mismo .winmd, junto con el binario. Si es necesario, el archivo remoto se descarga, lo que actualiza la memoria caché local. Esta estrategia garantiza que el .winmd de la caché local esté siempre actualizado, así como proporcionar un medio para almacenar manualmente en caché un archivo .winmd si no se puede encontrar de forma remota (por ejemplo, si la implementación de F5 no la ha puesto allí).

Para ver un ejemplo del comportamiento de la memoria caché, consulte la sección Solución de problemas, que encontrará a continuación.

Solución de problemas

El visualizador de depuración usa el evaluador de expresiones de C++ de Visual Studio para invocar la función WINRT_abi_val exportada para obtener valores de propiedad. Normalmente, el visualizador puede detectar excepciones no controladas y degradarse poco a poco, mostrando el mensaje "<Objeto sin inicializar o información no disponible>" en las ventanas Ver de Visual Studio.

Esto resulta útil cuando el visualizador intenta evaluar una variable local fuera de su ámbito de duración (por ejemplo, antes de la construcción). En algunos contextos, como las pruebas unitarias, se instala un filtro de excepción no controlado. Esto puede hacer que el proceso termine cuando el evaluador de expresiones de C++ tiene errores. Para evitar errores, el visualizador realiza varias llamadas a VirtualQuery en WINRT_abi_val.

Diagnóstico

Si una propiedad no se muestra correctamente, active el diagnóstico detallado de natvis en Visual Studio (Herramientas>Opciones>Depuración>Ventana Resultados>Mensajes de diagnóstico natvis) y, después, observe en la ventana Resultados los errores de natvis.

En el siguiente extracto se muestran varios intentos de sondear un archivo .winmd, seguidos de una descarga desde el destino remoto a la carpeta de la caché local y, después, una carga de ese mismo archivo.

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

Si el visualizador no encuentra un archivo .winmd, se genera este error:

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

Hay otros escenarios de error que generan diagnósticos.

Si hay metadatos disponibles, los diagnósticos de salida mostrarán muchas llamadas como esta:

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

La primera es una llamada a IStringable.ToString para obtener la representación de cadena de un tipo complejo (el valor que se muestra sin expandir).

La segunda es una llamada a IInspectable::GetRuntimeClassName que se refleje en las propiedades del tipo.

Las llamadas a WINRT_abi_val posteriores son evaluaciones de propiedades para cada interfaz detectada en el tipo.

Invocación de WINRT_abi_val

Puede usar las ventanas Inmediato/Comando de Visual Studio para invocar directamente a WINRT_abi_val para la solución de problemas.

Por ejemplo, dada una variable proyectada que se puede encadenar, puede evaluar IStringable.ToString como:

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