使用可视化工具捕获对象状态





5.00/5 (8投票s)
捕获对象状态的可视化工具。
引言
调试器可视化工具提供了一个调试时间接口,以有意义的方式呈现变量或对象。可视化工具在 Visual Studio 调试器中显示为数据提示上的放大镜图标、调试器变量窗口或快速监视对话框中。单击放大镜会列出变量可用的调试器可视化工具集。
本文演示了如何使用 Visual Studio 调试器可视化工具类在调试会话期间捕获 .NET 对象的状态。主要目标是持久化数据协定对象。我还尝试提供一个基本实现,用于将任何 .NET 对象保存/加载到以下格式之一:XML、二进制或 SOAP(二进制和 SOAP 格式正在开发中)。虽然这些对于简单对象可能足够了,但我们可能需要根据具体情况修改基本实现,以解决更复杂的场景。一旦状态被保存到文件中,就可以在稍后用于“填充”(加载)同一类型的对象。
背景
MSDN 文章中清晰地解释了可视化工具的架构:http://msdn.microsoft.com/en-us/library/zayyhzts.aspx。
可视化工具包含两个主要组件
- 调试目标端
- 调试器端
调试目标端
- 这是您需要可视化的对象所在的位置。
VisualizerObjectSource
类处理调试目标端的操作。- 如果您想影响目标对象如何传输到/从调试器端,则需要重写
VisualizerObjectSource
类。
调试器端
- 这是您可以在用户界面中显示对象的地方
- 在 Visual Studio 调试器中运行。
DebuggerSideVisualizer
类处理调试器端的操作。- 需要重写
DebuggerSideVisualizer
类中的Show()
方法,以便您可以为可视化工具显示 UI。 - 调试器端可以将更新后的对象发送回调试目标端,然后用于更新正在调试的对象。
使用代码
为了简单起见,我将只解释 DataContract 可视化部分,其余实现可以按相同的方式理解。
调试目标端
调试目标端类的外观如下
如果您要可视化的对象具有 Serializable
属性,则可以依赖可视化工具架构在调试目标端提供的默认实现来进行对象的双向传输。不幸的是,DataContract
对象不是这种情况,因此我们需要重写 VisualizerObjectSource
类中的几个方法。
- 在调试目标端,
DataContractVisualizerObjectSource
类扩展了VisualizerObjectSource
(参见上面的类图),以提供GetData()
和CreateReplacementObject()
方法的自定义实现。 GetData()
重写使用 DataContract 序列化,并将其写入“outgoingData
”流变量,以便调试器端可以接收。CreateReplacementObject()
重写方法使用传入的流数据并将其反序列化回被调试的对象。
下面的代码片段列出了调试目标端类 DataContractVisualizerObjectSource
。
/// <summary>
/// Extends the VisualizerObjectSource class, uses DataContract serialization to transport a
/// DataContract object between Debuggee and Debugger processes.
/// </summary>
public class DataContractVisualizerObjectSource : VisualizerObjectSource
{
public DataContractVisualizerObjectSource()
{
serializer = new DataContractSerialization();
}
/// <summary>
/// The serializer used by this class.
/// </summary>
private SerializationBase serializer;
/// <summary>
/// Gets data from the specified object and serializes it into the outgoing data stream.
/// </summary>
/// <param name="target">Object being visualized.</param>
/// <param name="outgoingData">Outgoing data stream.</param>
public override void GetData(object target, Stream outgoingData)
{
if (target == null)
return;
var writer = new StreamWriter(outgoingData);
writer.WriteLine(target.GetType().AssemblyQualifiedName);
writer.WriteLine(serializer.Serialize(target));
writer.Flush();
}
/// <summary>
/// Reads an incoming data stream from the debugger side and uses
/// the data to construct a replacement object for the target object.
/// This method is called when ReplaceData or ReplaceObject is called on the debugger side.
/// </summary>
/// <param name="target">Object being visualized.</param>
/// <param name="incomingData">Incoming data stream.</param>
/// <returns>An object, with contents constructed from the incoming data stream,
/// that can replace the target object. This method does not actually replace target
/// but rather provides a replacement object for the debugger
/// to do the actual replacement.</returns>
public override object CreateReplacementObject(object target, Stream incomingData)
{
StreamReader streamReader = new StreamReader(incomingData);
string targetObjectType = streamReader.ReadLine();
return (serializer.Deserialize(Type.GetType(targetObjectType), streamReader.ReadToEnd()));
}
}
调试器端
在调试器端,需要重写 DebuggerSideVisualizer
类的 Show()
方法以接收从调试目标端发送的对象并在 UI 中显示。调试器端实现中需要注意的几点是
- UI 组件与抽象类
DebuggerSideVisualizer
一起工作,该类派生自DialogDebuggerVisualizer
。 DebuggerSideVisualizer
类概述了一组属性和方法,每个调试器端可视化工具(即 DataContract、XML、二进制或 SOAP)都需要实现这些属性和方法,以便 UI 可以与它们一起工作,而无需知道它正在处理的确切可视化工具实例。- 属性
IsEditable
:指示目标对象是否可以在可视化工具 UI 中编辑;基于此,UI 将显示或隐藏可编辑界面(在这种情况下是文本框)。TargetObject
:保存从调试目标端接收的对象。FormattedString
:返回表示目标对象的格式化字符串。Serializer
:可视化工具将用于序列化/反序列化目标对象的序列化器。Name
:将在 UI 标题中显示的可视化工具的名称。IsUpdateRequired
:指示是否需要使用调试器端对象更新调试目标端对象。- 方法
SaveToFile
:将目标对象保存到文件LoadFromFile
:加载并替换调试器端的对象版本。UpdateTargetObject
:更新对象的调试器端版本。
下面的代码片段列出了 DebuggerSideDataContractVisualizer
类中 Show()
方法的定义
/// <summary>
/// Displays the user interface for the visualizer.
/// </summary>
/// <param name="windowService">An object of type
/// Microsoft.VisualStudio.DebuggerVisualizers.IDialogVisualizerService,
/// which provides methods that a visualizer can use
/// to display Windows forms, controls, and dialogs.</param>
/// <param name="objectProvider">An object of type
/// Microsoft.VisualStudio.DebuggerVisualizers.IVisualizerObjectProvider.
/// This object provides communication from the debugger side of the visualizer
/// to the object source (Microsoft.VisualStudio.DebuggerVisualizers.VisualizerObjectSource)
/// on the debuggee side.</param>
protected override void Show(IDialogVisualizerService windowService,
IVisualizerObjectProvider objectProvider)
{
try
{
// Get the object to display a visualizer for.
using(StreamReader streamReader = new StreamReader(objectProvider.GetData()))
{
string targetObjectType = streamReader.ReadLine();
_targetObjectType = Type.GetType(targetObjectType);
TargetObject = Serializer.Deserialize(_targetObjectType, streamReader.ReadToEnd());
}
}
catch (System.Exception exception)
{
MessageBox.Show(string.Format(
Properties.Resources.DeserializationOfXmlFailed, exception.Message));
return;
}
//Display the object in a UI.
using (frmVisualizerDialog displayForm = new frmVisualizerDialog(this))
{
windowService.ShowDialog(displayForm);
if (IsUpdateRequired == true)
{
if (objectProvider.IsObjectReplaceable)
{
//If the debuggee side object is replaceable and it needs to be updated then
//replace it with the target object(debugger side) .
using (MemoryStream outgoingData = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(outgoingData))
{
writer.WriteLine(TargetObject.GetType().AssemblyQualifiedName);
writer.WriteLine(Serializer.Serialize(TargetObject));
writer.Flush();
objectProvider.ReplaceData(outgoingData);
}
}
}
}
}
}
调试器端 UI
调试器端 UI 是一个带有文本框和几个按钮的 Windows 窗体。
使用 DataContract 可视化工具
根据您的场景添加可视化工具属性
- 场景 1:如果您可以修改 DataContract 类文件,则将可视化工具属性(如下粗斜体突出显示)复制并粘贴到 DataContract 类中,如下所示。
[DebuggerVisualizer(
@"DebuggerUtility.Visualizers.DataContract.DebuggerSideDataContractVisualizer, "
+ @"DebuggerUtility.Visualizers, "
+ @"Version=1.0.0.0, Culture=neutral, "
+ @"PublicKeyToken=e8c91feafdcfb6e2",
@"DebuggerUtility.Visualizers.DataContract.DataContractVisualizerObjectSource, "
+ @"DebuggerUtility.Visualizers, "
+ @"Version=1.0.0.0, Culture=neutral, "
+ @"PublicKeyToken=e8c91feafdcfb6e2",
Description = "DataContractVisualizer ")]
[DataContract]
public class MyDataContract
{
...
...
}
using DebuggerUtility.Visualizers.DataContract;
using System.Diagnostics;
[assembly: DebuggerVisualizer(typeof(DebuggerSideDataContractVisualizer),
typeof(DataContractVisualizerObjectSource),
TargetTypeName = "DebuggerUtility.Visualizers.Tests.MyDataContract,
DebuggerUtility.Visualizers.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
Description = "DataContract Visualizer")]
namespace DebuggerUtility.Visualizers.DataContract
{
…
…
/// <summary>
/// The debugger side class for DataContract visualizer .
/// </summary>
public class DebuggerSideDataContractVisualizer : DebuggerSideVisualizer
{
…
…
}
}
注意
- 如上所示,需要在命名空间定义正上方添加可视化工具属性。
- 对于
TargetTypeName
,您需要填写 DataContract 对象的程序集限定名。 - 编译代码并生成 DLL 文件 DebuggerUtility.Visualizers.dll。
- 一旦您确定根据您的场景正确添加了属性,请确保“DebuggerUtility.Visualizers.dll”已按下面安装部分中的说明安装。
- 启动 Visual Studio 调试器,并将控件置于目标类的实例化实例之后。
- 将鼠标悬停在该实例上,然后选择“DataContractVisualizer”。
这将弹出下面的可视化工具界面
- 可视化工具界面包含一个文本框,显示目标对象的序列化字符串等效形式。
- 文本框中的序列化字符串是可编辑的。
- 单击“更新”按钮将使用文本框中的内容更新被调试的对象。
- 将 DLL “DebuggerUtility.Visualizers.dll”复制到以下任一位置
- <VS2010 安装路径>\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\Visualizers
- 我的文档\Visual Studio 2010\Visualizers
- 要使用可视化工具进行远程调试,请将 DLL 复制到远程计算机上的相同路径。
- 重新启动 Visual Studio 调试会话。
- DataContract 和 XML 可视化工具不需要对象是可序列化的。
- 二进制和 SOAP 可视化工具将需要对象被标记为“
Serializable
”。 - 您可以使用可视化工具处理除
Object
和Array
以外的任何托管类的对象。 - 要使用可视化工具进行调试,您必须以完全信任模式运行代码。