Visualizadores de datos personalizados para el depurador de Visual Studio (.NET)

Un visualizador es un elemento de la interfaz de usuario del depurador de Visual Studio que muestra una variable o un objeto de la manera adecuada para su tipo de datos. Por ejemplo, un visualizador de mapa de bits interpreta una estructura de mapa de bits y muestra el gráfico que representa. Algunos visualizadores permiten modificar y ver los datos. En el depurador, un visualizador se representa mediante un icono de lupa VisualizerIcon. Puede seleccionar el icono de una información sobre datos, la ventana Inspección o el cuadro de diálogo Inspección rápida del depurador y luego seleccionar el visualizador adecuado para el objeto correspondiente.

Además de los visualizadores estándar incorporados, puede haber más visualizadores disponibles para su descarga por parte de Microsoft, terceros y la comunidad. También puede escribir sus propios visualizadores e instalarlos en el depurador de Visual Studio.

En este artículo se proporciona información general de alto nivel sobre la creación del visualizador. Para obtener instrucciones detalladas, consulte los artículos siguientes en su lugar:

Nota:

Los visualizadores personalizados no son compatibles con aplicaciones de la Plataforma universal de Windows (UWP) y Windows 8.x.

Información general

Puede escribir un visualizador personalizado para un objeto de cualquier clase administrada, excepto Object o Array.

La arquitectura de un visualizador del depurador tiene dos partes:

  • El lado depurador se ejecuta en el depurador de Visual Studio y crea y muestra la interfaz de usuario del visualizador.

    Dado que Visual Studio se ejecuta en el entorno de ejecución de .NET Framework, este componente debe escribirse para .NET Framework. Por este motivo, no es posible escribirlo para .NET Core.

  • El lado depurado se ejecuta dentro del proceso que Visual Studio depura (el depurado). El objeto de datos que se va a visualizar (por ejemplo, un objeto String) existe en el proceso depurado. El lado depurado envía el objeto al lado depurador, que lo muestra en la interfaz de usuario que se crea.

    El entorno de ejecución para el que se compila este componente debe coincidir con aquel en el que se ejecutará el proceso de depurado, es decir, .NET Framework o .NET Core.

El lado depurador recibe este objeto de datos que se visualizará desde un proveedor de objetos que implementa la interfaz IVisualizerObjectProvider. El lado depurado envía el objeto a través del origen de objeto, que se deriva de VisualizerObjectSource.

El proveedor de objetos también puede devolver los datos al origen de objeto, lo que permite escribir un visualizador que puede editar datos. Reemplace el proveedor de objetos para comunicarse con el evaluador de expresiones y el origen de objeto.

El lado depurado y el lado depurador se comunican entre sí a través de métodos Stream que serializan un objeto de datos en Stream y deserializan de nuevo Stream en un objeto de datos.

Solo se puede escribir un visualizador para un tipo genérico si el tipo es abierto. Esta restricción es igual que la restricción de uso del atributo DebuggerTypeProxy. Para más información, vea Uso del atributo DebuggerTypeProxy.

Los visualizadores personalizados pueden tener consideraciones de seguridad. Vea Consideraciones de seguridad del visualizador.

Creación de la interfaz de usuario del lado depurador

Para crear la interfaz de usuario del visualizador en el lado depurador, se crea una clase que herede de DialogDebuggerVisualizer y se reemplaza el método Microsoft.VisualStudio.DebuggerVisualizers.DialogDebuggerVisualizer.Show para mostrar la interfaz. Puede utilizar IDialogVisualizerService para mostrar formularios, cuadros de diálogo y controles de Windows Forms en el visualizador.

  1. Utilice los métodos IVisualizerObjectProvider para obtener el objeto visualizado en el lado depurador.

  2. Cree una clase que herede de DialogDebuggerVisualizer.

  3. Reemplace el método Microsoft.VisualStudio.DebuggerVisualizers.DialogDebuggerVisualizer.Show para mostrar su interfaz. Utilice métodos IDialogVisualizerService para mostrar formularios, cuadros de diálogo y controles de Windows Forms en la interfaz.

  4. Aplique DebuggerVisualizerAttribute asignándole el visualizador que se va a mostrar (DialogDebuggerVisualizer).

Consideraciones especiales del lado depurador para .NET 5.0+

Los visualizadores personalizados transfieren datos entre los lados depurado y depurador a través de la serialización binaria mediante la clase BinaryFormatter de forma predeterminada. Sin embargo, ese tipo de serialización se está reduciendo en .NET 5 y versiones superiores debido a los problemas de seguridad relativos a sus vulnerabilidades no corregibles. Además, se ha marcado como completamente obsoleto en ASP.NET Core 5 y su uso se regirá por la documentación de ASP.NET Core. En esta sección se describen los pasos que debe seguir para asegurarse de que el visualizador todavía se admite en este escenario.

  • Por motivos de compatibilidad, el método Show que se invalidó en la sección anterior sigue aceptando un objeto IVisualizerObjectProvider. Sin embargo, a partir de la versión 16.10 de Visual Studio 2019, es realmente de tipo IVisualizerObjectProvider2. Por este motivo, convierta el objeto objectProvider a la interfaz actualizada.

  • Al enviar objetos, como comandos o datos, al lado depurado, use el método IVisualizerObjectProvider2.Serialize para pasarlo a una secuencia, pues determinará el mejor formato de serialización para usar en función del entorno de ejecución del proceso depurado. A continuación, pase la secuencia al método IVisualizerObjectProvider2.TransferData.

  • Si el componente del visualizador del lado depurado necesita devolver algo al lado depurador, se ubicará en el objeto Stream devuelto por el método TransferData. Use el método IVisualizerObjectProvider2.GetDeserializableObjectFrom para obtener de él una instancia de IDeserializableObject y procesarla según sea necesario.

Consulte la sección Consideraciones especiales del lado depurado para .NET 5.0+ para saber qué otros cambios son necesarios en el lado depurado cuando no se admite el uso de la serialización binaria.

Nota

Si desea obtener más información sobre el problema, consulte la guía de seguridad de BinaryFormatter.

Crear el origen del objeto de visualizador del lado depurado

En el código del depurador, edite el objeto DebuggerVisualizerAttribute, asignándole el tipo que se va a visualizar (el origen del objeto del lado depurado) (VisualizerObjectSource). La propiedad Target establece el origen del objeto. Si omite el origen de objeto, se utilizará un origen de objeto predeterminado.

El código del lado depurado contiene el origen del objeto que se visualiza. El objeto de datos puede invalidar los métodos de VisualizerObjectSource. Un archivo DLL del lado depurado es necesario si desea crear un visualizador independiente.

En el código del lado depurado:

  • Para permitir al visualizador editar objetos de datos, el origen de objetos debe heredar de VisualizerObjectSource e invalidar los métodos TransferData o CreateReplacementObject.

  • Si necesita admitir la compatibilidad con múltiples versiones (multi-targeting) en el visualizador, puede usar los siguientes monikers de la plataforma de destino (TFM) en el archivo del proyecto del lado depurado.

    <TargetFrameworks>net20;netstandard2.0;netcoreapp2.0</TargetFrameworks>
    

    Estos son los únicos TFM admitidos.

Consideraciones especiales del lado depurado para .NET 5.0+

Importante

Es posible que se necesiten pasos adicionales para que un visualizador funcione a partir de .NET 5.0 debido a problemas de seguridad relacionados con el método de serialización binaria subyacente que se usa de forma predeterminada. Lea esta sección antes de continuar.

  • Si el visualizador implementa el método TransferData, use el método GetDeserializableObject recién agregado que está disponible en la versión más reciente de VisualizerObjectSource. El valor IDeserializableObject que devuelve ayuda a determinar el formato de serialización del objeto (binaria o JSON) y a deserializar el objeto subyacente para que se pueda usar.

  • Si el lado depurado devuelve datos al lado depurador como parte de la llamada TransferData, serialice la respuesta a la secuencia del lado depurador a través del método Serialize.