Visualizer Architecture
The architecture of a debugger visualizer has two parts:
The debugger side runs within the Visual Studio debugger. The debugger-side code creates and displays the user interface for your visualizer.
The debuggee side runs within the process Visual Studio is debugging (the debuggee).
A visualizer is a debugger component that enables the debugger to display (visualize) the contents of a data object in a meaningful, understandable form. Some visualizers support editing of the data object also. By writing custom visualizers, you can extend the debugger to handle your own custom data types.
The data object to be visualized resides within the process you are debugging (the debuggee process). The user interface that will display the data is created within the Visual Studio debugger process:
Debugger Process | Debuggee Process |
---|---|
Debugger user interface (DataTips, Watch Window, QuickWatch) | Data Object to be visualized |
To visualize the data object within the debugger interface, you need code to communicate between the two processes. Consequently, the visualizer architecture consists of two parts: debugger side code and debuggee side code.
The debugger-side code creates its own user interface, which can be invoked from the debugger interface, such as a DataTip, the Watch Window, or QuickWatch. The visualizer interface is created by using the DialogDebuggerVisualizer class and the IDialogVisualizerService interface. Like all Visualizer APIs, DialogDebuggerVisualizer and IDialogVisualizerService are found in the Microsoft.VisualStudio.DebuggerVisualizers namespace.
Debugger Side | Debuggee Side |
---|---|
DialogDebuggerVisualizer Class IDialogVisualizerService Interface |
Data Object |
The user interface gets the data to be visualized from an Object Provider, which exists on the debugger side:
Debugger Side | Debuggee Side |
---|---|
DialogDebuggerVisualizer Class IDialogVisualizerService Interface |
Data Object |
Object Provider (implements IVisualizerObjectProvider) |
There is a corresponding object on the debuggee side called the Object Source:
Debugger Side | Debuggee Side |
---|---|
DialogDebuggerVisualizer Class IDialogVisualizerService Interface |
Data Object |
Object Provider (implements IVisualizerObjectProvider) | Object Source (derived from VisualizerObjectSource) |
The Object Provider provides the object data that is to be visualized to the visualizer UI. The Object Provider gets the object data from the Object Source. The Object Provider and Object Source provide APIs to communicate object data between the debugger side and the debuggee side.
Every visualizer must get the data object to be visualized. The following table shows the corresponding APIs that the Object Provider and Object Source use for this purpose:
Object Provider | Object Source |
---|---|
GetData —or— GetObject |
GetData |
Notice that the object provider can use either GetData or GetObject. Either API results in a call to GetData on the Object Source. A call to Microsoft.VisualStudio.DebuggerVisualizers.VisualizerObjectSource.GetData fills in a System.IO.Stream, which represents a serialized form of the object that is being visualized.
Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.GetObject deserializes the data back into object form, which you can then display in the UI you create with DialogDebuggerVisualizer. Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.GetData fills in the data as a raw Stream
, which you must deserialize yourself. Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.GetObject works by calling Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.GetData to get the serialized Stream
, then deserializing the data. Use Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.GetData when the object is not serializable by .NET and requires custom serialization. In that case, you must also override the Microsoft.VisualStudio.DebuggerVisualizers.VisualizerObjectSource.Serialize method.
If you are creating a read-only visualizer, one-way communication with GetData or GetObject is sufficient. If you are creating a visualizer that supports editing of data objects, you must do more. You must be able to send a data object from the Object Provider back to the Object Source also. The following table shows the Object Provider and Object Source APIs used for this purpose:
Object Provider | Object Source |
---|---|
ReplaceData —or— ReplaceObject |
CreateReplacementObject |
Notice, again, that there are two APIs which the Object Provider can use. Data is always sent from the Object Provider to the Object Source as a Stream
, but ReplaceData requires that you serialize the object into a Stream
yourself.
ReplaceObject takes an object that you provide, serializes it into a Stream
, then calls ReplaceData to send the Stream
to CreateReplacementObject.
Using one of the Replace methods creates a new data object in the debuggee that replaces the object being visualized. If you want to change the contents of the original object without replacing it, use one of the Transfer methods shown in the following table. These APIs transfer data in both directions at the same time, without replacing the object that is being visualized:
Object Provider | Object Source |
---|---|
TransferData —or— TransferObject |
TransferData |