[C#]有趣的VS扩展DebuggerVisualizer

公司的研发平台底层封装了一种类似于DataTable的数据结构,具有行列特性但是直接基于键值对,每次调试时想要查看其中的内容都非常困难。不由得想起第一次使用VS调试Dataset时候弹出的哪个框,鉴于此,搜索了大量资料,在走过一些弯路后终于尝试成功,特记录于此,以备后效。

1.几个主要的概念

  1.DebuggerVisualizer:MSDN的说明中描述了一些基础的步骤,这个类位于System.Diagnostics命名空间下,用来制定类型的可视化工具属性。另外,在Microsoft.VisualStudio.DebuggerVisualizer.dll程序集中,封装了诸如调试时弹出窗口的一些接口和默认实现。

     2.DialogDebuggerVisualizer,Microsoft.VisualStudio.DebuggerVisualizer.dll程序集中用来弹出调试时窗口的基类。

   3.VisualizerObjectSource,负责将当前要可视化的对象序列化或者反序列化,以便在组件间进行传输。这里的序列化说明后续的对象最好事先了ISerializable接口或者标记为Serializable。如果不是,默认的对象是报无法序列化错误,后面我们介绍如何处理。

    4.VisualizerDevelopmentHost可视化工具开发宿主,并调用宿主以显示可视化工具。后续会用来测试。

   5.微软提供了官方的DebuggerVisualizer模板类,可以下载,不过目前我还没找到。

    DebuggerVisualizer对于开发人员具有比较重要的意义,能够将一些不便于调试、查看和编辑的对象可视化,方便查看和编辑数据。

2.主要实现

   相关的代码非常简单,CodeProject上有人用10行代码实现了一个图片调试工具,我们就以这个代码举例

[assembly: System.Diagnostics.DebuggerVisualizer(typeof(ImageVisualizer.DebuggerSide),typeof(VisualizerObjectSource),

    Target = typeof(System.Drawing.Image),Description = "Image Visualizer")]

namespace ImageVisualizer

{

    public class DebuggerSide : DialogDebuggerVisualizer

    {

        override protected void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)

        {

            Image image = (Image)objectProvider.GetObject();

            

            Form form = new Form();

            form.Text = string.Format("Width: {0}, Height: {1}", image.Width, image.Height);

            form.ClientSize = new Size(image.Width, image.Height);

            form.FormBorderStyle = FormBorderStyle.FixedToolWindow;

            

            PictureBox pictureBox = new PictureBox();

            pictureBox.Image = image;

            pictureBox.Parent = form;

            pictureBox.Dock = DockStyle.Fill;



            windowService.ShowDialog(form);



            // 如果在弹出窗口修改对象的数据,可以将objectProvider对象传给Form

            // 然后将修改后的数据传输回去

            // 需要注意,必须判断当前调试状态是否允许替换原本的调试初始值

            //TextBox text = win.FindName("txtValue") as TextBox;

            //Int32 newValue = Convert.ToInt32(text.Text);

            //if (objProvider.IsObjectReplaceable)

            //{

            //    objProvider.ReplaceObject(newValue);

            //}

        }

    }

  命名空间上的声明可以让VS识别该程序集,后续DebuggerSide 类重写了Show方法,通过IDialogVisualizerService的实例创建一个弹出窗口,objectProvider则将对象从编辑器中传输到处理函数以便进行后续处理。上文处理的对象是Image,如果该对象是不可序列化的对象,那么我们需要利用System.Diagnostics.DebuggerVisualizer的第二个参数。

  调试器端和调试对象端使用 VisualizerObjectSource 和 IVisualizerObjectProvider 相互通信。所以,对于未标记为序列化的对象,我们需要重写该类的部分方法,将对象以可序列化的方式进行传输。简而言之,对对象进行封装处理。

    public class ImageSource : VisualizerObjectSource

    {

        /// <summary>

        /// 用来将输入的数据进行处理和封装,转换为流以便在组件间通讯

        /// </summary>

        /// <param name="target">当前查看的对象</param>

        /// <param name="outgoingData">输出流</param>

        public override void GetData(object target, System.IO.Stream outgoingData)

        {

            // 获取调试对象

            Image image = target as Image;

            // 将不可序列化的对象转换为可序列化对象

            // Image本身是可序列化的,此处举例说明

            SerializableImage newImage = image as SerializableImage;

            //  传输对象

            base.GetData(newImage, outgoingData);

        }

    }



    public class SerializableImage : Image, ISerializable

    {

        // TODO: 序列化对象

    }

   这个WPF的教程更加仔细的介绍了一些步骤,包括如果更改调试变量的数据等。

  测试代码如下,任意创建控制台或者Winform等程序,启动即可调试:

        public static void TestVisualizer(object source)

        {

            // 直接使用当前自定义的调试器和数据处理对象类型,即可初始化进行测试

            VisualizerDevelopmentHost visualizeHost = new VisualizerDevelopmentHost(source,

                 typeof(DebuggerSide), typeof(ImageSource));

            visualizeHost.ShowVisualizer();

        }

    3.部署

  部署时也比较简单,直接将Dll复制到对应VS的安装目录下的\Common7\Packages\Debugger\Visualizers目录即可。你可以将下述命令修改保存为批处理,在程序集目录执行即可。

copy DictionaryVisualizer2010.dll "C:\Program Files\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\Visualizers" /y



copy ListVisualizer2010.dll "C:\Program Files\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\Visualizers" /y



copy VisualizerLib.dll "C:\Program Files\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\Visualizers" /y

    4.说明

  作为一种比较重型的扩展,有些情况我们可能需要对一些List或者自定义对象,在调试时无需弹出窗口,而希望直接鼠标置于其上时显示如数量或者关键字等信息,那么可以直接使用DebuggerDisplay和DebuggerTypeProxy 进行处理。CodeProject也有文章介绍了这方面的东西。下面是一些复制来的代码:  

    using System;

    using System.Collections;

    using System.Data;

    using System.Diagnostics;

    using System.Reflection;



    class DebugViewTest

    {

        // The following constant will appear in the debug window for DebugViewTest. 

        const string TabString = "    ";

        // The following DebuggerBrowsableAttribute prevents the property following it 

        // from appearing in the debug window for the class.

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]

        public static string y = "Test String";



        static void Main()

        {

            Hashtable hash = new Hashtable();

            MyHashtable myHashTable = new MyHashtable();

            DataSet ds = new DataSet();

            DataTable dt = new DataTable();

            ds.Tables.Add(dt);

            myHashTable.Add("one", 1);

            myHashTable.Add("two", 2);



            DBTable table = new DBTable();

            //table.Columns.Add("TEST1");

            //table.Columns.Add("TEST2");

            //table.Columns.Add("TEST3");

            //table.Rows.Add("1", "2", "3");

            //table.Rows.Add("1", "2", "3");

            //table.AcceptChanges();



            Console.WriteLine(myHashTable.ToString());

            Console.WriteLine("In Main.");

        }

    }

    [DebuggerDisplay("{value}", Name = "{key}")]

    internal class KeyValuePairs

    {

        private IDictionary dictionary;

        private object key;

        private object value;



        public KeyValuePairs(IDictionary dictionary, object key, object value)

        {

            this.value = value;

            this.key = key;

            this.dictionary = dictionary;

        }

    }

    [DebuggerDisplay("Count = {Count}")]

    [DebuggerTypeProxy(typeof(HashtableDebugView))]

    class MyHashtable : Hashtable

    {

        private const string TestString = "This should not appear in the debug window.";



        internal class HashtableDebugView

        {

            private Hashtable hashtable;

            public const string TestString = "This should appear in the debug window.";

            public HashtableDebugView(Hashtable hashtable)

            {

                this.hashtable = hashtable;

            }



            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]

            public KeyValuePairs[] Keys

            {

                get

                {

                    KeyValuePairs[] keys = new KeyValuePairs[hashtable.Count];



                    int i = 0;

                    foreach (object key in hashtable.Keys)

                    {

                        keys[i] = new KeyValuePairs(hashtable, key, hashtable[key]);

                        i++;

                    }

                    return keys;

                }

            }

        }

    }

   

 

你可能感兴趣的:(debugger)