• [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;
                    }
                }
            }
        }

       

  • 相关阅读:
    bootstrap-datetimepicker 十年视图、年月视图 附源码
    java面向对象程序设计的五个特性
    简述rtsp,rtmp,http三个协议
    iOS 实现毛玻璃效果
    一个裁剪图片的小工具类,通过一句代码调用
    iOS 中的正则匹配(工具类方法)
    博客园不支持Markdown语法,新博客将发在简书...
    一行代码,让你的应用中UIScrollView的滑动与侧滑返回并存
    仿照微信的效果,实现了一个支持多选、选原图和视频的图片选择器,适配了iOS6-10系统,3行代码即可集成.
    分享一下我封装iOS自定义控件的体会,附上三个好用的控件Demo <时间选择器&多行输入框&日期选择器>
  • 原文地址:https://www.cnblogs.com/tukzer/p/3069128.html
Copyright © 2020-2023  润新知